From e06e316b7d7670772bb6737c6d34db57f3ea4449 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Tue, 11 Jun 2024 10:30:57 -0400 Subject: [PATCH 01/16] pokken: fix achievement flags --- titles/pokken/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/titles/pokken/base.py b/titles/pokken/base.py index 562f3b9..c50654a 100644 --- a/titles/pokken/base.py +++ b/titles/pokken/base.py @@ -339,7 +339,7 @@ class PokkenBase: for ach_flg in req.achievement_flag: ach_flgs.append(ach_flg) - await self.data.profile.update_profile_tutorial_flags(user_id, ach_flg) + await self.data.profile.update_profile_achievement_flags(user_id, ach_flg) for evt_flg in req.event_achievement_flag: evt_flgs.append(evt_flg) From 3fd65da7fd8c0031008dd429fe5c0b63df1b8035 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Tue, 11 Jun 2024 12:25:45 -0400 Subject: [PATCH 02/16] pokken: backport changes from Diana --- titles/pokken/base.py | 159 +++++++++++++++++++++----------- titles/pokken/schema/profile.py | 98 ++++++++++++-------- 2 files changed, 161 insertions(+), 96 deletions(-) diff --git a/titles/pokken/base.py b/titles/pokken/base.py index c50654a..e22ad74 100644 --- a/titles/pokken/base.py +++ b/titles/pokken/base.py @@ -1,9 +1,7 @@ -from datetime import datetime, timedelta +from datetime import datetime import json, logging from typing import Any, Dict, List -import random -from core.data import Data from core import CoreConfig from .config import PokkenConfig from .proto import jackal_pb2 @@ -18,7 +16,6 @@ class PokkenBase: self.version = 0 self.logger = logging.getLogger("pokken") self.data = PokkenData(core_cfg) - self.SUPPORT_SET_NONE = 4294967295 async def handle_noop(self, request: Any) -> bytes: res = jackal_pb2.Response() @@ -38,7 +35,30 @@ class PokkenBase: res = jackal_pb2.Response() res.result = 1 res.type = jackal_pb2.MessageType.REGISTER_PCB - self.logger.info(f"Register PCB {request.register_pcb.pcb_id}") + pcbid = request.register_pcb.pcb_id + if not pcbid.isdigit() or len(pcbid) != 12 or \ + not pcbid.startswith(f"{PokkenConstants.SERIAL_IDENT[0]}{PokkenConstants.SERIAL_REGIONS[0]}{PokkenConstants.SERIAL_ROLES[0]}{PokkenConstants.SERIAL_CAB_IDENTS[0]}"): + self.logger.warn(f"Bad PCBID {pcbid}") + res.result = 0 + return res + + netid = PokkenConstants.NETID_PREFIX[0] + pcbid[5:] + + self.logger.info(f"Register PCB {pcbid} (netID {netid})") + + minfo = await self.data.arcade.get_machine(netid) + + if not minfo and not self.core_cfg.server.allow_unregistered_serials: + self.logger.warn(f"netID {netid} does not belong to any shop!") + res.result = 0 + return res + + elif not minfo: + self.logger.warn(f"Orphaned netID {netid} allowed to connect") + locid = 0 + + else: + locid = minfo['arcade'] regist_pcb = jackal_pb2.RegisterPcbResponseData() regist_pcb.server_time = int(datetime.now().timestamp()) @@ -46,7 +66,7 @@ class PokkenBase: "MatchingServer": { "host": f"https://{self.game_cfg.server.hostname}", "port": self.game_cfg.ports.game, - "url": "/SDAK/100/matching", + "url": "/pokken/matching", }, "StunServer": { "addr": self.game_cfg.server.stun_server_host, @@ -56,12 +76,12 @@ class PokkenBase: "addr": self.game_cfg.server.stun_server_host, "port": self.game_cfg.server.stun_server_port, }, - "AdmissionUrl": f"ws://{self.game_cfg.server.hostname}:{self.game_cfg.ports.admission}", - "locationId": 123, # FIXME: Get arcade's ID from the database - "logfilename": "JackalMatchingLibrary.log", - "biwalogfilename": "./biwa.log", + "AdmissionUrl": f"ws://{self.game_cfg.server.hostname}:{self.game_cfg.ports.admission}/pokken/admission", + "locationId": locid, + "logfilename": "J:\\JackalMatchingLibrary.log", + "biwalogfilename": "J:\\biwa_log.log", } - regist_pcb.bnp_baseuri = f"{self.core_cfg.server.hostname}/bna" + regist_pcb.bnp_baseuri = f"{self.core_cfg.mucha.hostname}/bna" regist_pcb.biwa_setting = json.dumps(biwa_setting) res.register_pcb.CopyFrom(regist_pcb) @@ -95,12 +115,11 @@ class PokkenBase: res.type = jackal_pb2.MessageType.LOAD_CLIENT_SETTINGS settings = jackal_pb2.LoadClientSettingsResponseData() - # TODO: Make configurable settings.money_magnification = 1 settings.continue_bonus_exp = 100 settings.continue_fight_money = 100 settings.event_bonus_exp = 100 - settings.level_cap = 999 + settings.level_cap = 100 settings.op_movie_flag = 0xFFFFFFFF settings.lucky_bonus_rate = 1 settings.fail_support_num = 10 @@ -132,9 +151,13 @@ class PokkenBase: res.type = jackal_pb2.MessageType.LOAD_USER access_code = request.load_user.access_code load_usr = jackal_pb2.LoadUserResponseData() - user_id = await self.data.card.get_user_id_from_card(access_code) + load_usr.load_hash = 1 + load_usr.access_code = access_code + load_usr.precedent_release_flag = 0xFFFFFFFF + load_usr.cardlock_status = False + card = await self.data.card.get_card_by_access_code(access_code) - if user_id is None and self.game_cfg.server.auto_register: + if card is None and self.game_cfg.server.auto_register: user_id = await self.data.user.create_user() card_id = await self.data.card.create_card(user_id, access_code) @@ -142,41 +165,27 @@ class PokkenBase: f"Register new card {access_code} (UserId {user_id}, CardId {card_id})" ) - elif user_id is None: + elif card is None: self.logger.info(f"Registration of card {access_code} blocked!") res.load_user.CopyFrom(load_usr) return res.SerializeToString() + + else: + user_id = card['user'] + card_id = card['id'] """ - TODO: Add repeated values - tutorial_progress_flag - rankmatch_progress + TODO: Unlock all supports? Probably support_pokemon_list - support_set_1 - support_set_2 - support_set_3 - aid_skill_list - achievement_flag - event_achievement_flag - event_achievement_param """ profile = await self.data.profile.get_profile(user_id) load_usr.commidserv_result = 1 - load_usr.load_hash = 1 - load_usr.cardlock_status = False load_usr.banapass_id = user_id - load_usr.access_code = access_code - load_usr.precedent_release_flag = 0xFFFFFFFF - if profile is None: + if profile is None or profile['trainer_name'] is None: profile_id = await self.data.profile.create_profile(user_id) profile_dict = {"id": profile_id, "user": user_id} pokemon_data = [] - tutorial_progress = [] - rankmatch_progress = [] - achievement_flag = [] - event_achievement_flag = [] - event_achievement_param = [] load_usr.new_card_flag = True else: @@ -185,11 +194,6 @@ class PokkenBase: f"Card-in user {user_id} (Trainer name {profile_dict.get('trainer_name', '')})" ) pokemon_data = await self.data.profile.get_all_pokemon_data(user_id) - tutorial_progress = [] - rankmatch_progress = [] - achievement_flag = [] - event_achievement_flag = [] - event_achievement_param = [] load_usr.new_card_flag = False load_usr.navi_newbie_flag = profile_dict.get("navi_newbie_flag", True) @@ -201,9 +205,9 @@ class PokkenBase: load_usr.trainer_name = profile_dict.get( "trainer_name", f"Newb{str(user_id).zfill(4)}" ) - load_usr.trainer_rank_point = profile_dict.get("trainer_rank_point", 0) - load_usr.wallet = profile_dict.get("wallet", 0) - load_usr.fight_money = profile_dict.get("fight_money", 0) + load_usr.trainer_rank_point = profile_dict.get("trainer_rank_point", 0) # determines rank + load_usr.wallet = profile_dict.get("wallet", 0) # pg count + load_usr.fight_money = profile_dict.get("fight_money", 0) # ? load_usr.score_point = profile_dict.get("score_point", 0) load_usr.grade_max_num = profile_dict.get("grade_max_num", 0) load_usr.extra_counter = profile_dict.get("extra_counter", 0) @@ -218,18 +222,18 @@ class PokkenBase: load_usr.rank_event = profile_dict.get("rank_event", 0) load_usr.awake_num = profile_dict.get("awake_num", 0) load_usr.use_support_num = profile_dict.get("use_support_num", 0) - load_usr.rankmatch_flag = profile_dict.get("rankmatch_flag", 0) + load_usr.rankmatch_flag = profile_dict.get("rankmatch_flag", 0) # flags that next rank match will be rank up load_usr.rankmatch_max = profile_dict.get("rankmatch_max", 0) load_usr.rankmatch_success = profile_dict.get("rankmatch_success", 0) load_usr.beat_num = profile_dict.get("beat_num", 0) - load_usr.title_text_id = profile_dict.get("title_text_id", 0) - load_usr.title_plate_id = profile_dict.get("title_plate_id", 0) - load_usr.title_decoration_id = profile_dict.get("title_decoration_id", 0) + load_usr.title_text_id = profile_dict.get("title_text_id", 2) + load_usr.title_plate_id = profile_dict.get("title_plate_id", 1) + load_usr.title_decoration_id = profile_dict.get("title_decoration_id", 1) load_usr.navi_trainer = profile_dict.get("navi_trainer", 0) load_usr.navi_version_id = profile_dict.get("navi_version_id", 0) load_usr.aid_skill = profile_dict.get("aid_skill", 0) - load_usr.comment_text_id = profile_dict.get("comment_text_id", 0) - load_usr.comment_word_id = profile_dict.get("comment_word_id", 0) + load_usr.comment_text_id = profile_dict.get("comment_text_id", 1) + load_usr.comment_word_id = profile_dict.get("comment_word_id", 1) load_usr.latest_use_pokemon = profile_dict.get("latest_use_pokemon", 0) load_usr.ex_ko_num = profile_dict.get("ex_ko_num", 0) load_usr.wko_num = profile_dict.get("wko_num", 0) @@ -237,11 +241,11 @@ class PokkenBase: load_usr.cool_ko_num = profile_dict.get("cool_ko_num", 0) load_usr.perfect_ko_num = profile_dict.get("perfect_ko_num", 0) load_usr.record_flag = profile_dict.get("record_flag", 0) - load_usr.site_register_status = profile_dict.get("site_register_status", 0) + load_usr.site_register_status = profile_dict.get("site_register_status", 1) load_usr.continue_num = profile_dict.get("continue_num", 0) load_usr.avatar_body = profile_dict.get("avatar_body", 0) - load_usr.avatar_gender = profile_dict.get("avatar_gender", 0) + load_usr.avatar_gender = profile_dict.get("avatar_gender", 1) load_usr.avatar_background = profile_dict.get("avatar_background", 0) load_usr.avatar_head = profile_dict.get("avatar_head", 0) load_usr.avatar_battleglass = profile_dict.get("avatar_battleglass", 0) @@ -283,6 +287,31 @@ class PokkenBase: pkm.bp_point_sp = pkmn_d.get('bp_point_sp', 0) load_usr.pokemon_data.append(pkm) + + for x in profile_dict.get("tutorial_progress_flag", []): + load_usr.tutorial_progress_flag.append(x) + + for x in profile_dict.get("achievement_flag", []): + load_usr.achievement_flag.append(x) + + for x in profile_dict.get("aid_skill_list", []): + load_usr.aid_skill_list.append(x) + + for x in profile_dict.get("rankmatch_progress", []): + load_usr.rankmatch_progress.append(x) + + for x in profile_dict.get("event_achievement_flag", []): + load_usr.event_achievement_flag.append(x) + + for x in profile_dict.get("event_achievement_param", []): + load_usr.event_achievement_param.append(x) + + load_usr.support_set_1.append(profile_dict.get("support_set_1_1", 4294967295)) + load_usr.support_set_1.append(profile_dict.get("support_set_1_2", 4294967295)) + load_usr.support_set_2.append(profile_dict.get("support_set_2_1", 4294967295)) + load_usr.support_set_2.append(profile_dict.get("support_set_2_2", 4294967295)) + load_usr.support_set_3.append(profile_dict.get("support_set_3_1", 4294967295)) + load_usr.support_set_3.append(profile_dict.get("support_set_3_2", 4294967295)) res.load_user.CopyFrom(load_usr) return res.SerializeToString() @@ -300,6 +329,8 @@ class PokkenBase: req = request.save_user user_id = req.banapass_id + + self.logger.info(f"Save user data for {user_id}") tut_flgs: List[int] = [] ach_flgs: List[int] = [] @@ -339,7 +370,7 @@ class PokkenBase: for ach_flg in req.achievement_flag: ach_flgs.append(ach_flg) - await self.data.profile.update_profile_achievement_flags(user_id, ach_flg) + await self.data.profile.update_profile_achievement_flags(user_id, ach_flgs) for evt_flg in req.event_achievement_flag: evt_flgs.append(evt_flg) @@ -353,18 +384,23 @@ class PokkenBase: await self.data.item.add_reward(user_id, reward.get_category_id, reward.get_content_id, reward.get_type_id) await self.data.profile.add_profile_points(user_id, get_rank_pts, get_money, get_score_pts, grade_max) + + # Inconsistant underscore use AND a typo?? + await self.data.profile.update_rankmatch_data(user_id, req.rankmatch_flag, req.rank_match_max, req.rank_match_success, req.rank_match_process) await self.data.profile.update_support_team(user_id, 1, req.support_set_1[0], req.support_set_1[1]) await self.data.profile.update_support_team(user_id, 2, req.support_set_2[0], req.support_set_2[1]) await self.data.profile.update_support_team(user_id, 3, req.support_set_3[0], req.support_set_3[1]) await self.data.profile.put_pokemon(user_id, mon.char_id, mon.illustration_book_no, mon.bp_point_atk, mon.bp_point_res, mon.bp_point_def, mon.bp_point_sp) - await self.data.profile.add_pokemon_xp(user_id, mon.char_id, mon.get_pokemon_exp) + await self.data.profile.add_pokemon_xp(user_id, mon.illustration_book_no, mon.get_pokemon_exp) + await self.data.profile.set_latest_mon(user_id, mon.illustration_book_no) for x in range(len(battle.play_mode)): + self.logger.info(f"Save {PokkenConstants.BATTLE_TYPE(battle.play_mode[x]).name} battle {PokkenConstants.BATTLE_RESULT(battle.result[x]).name} for {user_id} with mon {mon.illustration_book_no}") await self.data.profile.put_pokemon_battle_result( user_id, - mon.char_id, + mon.illustration_book_no, PokkenConstants.BATTLE_TYPE(battle.play_mode[x]), PokkenConstants.BATTLE_RESULT(battle.result[x]) ) @@ -391,7 +427,6 @@ class PokkenBase: last_evt ) - return res.SerializeToString() async def handle_save_ingame_log(self, data: jackal_pb2.Request) -> bytes: @@ -419,6 +454,13 @@ class PokkenBase: async def handle_matching_is_matching( self, data: Dict = {}, client_ip: str = "127.0.0.1" ) -> Dict: + """ + "sessionId":"12345678", + "A":{ + "pcb_id": data["data"]["must"]["pcb_id"], + "gip": client_ip + }, + """ return { "data": { "sessionId":"12345678", @@ -435,6 +477,11 @@ class PokkenBase: ) -> Dict: return {} + async def handle_matching_obtain_matching( + self, data: Dict = {}, client_ip: str = "127.0.0.1" + ) -> Dict: + return {} + async def handle_admission_noop(self, data: Dict, req_ip: str = "127.0.0.1") -> Dict: return {} diff --git a/titles/pokken/schema/profile.py b/titles/pokken/schema/profile.py index b7237de..38b93e7 100644 --- a/titles/pokken/schema/profile.py +++ b/titles/pokken/schema/profile.py @@ -1,11 +1,11 @@ from typing import Optional, Dict, List, Union from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_, case -from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON +from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON, INTEGER from sqlalchemy.schema import ForeignKey from sqlalchemy.sql import func, select, update, delete from sqlalchemy.sql.functions import coalesce from sqlalchemy.engine import Row -from sqlalchemy.dialects.mysql import insert +from sqlalchemy.dialects.postgresql import insert from core.data.schema import BaseData, metadata from ..const import PokkenConstants @@ -16,13 +16,8 @@ profile = Table( "pokken_profile", metadata, Column("id", Integer, primary_key=True, nullable=False), - Column( - "user", - ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), - nullable=False, - unique=True, - ), - Column("trainer_name", String(16)), # optional + Column("user", Integer, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False, unique=True), + Column("trainer_name", String(14)), # optional Column("home_region_code", Integer), Column("home_loc_name", String(255)), Column("pref_code", Integer), @@ -105,20 +100,15 @@ profile = Table( Column("battle_num_vs_cpu", Integer), # 2 Column("win_cpu", Integer), Column("battle_num_tutorial", Integer), # 1? - mysql_charset="utf8mb4", ) pokemon_data = Table( - "pokken_pokemon_data", + "pokken_pokemon", metadata, Column("id", Integer, primary_key=True, nullable=False), - Column( - "user", - ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), - nullable=False, - ), - Column("char_id", Integer, nullable=False), - Column("illustration_book_no", Integer), + Column("user", Integer, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("char_id", Integer), + Column("illustration_book_no", Integer, nullable=False), # This is the fucking pokedex number???? Column("pokemon_exp", Integer), Column("battle_num_vs_wan", Integer), # 4? Column("win_vs_wan", Integer), @@ -132,8 +122,7 @@ pokemon_data = Table( Column("bp_point_res", Integer), Column("bp_point_def", Integer), Column("bp_point_sp", Integer), - UniqueConstraint("user", "char_id", name="pokken_pokemon_data_uk"), - mysql_charset="utf8mb4", + UniqueConstraint("user", "illustration_book_no", name="pokken_pokemon_uk"), ) @@ -157,8 +146,8 @@ class PokkenProfileData(BaseData): return result.lastrowid async def set_profile_name(self, user_id: int, new_name: str, gender: Union[int, None] = None) -> None: - sql = update(profile).where(profile.c.user == user_id).values( - trainer_name=new_name, + sql = profile.update(profile.c.user == user_id).values( + trainer_name=new_name if new_name else profile.c.trainer_name, avatar_gender=gender if gender is not None else profile.c.avatar_gender ) result = await self.execute(sql) @@ -179,12 +168,12 @@ class PokkenProfileData(BaseData): aid_skill: int, last_evt: int ) -> None: - sql = update(profile).where(profile.c.user == user_id).values( + sql = profile.update(profile.c.user == user_id).values( extra_counter=extra_counter, event_reward_get_flag=evt_reward_get_flg, - total_play_days=total_play_days, - awake_num=awake_num, - use_support_num=use_support_ct, + total_play_days=coalesce(profile.c.total_play_days, 0) + total_play_days, + awake_num=coalesce(profile.c.awake_num, 0) + awake_num, + use_support_num=coalesce(profile.c.use_support_num, 0) + use_support_ct, beat_num=beat_num, aid_skill=aid_skill, last_play_event_id=last_evt @@ -195,7 +184,7 @@ class PokkenProfileData(BaseData): self.logger.error(f"Failed to put extra data for user {user_id}") async def update_profile_tutorial_flags(self, user_id: int, tutorial_flags: List) -> None: - sql = update(profile).where(profile.c.user == user_id).values( + sql = profile.update(profile.c.user == user_id).values( tutorial_progress_flag=tutorial_flags, ) result = await self.execute(sql) @@ -205,7 +194,7 @@ class PokkenProfileData(BaseData): ) async def update_profile_achievement_flags(self, user_id: int, achievement_flags: List) -> None: - sql = update(profile).where(profile.c.user == user_id).values( + sql = profile.update(profile.c.user == user_id).values( achievement_flag=achievement_flags, ) result = await self.execute(sql) @@ -215,7 +204,7 @@ class PokkenProfileData(BaseData): ) async def update_profile_event(self, user_id: int, event_state: List, event_flags: List[int], event_param: List[int], last_evt: int = None) -> None: - sql = update(profile).where(profile.c.user == user_id).values( + sql = profile.update(profile.c.user == user_id).values( event_state=event_state, event_achievement_flag=event_flags, event_achievement_param=event_param, @@ -230,12 +219,16 @@ class PokkenProfileData(BaseData): async def add_profile_points( self, user_id: int, rank_pts: int, money: int, score_pts: int, grade_max: int ) -> None: - sql = update(profile).where(profile.c.user == user_id).values( - trainer_rank_point = profile.c.trainer_rank_point + rank_pts, - fight_money = profile.c.fight_money + money, - score_point = profile.c.score_point + score_pts, + sql = profile.update(profile.c.user == user_id).values( + trainer_rank_point = coalesce(profile.c.trainer_rank_point, 0) + rank_pts, + wallet = coalesce(profile.c.wallet, 0) + money, + score_point = coalesce(profile.c.score_point, 0) + score_pts, grade_max_num = grade_max ) + + result = await self.execute(sql) + if result is None: + return None async def get_profile(self, user_id: int) -> Optional[Row]: sql = profile.select(profile.c.user == user_id) @@ -248,7 +241,7 @@ class PokkenProfileData(BaseData): self, user_id: int, pokemon_id: int, - illust_no: int, + pokedex_number: int, atk: int, res: int, defe: int, @@ -257,7 +250,7 @@ class PokkenProfileData(BaseData): sql = insert(pokemon_data).values( user=user_id, char_id=pokemon_id, - illustration_book_no=illust_no, + illustration_book_no=pokedex_number, pokemon_exp=0, battle_num_vs_wan=0, win_vs_wan=0, @@ -274,7 +267,7 @@ class PokkenProfileData(BaseData): ) conflict = sql.on_duplicate_key_update( - illustration_book_no=illust_no, + illustration_book_no=pokedex_number, bp_point_atk=pokemon_data.c.bp_point_atk + atk, bp_point_res=pokemon_data.c.bp_point_res + res, bp_point_def=pokemon_data.c.bp_point_def + defe, @@ -293,7 +286,7 @@ class PokkenProfileData(BaseData): pokemon_id: int, xp: int ) -> None: - sql = update(pokemon_data).where(and_(pokemon_data.c.user==user_id, pokemon_data.c.char_id==pokemon_id)).values( + sql = pokemon_data.update(and_(pokemon_data.c.user==user_id, pokemon_data.c.illustration_book_no==pokemon_id)).values( pokemon_exp=coalesce(pokemon_data.c.pokemon_exp, 0) + xp ) @@ -315,6 +308,14 @@ class PokkenProfileData(BaseData): return None return result.fetchall() + async def set_latest_mon(self, user_id: int, pokedex_no: int) -> None: + sql = profile.update(profile.c.user == user_id).values( + latest_use_pokemon=pokedex_no + ) + result = await self.execute(sql) + if result is None: + self.logger.warning(f"Failed to update user {user_id}'s last used pokemon {pokedex_no}") + async def put_pokemon_battle_result( self, user_id: int, pokemon_id: int, match_type: PokkenConstants.BATTLE_TYPE, match_result: PokkenConstants.BATTLE_RESULT ) -> None: @@ -322,7 +323,7 @@ class PokkenProfileData(BaseData): Records the match stats (type and win/loss) for the pokemon and profile coalesce(pokemon_data.c.win_vs_wan, 0) """ - sql = update(pokemon_data).where(and_(pokemon_data.c.user==user_id, pokemon_data.c.char_id==pokemon_id)).values( + sql = pokemon_data.update(and_(pokemon_data.c.user==user_id, pokemon_data.c.char_id==pokemon_id)).values( battle_num_tutorial=coalesce(pokemon_data.c.battle_num_tutorial, 0) + 1 if match_type==PokkenConstants.BATTLE_TYPE.TUTORIAL else coalesce(pokemon_data.c.battle_num_tutorial, 0), battle_all_num_tutorial=coalesce(pokemon_data.c.battle_all_num_tutorial, 0) + 1 if match_type==PokkenConstants.BATTLE_TYPE.TUTORIAL else coalesce(pokemon_data.c.battle_all_num_tutorial, 0), @@ -353,7 +354,7 @@ class PokkenProfileData(BaseData): """ Records profile stats """ - sql = update(profile).where(profile.c.user==user_id).values( + sql = profile.update(profile.c.user==user_id).values( ex_ko_num=coalesce(profile.c.ex_ko_num, 0) + exkos, wko_num=coalesce(profile.c.wko_num, 0) + wkos, timeup_win_num=coalesce(profile.c.timeup_win_num, 0) + timeout_wins, @@ -367,7 +368,12 @@ class PokkenProfileData(BaseData): self.logger.warning(f"Failed to update stats for user {user_id}") async def update_support_team(self, user_id: int, support_id: int, support1: int = None, support2: int = None) -> None: - sql = update(profile).where(profile.c.user==user_id).values( + if support1 == 4294967295: + support1 = None + + if support2 == 4294967295: + support2 = None + sql = profile.update(profile.c.user==user_id).values( support_set_1_1=support1 if support_id == 1 else profile.c.support_set_1_1, support_set_1_2=support2 if support_id == 1 else profile.c.support_set_1_2, support_set_2_1=support1 if support_id == 2 else profile.c.support_set_2_1, @@ -379,3 +385,15 @@ class PokkenProfileData(BaseData): result = await self.execute(sql) if result is None: self.logger.warning(f"Failed to update support team {support_id} for user {user_id}") + + async def update_rankmatch_data(self, user_id: int, flag: int, rm_max: Optional[int], success: Optional[int], progress: List[int]) -> None: + sql = profile.update(profile.c.user==user_id).values( + rankmatch_flag=flag, + rankmatch_max=rm_max, + rankmatch_progress=progress, + rankmatch_success=success, + ) + + result = await self.execute(sql) + if result is None: + self.logger.warning(f"Failed to update rankmatch data for user {user_id}") From fe4d978f7025c6565e54f8c838ad8b00f423e3db Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Tue, 11 Jun 2024 13:56:39 -0400 Subject: [PATCH 03/16] pokken: fix bnp_baseuri --- titles/pokken/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/titles/pokken/base.py b/titles/pokken/base.py index e22ad74..5830f43 100644 --- a/titles/pokken/base.py +++ b/titles/pokken/base.py @@ -81,7 +81,7 @@ class PokkenBase: "logfilename": "J:\\JackalMatchingLibrary.log", "biwalogfilename": "J:\\biwa_log.log", } - regist_pcb.bnp_baseuri = f"{self.core_cfg.mucha.hostname}/bna" + regist_pcb.bnp_baseuri = f"{self.core_cfg.server.hostname}/bna" regist_pcb.biwa_setting = json.dumps(biwa_setting) res.register_pcb.CopyFrom(regist_pcb) From eaab3728c45d36be0b6b47107cb94ec0f84c6d7f Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Tue, 11 Jun 2024 14:00:29 -0400 Subject: [PATCH 04/16] pokken: add additional logging --- titles/pokken/base.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/titles/pokken/base.py b/titles/pokken/base.py index 5830f43..199d226 100644 --- a/titles/pokken/base.py +++ b/titles/pokken/base.py @@ -184,15 +184,14 @@ class PokkenBase: if profile is None or profile['trainer_name'] is None: profile_id = await self.data.profile.create_profile(user_id) + self.logger.info(f"Create new profile {profile_id} for user {user_id}") profile_dict = {"id": profile_id, "user": user_id} pokemon_data = [] load_usr.new_card_flag = True else: profile_dict = {k: v for k, v in profile._asdict().items() if v is not None} - self.logger.info( - f"Card-in user {user_id} (Trainer name {profile_dict.get('trainer_name', '')})" - ) + self.logger.info(f"Card-in user {user_id} (Trainer name {profile_dict.get('trainer_name', '')})") pokemon_data = await self.data.profile.get_all_pokemon_data(user_id) load_usr.new_card_flag = False From e21568cfd96461506438fdbb24073d964d84167a Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Wed, 12 Jun 2024 23:24:45 -0400 Subject: [PATCH 05/16] pokken: fix profile loading fail --- core/utils.py | 2 +- titles/pokken/base.py | 2 +- titles/pokken/schema/profile.py | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/utils.py b/core/utils.py index a9d1705..24c174c 100644 --- a/core/utils.py +++ b/core/utils.py @@ -49,7 +49,7 @@ class Utils: def get_title_port_ssl(cls, cfg: CoreConfig): if cls.real_title_port_ssl is not None: return cls.real_title_port_ssl - cls.real_title_port_ssl = cfg.server.proxy_port_ssl if cfg.server.is_using_proxy and cfg.server.proxy_port_ssl else Utils.get_title_port(cfg) + cls.real_title_port_ssl = cfg.server.proxy_port_ssl if cfg.server.is_using_proxy and cfg.server.proxy_port_ssl else 443 return cls.real_title_port_ssl diff --git a/titles/pokken/base.py b/titles/pokken/base.py index 199d226..de974a3 100644 --- a/titles/pokken/base.py +++ b/titles/pokken/base.py @@ -385,7 +385,7 @@ class PokkenBase: await self.data.profile.add_profile_points(user_id, get_rank_pts, get_money, get_score_pts, grade_max) # Inconsistant underscore use AND a typo?? - await self.data.profile.update_rankmatch_data(user_id, req.rankmatch_flag, req.rank_match_max, req.rank_match_success, req.rank_match_process) + #await self.data.profile.update_rankmatch_data(user_id, req.rankmatch_flag, req.rank_match_max, req.rank_match_success, req.rank_match_process) await self.data.profile.update_support_team(user_id, 1, req.support_set_1[0], req.support_set_1[1]) await self.data.profile.update_support_team(user_id, 2, req.support_set_2[0], req.support_set_2[1]) diff --git a/titles/pokken/schema/profile.py b/titles/pokken/schema/profile.py index 38b93e7..f66ebb6 100644 --- a/titles/pokken/schema/profile.py +++ b/titles/pokken/schema/profile.py @@ -5,7 +5,7 @@ from sqlalchemy.schema import ForeignKey from sqlalchemy.sql import func, select, update, delete from sqlalchemy.sql.functions import coalesce from sqlalchemy.engine import Row -from sqlalchemy.dialects.postgresql import insert +from sqlalchemy.dialects.mysql import insert from core.data.schema import BaseData, metadata from ..const import PokkenConstants @@ -103,7 +103,7 @@ profile = Table( ) pokemon_data = Table( - "pokken_pokemon", + "pokken_pokemon_data", metadata, Column("id", Integer, primary_key=True, nullable=False), Column("user", Integer, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), @@ -295,7 +295,7 @@ class PokkenProfileData(BaseData): self.logger.warning(f"Failed to add {xp} XP to pokemon ID {pokemon_id} for user {user_id}") async def get_pokemon_data(self, user_id: int, pokemon_id: int) -> Optional[Row]: - sql = pokemon_data.select(and_(pokemon_data.c.user == user_id, pokemon_data.c.char_id == pokemon_id)) + sql = pokemon_data.select(and_(pokemon_data.c.user == user_id, pokemon_data.c.illustration_book_no == pokemon_id)) result = await self.execute(sql) if result is None: return None @@ -323,7 +323,7 @@ class PokkenProfileData(BaseData): Records the match stats (type and win/loss) for the pokemon and profile coalesce(pokemon_data.c.win_vs_wan, 0) """ - sql = pokemon_data.update(and_(pokemon_data.c.user==user_id, pokemon_data.c.char_id==pokemon_id)).values( + sql = pokemon_data.update(and_(pokemon_data.c.user==user_id, pokemon_data.c.illustration_book_no==pokemon_id)).values( battle_num_tutorial=coalesce(pokemon_data.c.battle_num_tutorial, 0) + 1 if match_type==PokkenConstants.BATTLE_TYPE.TUTORIAL else coalesce(pokemon_data.c.battle_num_tutorial, 0), battle_all_num_tutorial=coalesce(pokemon_data.c.battle_all_num_tutorial, 0) + 1 if match_type==PokkenConstants.BATTLE_TYPE.TUTORIAL else coalesce(pokemon_data.c.battle_all_num_tutorial, 0), From ee7d5c4e217dae3c92d56ce6972c9a0c1b6b2f22 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Thu, 13 Jun 2024 14:14:07 -0400 Subject: [PATCH 06/16] pokken: readd mysql_charset to profile and pokemon data --- titles/pokken/data/fighters.json | 140 +++++++++++++++++++++++++++++++ titles/pokken/data/support.json | 3 + titles/pokken/schema/profile.py | 4 +- 3 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 titles/pokken/data/fighters.json create mode 100644 titles/pokken/data/support.json diff --git a/titles/pokken/data/fighters.json b/titles/pokken/data/fighters.json new file mode 100644 index 0000000..cc22c32 --- /dev/null +++ b/titles/pokken/data/fighters.json @@ -0,0 +1,140 @@ +{ + "448": { + "name_en": "Lucario", + "name_jp": "ルカリオ", + "type": 0, + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/448.png" + }, + "25": { + "name_en": "Pikachu", + "name_jp": "ピカチュウ", + "type": 0, + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/25.png" + }, + "68": { + "name_en": "Machamp", + "name_jp": "カイリキー", + "type": 1, + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/68.png" + }, + "282": { + "name_en": "Gardevoir", + "name_jp": "サーナイト", + "type": 2, + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/282.png" + }, + "461": { + "name_en": "Weavile", + "name_jp": "マニューラ", + "type": 3, + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/461.png" + }, + "245": { + "name_en": "Suicune", + "name_jp": "スイクン", + "type": 0, + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/245.png" + }, + "6": { + "name_en": "Charizard", + "name_jp": "リザードン", + "type": 1, + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/6.png" + }, + "94": { + "name_en": "Gengar", + "name_jp": "ゲンガー", + "type": 2, + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/94.png" + }, + "257": { + "name_en": "Blaziken", + "name_jp": "バシャーモ", + "type": 0, + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/257.png" + }, + "4029": { + "name_en": "Pikachu Libre", + "name_jp": "マスクド・ピカチュウ", + "type": 3, + "artwork": "https://archives.bulbagarden.net/media/upload/thumb/6/63/Pokk%C3%A9n_Pikachu_Libre.png/663px-Pokk%C3%A9n_Pikachu_Libre.png" + }, + "254": { + "name_en": "Sceptile", + "name_jp": "ジュカイン", + "type": 3, + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/254.png" + }, + "609": { + "name_en": "Chandelure", + "name_jp": "シャンデラ", + "type": 1, + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/609.png" + }, + "150": { + "name_en": "Mewtwo", + "name_jp": "ミュウツー", + "type": 0, + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/150.png" + }, + "10150": { + "name_en": "Shadow Mewtwo", + "name_jp": "ダークミュウツー", + "type": 2, + "artwork": "https://archives.bulbagarden.net/media/upload/7/7a/Pokk%C3%A9n_Shadow_Mewtwo.png" + }, + "445": { + "name_en": "Garchomp", + "name_jp": "ガブリアス", + "type": 1, + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/445.png" + }, + "654": { + "name_en": "Braixen", + "name_jp": "テールナー", + "type": 0, + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/654.png" + }, + "491": { + "name_en": "Darkrai", + "name_jp": "ダークライ", + "type": 2, + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/491.png" + }, + "212": { + "name_en": "Scizor", + "name_jp": "ハッサム", + "type": 1, + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/212.png" + }, + "453": { + "name_en": "Croagunk", + "name_jp": "グレッグル", + "type": 3, + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/453.png" + }, + "395": { + "name_en": "Empoleon", + "name_jp": "エンペルト", + "type": 0, + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/395.png" + }, + "724": { + "name_en": "Decidueye", + "name_jp": "ジュナイパー", + "type": 0, + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/724.png" + }, + "681": { + "name_en": "Aegislash", + "name_jp": "ギルガルド", + "type": 2, + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/681.png" + }, + "9": { + "name_en": "Blastoise", + "name_jp": "カメックス", + "type": 1, + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/9.png" + } +} \ No newline at end of file diff --git a/titles/pokken/data/support.json b/titles/pokken/data/support.json new file mode 100644 index 0000000..a514dfe --- /dev/null +++ b/titles/pokken/data/support.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/titles/pokken/schema/profile.py b/titles/pokken/schema/profile.py index f66ebb6..2976ff7 100644 --- a/titles/pokken/schema/profile.py +++ b/titles/pokken/schema/profile.py @@ -61,7 +61,7 @@ profile = Table( Column("navi_trainer", Integer), Column("navi_version_id", Integer), Column("aid_skill_list", JSON), # Repeated, Integer - Column("aid_skill", Integer), + Column("aid_skill", Integer), # Cheer skill, 6 of them, unlocked by lucky bonus Column("comment_text_id", Integer), Column("comment_word_id", Integer), Column("latest_use_pokemon", Integer), @@ -100,6 +100,7 @@ profile = Table( Column("battle_num_vs_cpu", Integer), # 2 Column("win_cpu", Integer), Column("battle_num_tutorial", Integer), # 1? + mysql_charset="utf8mb4" ) pokemon_data = Table( @@ -123,6 +124,7 @@ pokemon_data = Table( Column("bp_point_def", Integer), Column("bp_point_sp", Integer), UniqueConstraint("user", "illustration_book_no", name="pokken_pokemon_uk"), + mysql_charset="utf8mb4" ) From a523c25d8421c983f72632ebc6a59f855fee665e Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Thu, 13 Jun 2024 18:06:16 -0400 Subject: [PATCH 07/16] pokken: finish filling out support list --- titles/pokken/data/support.json | 217 +++++++++++++++++++++++++++++++- 1 file changed, 216 insertions(+), 1 deletion(-) diff --git a/titles/pokken/data/support.json b/titles/pokken/data/support.json index a514dfe..ef9f079 100644 --- a/titles/pokken/data/support.json +++ b/titles/pokken/data/support.json @@ -1,3 +1,218 @@ { - + "587": { + "name_en": "Emolga", + "name_jp": "エモンガ", + "desc": "Uses Shock Wave to shock the opponent and temporarily decrease its speed.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/587.png" + }, + "653": { + "name_en": "Fennekin", + "name_jp": "フォッコ", + "desc": "Uses Ember to surround itself with fire, creating a trap.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/653.png" + }, + "495": { + "name_en": "Snivy", + "name_jp": "ツタージャ", + "desc": "Uses Leaf Tornado to perform an anti-air attack and send the opponent flying.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/495.png" + }, + "131": { + "name_en": "Lapras", + "name_jp": "ラプラス", + "desc": "Uses Surf as it enters the stage, damaging the enemy with a wave of water.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/131.png" + }, + "657": { + "name_en": "Frogadier", + "name_jp": "ゲコガシラ", + "desc": "Uses Water Pulse to attack from a distance by firing water bullets. Effective when striking from long distance.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/657.png" + }, + "133": { + "name_en": "Eevee", + "name_jp": "イーブイ", + "desc": "Uses Helping Hand to heal the user and temporarily increase their attack power.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/133.png" + }, + "385": { + "name_en": "Jirachi", + "name_jp": "ジラーチ", + "desc": "Uses Wish to restore the Synergy Gauge and temporarily strengthen the user's attack power during Synergy Burst.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/385.png" + }, + "547": { + "name_en": "Whimsicott", + "name_jp": "エルフーン", + "desc": "Uses Substitute to render attacks from opponents useless and heal the user.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/547.png" + }, + "38": { + "name_en": "Ninetales", + "name_jp": "キュウコン", + "desc": "Uses Will-O-Wisp to send small flames in front of the user. Enemy's attack power decreased temporarily when contacted.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/38.png" + }, + "429": { + "name_en": "Mismagius", + "name_jp": "ムウマージ", + "desc": "Uses Ominous Wind to attack the opponent and temporarily increase the user's attack power.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/429.png" + }, + "83": { + "name_en": "Farfetch'd", + "name_jp": "カモネギ", + "desc": "Uses Fury Cutter to perform a flurry of attacks toward the opponent.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/83.png" + }, + "101": { + "name_en": "Electrode", + "name_jp": "マルマイン", + "desc": "Uses Explosion to counter an opponent's attack upon defending.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/101.png" + }, + "479": { + "name_en": "Rotom", + "name_jp": "ロトム", + "desc": "Uses Thunder Shock to target enemies in the air and temporarily decrease their speed.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/479.png" + }, + "468": { + "name_en": "Togekiss", + "name_jp": "トゲキッス", + "desc": "Uses Tailwind to temporarily increase the user's speed and recover some health.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/468.png" + }, + "149": { + "name_en": "Dragonite", + "name_jp": "カイリュー", + "desc": "Uses Draco Meteor to attack multiple times over a wide area.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/149.png" + }, + "494": { + "name_en": "Victini", + "name_jp": "ビクティニ", + "desc": "Uses V-create to temporarily make the user's attacks critical hits, restores some of the user's health, and increases the user's Synergy Gauge. Unlike other Enhance Pokémon, Victini can actually damage the foe if they're above it when flying off the screen.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/494.png" + }, + "453": { + "name_en": "Croagunk", + "name_jp": "グレッグル", + "desc": "Uses Toxic to attack opponent and temporarily decrease its defense.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/453.png" + }, + "700": { + "name_en": "Sylveon", + "name_jp": "ニンフィア", + "desc": "Uses Reflect to heal user and temporarily increase their defense.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/700.png" + }, + "417": { + "name_en": "Pachirisu", + "name_jp": "パチリス", + "desc": "Uses Follow Me to eliminate long distance attacks. Effective when get in close.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/417.png" + }, + "129": { + "name_en": "Magikarp", + "name_jp": "コイキング", + "desc": "Uses Bounce to disrupt the enemy's attack when hit by an opponent. Effective for interrupting combos.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/129.png" + }, + "104": { + "name_en": "Cubone", + "name_jp": "カラカラ", + "desc": "Uses Bonemerang to attack from a distance and can pull an enemy in.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/104.png" + }, + "50": { + "name_en": "Diglett", + "name_jp": "ディグダ", + "desc": "Uses Dig to attack from below, making easy to aim for a combo.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/50.png" + }, + "82": { + "name_en": "Magneton", + "name_jp": "レアコイル", + "desc": "Uses Tri Attack to attack from a distance diagonally upward and inflict two random negative statuses.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/82.png" + }, + "195": { + "name_en": "Quagsire", + "name_jp": "ヌオー", + "desc": "Uses Mud Bomb to attack opponent on the ground, even when blocked.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/195.png" + }, + "196": { + "name_en": "Espeon", + "name_jp": "エーフィ", + "desc": "Uses Morning Sun to remove any statuses and recover health, with more health recovered with less time remaining in the round.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/196.png" + }, + "197": { + "name_en": "Umbreon", + "name_jp": "ブラッキー", + "desc": "Uses Snarl to absorb an opponent's Synergy Gauge and prevent them from performing any critical hits.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/197.png" + }, + "643": { + "name_en": "Reshiram", + "name_jp": "レシラム", + "desc": "Uses Blue Flare to attack straight forward with a powerful flame. In the DX version, it can only be called once per round.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/643.png" + }, + "488": { + "name_en": "Cresselia", + "name_jp": "クレセリア", + "desc": "Uses Lunar Dance to heal the user of any negative status, recovers health and Synergy Gauge, but can only be used once per round.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/488.png" + }, + "717": { + "name_en": "Yveltal", + "name_jp": "イベルタル", + "desc": "Uses Oblivion Wing to attack from the sky and seal off the opponent's Synergy Burst. In the DX version, it can only be called once per round.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/717.png" + }, + "381": { + "name_en": "Latios", + "name_jp": "ラティオス", + "desc": "Uses Luster Purge to place attacks around the enemy in order to restrict their movements. In the DX version, it can only be called once per round.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/381.png" + }, + "725": { + "name_en": "Litten", + "name_jp": "ニャビー", + "desc": "Uses Fire Fang to attack toward the enemy. Damage increases when the player's at lower HP.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/725.png" + }, + "728": { + "name_en": "Popplio", + "name_jp": "アシマリ", + "desc": "Uses Bubble Beam to temporarily increase attack and grant a double jump while in midair.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/728.png" + }, + "384": { + "name_en": "Mega Rayquaza", + "name_jp": "レックウザ", + "desc": "Uses Dragon Ascent to attack from a distance at tremendous speed. It also consumes the user's Synergy Gauge. It can only be called once per round.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/384.png" + }, + "778": { + "name_en": "Mimikyu", + "name_jp": "ミミッキュ", + "desc": "Uses Play Rough to attack continuously from behind and inflict double negative status.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/778.png" + }, + "151": { + "name_en": "Mew", + "name_jp": "ミュウ", + "desc": "Uses Miraculous Power to randomly increase the user's Synergy Gauge, temporarily makes the user's attacks critical hits, and/or gives the user additional random positive status.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/151.png" + }, + "251": { + "name_en": "Celebi", + "name_jp": "セレビィ", + "desc": "Uses Time Travel (Japanese: ときわたり Time Travel) to switch between Phases at almost any given moment, even when enemy guards an attack.", + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/251.png" + } } \ No newline at end of file From bf54969bc160609066685a25b24bc33e7617ffe6 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Thu, 13 Jun 2024 18:15:38 -0400 Subject: [PATCH 08/16] pokken: fix some image links --- titles/pokken/data/fighters.json | 2 +- titles/pokken/data/support.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/titles/pokken/data/fighters.json b/titles/pokken/data/fighters.json index cc22c32..90681ec 100644 --- a/titles/pokken/data/fighters.json +++ b/titles/pokken/data/fighters.json @@ -57,7 +57,7 @@ "name_en": "Pikachu Libre", "name_jp": "マスクド・ピカチュウ", "type": 3, - "artwork": "https://archives.bulbagarden.net/media/upload/thumb/6/63/Pokk%C3%A9n_Pikachu_Libre.png/663px-Pokk%C3%A9n_Pikachu_Libre.png" + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/10084.png" }, "254": { "name_en": "Sceptile", diff --git a/titles/pokken/data/support.json b/titles/pokken/data/support.json index ef9f079..ef5368e 100644 --- a/titles/pokken/data/support.json +++ b/titles/pokken/data/support.json @@ -191,11 +191,11 @@ "desc": "Uses Bubble Beam to temporarily increase attack and grant a double jump while in midair.", "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/728.png" }, - "384": { + "10079": { "name_en": "Mega Rayquaza", "name_jp": "レックウザ", "desc": "Uses Dragon Ascent to attack from a distance at tremendous speed. It also consumes the user's Synergy Gauge. It can only be called once per round.", - "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/384.png" + "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/10079.png" }, "778": { "name_en": "Mimikyu", From 00224585bb8772046061a36d4c6fab6eab2ccde4 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Fri, 14 Jun 2024 00:06:39 -0400 Subject: [PATCH 09/16] pokken: fix pokemon_data uk --- .../3657efefc5a4_pokken_fix_pokemon_uk.py | 54 +++++++++++++++++++ titles/pokken/base.py | 12 ++--- titles/pokken/data/fighters.json | 2 +- 3 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 core/data/alembic/versions/3657efefc5a4_pokken_fix_pokemon_uk.py diff --git a/core/data/alembic/versions/3657efefc5a4_pokken_fix_pokemon_uk.py b/core/data/alembic/versions/3657efefc5a4_pokken_fix_pokemon_uk.py new file mode 100644 index 0000000..2867fb7 --- /dev/null +++ b/core/data/alembic/versions/3657efefc5a4_pokken_fix_pokemon_uk.py @@ -0,0 +1,54 @@ +"""pokken_fix_pokemon_uk + +Revision ID: 3657efefc5a4 +Revises: 4a02e623e5e6 +Create Date: 2024-06-13 23:50:57.611998 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import mysql + +# revision identifiers, used by Alembic. +revision = '3657efefc5a4' +down_revision = '4a02e623e5e6' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('pokken_pokemon_data', 'char_id', + existing_type=mysql.INTEGER(display_width=11), + nullable=True) + op.alter_column('pokken_pokemon_data', 'illustration_book_no', + existing_type=mysql.INTEGER(display_width=11), + nullable=False) + op.drop_constraint('pokken_pokemon_data_ibfk_1', table_name='pokken_pokemon_data', type_='foreignkey') + op.drop_index('pokken_pokemon_data_uk', table_name='pokken_pokemon_data') + op.create_unique_constraint('pokken_pokemon_uk', 'pokken_pokemon_data', ['user', 'illustration_book_no']) + op.create_foreign_key("pokken_pokemon_data_ibfk_1", "pokken_pokemon_data", "aime_user", ['user'], ['id']) + op.alter_column('pokken_profile', 'trainer_name', + existing_type=mysql.VARCHAR(length=16), + type_=sa.String(length=14), + existing_nullable=True) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('pokken_profile', 'trainer_name', + existing_type=sa.String(length=14), + type_=mysql.VARCHAR(length=16), + existing_nullable=True) + op.drop_constraint('pokken_pokemon_data_ibfk_1', table_name='pokken_pokemon_data', type_='foreignkey') + op.drop_constraint('pokken_pokemon_uk', 'pokken_pokemon_data', type_='unique') + op.create_index('pokken_pokemon_data_uk', 'pokken_pokemon_data', ['user', 'char_id'], unique=True) + op.create_foreign_key("pokken_pokemon_data_ibfk_1", "pokken_pokemon_data", "aime_user", ['user'], ['id']) + op.alter_column('pokken_pokemon_data', 'illustration_book_no', + existing_type=mysql.INTEGER(display_width=11), + nullable=True) + op.alter_column('pokken_pokemon_data', 'char_id', + existing_type=mysql.INTEGER(display_width=11), + nullable=False) + # ### end Alembic commands ### diff --git a/titles/pokken/base.py b/titles/pokken/base.py index de974a3..fe11e99 100644 --- a/titles/pokken/base.py +++ b/titles/pokken/base.py @@ -305,12 +305,12 @@ class PokkenBase: for x in profile_dict.get("event_achievement_param", []): load_usr.event_achievement_param.append(x) - load_usr.support_set_1.append(profile_dict.get("support_set_1_1", 4294967295)) - load_usr.support_set_1.append(profile_dict.get("support_set_1_2", 4294967295)) - load_usr.support_set_2.append(profile_dict.get("support_set_2_1", 4294967295)) - load_usr.support_set_2.append(profile_dict.get("support_set_2_2", 4294967295)) - load_usr.support_set_3.append(profile_dict.get("support_set_3_1", 4294967295)) - load_usr.support_set_3.append(profile_dict.get("support_set_3_2", 4294967295)) + load_usr.support_set_1.append(profile_dict.get("support_set_1_1", 587)) + load_usr.support_set_1.append(profile_dict.get("support_set_1_2", 653)) + load_usr.support_set_2.append(profile_dict.get("support_set_2_1", 495)) + load_usr.support_set_2.append(profile_dict.get("support_set_2_2", 131)) + load_usr.support_set_3.append(profile_dict.get("support_set_3_1", 657)) + load_usr.support_set_3.append(profile_dict.get("support_set_3_2", 133)) res.load_user.CopyFrom(load_usr) return res.SerializeToString() diff --git a/titles/pokken/data/fighters.json b/titles/pokken/data/fighters.json index 90681ec..a5ba6f8 100644 --- a/titles/pokken/data/fighters.json +++ b/titles/pokken/data/fighters.json @@ -53,7 +53,7 @@ "type": 0, "artwork": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/257.png" }, - "4029": { + "10025": { "name_en": "Pikachu Libre", "name_jp": "マスクド・ピカチュウ", "type": 3, From 784ac544f070c8c620aa7c6523d585d5dd2e6ba3 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Sun, 16 Jun 2024 14:42:23 -0400 Subject: [PATCH 10/16] fix event log ordering, paging, add timestamp --- core/data/schema/base.py | 2 +- core/templates/sys/logs.jinja | 40 ++++++++++++++++++++++++----------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/core/data/schema/base.py b/core/data/schema/base.py index e315964..d74198b 100644 --- a/core/data/schema/base.py +++ b/core/data/schema/base.py @@ -109,7 +109,7 @@ class BaseData: return result.lastrowid async def get_event_log(self, entries: int = 100) -> Optional[List[Row]]: - sql = event_log.select().limit(entries) + sql = event_log.select().order_by(event_log.c.id.desc()).limit(entries) result = await self.execute(sql) if result is None: diff --git a/core/templates/sys/logs.jinja b/core/templates/sys/logs.jinja index 3dfe531..9b7a987 100644 --- a/core/templates/sys/logs.jinja +++ b/core/templates/sys/logs.jinja @@ -6,6 +6,7 @@ Severity + Timestamp System Name User @@ -19,7 +20,7 @@ {% if events is not defined or events|length == 0 %} - No Events + No Events {% endif %} @@ -66,7 +67,11 @@ function update_tbl() { for (var i = 0; i < per_page; i++) { let off = (page * per_page) + i; - if (off >= TBL_DATA.length ) { + if (off >= TBL_DATA.length) { + if (page != 0) { + document.getElementById("btn_next").disabled = true; + document.getElementById("btn_prev").disabled = false; + } break; } @@ -99,56 +104,59 @@ function update_tbl() { row.classList.add("table-primary"); break; } + + var cell_ts = row.insertCell(1); + cell_ts.innerHTML = data.when_logged; - var cell_mod = row.insertCell(1); + var cell_mod = row.insertCell(2); cell_mod.innerHTML = data.system; - var cell_name = row.insertCell(2); + var cell_name = row.insertCell(3); cell_name.innerHTML = data.type; - var cell_usr = row.insertCell(3); + var cell_usr = row.insertCell(4); if (data.user == 'NONE') { cell_usr.innerHTML = "---"; } else { cell_usr.innerHTML = "" + data.user + ""; } - var cell_arcade = row.insertCell(4); + var cell_arcade = row.insertCell(5); if (data.arcade == 'NONE') { cell_arcade.innerHTML = "---"; } else { cell_arcade.innerHTML = "" + data.arcade + ""; } - var cell_machine = row.insertCell(5); + var cell_machine = row.insertCell(6); if (data.arcade == 'NONE') { cell_machine.innerHTML = "---"; } else { cell_machine.innerHTML = "" + data.machine + ""; } - var cell_game = row.insertCell(6); + var cell_game = row.insertCell(7); if (data.game == 'NONE') { cell_game.innerHTML = "---"; } else { cell_game.innerHTML = data.game; } - var cell_version = row.insertCell(7); + var cell_version = row.insertCell(8); if (data.version == 'NONE') { cell_version.innerHTML = "---"; } else { cell_version.innerHTML = data.version; } - var cell_msg = row.insertCell(8); + var cell_msg = row.insertCell(9); if (data.message == '') { cell_msg.innerHTML = "---"; } else { cell_msg.innerHTML = data.message; } - var cell_deets = row.insertCell(9); + var cell_deets = row.insertCell(10); if (data.details == '{}') { cell_deets.innerHTML = "---"; } else { @@ -160,9 +168,11 @@ function update_tbl() { function chg_page(num) { var max_page = TBL_DATA.length / per_page; + console.log(max_page); page = page + num; - if (page > max_page) { + + if (page > max_page && max_page >= 1) { page = max_page; document.getElementById("btn_next").disabled = true; document.getElementById("btn_prev").disabled = false; @@ -172,6 +182,12 @@ function chg_page(num) { document.getElementById("btn_next").disabled = false; document.getElementById("btn_prev").disabled = true; return; + } else if (page == 0) { + document.getElementById("btn_next").disabled = false; + document.getElementById("btn_prev").disabled = true; + } else { + document.getElementById("btn_next").disabled = false; + document.getElementById("btn_prev").disabled = false; } update_tbl(); From 68bf3843ec0515f9c956a62b9319ffe6667de939 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Sun, 16 Jun 2024 14:45:41 -0400 Subject: [PATCH 11/16] pokken: fix default title plate id --- titles/pokken/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/titles/pokken/base.py b/titles/pokken/base.py index fe11e99..f864824 100644 --- a/titles/pokken/base.py +++ b/titles/pokken/base.py @@ -226,7 +226,7 @@ class PokkenBase: load_usr.rankmatch_success = profile_dict.get("rankmatch_success", 0) load_usr.beat_num = profile_dict.get("beat_num", 0) load_usr.title_text_id = profile_dict.get("title_text_id", 2) - load_usr.title_plate_id = profile_dict.get("title_plate_id", 1) + load_usr.title_plate_id = profile_dict.get("title_plate_id", 31) load_usr.title_decoration_id = profile_dict.get("title_decoration_id", 1) load_usr.navi_trainer = profile_dict.get("navi_trainer", 0) load_usr.navi_version_id = profile_dict.get("navi_version_id", 0) From 766912c51d3a35e9fc1d7ed8606bc39956cfcf65 Mon Sep 17 00:00:00 2001 From: zaphkito Date: Tue, 18 Jun 2024 03:13:32 +0000 Subject: [PATCH 12/16] ongeki: fix base version title work --- titles/ongeki/index.py | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/titles/ongeki/index.py b/titles/ongeki/index.py index d7f3e0e..f91bf8f 100644 --- a/titles/ongeki/index.py +++ b/titles/ongeki/index.py @@ -216,16 +216,20 @@ class OngekiServlet(BaseServlet): ) return Response(zlib.compress(b'{"stat": "0"}')) - try: - unzip = zlib.decompress(req_raw) + if version < 105: + # O.N.G.E.K.I base don't use zlib + req_data = req_raw + else: + try: + unzip = zlib.decompress(req_raw) + + except zlib.error as e: + self.logger.error( + f"Failed to decompress v{version} {endpoint} request -> {e}" + ) + return Response(zlib.compress(b'{"stat": "0"}')) - except zlib.error as e: - self.logger.error( - f"Failed to decompress v{version} {endpoint} request -> {e}" - ) - return Response(zlib.compress(b'{"stat": "0"}')) - - req_data = json.loads(unzip) + req_data = json.loads(unzip) self.logger.info( f"v{version} {endpoint} request from {client_ip}" @@ -251,9 +255,12 @@ class OngekiServlet(BaseServlet): self.logger.debug(f"Response {resp}") - zipped = zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8")) + resp_raw = json.dumps(resp, ensure_ascii=False).encode("utf-8") + zipped = zlib.compress(resp_raw) if not encrtped or version < 120: + if version < 105: + return Response(resp_raw) return Response(zipped) padded = pad(zipped, 16) From 51f65f9293020a0cefd0520638688ae82de95691 Mon Sep 17 00:00:00 2001 From: zaphkito Date: Tue, 18 Jun 2024 04:15:40 +0000 Subject: [PATCH 13/16] ongeki: I forgot json load --- 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 f91bf8f..3bd0e15 100644 --- a/titles/ongeki/index.py +++ b/titles/ongeki/index.py @@ -218,7 +218,7 @@ class OngekiServlet(BaseServlet): if version < 105: # O.N.G.E.K.I base don't use zlib - req_data = req_raw + req_data = json.loads(req_raw) else: try: unzip = zlib.decompress(req_raw) From 5378655c520d723ca3977bedbbf0d8f4359372f2 Mon Sep 17 00:00:00 2001 From: beerpsi Date: Thu, 20 Jun 2024 08:11:24 +0700 Subject: [PATCH 14/16] [chunithm] Support LUMINOUS --- changelog.md | 4 + .../b23f985100ba_chunithm_luminous.py | 87 +++++++ docs/game_specific_info.md | 1 + example_config/chuni.yaml | 3 + titles/chuni/const.py | 20 +- titles/chuni/index.py | 21 +- titles/chuni/luminous.py | 244 ++++++++++++++++++ titles/chuni/new.py | 2 + titles/chuni/schema/item.py | 93 +++++++ 9 files changed, 467 insertions(+), 8 deletions(-) create mode 100644 core/data/alembic/versions/b23f985100ba_chunithm_luminous.py create mode 100644 titles/chuni/luminous.py diff --git a/changelog.md b/changelog.md index 3f8c6ba..07f17c8 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,10 @@ # Changelog Documenting updates to ARTEMiS, to be updated every time the master branch is pushed to. +## 20240620 +### CHUNITHM ++ CHUNITHM LUMINOUS support + ## 20240530 ### DIVA + Fix reader for when dificulty is not a int diff --git a/core/data/alembic/versions/b23f985100ba_chunithm_luminous.py b/core/data/alembic/versions/b23f985100ba_chunithm_luminous.py new file mode 100644 index 0000000..dd52974 --- /dev/null +++ b/core/data/alembic/versions/b23f985100ba_chunithm_luminous.py @@ -0,0 +1,87 @@ +"""CHUNITHM LUMINOUS + +Revision ID: b23f985100ba +Revises: 3657efefc5a4 +Create Date: 2024-06-20 08:08:08.759261 + +""" +from alembic import op +from sqlalchemy import Column, Integer, Boolean, UniqueConstraint + + +# revision identifiers, used by Alembic. +revision = 'b23f985100ba' +down_revision = '3657efefc5a4' +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table( + "chuni_profile_net_battle", + Column("id", Integer, primary_key=True, nullable=False), + Column("user", Integer, nullable=False), + Column("isRankUpChallengeFailed", Boolean), + Column("highestBattleRankId", Integer), + Column("battleIconId", Integer), + Column("battleIconNum", Integer), + Column("avatarEffectPoint", Integer), + mysql_charset="utf8mb4", + ) + op.create_foreign_key( + None, + "chuni_profile_net_battle", + "aime_user", + ["user"], + ["id"], + ondelete="cascade", + onupdate="cascade", + ) + + op.create_table( + "chuni_item_cmission", + Column("id", Integer, primary_key=True, nullable=False), + Column("user", Integer, nullable=False), + Column("missionId", Integer, nullable=False), + Column("point", Integer), + UniqueConstraint("user", "missionId", name="chuni_item_cmission_uk"), + mysql_charset="utf8mb4", + ) + op.create_foreign_key( + None, + "chuni_item_cmission", + "aime_user", + ["user"], + ["id"], + ondelete="cascade", + onupdate="cascade", + ) + + op.create_table( + "chuni_item_cmission_progress", + Column("id", Integer, primary_key=True, nullable=False), + Column("user", Integer, nullable=False), + Column("missionId", Integer, nullable=False), + Column("order", Integer), + Column("stage", Integer), + Column("progress", Integer), + UniqueConstraint( + "user", "missionId", "order", name="chuni_item_cmission_progress_uk" + ), + mysql_charset="utf8mb4", + ) + op.create_foreign_key( + None, + "chuni_item_cmission_progress", + "aime_user", + ["user"], + ["id"], + ondelete="cascade", + onupdate="cascade", + ) + + +def downgrade(): + op.drop_table("chuni_profile_net_battle") + op.drop_table("chuni_item_cmission") + op.drop_table("chuni_item_cmission_progress") diff --git a/docs/game_specific_info.md b/docs/game_specific_info.md index a8e63c5..53c076c 100644 --- a/docs/game_specific_info.md +++ b/docs/game_specific_info.md @@ -63,6 +63,7 @@ Games listed below have been tested and confirmed working. | 12 | CHUNITHM NEW PLUS!! | | 13 | CHUNITHM SUN | | 14 | CHUNITHM SUN PLUS | +| 15 | CHUNITHM LUMINOUS | ### Importer diff --git a/example_config/chuni.yaml b/example_config/chuni.yaml index 53da186..4855fa1 100644 --- a/example_config/chuni.yaml +++ b/example_config/chuni.yaml @@ -22,6 +22,9 @@ version: 14: rom: 2.15.00 data: 2.15.00 + 15: + rom: 2.20.00 + data: 2.20.00 crypto: encrypted_only: False diff --git a/titles/chuni/const.py b/titles/chuni/const.py index 3e83378..2b79582 100644 --- a/titles/chuni/const.py +++ b/titles/chuni/const.py @@ -1,3 +1,6 @@ +from enum import Enum + + class ChuniConstants: GAME_CODE = "SDBT" GAME_CODE_NEW = "SDHD" @@ -20,6 +23,7 @@ class ChuniConstants: VER_CHUNITHM_NEW_PLUS = 12 VER_CHUNITHM_SUN = 13 VER_CHUNITHM_SUN_PLUS = 14 + VER_CHUNITHM_LUMINOUS = 15 VERSION_NAMES = [ "CHUNITHM", "CHUNITHM PLUS", @@ -35,9 +39,21 @@ class ChuniConstants: "CHUNITHM NEW!!", "CHUNITHM NEW PLUS!!", "CHUNITHM SUN", - "CHUNITHM SUN PLUS" + "CHUNITHM SUN PLUS", + "CHUNITHM LUMINOUS", ] @classmethod def game_ver_to_string(cls, ver: int): - return cls.VERSION_NAMES[ver] \ No newline at end of file + return cls.VERSION_NAMES[ver] + + +class MapAreaConditionType(Enum): + UNLOCKED = "0" + MAP_AREA_CLEARED = "2" + TROPHY_OBTAINED = "3" + + +class MapAreaConditionLogicalOperator(Enum): + OR = "0" + AND = "1" diff --git a/titles/chuni/index.py b/titles/chuni/index.py index f0f1eac..39dd9ba 100644 --- a/titles/chuni/index.py +++ b/titles/chuni/index.py @@ -1,7 +1,8 @@ from starlette.requests import Request from starlette.routing import Route from starlette.responses import Response -import logging, coloredlogs +import logging +import coloredlogs from logging.handlers import TimedRotatingFileHandler import zlib import yaml @@ -34,6 +35,7 @@ from .new import ChuniNew from .newplus import ChuniNewPlus from .sun import ChuniSun from .sunplus import ChuniSunPlus +from .luminous import ChuniLuminous class ChuniServlet(BaseServlet): def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None: @@ -61,6 +63,7 @@ class ChuniServlet(BaseServlet): ChuniNewPlus, ChuniSun, ChuniSunPlus, + ChuniLuminous, ] self.logger = logging.getLogger("chuni") @@ -103,7 +106,9 @@ class ChuniServlet(BaseServlet): for method in method_list: method_fixed = inflection.camelize(method)[6:-7] # number of iterations was changed to 70 in SUN and then to 36 - if version == ChuniConstants.VER_CHUNITHM_SUN_PLUS: + if version == ChuniConstants.VER_CHUNITHM_LUMINOUS: + iter_count = 8 + elif version == ChuniConstants.VER_CHUNITHM_SUN_PLUS: iter_count = 36 elif version == ChuniConstants.VER_CHUNITHM_SUN: iter_count = 70 @@ -195,8 +200,10 @@ class ChuniServlet(BaseServlet): internal_ver = ChuniConstants.VER_CHUNITHM_NEW_PLUS elif version >= 210 and version < 215: # SUN internal_ver = ChuniConstants.VER_CHUNITHM_SUN - elif version >= 215: # SUN PLUS + elif version >= 215 and version < 220: # SUN PLUS internal_ver = ChuniConstants.VER_CHUNITHM_SUN_PLUS + elif version >= 220: # LUMINOUS + internal_ver = ChuniConstants.VER_CHUNITHM_LUMINOUS elif game_code == "SDGS": # Int if version < 110: # SUPERSTAR / SUPERSTAR PLUS internal_ver = ChuniConstants.VER_CHUNITHM_PARADISE # SUPERSTAR / SUPERSTAR PLUS worked fine with it @@ -206,8 +213,10 @@ class ChuniServlet(BaseServlet): internal_ver = ChuniConstants.VER_CHUNITHM_NEW_PLUS elif version >= 120 and version < 125: # SUN internal_ver = ChuniConstants.VER_CHUNITHM_SUN - elif version >= 125: # SUN PLUS + elif version >= 125 and version < 130: # SUN PLUS internal_ver = ChuniConstants.VER_CHUNITHM_SUN_PLUS + elif version >= 130: # LUMINOUS + internal_ver = ChuniConstants.VER_CHUNITHM_LUMINOUS if all(c in string.hexdigits for c in endpoint) and len(endpoint) == 32: # If we get a 32 character long hex string, it's a hash and we're @@ -295,7 +304,7 @@ class ChuniServlet(BaseServlet): self.logger.error(f"Error handling v{version} method {endpoint} - {e}") return Response(zlib.compress(b'{"stat": "0"}')) - if resp == None: + if resp is None: resp = {"returnCode": 1} self.logger.debug(f"Response {resp}") @@ -313,4 +322,4 @@ class ChuniServlet(BaseServlet): bytes.fromhex(self.game_cfg.crypto.keys[internal_ver][1]), ) - return Response(crypt.encrypt(padded)) \ No newline at end of file + return Response(crypt.encrypt(padded)) diff --git a/titles/chuni/luminous.py b/titles/chuni/luminous.py new file mode 100644 index 0000000..2ced55b --- /dev/null +++ b/titles/chuni/luminous.py @@ -0,0 +1,244 @@ +from typing import Dict + +from core.config import CoreConfig +from titles.chuni.sunplus import ChuniSunPlus +from titles.chuni.const import ChuniConstants, MapAreaConditionLogicalOperator, MapAreaConditionType +from titles.chuni.config import ChuniConfig + + +class ChuniLuminous(ChuniSunPlus): + def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None: + super().__init__(core_cfg, game_cfg) + self.version = ChuniConstants.VER_CHUNITHM_LUMINOUS + + async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict: + user_data = await super().handle_cm_get_user_preview_api_request(data) + + # Does CARD MAKER 1.35 work this far up? + user_data["lastDataVersion"] = "2.20.00" + return user_data + + async def handle_get_user_c_mission_api_request(self, data: Dict) -> Dict: + user_id = data["userId"] + mission_id = data["missionId"] + + progress_list = [] + point = 0 + + mission_data = await self.data.item.get_cmission(user_id, mission_id) + progress_data = await self.data.item.get_cmission_progress(user_id, mission_id) + + if mission_data and progress_data: + point = mission_data["point"] + + for progress in progress_data: + progress_list.append( + { + "order": progress["order"], + "stage": progress["stage"], + "progress": progress["progress"], + } + ) + + return { + "userId": user_id, + "missionId": mission_id, + "point": point, + "userCMissionProgressList": progress_list, + } + + async def handle_get_user_net_battle_ranking_info_api_request(self, data: Dict) -> Dict: + user_id = data["userId"] + + net_battle = {} + net_battle_data = await self.data.profile.get_net_battle(user_id) + + if net_battle_data: + net_battle = { + "isRankUpChallengeFailed": net_battle_data["isRankUpChallengeFailed"], + "highestBattleRankId": net_battle_data["highestBattleRankId"], + "battleIconId": net_battle_data["battleIconId"], + "battleIconNum": net_battle_data["battleIconNum"], + "avatarEffectPoint": net_battle_data["avatarEffectPoint"], + } + + return { + "userId": user_id, + "userNetBattleData": net_battle, + } + + async def handle_get_game_map_area_condition_api_request(self, data: Dict) -> Dict: + # There is no game data for this, everything is server side. + # TODO: Figure out conditions for 1UM1N0US ep.111 + return { + "length": "7", + "gameMapAreaConditionList": [ + # Secret AREA: MUSIC GAME + { + "mapAreaId": "2206201", # BlythE ULTIMA + "length": "1", + # Obtain the trophy "MISSION in progress", which is only available + # when running the first CHUNITHM mission + "mapAreaConditionList": [ + { + "type": MapAreaConditionType.TROPHY_OBTAINED.value, + "conditionId": "6832", + "logicalOpe": MapAreaConditionLogicalOperator.AND.value, + "startDate": "2023-12-14 07:00:00.0", + "endDate": "2024-01-25 02:00:00.0", + } + ], + }, + { + "mapAreaId": "2206202", # PRIVATE SERVICE ULTIMA + "length": "1", + # Obtain the trophy "MISSION in progress", which is only available + # when running the first CHUNITHM mission + "mapAreaConditionList": [ + { + "type": MapAreaConditionType.TROPHY_OBTAINED.value, + "conditionId": "6832", + "logicalOpe": MapAreaConditionLogicalOperator.AND.value, + "startDate": "2023-12-14 07:00:00.0", + "endDate": "2024-01-25 02:00:00.0", + } + ], + }, + { + "mapAreaId": "2206203", # New York Back Raise + "length": "1", + # SS NightTheater's EXPERT chart and get the title + # "今宵、劇場に映し出される景色とは――――。" + "mapAreaConditionList": [ + { + "type": MapAreaConditionType.TROPHY_OBTAINED.value, + "conditionId": "6833", + "logicalOpe": MapAreaConditionLogicalOperator.AND.value, + "startDate": "2023-12-14 07:00:00.0", + "endDate": "2099-12-31 00:00:00.0", + }, + ], + }, + { + "mapAreaId": "2206204", # Spasmodic + "length": "2", + # - Get 1 miss on Random (any difficulty) and get the title "当たり待ち" + # - Get 1 miss on 花たちに希望を (any difficulty) and get the title "花たちに希望を" + "mapAreaConditionList": [ + { + "type": MapAreaConditionType.TROPHY_OBTAINED.value, + "conditionId": "6834", + "logicalOpe": MapAreaConditionLogicalOperator.AND.value, + "startDate": "2023-12-14 07:00:00.0", + "endDate": "2099-12-31 00:00:00.0", + }, + { + "type": MapAreaConditionType.TROPHY_OBTAINED.value, + "conditionId": "6835", + "logicalOpe": MapAreaConditionLogicalOperator.AND.value, + "startDate": "2023-12-14 07:00:00.0", + "endDate": "2099-12-31 00:00:00.0", + }, + ], + }, + { + "mapAreaId": "2206205", # ΩΩPARTS + "length": "2", + # - S Sage EXPERT to get the title "マターリ進行キボンヌ" + # - Equip this title and play cab-to-cab with another person with this title + # to get "マターリしようよ" + "mapAreaConditionList": [ + { + "type": MapAreaConditionType.TROPHY_OBTAINED.value, + "conditionId": "6836", + "logicalOpe": MapAreaConditionLogicalOperator.AND.value, + "startDate": "2023-12-14 07:00:00.0", + "endDate": "2099-12-31 00:00:00.0", + }, + { + "type": MapAreaConditionType.TROPHY_OBTAINED.value, + "conditionId": "6837", + "logicalOpe": MapAreaConditionLogicalOperator.AND.value, + "startDate": "2023-12-14 07:00:00.0", + "endDate": "2024-01-25 02:00:00.0", + }, + ], + }, + { + "mapAreaId": "2206206", # Blow My Mind + "length": "1", + # SS on CHAOS EXPERT, Hydra EXPERT, Surive EXPERT and Jakarta PROGRESSION EXPERT + # to get the title "Can you hear me?" + "mapAreaConditionList": [ + { + "type": MapAreaConditionType.TROPHY_OBTAINED.value, + "conditionId": "6838", + "logicalOpe": MapAreaConditionLogicalOperator.AND.value, + "startDate": "2023-12-14 07:00:00.0", + "endDate": "2099-12-31 00:00:00.0", + }, + ], + }, + { + "mapAreaId": "2206207", # VALLIS-NERIA + "length": "6", + # Finish the 6 other areas + "mapAreaConditionList": [ + { + "type": MapAreaConditionType.MAP_AREA_CLEARED.value, + "conditionId": str(x), + "logicalOpe": MapAreaConditionLogicalOperator.AND.value, + "startDate": "2023-12-14 07:00:00.0", + "endDate": "2099-12-31 00:00:00.0", + } + for x in range(2206201, 2206207) + ], + }, + # { + # "mapAreaId": "3229301", # Mystic Rainbow of LUMINOUS Area 1 + # "length": "1", + # # Unlocks when any of the mainline LUMINOUS maps are completed? + # "mapAreaConditionList": [ + # # TODO + # ] + # }, + # { + # "mapAreaId": "3229302", # Mystic Rainbow of LUMINOUS Area 2 + # "length": "5", + # # Unlocks when LUMINOUS ep. I is completed + # "mapAreaConditionList": [ + # { + # "type": MapAreaConditionType.MAP_AREA_CLEARED.value, + # "conditionId": str(x), + # "logicalOpe": MapAreaConditionLogicalOperator.AND.value, + # "startDate": "2023-12-14 07:00:00.0", + # "endDate": "2099-12-31 00:00:00.0", + # } + # for x in range(3220101, 3220106) + # ] + # }, + # { + # "mapAreaId": "3229303", # Mystic Rainbow of LUMINOUS Area 3 + # "length": "5", + # # Unlocks when LUMINOUS ep. II is completed + # "mapAreaConditionList": [ + # { + # "type": MapAreaConditionType.MAP_AREA_CLEARED.value, + # "conditionId": str(x), + # "logicalOpe": MapAreaConditionLogicalOperator.AND.value, + # "startDate": "2023-12-14 07:00:00.0", + # "endDate": "2099-12-31 00:00:00.0", + # } + # for x in range(3220201, 3220206) + # ] + # }, + # { + # "mapAreaId": "3229304", # Mystic Rainbow of LUMINOUS Area 4 + # "length": "5", + # # Unlocks when LUMINOUS ep. III is completed + # "mapAreaConditionList": [ + + # ] + # } + ], + } diff --git a/titles/chuni/new.py b/titles/chuni/new.py index 9709f00..2275a6e 100644 --- a/titles/chuni/new.py +++ b/titles/chuni/new.py @@ -32,6 +32,8 @@ class ChuniNew(ChuniBase): return "210" if self.version == ChuniConstants.VER_CHUNITHM_SUN_PLUS: return "215" + if self.version == ChuniConstants.VER_CHUNITHM_LUMINOUS: + return "220" async def handle_get_game_setting_api_request(self, data: Dict) -> Dict: # use UTC time and convert it to JST time by adding +9 diff --git a/titles/chuni/schema/item.py b/titles/chuni/schema/item.py index 5077e14..2f386ef 100644 --- a/titles/chuni/schema/item.py +++ b/titles/chuni/schema/item.py @@ -243,6 +243,36 @@ matching = Table( mysql_charset="utf8mb4", ) +cmission = Table( + "chuni_item_cmission", + metadata, + Column("id", Integer, primary_key=True, nullable=False), + Column( + "user", + ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), + nullable=False, + ), + Column("missionId", Integer, nullable=False), + Column("point", Integer), + UniqueConstraint("user", "missionId", name="chuni_item_cmission_uk"), + mysql_charset="utf8mb4", +) + +cmission_progress = Table( + "chuni_item_cmission_progress", + metadata, + Column("id", Integer, primary_key=True, nullable=False), + Column("user", ForeignKey("aime_user.id", ondelete="cascade"), nullable=False), + Column("missionId", Integer, nullable=False), + Column("order", Integer), + Column("stage", Integer), + Column("progress", Integer), + UniqueConstraint( + "user", "missionId", "order", name="chuni_item_cmission_progress_uk" + ), + mysql_charset="utf8mb4", +) + class ChuniItemData(BaseData): async def get_oldest_free_matching(self, version: int) -> Optional[Row]: @@ -594,3 +624,66 @@ class ChuniItemData(BaseData): ) return None return result.lastrowid + + async def put_cmission_progress( + self, user_id: int, mission_id: int, progress_data: Dict + ) -> Optional[int]: + progress_data["user"] = user_id + progress_data["missionId"] = mission_id + + sql = insert(cmission_progress).values(**progress_data) + conflict = sql.on_duplicate_key_update(**progress_data) + result = await self.execute(conflict) + + if result is None: + return None + + return result.lastrowid + + async def get_cmission_progress( + self, user_id: int, mission_id: int + ) -> Optional[List[Row]]: + sql = cmission_progress.select( + and_( + cmission_progress.c.user == user_id, + cmission_progress.c.missionId == mission_id, + ) + ).order_by(cmission_progress.c.order.asc()) + result = await self.execute(sql) + + if result is None: + return None + + return result.fetchall() + + async def get_cmission(self, user_id: int, mission_id: int) -> Optional[Row]: + sql = cmission.select( + and_(cmission.c.user == user_id, cmission.c.missionId == mission_id) + ) + result = await self.execute(sql) + + if result is None: + return None + + return result.fetchone() + + async def put_cmission(self, user_id: int, mission_data: Dict) -> Optional[int]: + mission_data["user"] = user_id + + sql = insert(cmission).values(**mission_data) + conflict = sql.on_duplicate_key_update(**mission_data) + result = await self.execute(conflict) + + if result is None: + return None + + return result.lastrowid + + async def get_cmissions(self, user_id: int) -> Optional[List[Row]]: + sql = cmission.select(cmission.c.user == user_id) + result = await self.execute(sql) + + if result is None: + return None + + return result.fetchall() From a1d54efbace85066234e2ec90620947e0d086cad Mon Sep 17 00:00:00 2001 From: beerpsi Date: Thu, 20 Jun 2024 20:28:52 +0700 Subject: [PATCH 15/16] add upserts --- titles/chuni/base.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/titles/chuni/base.py b/titles/chuni/base.py index 9e8a634..2a662d7 100644 --- a/titles/chuni/base.py +++ b/titles/chuni/base.py @@ -941,6 +941,31 @@ class ChuniBase: rating_type, upsert[rating_type], ) + + # added in LUMINOUS + if "userCMissionList" in upsert: + for cmission in upsert["userCMissionList"]: + mission_id = cmission["missionId"] + + await self.data.item.put_cmission( + user_id, + { + "missionId": mission_id, + "point": cmission["point"], + }, + ) + + for progress in cmission["userCMissionProgressList"]: + await self.data.item.put_cmission_progress(user_id, mission_id, progress) + + if "userNetBattleData" in upsert: + net_battle = upsert["userNetBattleData"][0] + + # fix the boolean + net_battle["isRankUpChallengeFailed"] = ( + False if net_battle["isRankUpChallengeFailed"] == "false" else True + ) + await self.data.profile.put_net_battle(user_id, net_battle) return {"returnCode": "1"} @@ -969,4 +994,4 @@ class ChuniBase: return { "userId": data["userId"], "userNetBattleData": {"recentNBSelectMusicList": []}, - } \ No newline at end of file + } From a5b47e20959b6394972061d2989bb7fb22939b60 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Thu, 20 Jun 2024 10:59:26 -0400 Subject: [PATCH 16/16] wacca: fix vs song updates --- titles/wacca/base.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/titles/wacca/base.py b/titles/wacca/base.py index 8a37c2b..58b1ba9 100644 --- a/titles/wacca/base.py +++ b/titles/wacca/base.py @@ -833,14 +833,14 @@ class WaccaBase: # TODO: Coop and vs data async def handle_user_music_updateCoop_request(self, data: Dict) -> Dict: coop_info = data["params"][4] - return self.handle_user_music_update_request(data) + return await self.handle_user_music_update_request(data) async def handle_user_music_updateVersus_request(self, data: Dict) -> Dict: vs_info = data["params"][4] - return self.handle_user_music_update_request(data) + return await self.handle_user_music_update_request(data) async def handle_user_music_updateTrial_request(self, data: Dict) -> Dict: - return self.handle_user_music_update_request(data) + return await self.handle_user_music_update_request(data) async def handle_user_mission_update_request(self, data: Dict) -> Dict: req = UserMissionUpdateRequest(data)