From 8fcd227b331bad66f0778b4f05e70d2e41ac53e6 Mon Sep 17 00:00:00 2001 From: EmmyHeart Date: Fri, 20 Oct 2023 03:24:25 +0000 Subject: [PATCH 1/6] Added functions for checking if Aime card is locked or banned --- core/data/schema/card.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/core/data/schema/card.py b/core/data/schema/card.py index d8f5fc0..a95684e 100644 --- a/core/data/schema/card.py +++ b/core/data/schema/card.py @@ -64,6 +64,27 @@ class CardData(BaseData): return int(card["user"]) + def get_card_banned(self, access_code: str) -> Optional[bool]: + """ + Given a 20 digit access code as a string, check if the card is banned + """ + card = self.get_card_by_access_code(access_code) + if card is None: + return None + if card["is_banned"]: + return True + return False + def get_card_locked(self, access_code: str) -> Optional[bool]: + """ + Given a 20 digit access code as a string, check if the card is locked + """ + card = self.get_card_by_access_code(access_code) + if card is None: + return None + if card["is_locked"]: + return True + return False + def delete_card(self, card_id: int) -> None: sql = aime_card.delete(aime_card.c.id == card_id) From 719ae9cfb1cc8418918d5eed358b48ac8272e8d4 Mon Sep 17 00:00:00 2001 From: EmmyHeart Date: Fri, 20 Oct 2023 03:25:12 +0000 Subject: [PATCH 2/6] Added checks for if Aime card is locked or banned, and pass that status to the game --- core/aimedb.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/core/aimedb.py b/core/aimedb.py index 3ac8b9d..5388600 100644 --- a/core/aimedb.py +++ b/core/aimedb.py @@ -145,7 +145,15 @@ class AimedbProtocol(Protocol): def handle_lookup(self, data: bytes, resp_code: int) -> ADBBaseResponse: req = ADBLookupRequest(data) user_id = self.data.card.get_user_id_from_card(req.access_code) + is_banned = self.data.card.get_card_banned(req.access_code) + is_locked = self.data.card.get_card_locked(req.access_code) + if is_banned and is_locked: + ret.head.status = ADBStatus.BAN_SYS_USER + elif is_banned: + ret.head.status = ADBStatus.BAN_SYS + elif is_locked: + ret.head.status = ADBStatus.LOCK_USER ret = ADBLookupResponse.from_req(req.head, user_id) self.logger.info( @@ -157,8 +165,17 @@ class AimedbProtocol(Protocol): req = ADBLookupRequest(data) user_id = self.data.card.get_user_id_from_card(req.access_code) + is_banned = self.data.card.get_card_banned(req.access_code) + is_locked = self.data.card.get_card_locked(req.access_code) + ret = ADBLookupExResponse.from_req(req.head, user_id) - + if is_banned and is_locked: + ret.head.status = ADBStatus.BAN_SYS_USER + elif is_banned: + ret.head.status = ADBStatus.BAN_SYS + elif is_locked: + ret.head.status = ADBStatus.LOCK_USER + self.logger.info( f"access_code {req.access_code} -> user_id {ret.user_id}" ) @@ -237,7 +254,7 @@ class AimedbProtocol(Protocol): def handle_register(self, data: bytes, resp_code: int) -> bytes: req = ADBLookupRequest(data) user_id = -1 - + if self.config.server.allow_user_registration: user_id = self.data.user.create_user() From 3f8c62044c5631580371cb945d664b113eb8e264 Mon Sep 17 00:00:00 2001 From: EmmyHeart Date: Fri, 20 Oct 2023 03:26:51 +0000 Subject: [PATCH 3/6] Optimized rival music list, added ranking API, and began work on Team Courses (need help) --- titles/chuni/base.py | 70 ++++++++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/titles/chuni/base.py b/titles/chuni/base.py index 775b09b..ffc512d 100644 --- a/titles/chuni/base.py +++ b/titles/chuni/base.py @@ -194,7 +194,8 @@ class ChuniBase: } def handle_get_game_ranking_api_request(self, data: Dict) -> Dict: - return {"type": data["type"], "gameRankingList": []} + rankings = self.data.score.get_rankings(self.version) + return {"type": data["type"], "gameRankingList": rankings} def handle_get_game_sale_api_request(self, data: Dict) -> Dict: return {"type": data["type"], "length": 0, "gameSaleList": []} @@ -401,7 +402,6 @@ class ChuniBase: "userId": data["userId"], "userRivalData": userRivalData } - def handle_get_user_rival_music_api_request(self, data: Dict) -> Dict: rival_id = data["rivalId"] next_index = int(data["nextIndex"]) @@ -415,10 +415,10 @@ class ChuniBase: for music in all_entries[next_index:]: music_id = music["musicId"] level = music["level"] - score = music["score"] - rank = music["rank"] + score = music["scoreMax"] + rank = music["scoreRank"] - # Create a music entry for the current music_id if it's unique + # Create a music entry for the current music_id music_entry = next((entry for entry in user_rival_music_list if entry["musicId"] == music_id), None) if music_entry is None: music_entry = { @@ -428,20 +428,15 @@ class ChuniBase: } user_rival_music_list.append(music_entry) - # Create a level entry for the current level if it's unique or has a higher score - level_entry = next((entry for entry in music_entry["userRivalMusicDetailList"] if entry["level"] == level), None) - if level_entry is None: - level_entry = { - "level": level, - "scoreMax": score, - "scoreRank": rank - } - music_entry["userRivalMusicDetailList"].append(level_entry) - elif score > level_entry["scoreMax"]: - level_entry["scoreMax"] = score - level_entry["scoreRank"] = rank + # Create a level entry for the current level + level_entry = { + "level": level, + "scoreMax": score, + "scoreRank": rank + } + music_entry["userRivalMusicDetailList"].append(level_entry) - # Calculate the length for each "musicId" by counting the unique levels + # Calculate the length for each "musicId" by counting the levels for music_entry in user_rival_music_list: music_entry["length"] = len(music_entry["userRivalMusicDetailList"]) @@ -729,17 +724,42 @@ class ChuniBase: def handle_get_team_course_setting_api_request(self, data: Dict) -> Dict: return { "userId": data["userId"], - "length": 0, - "nextIndex": 0, - "teamCourseSettingList": [], + "length": 1, + "nextIndex": -1, + "teamCourseSettingList": [ + { + "orderId": 1, + "courseId": 1, + "classId": 1, + "ruleId": 1, + "courseName": "Test", + "teamCourseMusicList": [ + {"track": 184, "type": 1, "level": 3, "selectLevel": -1}, + {"track": 184, "type": 1, "level": 3, "selectLevel": -1}, + {"track": 184, "type": 1, "level": 3, "selectLevel": -1} + ], + "teamCourseRankingInfoList": [], + "recodeDate": "2099-12-31 11:59:99.0", + "isPlayed": False + } + ], } def handle_get_team_course_rule_api_request(self, data: Dict) -> Dict: return { "userId": data["userId"], - "length": 0, - "nextIndex": 0, - "teamCourseRuleList": [], + "length": 1, + "nextIndex": -1, + "teamCourseRuleList": [ + { + "recoveryLife": 0, + "clearLife": 100, + "damageMiss": 1, + "damageAttack": 1, + "damageJustice": 1, + "damageJusticeC": 1 + } + ], } def handle_upsert_user_all_api_request(self, data: Dict) -> Dict: @@ -813,7 +833,7 @@ class ChuniBase: playlog["playedUserName1"] = self.read_wtf8(playlog["playedUserName1"]) playlog["playedUserName2"] = self.read_wtf8(playlog["playedUserName2"]) playlog["playedUserName3"] = self.read_wtf8(playlog["playedUserName3"]) - self.data.score.put_playlog(user_id, playlog) + self.data.score.put_playlog(user_id, playlog, self.version) if "userTeamPoint" in upsert: team_points = upsert["userTeamPoint"] From 32903d979e88ca3d70351e560699013c645ca9d0 Mon Sep 17 00:00:00 2001 From: EmmyHeart Date: Fri, 20 Oct 2023 03:29:05 +0000 Subject: [PATCH 4/6] Added code for song rankings using romVersion, and ensuring romVersion is set on playlog upsert --- titles/chuni/schema/score.py | 55 ++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/titles/chuni/schema/score.py b/titles/chuni/schema/score.py index 67cf141..7712e3f 100644 --- a/titles/chuni/schema/score.py +++ b/titles/chuni/schema/score.py @@ -6,7 +6,7 @@ from sqlalchemy.schema import ForeignKey from sqlalchemy.engine import Row from sqlalchemy.sql import func, select from sqlalchemy.dialects.mysql import insert - +from sqlalchemy.sql.expression import exists from core.data.schema import BaseData, metadata course = Table( @@ -189,9 +189,28 @@ class ChuniScoreData(BaseData): return None return result.fetchall() - def put_playlog(self, aime_id: int, playlog_data: Dict) -> Optional[int]: + def put_playlog(self, aime_id: int, playlog_data: Dict, version: int) -> Optional[int]: + # Calculate the ROM version that should be inserted into the DB, based on the version of the ggame being inserted + # We only need from Version 10 (Plost) and back, as newer versions include romVersion in their upsert + # This matters both for gameRankings, as well as a future DB update to keep version data separate + romVer = { + 10: "1.50.0", + 9: "1.45.0", + 8: "1.40.0", + 7: "1.35.0", + 6: "1.30.0", + 5: "1.25.0", + 4: "1.20.0", + 3: "1.15.0", + 2: "1.10.0", + 1: "1.05.0", + 0: "1.00.0" + } + playlog_data["user"] = aime_id playlog_data = self.fix_bools(playlog_data) + if "romVersion" not in playlog_data: + playlog_data["romVersion"] = romVer.get(version, "1.00.0") sql = insert(playlog).values(**playlog_data) conflict = sql.on_duplicate_key_update(**playlog_data) @@ -201,9 +220,39 @@ class ChuniScoreData(BaseData): return None return result.lastrowid + def get_rankings(self, version: int) -> Optional[List[Dict]]: + # Calculates the ROM version that should be fetched for rankings, based on the game version being retrieved + # This prevents tracks that are not accessible in your version from counting towards the 10 results + romVer = { + 13: "2.10%", + 12: "2.05%", + 11: "2.00%", + 10: "1.50%", + 9: "1.45%", + 8: "1.40%", + 7: "1.35%", + 6: "1.30%", + 5: "1.25%", + 4: "1.20%", + 3: "1.15%", + 2: "1.10%", + 1: "1.05%", + 0: "1.00%" + } + sql = select([playlog.c.musicId.label('id'), func.count(playlog.c.musicId).label('point')]).where((playlog.c.level != 4) & (playlog.c.romVersion.like(romVer.get(version, "%")))).group_by(playlog.c.musicId).order_by(func.count(playlog.c.musicId).desc()).limit(10) + result = self.execute(sql) + + if result is None: + return None + + rows = result.fetchall() + return [dict(row) for row in rows] + def get_rival_music(self, rival_id: int) -> Optional[List[Dict]]: - sql = select(playlog).where(playlog.c.user == rival_id) + sql = select(best_score).where(best_score.c.user == rival_id) + result = self.execute(sql) if result is None: return None return result.fetchall() + From 21cb37001b52a77e66c7d2d74be9d7f92bad8bfe Mon Sep 17 00:00:00 2001 From: EmmyHeart Date: Fri, 20 Oct 2023 03:31:36 +0000 Subject: [PATCH 5/6] Stubbed Team Course functions as they do not currently do anything --- titles/chuni/base.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/titles/chuni/base.py b/titles/chuni/base.py index ffc512d..ce9ef3c 100644 --- a/titles/chuni/base.py +++ b/titles/chuni/base.py @@ -722,6 +722,14 @@ class ChuniBase: }, } def handle_get_team_course_setting_api_request(self, data: Dict) -> Dict: + return { + "userId": data["userId"], + "length": 0, + "nextIndex": -1, + "teamCourseSettingList": [], + } + + def handle_get_team_course_setting_api_request_proto(self, data: Dict) -> Dict: return { "userId": data["userId"], "length": 1, @@ -746,6 +754,14 @@ class ChuniBase: } def handle_get_team_course_rule_api_request(self, data: Dict) -> Dict: + return { + "userId": data["userId"], + "length": 0, + "nextIndex": -1, + "teamCourseRuleList": [] + } + + def handle_get_team_course_rule_api_request_proto(self, data: Dict) -> Dict: return { "userId": data["userId"], "length": 1, From 990d60cf27c3f89348caffc738bb9dfc87a92d72 Mon Sep 17 00:00:00 2001 From: EmmyHeart Date: Fri, 20 Oct 2023 04:25:08 +0000 Subject: [PATCH 6/6] Forgot to remove rank scale option from the example config, as it does literally nothing now lol --- example_config/chuni.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/example_config/chuni.yaml b/example_config/chuni.yaml index 09e3569..578bcb3 100644 --- a/example_config/chuni.yaml +++ b/example_config/chuni.yaml @@ -5,7 +5,6 @@ server: team: name: ARTEMiS # If this is set, all players that are not on a team will use this one by default. - rank_scale: True # Scales the in-game ranking based on the number of teams within the database, rather than the default scale of ~100 that the game normally uses. mods: use_login_bonus: True