From 4da886a083c9e9a7ab384eb966d9ebb1c6cfcd6b Mon Sep 17 00:00:00 2001 From: phantomlan Date: Sun, 5 Nov 2023 18:09:58 +0100 Subject: [PATCH 01/10] - Add Ranking Event Support - Add Technical Challenge Event Support - Fix Event Enumeration for EVT_TYPES as Enum starts with 1, to be in spec with what game expects, also add missing Max EVT_TYPE - Add documentation on how to properly configure and run Events for ONGEKI --- docs/game_specific_info.md | 38 +++++++++++++++++- titles/ongeki/base.py | 75 ++++++++++++++++++++++++++--------- titles/ongeki/const.py | 3 +- titles/ongeki/schema/item.py | 76 ++++++++++++++++++++++++++++++++++++ 4 files changed, 171 insertions(+), 21 deletions(-) diff --git a/docs/game_specific_info.md b/docs/game_specific_info.md index 9f39a7e..c338279 100644 --- a/docs/game_specific_info.md +++ b/docs/game_specific_info.md @@ -336,6 +336,42 @@ perform all previous updates as well: python dbutils.py --game SDDT upgrade ``` +### Controlling Events (Ranking Event, Technical Challenge Event, Mission Event) + +Events are controlled by 2 types of enabled events: +- RankingEvent (type 6), TechChallengeEvent (type 17) +- AcceptRankingEvent (type 7), AcceptTechChallengeEvent (type 18) + +Both Ranking and Accept must be enabled for event to function properly +Event will run for the time specified in startDate and endDate +AcceptRankingEvent and AcceptTechChallengeEvent are reward period for events, which specify from what startDate until endDate you can collect the rewards for attending the event, so the reward period must start in the future, e.g. : + +- RankingEvent startDate 2023-12-01 - endDate 2023-12-30 - period in which whole event is running +- AcceptRankingEvent startDate 2023-12-23 - endDate 2023-12-30 - period in which you can collect rewards for the event + +If player misses the AcceptRankingEvent period - ranking will be invalidated and receive lowest reward from the event (typically 500x money) + +Technical Challenge Song List: +Songs that are used for Technical Challenge are not stored anywhere in data files, so you need to fill the database table by yourself, you can gather all songs that should be in Technical Challenges from ONGEKI japanese wikis, or, you can create your own sets: + +Database table : `ongeki_tech_music_list` +``` +id: Id in table, just increment for each entry +eventId: Id of the event in ongeki_static_events, insert the Id of the TechChallengeEvent (type 17) you want the song be assigned to +musicId: Id of the song you want to add, use songId from ongeki_static_music table +level: Difficulty of the song you want to track during the event, from 0(basic) to 3(master) + +``` + +Current implementation of Ranking and Technical Challenge Events are updated on every profile save to the Network, and Ranked on each player login, in official specification, calculation for current rank on the network should be done in the maintenance window + +Mission Event (type 13) is a monthly type of event, which is used when another event doesn't have it's own Ranking or Technical Challenge Event running, only one Mission Event should be running at a time, so enable only the specific Mission you want to run currently on the Network + +If you're often trying fresh cards, registering new profiles etc., you can also consider disabling all Announcement Events (type 1), as it will disable all the banners that pop up on login (they show up only once though, so if you click through them once they won't show again) + + + + ## Card Maker ### SDED @@ -587,4 +623,4 @@ python dbutils.py --game SDEW upgrade - Midorica - Limited Network Support - Dniel97 - Helping with network base -- tungnotpunk - Source \ No newline at end of file +- tungnotpunk - Source diff --git a/titles/ongeki/base.py b/titles/ongeki/base.py index c72c086..ab7c3ec 100644 --- a/titles/ongeki/base.py +++ b/titles/ongeki/base.py @@ -228,7 +228,21 @@ class OngekiBase: return {"length": 0, "gameSaleList": []} def handle_get_game_tech_music_api_request(self, data: Dict) -> Dict: - return {"length": 0, "gameTechMusicList": []} + music_list = self.data.item.get_tech_music() + + prep_music_list = [] + for music in music_list: + tmp = music._asdict() + tmp.pop("id") + prep_music_list.append(tmp) + + if prep_music_list is None: + return {"length": 0, "gameTechMusicList": []} + + return { + "length": len(prep_music_list), + "gameTechMusicList": prep_music_list, + } def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict: return {"returnCode": 1, "apiName": "UpsertClientSettingApi"} @@ -283,7 +297,7 @@ class OngekiBase: "endDate": "2099-12-31 00:00:00.0", } ) - + return { "type": data["type"], "length": len(evt_list), @@ -403,15 +417,24 @@ class OngekiBase: } def handle_get_user_tech_event_ranking_api_request(self, data: Dict) -> Dict: - # user_event_ranking_list = self.data.item.get_tech_event_ranking(data["userId"]) - # if user_event_ranking_list is None: return {} + user_tech_event_ranks = self.data.item.get_tech_event_ranking(data["userId"]) + if user_tech_event_ranks is None: + return { + "userId": data["userId"], + "length": 0, + "userTechEventRankingList": [], + } + # collect the whole table and clear other players, to preserve proper ranking evt_ranking = [] - # for evt in user_event_ranking_list: - # tmp = evt._asdict() - # tmp.pop("id") - # tmp.pop("user") - # evt_ranking.append(tmp) + for evt in user_tech_event_ranks: + tmp = evt._asdict() + if tmp["user"] != data["userId"]: + tmp.clear() + else: + tmp.pop("id") + tmp.pop("user") + evt_ranking.append(tmp) return { "userId": data["userId"], @@ -533,20 +556,26 @@ class OngekiBase: return {"userId": data["userId"], "userData": user_data} def handle_get_user_event_ranking_api_request(self, data: Dict) -> Dict: - # user_event_ranking_list = self.data.item.get_event_ranking(data["userId"]) - # if user_event_ranking_list is None: return {} + user_event_ranking_list = self.data.item.get_ranking_event_ranks(data["userId"]) + if user_event_ranking_list is None: + return {} - evt_ranking = [] - # for evt in user_event_ranking_list: - # tmp = evt._asdict() - # tmp.pop("id") - # tmp.pop("user") - # evt_ranking.append(tmp) + # We collect the whole ranking table, and clear out any not needed data, this way we preserve the proper ranking + # In official spec this should be done server side, in maintenance period + prep_event_ranking = [] + for evt in user_event_ranking_list: + tmp = evt._asdict() + if tmp["user"] != data["userId"]: + tmp.clear() + else: + tmp.pop("id") + tmp.pop("user") + prep_event_ranking.append(tmp) return { "userId": data["userId"], - "length": len(evt_ranking), - "userEventRankingList": evt_ranking, + "length": len(prep_event_ranking), + "userEventRankingList": prep_event_ranking, } def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict: @@ -788,6 +817,7 @@ class OngekiBase: tmp.pop("user") mission_point_list.append(tmp) + return { "userId": data["userId"], "length": len(mission_point_list), @@ -804,6 +834,10 @@ class OngekiBase: tmp = evt_music._asdict() tmp.pop("id") tmp.pop("user") + # pop other stuff event_point doesn't want + tmp.pop("rank") + tmp.pop("type") + tmp.pop("date") event_point_list.append(tmp) return { @@ -987,6 +1021,9 @@ class OngekiBase: for x in upsert["userTechEventList"]: self.data.item.put_tech_event(user_id, x) + # This should be updated once a day in maintenance window, but for time being we will push the update on each upsert + self.data.item.put_tech_event_ranking(user_id, x) + if "userKopList" in upsert: for x in upsert["userKopList"]: self.data.profile.put_kop(user_id, x) diff --git a/titles/ongeki/const.py b/titles/ongeki/const.py index 8d4c4ab..5c72403 100644 --- a/titles/ongeki/const.py +++ b/titles/ongeki/const.py @@ -19,7 +19,6 @@ class OngekiConstants: EVT_TYPES: Enum = Enum( "EVT_TYPES", [ - "None", "Announcement", "Movie", "AddMyList", @@ -39,6 +38,8 @@ class OngekiConstants: "TechChallengeEvent", "AcceptTechChallengeEvent", "SilverJewelEvent", + "Max", + "None", ], ) diff --git a/titles/ongeki/schema/item.py b/titles/ongeki/schema/item.py index dcaf286..4d0dcfd 100644 --- a/titles/ongeki/schema/item.py +++ b/titles/ongeki/schema/item.py @@ -1,3 +1,4 @@ +from datetime import date, datetime, timedelta from typing import Dict, Optional, List from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_ from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON @@ -172,6 +173,9 @@ event_point = Table( Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")), Column("eventId", Integer), Column("point", Integer), + Column("rank", Integer), + Column("type", Integer), + Column("date", String(25)), Column("isRankingRewarded", Boolean), UniqueConstraint("user", "eventId", name="ongeki_user_event_point_uk"), mysql_charset="utf8mb4", @@ -247,6 +251,31 @@ tech_event = Table( mysql_charset="utf8mb4", ) +tech_music = Table( + "ongeki_tech_music_list", + metadata, + Column("id", Integer, primary_key=True, nullable=False), + Column("eventId", Integer), + Column("musicId", Integer), + Column("level", Integer), + UniqueConstraint("musicId", name="ongeki_tech_music_list_uk"), + mysql_charset="utf8mb4", +) + +tech_ranking = Table( + "ongeki_tech_event_ranking", + metadata, + Column("id", Integer, primary_key=True, nullable=False), + Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("date", String(25)), + Column("eventId", Integer), + Column("rank", Integer), + Column("totalPlatinumScore", Integer), + Column("totalTechScore", Integer), + UniqueConstraint("user", "eventId", name="ongeki_tech_event_ranking_uk"), + mysql_charset="utf8mb4", +) + gacha = Table( "ongeki_user_gacha", metadata, @@ -534,7 +563,12 @@ class OngekiItemData(BaseData): return result.fetchall() def put_event_point(self, aime_id: int, event_point_data: Dict) -> Optional[int]: + # We update only the newest (type: 1) entry, in official spec game watches for both latest(type:1) and previous (type:2) entries to give an additional info how many ranks has player moved up or down + # This fully featured is on TODO list, at the moment we just update the tables as data comes and give out rank as request comes event_point_data["user"] = aime_id + event_point_data["type"] = 1 + event_point_time = datetime.now() + event_point_data["date"] = datetime.strftime(event_point_time, "%Y-%m-%d %H:%M") sql = insert(event_point).values(**event_point_data) conflict = sql.on_duplicate_key_update(**event_point_data) @@ -613,6 +647,14 @@ class OngekiItemData(BaseData): return None return result.fetchall() + def get_tech_music(self) -> Optional[List[Dict]]: + sql = select(tech_music) + result = self.execute(sql) + + if result is None: + return None + return result.fetchall() + def put_tech_event(self, aime_id: int, tech_event_data: Dict) -> Optional[int]: tech_event_data["user"] = aime_id @@ -625,6 +667,22 @@ class OngekiItemData(BaseData): return None return result.lastrowid + def put_tech_event_ranking(self, aime_id: int, tech_event_data: Dict) -> Optional[int]: + tech_event_data["user"] = aime_id + tech_event_data.pop("isRankingRewarded") + tech_event_data.pop("isTotalTechNewRecord") + tech_event_data["date"] = tech_event_data.pop("techRecordDate") + tech_event_data["rank"] = 0 + + sql = insert(tech_ranking).values(**tech_event_data) + conflict = sql.on_duplicate_key_update(**tech_event_data) + result = self.execute(conflict) + + if result is None: + self.logger.warning(f"put_tech_event_ranking: Failed to update ranking! aime_id {aime_id}") + return None + return result.lastrowid + def get_tech_event(self, aime_id: int) -> Optional[List[Dict]]: sql = select(tech_event).where(tech_event.c.user == aime_id) result = self.execute(sql) @@ -714,3 +772,21 @@ class OngekiItemData(BaseData): ) return None return result.lastrowid + + + def get_ranking_event_ranks(self, aime_id: int) -> Optional[List[Dict]]: + # Calculates player rank on GameRequest from server, and sends it back, official spec would rank players in maintenance period, on TODO list + sql = select(event_point.c.id, event_point.c.user, event_point.c.eventId, event_point.c.type, func.row_number().over(partition_by=event_point.c.eventId, order_by=event_point.c.point.desc()).label('rank'), event_point.c.date, event_point.c.point) + result = self.execute(sql) + if result is None: + self.logger.error(f"failed to rank aime_id: {aime_id} ranking event positions") + return None + return result.fetchall() + + def get_tech_event_ranking(self, aime_id: int) -> Optional[List[Dict]]: + sql = select(tech_ranking.c.id, tech_ranking.c.user, tech_ranking.c.date, tech_ranking.c.eventId, func.row_number().over(partition_by=tech_ranking.c.eventId, order_by=[tech_ranking.c.totalTechScore.desc(),tech_ranking.c.totalPlatinumScore.desc()]).label('rank'), tech_ranking.c.totalTechScore, tech_ranking.c.totalPlatinumScore) + result = self.execute(sql) + if result is None: + self.logger.warning(f"aime_id: {aime_id} has no tech ranking ranks") + return None + return result.fetchall() From 1897e8002dfc643423efdafb8166e28a47fae42b Mon Sep 17 00:00:00 2001 From: phantomlan Date: Tue, 7 Nov 2023 02:14:15 +0100 Subject: [PATCH 02/10] add RewardList support add PresentList support add reading rewards to read.py add Ranking Music List support --- titles/ongeki/base.py | 50 +++++++++++++++++++-- titles/ongeki/const.py | 28 ++++++++++++ titles/ongeki/read.py | 26 +++++++++++ titles/ongeki/schema/static.py | 81 ++++++++++++++++++++++++++++++++++ 4 files changed, 181 insertions(+), 4 deletions(-) diff --git a/titles/ongeki/base.py b/titles/ongeki/base.py index ab7c3ec..03ae18a 100644 --- a/titles/ongeki/base.py +++ b/titles/ongeki/base.py @@ -157,7 +157,21 @@ class OngekiBase: return {"type": data["type"], "length": 0, "gameIdlistList": []} def handle_get_game_ranking_api_request(self, data: Dict) -> Dict: - return {"length": 0, "gameRankingList": []} + game_ranking_list = self.data.static.get_ranking_list() + + ranking_list = [] + for music in game_ranking_list: + tmp = music._asdict() + ranking_list.append(tmp) + + if ranking_list is None: + return {"length": 0, "gameRankingList": []} + return { + "type": data["type"], + #"length": len(ranking_list), + "gameRankingList": ranking_list, + } + def handle_get_game_point_api_request(self, data: Dict) -> Dict: """ @@ -215,11 +229,39 @@ class OngekiBase: return {"returnCode": 1, "apiName": "ExtendLockTimeApi"} def handle_get_game_reward_api_request(self, data: Dict) -> Dict: - # TODO: reward list - return {"length": 0, "gameRewardList": []} + get_game_rewards = self.data.static.get_reward_list(self.version) + + reward_list = [] + for reward in get_game_rewards: + tmp = reward._asdict() + tmp.pop("id") + tmp.pop("version") + tmp.pop("rewardname") + reward_list.append(tmp) + + if reward_list is None: + return {"length": 0, "gameRewardList": []} + return { + "length": len(reward_list), + "gameRewardList": reward_list, + } def handle_get_game_present_api_request(self, data: Dict) -> Dict: - return {"length": 0, "gamePresentList": []} + get_present = self.data.static.get_present_list(self.version) + + present_list = [] + for present in get_present: + tmp = present._asdict() + tmp.pop("id") + tmp.pop("version") + present_list.append(tmp) + + if present_list is None: + return {"length": 0, "gamePresentList": []} + return { + "length": len(present_list), + "gamePresentList": present_list, + } def handle_get_game_message_api_request(self, data: Dict) -> Dict: return {"length": 0, "gameMessageList": []} diff --git a/titles/ongeki/const.py b/titles/ongeki/const.py index 5c72403..a7658f7 100644 --- a/titles/ongeki/const.py +++ b/titles/ongeki/const.py @@ -43,6 +43,34 @@ class OngekiConstants: ], ) + REWARD_TYPES: Enum = Enum( + "REWARD_TYPES", + [ + "Card", + "NamePlate", + "Trophy", + "LimitBreakItem", + "AlmightyJewel", + "Money", + "Music", + "ProfileVoice", + "Present", + "ChapterJewel", + "GachaTicket", + "KaikaItem", + "ExpUpItem", + "IntimateUpItem", + "BookItem", + "SystemVoice", + "Costume", + "Medal", + "Attachment", + "UnlockItem", + "Max", + "None", + ], + ) + class CM_GACHA_KINDS(Enum): Normal = 0 Pickup = 1 diff --git a/titles/ongeki/read.py b/titles/ongeki/read.py index b64194f..6e94094 100644 --- a/titles/ongeki/read.py +++ b/titles/ongeki/read.py @@ -44,6 +44,7 @@ class OngekiReader(BaseReader): self.read_events(f"{dir}/event") self.read_music(f"{dir}/music") self.read_card(f"{dir}/card") + self.read_reward(f"{dir}/reward") def read_card(self, base_dir: str) -> None: self.logger.info(f"Reading cards from {base_dir}...") @@ -171,3 +172,28 @@ class OngekiReader(BaseReader): self.version, song_id, chart_id, title, artist, genre, level ) self.logger.info(f"Added song {song_id} chart {chart_id}") + + def read_reward(self, base_dir: str) -> None: + self.logger.info(f"Reading rewards from {base_dir}...") + + for root, dirs, files in os.walk(base_dir): + for dir in dirs: + if os.path.exists(f"{root}/{dir}/Reward.xml"): + strdata = "" + + with open(f"{root}/{dir}/Reward.xml", "r", encoding="utf-8") as f: + strdata = f.read() + + troot = ET.fromstring(strdata) + + if root is None: + continue + + name = troot.find("Name") + rewardId = name.find("id").text + rewardname = name.find("str").text + itemKind = OngekiConstants.REWARD_TYPES[troot.find("ItemType").text].value + itemId = troot.find("RewardItem").find("ItemName").find("id").text + + self.data.static.put_reward(self.version, rewardId, rewardname, itemKind, itemId) + self.logger.info(f"Added reward {rewardId}") diff --git a/titles/ongeki/schema/static.py b/titles/ongeki/schema/static.py index 829b6be..730eb4c 100644 --- a/titles/ongeki/schema/static.py +++ b/titles/ongeki/schema/static.py @@ -96,6 +96,45 @@ cards = Table( mysql_charset="utf8mb4", ) +music_ranking = Table( + "ongeki_static_music_ranking_list", + metadata, + Column("id", Integer, primary_key=True, nullable=False), + Column("musicId", Integer, nullable=False), + Column("point", Integer, nullable=False), + Column("userName", String(255)), + UniqueConstraint("musicId", name="ongeki_static_music_ranking_uk"), + mysql_charset="utf8mb4", +) + +rewards = Table( + "ongeki_static_rewards", + metadata, + Column("id", Integer, primary_key=True, nullable=False), + Column("version", Integer, nullable=False), + Column("rewardId", Integer, nullable=False), + Column("rewardname", String(255), nullable=False), + Column("itemKind", Integer, nullable=False), + Column("itemId", Integer, nullable=False), + UniqueConstraint("version","itemKind","rewardId", name="ongeki_static_rewards_uk"), + mysql_charset="utf8mb4", +) + +present = Table( + "ongeki_static_present_list", + metadata, + Column("id", Integer, primary_key=True, nullable=False), + Column("version", Integer, nullable=False), + Column("presentId", Integer, nullable=False), + Column("presentName", String(255), nullable=False), + Column("rewardId", Integer, nullable=False), + Column("stock", Integer, nullable=False), + Column("message", String(255)), + Column("startDate", String(25), nullable=False), + Column("endDate", String(25), nullable=False), + UniqueConstraint("version","presentId", name="ongeki_static_present_list_uk"), + mysql_charset="utf8mb4", +) class OngekiStaticData(BaseData): def put_card(self, version: int, card_id: int, **card_data) -> Optional[int]: @@ -333,3 +372,45 @@ class OngekiStaticData(BaseData): if result is None: return None return result.fetchone() + + def get_ranking_list(self) -> Optional[List[Dict]]: + sql = select(music_ranking.c.musicId.label('id'), music_ranking.c.point, music_ranking.c.userName) + result = self.execute(sql) + if result is None: + return None + return result.fetchall() + + def put_reward(self, version: int, rewardId: int, rewardname: str, itemKind: int, itemId: int) -> Optional[int]: + sql = insert(rewards).values( + version=version, + rewardId=rewardId, + rewardname=rewardname, + itemKind=itemKind, + itemId=itemId, + ) + conflict = sql.on_duplicate_key_update( + rewardname=rewardname, + ) + result = self.execute(conflict) + if result is None: + self.logger.warning(f"Failed to insert reward! reward_id: {rewardId}") + return None + return result.lastrowid + + def get_reward_list(self, version: int) -> Optional[List[Dict]]: + sql = select(rewards).where(rewards.c.version == version) + + result = self.execute(sql) + if result is None: + self.logger.warning(f"Failed to load reward list") + return None + return result.fetchall() + + def get_present_list(self, version: int) -> Optional[List[Dict]]: + sql = select(present).where(present.c.version == version) + + result = self.execute(sql) + if result is None: + self.logger.warning(f"Failed to load present list") + return None + return result.fetchall() From d2e2c14074299ae2a8da1fd66f97d26e57f03d27 Mon Sep 17 00:00:00 2001 From: phantomlan Date: Tue, 7 Nov 2023 04:38:04 +0100 Subject: [PATCH 03/10] update Event Ranking, Tech Challenge, and Music Ranking --- titles/ongeki/base.py | 19 +++++------ titles/ongeki/schema/item.py | 58 +++++++++++++--------------------- titles/ongeki/schema/static.py | 26 +++++++++++++-- 3 files changed, 56 insertions(+), 47 deletions(-) diff --git a/titles/ongeki/base.py b/titles/ongeki/base.py index 03ae18a..ad64c15 100644 --- a/titles/ongeki/base.py +++ b/titles/ongeki/base.py @@ -157,7 +157,7 @@ class OngekiBase: return {"type": data["type"], "length": 0, "gameIdlistList": []} def handle_get_game_ranking_api_request(self, data: Dict) -> Dict: - game_ranking_list = self.data.static.get_ranking_list() + game_ranking_list = self.data.static.get_ranking_list(self.version) ranking_list = [] for music in game_ranking_list: @@ -168,7 +168,6 @@ class OngekiBase: return {"length": 0, "gameRankingList": []} return { "type": data["type"], - #"length": len(ranking_list), "gameRankingList": ranking_list, } @@ -270,12 +269,13 @@ class OngekiBase: return {"length": 0, "gameSaleList": []} def handle_get_game_tech_music_api_request(self, data: Dict) -> Dict: - music_list = self.data.item.get_tech_music() + music_list = self.data.static.get_tech_music(self.version) prep_music_list = [] for music in music_list: tmp = music._asdict() tmp.pop("id") + tmp.pop("version") prep_music_list.append(tmp) if prep_music_list is None: @@ -441,7 +441,7 @@ class OngekiBase: } def handle_get_user_tech_event_api_request(self, data: Dict) -> Dict: - user_tech_event_list = self.data.item.get_tech_event(data["userId"]) + user_tech_event_list = self.data.item.get_tech_event(self.version, data["userId"]) if user_tech_event_list is None: return {} @@ -450,6 +450,7 @@ class OngekiBase: tmp = evt._asdict() tmp.pop("id") tmp.pop("user") + tmp.pop("version") tech_evt.append(tmp) return { @@ -459,7 +460,7 @@ class OngekiBase: } def handle_get_user_tech_event_ranking_api_request(self, data: Dict) -> Dict: - user_tech_event_ranks = self.data.item.get_tech_event_ranking(data["userId"]) + user_tech_event_ranks = self.data.item.get_tech_event_ranking(self.version, data["userId"]) if user_tech_event_ranks is None: return { "userId": data["userId"], @@ -598,7 +599,7 @@ class OngekiBase: return {"userId": data["userId"], "userData": user_data} def handle_get_user_event_ranking_api_request(self, data: Dict) -> Dict: - user_event_ranking_list = self.data.item.get_ranking_event_ranks(data["userId"]) + user_event_ranking_list = self.data.item.get_ranking_event_ranks(self.version, data["userId"]) if user_event_ranking_list is None: return {} @@ -1027,7 +1028,7 @@ class OngekiBase: if "userEventPointList" in upsert: for x in upsert["userEventPointList"]: - self.data.item.put_event_point(user_id, x) + self.data.item.put_event_point(user_id, self.version, x) if "userMissionPointList" in upsert: for x in upsert["userMissionPointList"]: @@ -1061,10 +1062,10 @@ class OngekiBase: if "userTechEventList" in upsert: for x in upsert["userTechEventList"]: - self.data.item.put_tech_event(user_id, x) + self.data.item.put_tech_event(user_id, self.version, x) # This should be updated once a day in maintenance window, but for time being we will push the update on each upsert - self.data.item.put_tech_event_ranking(user_id, x) + self.data.item.put_tech_event_ranking(user_id, self.version, x) if "userKopList" in upsert: for x in upsert["userKopList"]: diff --git a/titles/ongeki/schema/item.py b/titles/ongeki/schema/item.py index 4d0dcfd..0e82f16 100644 --- a/titles/ongeki/schema/item.py +++ b/titles/ongeki/schema/item.py @@ -171,10 +171,11 @@ event_point = Table( metadata, Column("id", Integer, primary_key=True, nullable=False), Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")), - Column("eventId", Integer), - Column("point", Integer), + Column("version", Integer, nullable=False), + Column("eventId", Integer, nullable=False), + Column("point", Integer, nullable=False), Column("rank", Integer), - Column("type", Integer), + Column("type", Integer, nullable=False), Column("date", String(25)), Column("isRankingRewarded", Boolean), UniqueConstraint("user", "eventId", name="ongeki_user_event_point_uk"), @@ -241,9 +242,10 @@ tech_event = Table( metadata, Column("id", Integer, primary_key=True, nullable=False), Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")), - Column("eventId", Integer), - Column("totalTechScore", Integer), - Column("totalPlatinumScore", Integer), + Column("version", Integer, nullable=False), + Column("eventId", Integer, nullable=False), + Column("totalTechScore", Integer, nullable=False), + Column("totalPlatinumScore", Integer, nullable=False), Column("techRecordDate", String(25)), Column("isRankingRewarded", Boolean), Column("isTotalTechNewRecord", Boolean), @@ -251,27 +253,17 @@ tech_event = Table( mysql_charset="utf8mb4", ) -tech_music = Table( - "ongeki_tech_music_list", - metadata, - Column("id", Integer, primary_key=True, nullable=False), - Column("eventId", Integer), - Column("musicId", Integer), - Column("level", Integer), - UniqueConstraint("musicId", name="ongeki_tech_music_list_uk"), - mysql_charset="utf8mb4", -) - tech_ranking = Table( "ongeki_tech_event_ranking", metadata, Column("id", Integer, primary_key=True, nullable=False), Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("version", Integer, nullable=False), Column("date", String(25)), - Column("eventId", Integer), + Column("eventId", Integer, nullable=False), Column("rank", Integer), - Column("totalPlatinumScore", Integer), - Column("totalTechScore", Integer), + Column("totalPlatinumScore", Integer, nullable=False), + Column("totalTechScore", Integer, nullable=False), UniqueConstraint("user", "eventId", name="ongeki_tech_event_ranking_uk"), mysql_charset="utf8mb4", ) @@ -562,10 +554,11 @@ class OngekiItemData(BaseData): return None return result.fetchall() - def put_event_point(self, aime_id: int, event_point_data: Dict) -> Optional[int]: + def put_event_point(self, aime_id: int, version: int, event_point_data: Dict) -> Optional[int]: # We update only the newest (type: 1) entry, in official spec game watches for both latest(type:1) and previous (type:2) entries to give an additional info how many ranks has player moved up or down # This fully featured is on TODO list, at the moment we just update the tables as data comes and give out rank as request comes event_point_data["user"] = aime_id + event_point_data["version"] = version event_point_data["type"] = 1 event_point_time = datetime.now() event_point_data["date"] = datetime.strftime(event_point_time, "%Y-%m-%d %H:%M") @@ -647,14 +640,6 @@ class OngekiItemData(BaseData): return None return result.fetchall() - def get_tech_music(self) -> Optional[List[Dict]]: - sql = select(tech_music) - result = self.execute(sql) - - if result is None: - return None - return result.fetchall() - def put_tech_event(self, aime_id: int, tech_event_data: Dict) -> Optional[int]: tech_event_data["user"] = aime_id @@ -667,8 +652,9 @@ class OngekiItemData(BaseData): return None return result.lastrowid - def put_tech_event_ranking(self, aime_id: int, tech_event_data: Dict) -> Optional[int]: + def put_tech_event_ranking(self, version: int, aime_id: int, tech_event_data: Dict) -> Optional[int]: tech_event_data["user"] = aime_id + tech_event_data["version"] = version tech_event_data.pop("isRankingRewarded") tech_event_data.pop("isTotalTechNewRecord") tech_event_data["date"] = tech_event_data.pop("techRecordDate") @@ -683,8 +669,8 @@ class OngekiItemData(BaseData): return None return result.lastrowid - def get_tech_event(self, aime_id: int) -> Optional[List[Dict]]: - sql = select(tech_event).where(tech_event.c.user == aime_id) + def get_tech_event(self, version: int, aime_id: int) -> Optional[List[Dict]]: + sql = select(tech_event).where(and_(tech_event.c.user == aime_id, tech_event.c.version == version)) result = self.execute(sql) if result is None: @@ -774,17 +760,17 @@ class OngekiItemData(BaseData): return result.lastrowid - def get_ranking_event_ranks(self, aime_id: int) -> Optional[List[Dict]]: + def get_ranking_event_ranks(self, version: int, aime_id: int) -> Optional[List[Dict]]: # Calculates player rank on GameRequest from server, and sends it back, official spec would rank players in maintenance period, on TODO list - sql = select(event_point.c.id, event_point.c.user, event_point.c.eventId, event_point.c.type, func.row_number().over(partition_by=event_point.c.eventId, order_by=event_point.c.point.desc()).label('rank'), event_point.c.date, event_point.c.point) + sql = select(event_point.c.id, event_point.c.user, event_point.c.eventId, event_point.c.type, func.row_number().over(partition_by=event_point.c.eventId, order_by=event_point.c.point.desc()).label('rank'), event_point.c.date, event_point.c.point).where(event_point.c.version == version) result = self.execute(sql) if result is None: self.logger.error(f"failed to rank aime_id: {aime_id} ranking event positions") return None return result.fetchall() - def get_tech_event_ranking(self, aime_id: int) -> Optional[List[Dict]]: - sql = select(tech_ranking.c.id, tech_ranking.c.user, tech_ranking.c.date, tech_ranking.c.eventId, func.row_number().over(partition_by=tech_ranking.c.eventId, order_by=[tech_ranking.c.totalTechScore.desc(),tech_ranking.c.totalPlatinumScore.desc()]).label('rank'), tech_ranking.c.totalTechScore, tech_ranking.c.totalPlatinumScore) + def get_tech_event_ranking(self, version: int, aime_id: int) -> Optional[List[Dict]]: + sql = select(tech_ranking.c.id, tech_ranking.c.user, tech_ranking.c.date, tech_ranking.c.eventId, func.row_number().over(partition_by=tech_ranking.c.eventId, order_by=[tech_ranking.c.totalTechScore.desc(),tech_ranking.c.totalPlatinumScore.desc()]).label('rank'), tech_ranking.c.totalTechScore, tech_ranking.c.totalPlatinumScore).where(tech_ranking.c.version == version) result = self.execute(sql) if result is None: self.logger.warning(f"aime_id: {aime_id} has no tech ranking ranks") diff --git a/titles/ongeki/schema/static.py b/titles/ongeki/schema/static.py index 730eb4c..16aedbd 100644 --- a/titles/ongeki/schema/static.py +++ b/titles/ongeki/schema/static.py @@ -100,6 +100,7 @@ music_ranking = Table( "ongeki_static_music_ranking_list", metadata, Column("id", Integer, primary_key=True, nullable=False), + Column("version", Integer, nullable=False), Column("musicId", Integer, nullable=False), Column("point", Integer, nullable=False), Column("userName", String(255)), @@ -136,6 +137,18 @@ present = Table( mysql_charset="utf8mb4", ) +tech_music = Table( + "ongeki_static_tech_music", + metadata, + Column("id", Integer, primary_key=True, nullable=False), + Column("version", Integer, nullable=False), + Column("eventId", Integer, nullable=False), + Column("musicId", Integer, nullable=False), + Column("level", Integer, nullable=False), + UniqueConstraint("version", "musicId", name="ongeki_static_tech_music_uk"), + mysql_charset="utf8mb4", +) + class OngekiStaticData(BaseData): def put_card(self, version: int, card_id: int, **card_data) -> Optional[int]: sql = insert(cards).values(version=version, cardId=card_id, **card_data) @@ -373,8 +386,8 @@ class OngekiStaticData(BaseData): return None return result.fetchone() - def get_ranking_list(self) -> Optional[List[Dict]]: - sql = select(music_ranking.c.musicId.label('id'), music_ranking.c.point, music_ranking.c.userName) + def get_ranking_list(self, version: int) -> Optional[List[Dict]]: + sql = select(music_ranking.c.musicId.label('id'), music_ranking.c.point, music_ranking.c.userName).where(music_ranking.c.version == version) result = self.execute(sql) if result is None: return None @@ -414,3 +427,12 @@ class OngekiStaticData(BaseData): self.logger.warning(f"Failed to load present list") return None return result.fetchall() + + def get_tech_music(self, version: int) -> Optional[List[Dict]]: + sql = select(tech_music).where(tech_music.c.version == version) + + result = self.execute(sql) + + if result is None: + return None + return result.fetchall() From f81c53558ec63d154a907be49c98956e64007d03 Mon Sep 17 00:00:00 2001 From: phantomlan Date: Thu, 9 Nov 2023 02:40:57 +0100 Subject: [PATCH 04/10] - Add ClientTestmode upsert - Add ClientSetting upsert - Add endDate for Events - Add Upgrade Schema - Small bugfixes for events --- core/data/schema/versions/SDDT_6_upgrade.sql | 88 ++++++++++++++++++++ docs/game_specific_info.md | 7 +- titles/ongeki/base.py | 24 ++++-- titles/ongeki/schema/item.py | 11 ++- titles/ongeki/schema/static.py | 52 +++++++++++- 5 files changed, 166 insertions(+), 16 deletions(-) create mode 100644 core/data/schema/versions/SDDT_6_upgrade.sql diff --git a/core/data/schema/versions/SDDT_6_upgrade.sql b/core/data/schema/versions/SDDT_6_upgrade.sql new file mode 100644 index 0000000..3bc6a8e --- /dev/null +++ b/core/data/schema/versions/SDDT_6_upgrade.sql @@ -0,0 +1,88 @@ +SET FOREIGN_KEY_CHECKS=0; + +ALTER TABLE ongeki_user_event_point ADD COLUMN version INTEGER NOT NULL; +ALTER TABLE ongeki_user_event_point ADD COLUMN rank INTEGER; +ALTER TABLE ongeki_user_event_point ADD COLUMN type INTEGER NOT NULL; +ALTER TABLE ongeki_user_event_point ADD COLUMN date VARCHAR(25); + +ALTER TABLE ongeki_user_tech_event ADD COLUMN version INTEGER NOT NULL; + +ALTER TABLE ongeki_user_mission_point ADD COLUMN version INTEGER NOT NULL; + +ALTER TABLE ongeki_static_events ADD COLUMN endDate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP; + +CREATE TABLE ongeki_tech_event_ranking ( + id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, + user INT NOT NULL, + version INT NOT NULL, + date VARCHAR(25), + eventId INT NOT NULL, + rank INT, + totalPlatinumScore INT NOT NULL, + totalTechScore INT NOT NULL, + UNIQUE KEY ongeki_tech_event_ranking_uk (user, eventId), + CONSTRAINT ongeki_tech_event_ranking_ibfk1 FOREIGN KEY (user) REFERENCES aime_user(id) ON DELETE CASCADE ON UPDATE CASCADE +); + +CREATE TABLE ongeki_static_music_ranking_list ( + id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, + version INT NOT NULL, + musicId INT NOT NULL, + point INT NOT NULL, + userName VARCHAR(255), + UNIQUE KEY ongeki_static_music_ranking_list_uk (version, musicId) +); + +CREATE TABLE ongeki_static_rewards ( + id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, + version INT NOT NULL, + rewardId INT NOT NULL, + rewardName VARCHAR(255) NOT NULL, + itemKind INT NOT NULL, + itemId INT NOT NULL, + UNIQUE KEY ongeki_tech_event_ranking_uk (version, rewardId) +); + +CREATE TABLE ongeki_static_present_list ( + id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, + version INT NOT NULL, + presentId INT NOT NULL, + presentName VARCHAR(255) NOT NULL, + rewardId INT NOT NULL, + stock INT NOT NULL, + message VARCHAR(255), + startDate VARCHAR(25) NOT NULL, + endDate VARCHAR(25) NOT NULL, + UNIQUE KEY ongeki_static_present_list_uk (version, presentId, rewardId) +); + +CREATE TABLE ongeki_static_tech_music ( + id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, + version INT NOT NULL, + eventId INT NOT NULL, + musicId INT NOT NULL, + level INT NOT NULL, + UNIQUE KEY ongeki_static_tech_music_uk (version, musicId, eventId) +); + +CREATE TABLE ongeki_static_client_testmode ( + id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, + regionId INT NOT NULL, + placeId INT NOT NULL, + clientId VARCHAR(11) NOT NULL, + updateDate TIMESTAMP NOT NULL, + isDelivery BOOLEAN NOT NULL, + groupId INT NOT NULL, + groupRole INT NOT NULL, + continueMode INT NOT NULL, + selectMusicTime INT NOT NULL, + advertiseVolume INT NOT NULL, + eventMode INT NOT NULL, + eventMusicNum INT NOT NULL, + patternGp INT NOT NULL, + limitGp INT NOT NULL, + maxLeverMovable INT NOT NULL, + minLeverMovable INT NOT NULL, + UNIQUE KEY ongeki_static_client_testmode_uk (clientId) +); +SET FOREIGN_KEY_CHECKS=1; diff --git a/docs/game_specific_info.md b/docs/game_specific_info.md index c338279..4ea32a0 100644 --- a/docs/game_specific_info.md +++ b/docs/game_specific_info.md @@ -343,7 +343,9 @@ Events are controlled by 2 types of enabled events: - AcceptRankingEvent (type 7), AcceptTechChallengeEvent (type 18) Both Ranking and Accept must be enabled for event to function properly + Event will run for the time specified in startDate and endDate + AcceptRankingEvent and AcceptTechChallengeEvent are reward period for events, which specify from what startDate until endDate you can collect the rewards for attending the event, so the reward period must start in the future, e.g. : - RankingEvent startDate 2023-12-01 - endDate 2023-12-30 - period in which whole event is running @@ -352,11 +354,14 @@ AcceptRankingEvent and AcceptTechChallengeEvent are reward period for events, wh If player misses the AcceptRankingEvent period - ranking will be invalidated and receive lowest reward from the event (typically 500x money) Technical Challenge Song List: + Songs that are used for Technical Challenge are not stored anywhere in data files, so you need to fill the database table by yourself, you can gather all songs that should be in Technical Challenges from ONGEKI japanese wikis, or, you can create your own sets: -Database table : `ongeki_tech_music_list` +Database table : `ongeki_static_tech_music` + ``` id: Id in table, just increment for each entry +version: version of the game you want the tech challenge to be in (from RED and up) eventId: Id of the event in ongeki_static_events, insert the Id of the TechChallengeEvent (type 17) you want the song be assigned to musicId: Id of the song you want to add, use songId from ongeki_static_music table level: Difficulty of the song you want to track during the event, from 0(basic) to 3(master) diff --git a/titles/ongeki/base.py b/titles/ongeki/base.py index ad64c15..7e67fd0 100644 --- a/titles/ongeki/base.py +++ b/titles/ongeki/base.py @@ -287,9 +287,21 @@ class OngekiBase: } def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict: + if self.core_cfg.server.is_develop: + return {"returnCode": 1, "apiName": "UpsertClientSettingApi"} + + client_id = data["clientId"] + client_setting_data = data["clientSetting"] + self.data.static.put_client_setting_data(client_id, client_setting_data) return {"returnCode": 1, "apiName": "UpsertClientSettingApi"} def handle_upsert_client_testmode_api_request(self, data: Dict) -> Dict: + if self.core_cfg.server.is_develop: + return {"returnCode": 1, "apiName": "UpsertClientTestmodeApi"} + + region_id = data["regionId"] + client_testmode_data = data["clientTestmode"] + self.data.static.put_client_testmode_data(region_id, client_testmode_data) return {"returnCode": 1, "apiName": "UpsertClientTestmodeApi"} def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict: @@ -333,10 +345,9 @@ class OngekiBase: "id": event["eventId"], # actually use the startDate from the import so it # properly shows all the events when new ones are imported - "startDate": datetime.strftime( - event["startDate"], "%Y-%m-%d %H:%M:%S.0" - ), - "endDate": "2099-12-31 00:00:00.0", + "startDate": datetime.strftime(event["startDate"], "%Y-%m-%d %H:%M:%S.0"), + #"endDate": "2099-12-31 00:00:00.0", + "endDate": datetime.strftime(event["endDate"], "%Y-%m-%d %H:%M:%S.0"), } ) @@ -849,7 +860,7 @@ class OngekiBase: } def handle_get_user_mission_point_api_request(self, data: Dict) -> Dict: - user_mission_point_list = self.data.item.get_mission_points(data["userId"]) + user_mission_point_list = self.data.item.get_mission_points(self.version, data["userId"]) if user_mission_point_list is None: return {} @@ -858,6 +869,7 @@ class OngekiBase: tmp = evt_music._asdict() tmp.pop("id") tmp.pop("user") + tmp.pop("version") mission_point_list.append(tmp) @@ -1032,7 +1044,7 @@ class OngekiBase: if "userMissionPointList" in upsert: for x in upsert["userMissionPointList"]: - self.data.item.put_mission_point(user_id, x) + self.data.item.put_mission_point(user_id, self.version, x) if "userRatinglogList" in upsert: for x in upsert["userRatinglogList"]: diff --git a/titles/ongeki/schema/item.py b/titles/ongeki/schema/item.py index 0e82f16..a7854c8 100644 --- a/titles/ongeki/schema/item.py +++ b/titles/ongeki/schema/item.py @@ -187,6 +187,7 @@ mission_point = Table( metadata, Column("id", Integer, primary_key=True, nullable=False), Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")), + Column("version", Integer), Column("eventId", Integer), Column("point", Integer), UniqueConstraint("user", "eventId", name="ongeki_user_mission_point_uk"), @@ -337,7 +338,6 @@ print_detail = Table( mysql_charset="utf8mb4", ) - class OngekiItemData(BaseData): def put_card(self, aime_id: int, card_data: Dict) -> Optional[int]: card_data["user"] = aime_id @@ -532,10 +532,9 @@ class OngekiItemData(BaseData): return None return result.fetchall() - def put_mission_point( - self, aime_id: int, mission_point_data: Dict - ) -> Optional[int]: + def put_mission_point(self, version: int, aime_id: int, mission_point_data: Dict) -> Optional[int]: mission_point_data["user"] = aime_id + mission_point_data["version"] = version sql = insert(mission_point).values(**mission_point_data) conflict = sql.on_duplicate_key_update(**mission_point_data) @@ -546,8 +545,8 @@ class OngekiItemData(BaseData): return None return result.lastrowid - def get_mission_points(self, aime_id: int) -> Optional[List[Dict]]: - sql = select(mission_point).where(mission_point.c.user == aime_id) + def get_mission_points(self, version: int, aime_id: int) -> Optional[List[Dict]]: + sql = select(mission_point).where(and_(mission_point.c.user == aime_id, mission_point.c.version == version)) result = self.execute(sql) if result is None: diff --git a/titles/ongeki/schema/static.py b/titles/ongeki/schema/static.py index 16aedbd..fe9d468 100644 --- a/titles/ongeki/schema/static.py +++ b/titles/ongeki/schema/static.py @@ -17,6 +17,7 @@ events = Table( Column("type", Integer), Column("name", String(255)), Column("startDate", TIMESTAMP, server_default=func.now()), + Column("endDate", TIMESTAMP, server_default=func.now()), Column("enabled", Boolean, server_default="1"), UniqueConstraint("version", "eventId", "type", name="ongeki_static_events_uk"), mysql_charset="utf8mb4", @@ -104,7 +105,7 @@ music_ranking = Table( Column("musicId", Integer, nullable=False), Column("point", Integer, nullable=False), Column("userName", String(255)), - UniqueConstraint("musicId", name="ongeki_static_music_ranking_uk"), + UniqueConstraint("version", "musicId", name="ongeki_static_music_ranking_uk"), mysql_charset="utf8mb4", ) @@ -117,7 +118,7 @@ rewards = Table( Column("rewardname", String(255), nullable=False), Column("itemKind", Integer, nullable=False), Column("itemId", Integer, nullable=False), - UniqueConstraint("version","itemKind","rewardId", name="ongeki_static_rewards_uk"), + UniqueConstraint("version", "rewardId", name="ongeki_static_rewards_uk"), mysql_charset="utf8mb4", ) @@ -133,7 +134,7 @@ present = Table( Column("message", String(255)), Column("startDate", String(25), nullable=False), Column("endDate", String(25), nullable=False), - UniqueConstraint("version","presentId", name="ongeki_static_present_list_uk"), + UniqueConstraint("version", "presentId", "rewardId", name="ongeki_static_present_list_uk", mysql_charset="utf8mb4", ) @@ -149,6 +150,30 @@ tech_music = Table( mysql_charset="utf8mb4", ) +client_testmode = Table( + "ongeki_static_client_testmode", + metadata, + Column("id", Integer, primary_key=True, nullable=False), + Column("regionId", Integer, nullable=False), + Column("placeId", Integer, nullable=False), + Column("clientId", String(11), nullable=False), + Column("updateDate", TIMESTAMP, nullable=False), + Column("isDelivery", Boolean, nullable=False), + Column("groupId", Integer, nullable=False), + Column("groupRole", Integer, nullable=False), + Column("continueMode", Integer, nullable=False), + Column("selectMusicTime", Integer, nullable=False), + Column("advertiseVolume", Integer, nullable=False), + Column("eventMode", Integer, nullable=False), + Column("eventMusicNum", Integer, nullable=False), + Column("patternGp", Integer, nullable=False), + Column("limitGp", Integer, nullable=False), + Column("maxLeverMovable", Integer, nullable=False), + Column("minLeverMovable", Integer, nullable=False), + UniqueConstraint("clientId", name="ongeki_static_client_testmode_uk"), + mysql_charset="utf8mb4", +) + class OngekiStaticData(BaseData): def put_card(self, version: int, card_id: int, **card_data) -> Optional[int]: sql = insert(cards).values(version=version, cardId=card_id, **card_data) @@ -287,6 +312,7 @@ class OngekiStaticData(BaseData): eventId=event_id, type=event_type, name=event_name, + endDate=f"2038-01-01 00:00:00", ) conflict = sql.on_duplicate_key_update( @@ -436,3 +462,23 @@ class OngekiStaticData(BaseData): if result is None: return None return result.fetchall() + + def put_client_testmode_data(self, region_id: int, client_testmode_data: Dict) -> Optional[List[Dict]]: + sql = insert(client_testmode).values(regionId=region_id, **client_testmode_data) + conflict = sql.on_duplicate_key_update(regionId=region_id, **client_testmode_data) + + result = self.execute(conflict) + if result is None: + self.logger.warning(f"clientId: {clientId} Failed to update ClientTestMode data"), + return None + return result.lastrowid + + def put_client_setting_data(self, client_id: str, client_setting_data: Dict) -> Optional[List[Dict]]: + sql = insert(machine).values(data=client_setting_data) + conflict = sql.on_duplicate_key_update(serial=client_id) + + result = self.execute(conflict) + if result is None: + self.logger.warning(f"clientId: {clientId} Failed to update ClientSetting data"), + return None + return result.lastrowid From 530016ef84ff1335fe480415d76d4e22275cec37 Mon Sep 17 00:00:00 2001 From: phantomlan Date: Thu, 9 Nov 2023 03:10:48 +0100 Subject: [PATCH 05/10] i ate one ) --- titles/ongeki/schema/static.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/titles/ongeki/schema/static.py b/titles/ongeki/schema/static.py index fe9d468..5b459b8 100644 --- a/titles/ongeki/schema/static.py +++ b/titles/ongeki/schema/static.py @@ -134,7 +134,7 @@ present = Table( Column("message", String(255)), Column("startDate", String(25), nullable=False), Column("endDate", String(25), nullable=False), - UniqueConstraint("version", "presentId", "rewardId", name="ongeki_static_present_list_uk", + UniqueConstraint("version", "presentId", "rewardId", name="ongeki_static_present_list_uk"), mysql_charset="utf8mb4", ) From 31d39ffb37e0f1e123c14fceaec5365470d4cdf5 Mon Sep 17 00:00:00 2001 From: phantomlan Date: Thu, 9 Nov 2023 10:29:42 +0100 Subject: [PATCH 06/10] merge conflict fix --- titles/ongeki/index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/titles/ongeki/index.py b/titles/ongeki/index.py index 9cc8891..edb06ba 100644 --- a/titles/ongeki/index.py +++ b/titles/ongeki/index.py @@ -263,4 +263,4 @@ class OngekiServlet(BaseServlet): bytes.fromhex(self.game_cfg.crypto.keys[internal_ver][1]), ) - return crypt.encrypt(padded) \ No newline at end of file + return crypt.encrypt(padded) From d663b1ef7c009afdcd846c0dd38fa811c9bce716 Mon Sep 17 00:00:00 2001 From: phantomlan Date: Mon, 13 Nov 2023 03:03:04 +0100 Subject: [PATCH 07/10] fix foreign key issue --- titles/ongeki/schema/item.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/titles/ongeki/schema/item.py b/titles/ongeki/schema/item.py index a7854c8..2c5b526 100644 --- a/titles/ongeki/schema/item.py +++ b/titles/ongeki/schema/item.py @@ -532,7 +532,7 @@ class OngekiItemData(BaseData): return None return result.fetchall() - def put_mission_point(self, version: int, aime_id: int, mission_point_data: Dict) -> Optional[int]: + def put_mission_point(self, aime_id: int, version: int, mission_point_data: Dict) -> Optional[int]: mission_point_data["user"] = aime_id mission_point_data["version"] = version From b56a5f020b77129038ba2ba2e64243135ae13399 Mon Sep 17 00:00:00 2001 From: phantomlan Date: Mon, 13 Nov 2023 03:26:09 +0100 Subject: [PATCH 08/10] add rollback sql move GP support to table in DB small fixes --- core/data/schema/versions/SDDT_5_rollback.sql | 22 +++++++ core/data/schema/versions/SDDT_6_upgrade.sql | 10 +++ titles/ongeki/base.py | 65 ++++++------------- titles/ongeki/schema/static.py | 30 ++++++++- 4 files changed, 82 insertions(+), 45 deletions(-) create mode 100644 core/data/schema/versions/SDDT_5_rollback.sql diff --git a/core/data/schema/versions/SDDT_5_rollback.sql b/core/data/schema/versions/SDDT_5_rollback.sql new file mode 100644 index 0000000..08324e0 --- /dev/null +++ b/core/data/schema/versions/SDDT_5_rollback.sql @@ -0,0 +1,22 @@ +SET FOREIGN_KEY_CHECKS=0; + +ALTER TABLE ongeki_user_event_point DROP COLUMN version; +ALTER TABLE ongeki_user_event_point DROP COLUMN rank; +ALTER TABLE ongeki_user_event_point DROP COLUMN type; +ALTER TABLE ongeki_user_event_point DROP COLUMN date; + +ALTER TABLE ongeki_user_tech_event DROP COLUMN version; + +ALTER TABLE ongeki_user_mission_point DROP COLUMN version; + +ALTER TABLE ongeki_static_event DROP COLUMN endDate; + +DROP TABLE ongeki_tech_event_ranking; +DROP TABLE ongeki_static_music_ranking_list; +DROP TABLE ongeki_static_rewards; +DROP TABLE ongeki_static_present_list; +DROP TABLE ongeki_static_tech_music; +DROP TABLE ongeki_static_client_testmode; +DROP TABLE ongeki_static_game_point; + +SET FOREIGN_KEY_CHECKS=1; diff --git a/core/data/schema/versions/SDDT_6_upgrade.sql b/core/data/schema/versions/SDDT_6_upgrade.sql index 3bc6a8e..82d5336 100644 --- a/core/data/schema/versions/SDDT_6_upgrade.sql +++ b/core/data/schema/versions/SDDT_6_upgrade.sql @@ -85,4 +85,14 @@ CREATE TABLE ongeki_static_client_testmode ( minLeverMovable INT NOT NULL, UNIQUE KEY ongeki_static_client_testmode_uk (clientId) ); + +CREATE TABLE ongeki_static_game_point ( + id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, + type INT NOT NULL, + cost INT NOT NULL, + startDate VARCHAR(25) NOT NULL DEFAULT "2000-01-01 05:00:00.0", + endDate VARCHAR(25) NOT NULL DEFAULT "2099-01-01 05:00:00.0", + UNIQUE KEY ongeki_static_game_point_uk (type) +); + SET FOREIGN_KEY_CHECKS=1; diff --git a/titles/ongeki/base.py b/titles/ongeki/base.py index 7e67fd0..2f87b19 100644 --- a/titles/ongeki/base.py +++ b/titles/ongeki/base.py @@ -173,51 +173,28 @@ class OngekiBase: def handle_get_game_point_api_request(self, data: Dict) -> Dict: - """ - Sets the GP amount for A and B sets for 1 - 3 credits - """ - return { - "length": 6, - "gamePointList": [ - { - "type": 0, - "cost": 100, - "startDate": "2000-01-01 05:00:00.0", - "endDate": "2099-01-01 05:00:00.0", - }, - { - "type": 1, - "cost": 230, - "startDate": "2000-01-01 05:00:00.0", - "endDate": "2099-01-01 05:00:00.0", - }, - { - "type": 2, - "cost": 370, - "startDate": "2000-01-01 05:00:00.0", - "endDate": "2099-01-01 05:00:00.0", - }, - { - "type": 3, - "cost": 120, - "startDate": "2000-01-01 05:00:00.0", - "endDate": "2099-01-01 05:00:00.0", - }, - { - "type": 4, - "cost": 240, - "startDate": "2000-01-01 05:00:00.0", - "endDate": "2099-01-01 05:00:00.0", - }, - { - "type": 5, - "cost": 360, - "startDate": "2000-01-01 05:00:00.0", - "endDate": "2099-01-01 05:00:00.0", - }, - ], - } + get_game_point = self.data.static.get_static_game_point() + game_point = [] + if not get_game_point: + self.logger.info(f"GP table is empty, inserting defaults") + self.data.static.put_static_game_point_defaults() + get_game_point = self.data.static.get_static_game_point() + for gp in get_game_point: + tmp = gp._asdict() + game_point.append(tmp) + return { + "length": len(game_point), + "gamePointList": game_point, + } + for gp in get_game_point: + tmp = gp._asdict() + game_point.append(tmp) + return { + "length": len(game_point), + "gamePointList": game_point, + } + def handle_game_login_api_request(self, data: Dict) -> Dict: return {"returnCode": 1, "apiName": "gameLogin"} diff --git a/titles/ongeki/schema/static.py b/titles/ongeki/schema/static.py index 5b459b8..0a5d176 100644 --- a/titles/ongeki/schema/static.py +++ b/titles/ongeki/schema/static.py @@ -134,7 +134,7 @@ present = Table( Column("message", String(255)), Column("startDate", String(25), nullable=False), Column("endDate", String(25), nullable=False), - UniqueConstraint("version", "presentId", "rewardId", name="ongeki_static_present_list_uk"), + UniqueConstraint("version", "presentId", name="ongeki_static_present_list_uk"), mysql_charset="utf8mb4", ) @@ -174,6 +174,18 @@ client_testmode = Table( mysql_charset="utf8mb4", ) +game_point = Table( + "ongeki_static_game_point", + metadata, + Column("id", Integer, primary_key=True, nullable=False), + Column("type", Integer, nullable=False), + Column("cost", Integer, nullable=False), + Column("startDate", String(25), nullable=False, server_default="2000-01-01 05:00:00.0"), + Column("endDate", String(25), nullable=False, server_default="2099-01-01 05:00:00.0"), + UniqueConstraint("type", name="ongeki_static_game_point_uk"), + mysql_charset="utf8mb4", +) + class OngekiStaticData(BaseData): def put_card(self, version: int, card_id: int, **card_data) -> Optional[int]: sql = insert(cards).values(version=version, cardId=card_id, **card_data) @@ -482,3 +494,19 @@ class OngekiStaticData(BaseData): self.logger.warning(f"clientId: {clientId} Failed to update ClientSetting data"), return None return result.lastrowid + + def put_static_game_point_defaults(self) -> Optional[List[Dict]]: + game_point_defaults = [{"type": 0, "cost": 100},{"type": 1, "cost": 230},{"type": 2, "cost": 370},{"type": 3, "cost": 120},{"type": 4, "cost": 240},{"type": 5, "cost": 360}] + sql = insert(game_point).values(game_point_defaults) + result = self.execute(sql) + if result is None: + self.logger.warning(f"Failed to insert default GP table!") + return None + return result.lastrowid + + def get_static_game_point(self) -> Optional[List[Dict]]: + sql = select(game_point.c.type, game_point.c.cost, game_point.c.startDate, game_point.c.endDate) + result = self.execute(sql) + if result is None: + return None + return result.fetchall() From e461ffe5875efeea2e59417219fa88fa38af94cd Mon Sep 17 00:00:00 2001 From: phantomlan Date: Mon, 13 Nov 2023 03:38:30 +0100 Subject: [PATCH 09/10] add more info --- docs/game_specific_info.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/game_specific_info.md b/docs/game_specific_info.md index 4ea32a0..2bf6dc4 100644 --- a/docs/game_specific_info.md +++ b/docs/game_specific_info.md @@ -374,6 +374,27 @@ Mission Event (type 13) is a monthly type of event, which is used when another e If you're often trying fresh cards, registering new profiles etc., you can also consider disabling all Announcement Events (type 1), as it will disable all the banners that pop up on login (they show up only once though, so if you click through them once they won't show again) +Event type 2 in Database are Advertisement Movies, enable only 1 you want to currently play, and disable others + + +Present and Reward List - populate reward list using read.py + +Create present for players by adding an entry in `ongeki_static_present_list` +``` +id: unique for each entry +version: game version you want the present be in +presentId: id of the present - starts with 1001 and go up from that, must be unique for each reward(don't set multiple rewardIds with same presentId) +presentName: present name which will be shown on the bottom when received +rewardId: ID of item from ongeki_static_rewards +stock: how many you want to give (like 5 copies of same card, or 10000 money, etc.) +message: no idea, can be left empty +startDate: date when to start giving out +endDate: date when ends +``` + +After inserting present to the table, add the presentId into players `ongeki_static_item`, where itemKind is 9, itemId is the presentId, and stock set 1 and isValid to 1 + +After that, on next login the present should be received (or whenever it supposed to happen) From 80ae372ccf4cf0eaf7f0692eab2acc9a94347b1b Mon Sep 17 00:00:00 2001 From: phantomlan Date: Mon, 13 Nov 2023 04:05:13 +0100 Subject: [PATCH 10/10] fix rollback --- core/data/schema/versions/SDDT_5_rollback.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/data/schema/versions/SDDT_5_rollback.sql b/core/data/schema/versions/SDDT_5_rollback.sql index 08324e0..007716c 100644 --- a/core/data/schema/versions/SDDT_5_rollback.sql +++ b/core/data/schema/versions/SDDT_5_rollback.sql @@ -9,7 +9,7 @@ ALTER TABLE ongeki_user_tech_event DROP COLUMN version; ALTER TABLE ongeki_user_mission_point DROP COLUMN version; -ALTER TABLE ongeki_static_event DROP COLUMN endDate; +ALTER TABLE ongeki_static_events DROP COLUMN endDate; DROP TABLE ongeki_tech_event_ranking; DROP TABLE ongeki_static_music_ranking_list;