diff --git a/core/allnet.py b/core/allnet.py index 07340a2..119f0ae 100644 --- a/core/allnet.py +++ b/core/allnet.py @@ -105,9 +105,11 @@ class AllnetServlet: resp.stat = 0 return self.dict_to_http_form_string([vars(resp)]) - + else: - self.logger.info(f"Allowed unknown game {req.game_id} v{req.ver} to authenticate from {request_ip} due to 'is_develop' being enabled. S/N: {req.serial}") + self.logger.info( + f"Allowed unknown game {req.game_id} v{req.ver} to authenticate from {request_ip} due to 'is_develop' being enabled. S/N: {req.serial}" + ) resp.uri = f"http://{self.config.title.hostname}:{self.config.title.port}/{req.game_id}/{req.ver.replace('.', '')}/" resp.host = f"{self.config.title.hostname}:{self.config.title.port}" return self.dict_to_http_form_string([vars(resp)]) @@ -189,35 +191,49 @@ class AllnetServlet: self.logger.error(e) return b"" - self.logger.info(f"DownloadOrder from {request_ip} -> {req.game_id} v{req.ver} serial {req.serial}") + self.logger.info( + f"DownloadOrder from {request_ip} -> {req.game_id} v{req.ver} serial {req.serial}" + ) resp = AllnetDownloadOrderResponse() - - if not self.config.allnet.allow_online_updates or not self.config.allnet.update_cfg_folder: + + if ( + not self.config.allnet.allow_online_updates + or not self.config.allnet.update_cfg_folder + ): return self.dict_to_http_form_string([vars(resp)]) else: # TODO: Keychip check - if path.exists(f"{self.config.allnet.update_cfg_folder}/{req.game_id}-{req.ver}-app.ini"): + if path.exists( + f"{self.config.allnet.update_cfg_folder}/{req.game_id}-{req.ver}-app.ini" + ): resp.uri = f"http://{self.config.title.hostname}:{self.config.title.port}/dl/ini/{req.game_id}-{req.ver.replace('.', '')}-app.ini" - - if path.exists(f"{self.config.allnet.update_cfg_folder}/{req.game_id}-{req.ver}-opt.ini"): + + if path.exists( + f"{self.config.allnet.update_cfg_folder}/{req.game_id}-{req.ver}-opt.ini" + ): resp.uri += f"|http://{self.config.title.hostname}:{self.config.title.port}/dl/ini/{req.game_id}-{req.ver.replace('.', '')}-opt.ini" - + self.logger.debug(f"Sending download uri {resp.uri}") return self.dict_to_http_form_string([vars(resp)]) - def handle_dlorder_ini(self, request:Request, match: Dict) -> bytes: - if "file" not in match: return b"" + def handle_dlorder_ini(self, request: Request, match: Dict) -> bytes: + if "file" not in match: + return b"" + + req_file = match["file"].replace("%0A", "") - req_file = match['file'].replace('%0A', '') - if path.exists(f"{self.config.allnet.update_cfg_folder}/{req_file}"): - return open(f"{self.config.allnet.update_cfg_folder}/{req_file}", "rb").read() - + return open( + f"{self.config.allnet.update_cfg_folder}/{req_file}", "rb" + ).read() + self.logger.info(f"DL INI File {req_file} not found") return b"" - def handle_dlorder_report(self, request:Request, match: Dict) -> bytes: - self.logger.info(f"DLI Report from {Utils.get_ip_addr(request)}: {request.content.getvalue()}") + def handle_dlorder_report(self, request: Request, match: Dict) -> bytes: + self.logger.info( + f"DLI Report from {Utils.get_ip_addr(request)}: {request.content.getvalue()}" + ) return b"" def handle_billing_request(self, request: Request, _: Dict): diff --git a/core/config.py b/core/config.py index 84311af..3fb0dbe 100644 --- a/core/config.py +++ b/core/config.py @@ -194,6 +194,7 @@ class AllnetConfig: self.__config, "core", "allnet", "update_cfg_folder", default="" ) + class BillingConfig: def __init__(self, parent_config: "CoreConfig") -> None: self.__config = parent_config diff --git a/core/data/database.py b/core/data/database.py index 40bbf33..719d05e 100644 --- a/core/data/database.py +++ b/core/data/database.py @@ -71,7 +71,9 @@ class Data: games = Utils.get_all_titles() for game_dir, game_mod in games.items(): try: - if hasattr(game_mod, "database") and hasattr(game_mod, "current_schema_version"): + if hasattr(game_mod, "database") and hasattr( + game_mod, "current_schema_version" + ): game_mod.database(self.config) metadata.create_all(self.__engine.connect()) @@ -135,21 +137,26 @@ class Data: if version is None: if not game == "CORE": titles = Utils.get_all_titles() - + for folder, mod in titles.items(): - if not mod.game_codes[0] == game: continue - + if not mod.game_codes[0] == game: + continue + if hasattr(mod, "current_schema_version"): version = mod.current_schema_version - + else: - self.logger.warn(f"current_schema_version not found for {folder}") - + self.logger.warn( + f"current_schema_version not found for {folder}" + ) + else: version = self.current_schema_version - + if version is None: - self.logger.warn(f"Could not determine latest version for {game}, please specify --version") + self.logger.warn( + f"Could not determine latest version for {game}, please specify --version" + ) if old_ver is None: self.logger.error( @@ -184,7 +191,7 @@ class Data: if result is None: self.logger.error("Error execuing sql script!") return None - + else: for x in range(old_ver, version, -1): if not os.path.exists( @@ -285,13 +292,13 @@ class Data: if all_game_versions is None: self.logger.warn("Failed to get schema versions") return - + all_games = Utils.get_all_titles() all_games_list: Dict[str, int] = {} for _, mod in all_games.items(): if hasattr(mod, "current_schema_version"): all_games_list[mod.game_codes[0]] = mod.current_schema_version - + for x in all_game_versions: failed = False game = x["game"].upper() @@ -299,27 +306,30 @@ class Data: latest_ver = all_games_list.get(game, 1) if game == "CORE": latest_ver = self.current_schema_version - - if update_ver == latest_ver: + + if update_ver == latest_ver: self.logger.info(f"{game} is already latest version") continue - + for y in range(update_ver + 1, latest_ver + 1): if os.path.exists(f"core/data/schema/versions/{game}_{y}_upgrade.sql"): with open( - f"core/data/schema/versions/{game}_{y}_upgrade.sql", - "r", - encoding="utf-8", + f"core/data/schema/versions/{game}_{y}_upgrade.sql", + "r", + encoding="utf-8", ) as f: sql = f.read() result = self.base.execute(sql) if result is None: - self.logger.error(f"Error execuing sql script for game {game} v{y}!") + self.logger.error( + f"Error execuing sql script for game {game} v{y}!" + ) failed = True break else: self.logger.warning(f"Could not find script {game}_{y}_upgrade.sql") failed = True - - if not failed: self.base.set_schema_ver(latest_ver, game) + + if not failed: + self.base.set_schema_ver(latest_ver, game) diff --git a/core/data/schema/base.py b/core/data/schema/base.py index ea2fc88..7957301 100644 --- a/core/data/schema/base.py +++ b/core/data/schema/base.py @@ -81,7 +81,7 @@ class BaseData: Generate a random 5-7 digit id """ return randrange(10000, 9999999) - + def get_all_schema_vers(self) -> Optional[List[Row]]: sql = select(schema_ver) diff --git a/core/frontend.py b/core/frontend.py index 0c96fdc..c992e76 100644 --- a/core/frontend.py +++ b/core/frontend.py @@ -66,14 +66,16 @@ class FrontendServlet(resource.Resource): fe_game = FE_Game(cfg, self.environment) games = Utils.get_all_titles() for game_dir, game_mod in games.items(): - if hasattr(game_mod, "frontend"): + if hasattr(game_mod, "frontend"): try: game_fe = game_mod.frontend(cfg, self.environment, config_dir) self.game_list.append({"url": game_dir, "name": game_fe.nav_name}) fe_game.putChild(game_dir.encode(), game_fe) - + except Exception as e: - self.logger.error(f"Failed to import frontend from {game_dir} because {e}") + self.logger.error( + f"Failed to import frontend from {game_dir} because {e}" + ) self.environment.globals["game_list"] = self.game_list self.putChild(b"gate", FE_Gate(cfg, self.environment)) diff --git a/core/mucha.py b/core/mucha.py index 9dfef03..a90ab53 100644 --- a/core/mucha.py +++ b/core/mucha.py @@ -46,9 +46,7 @@ class MuchaServlet: if enabled: self.mucha_registry.append(game_cd) - self.logger.info( - f"Serving {len(self.mucha_registry)} games" - ) + self.logger.info(f"Serving {len(self.mucha_registry)} games") def handle_boardauth(self, request: Request, _: Dict) -> bytes: req_dict = self.mucha_preprocess(request.content.getvalue()) @@ -62,9 +60,7 @@ class MuchaServlet: req = MuchaAuthRequest(req_dict) self.logger.debug(f"Mucha request {vars(req)}") - self.logger.info( - f"Boardauth request from {client_ip} for {req.gameVer}" - ) + self.logger.info(f"Boardauth request from {client_ip} for {req.gameVer}") if req.gameCd not in self.mucha_registry: self.logger.warn(f"Unknown gameCd {req.gameCd}") @@ -92,9 +88,7 @@ class MuchaServlet: req = MuchaUpdateRequest(req_dict) self.logger.debug(f"Mucha request {vars(req)}") - self.logger.info( - f"Updatecheck request from {client_ip} for {req.gameVer}" - ) + self.logger.info(f"Updatecheck request from {client_ip} for {req.gameVer}") if req.gameCd not in self.mucha_registry: self.logger.warn(f"Unknown gameCd {req.gameCd}") diff --git a/core/utils.py b/core/utils.py index 6bd5c08..f364785 100644 --- a/core/utils.py +++ b/core/utils.py @@ -16,14 +16,20 @@ class Utils: if not dir.startswith("__"): try: mod = importlib.import_module(f"titles.{dir}") - if hasattr(mod, "game_codes") and hasattr(mod, "index"): # Minimum required to function + if hasattr(mod, "game_codes") and hasattr( + mod, "index" + ): # Minimum required to function ret[dir] = mod except ImportError as e: logging.getLogger("core").error(f"get_all_titles: {dir} - {e}") raise return ret - + @classmethod def get_ip_addr(cls, req: Request) -> str: - return req.getAllHeaders()[b"x-forwarded-for"].decode() if b"x-forwarded-for" in req.getAllHeaders() else req.getClientAddress().host + return ( + req.getAllHeaders()[b"x-forwarded-for"].decode() + if b"x-forwarded-for" in req.getAllHeaders() + else req.getClientAddress().host + ) diff --git a/dbutils.py b/dbutils.py index 3be3ebe..d959232 100644 --- a/dbutils.py +++ b/dbutils.py @@ -34,12 +34,12 @@ if __name__ == "__main__": cfg = CoreConfig() if path.exists(f"{args.config}/core.yaml"): cfg_dict = yaml.safe_load(open(f"{args.config}/core.yaml")) - cfg_dict.get('database', {})['loglevel'] = 'info' + cfg_dict.get("database", {})["loglevel"] = "info" cfg.update(cfg_dict) - + if not path.exists(cfg.server.log_dir): mkdir(cfg.server.log_dir) - + if not access(cfg.server.log_dir, W_OK): print( f"Log directory {cfg.server.log_dir} NOT writable, please check permissions" @@ -47,7 +47,6 @@ if __name__ == "__main__": exit(1) data = Data(cfg) - if args.action == "create": data.create_database() @@ -61,10 +60,18 @@ if __name__ == "__main__": if args.game is None: data.logger.warn("No game set, upgrading core schema") - data.migrate_database("CORE", int(args.version) if args.version is not None else None, args.action) + data.migrate_database( + "CORE", + int(args.version) if args.version is not None else None, + args.action, + ) else: - data.migrate_database(args.game, int(args.version) if args.version is not None else None, args.action) + data.migrate_database( + args.game, + int(args.version) if args.version is not None else None, + args.action, + ) elif args.action == "autoupgrade": data.autoupgrade() diff --git a/index.py b/index.py index 1e1193a..11fad94 100644 --- a/index.py +++ b/index.py @@ -25,7 +25,7 @@ class HttpDispatcher(resource.Resource): self.allnet = AllnetServlet(cfg, config_dir) self.title = TitleServlet(cfg, config_dir) self.mucha = MuchaServlet(cfg, config_dir) - + self.map_get.connect( "allnet_downloadorder_ini", "/dl/ini/{file}", @@ -109,7 +109,6 @@ class HttpDispatcher(resource.Resource): conditions=dict(method=["POST"]), requirements=dict(game=R"S..."), ) - def render_GET(self, request: Request) -> bytes: self.logger.debug(request.uri) diff --git a/read.py b/read.py index a1bd0ab..14c5cc2 100644 --- a/read.py +++ b/read.py @@ -135,8 +135,7 @@ if __name__ == "__main__": for dir, mod in titles.items(): if args.series in mod.game_codes: - handler = mod.reader(config, args.version, - bin_arg, opt_arg, args.extra) + handler = mod.reader(config, args.version, bin_arg, opt_arg, args.extra) handler.read() logger.info("Done") diff --git a/titles/chuni/index.py b/titles/chuni/index.py index 53db19f..a7545ba 100644 --- a/titles/chuni/index.py +++ b/titles/chuni/index.py @@ -82,21 +82,33 @@ class ChuniServlet: level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str ) self.logger.inited = True - + for version, keys in self.game_cfg.crypto.keys.items(): if len(keys) < 3: continue - + self.hash_table[version] = {} - - method_list = [method for method in dir(self.versions[version]) if not method.startswith('__')] + + method_list = [ + method + for method in dir(self.versions[version]) + if not method.startswith("__") + ] for method in method_list: method_fixed = inflection.camelize(method)[6:-7] - hash = PBKDF2(method_fixed, bytes.fromhex(keys[2]), 128, count=44, hmac_hash_module=SHA1) - + hash = PBKDF2( + method_fixed, + bytes.fromhex(keys[2]), + 128, + count=44, + hmac_hash_module=SHA1, + ) + self.hash_table[version][hash.hex()] = method_fixed - - self.logger.debug(f"Hashed v{version} method {method_fixed} with {bytes.fromhex(keys[2])} to get {hash.hex()}") + + self.logger.debug( + f"Hashed v{version} method {method_fixed} with {bytes.fromhex(keys[2])} to get {hash.hex()}" + ) @classmethod def get_allnet_info( @@ -164,18 +176,22 @@ class ChuniServlet: # technically not 0 if internal_ver < ChuniConstants.VER_CHUNITHM_NEW: endpoint = request.getHeader("User-Agent").split("#")[0] - + else: if internal_ver not in self.hash_table: - self.logger.error(f"v{version} does not support encryption or no keys entered") + self.logger.error( + f"v{version} does not support encryption or no keys entered" + ) return zlib.compress(b'{"stat": "0"}') - + elif endpoint.lower() not in self.hash_table[internal_ver]: - self.logger.error(f"No hash found for v{version} endpoint {endpoint}") + self.logger.error( + f"No hash found for v{version} endpoint {endpoint}" + ) return zlib.compress(b'{"stat": "0"}') endpoint = self.hash_table[internal_ver][endpoint.lower()] - + try: crypt = AES.new( bytes.fromhex(self.game_cfg.crypto.keys[internal_ver][0]), @@ -193,7 +209,11 @@ class ChuniServlet: encrtped = True - if not encrtped and self.game_cfg.crypto.encrypted_only and internal_ver >= ChuniConstants.VER_CHUNITHM_CRYSTAL_PLUS: + if ( + not encrtped + and self.game_cfg.crypto.encrypted_only + and internal_ver >= ChuniConstants.VER_CHUNITHM_CRYSTAL_PLUS + ): self.logger.error( f"Unencrypted v{version} {endpoint} request, but config is set to encrypted only: {req_raw}" ) @@ -210,9 +230,7 @@ class ChuniServlet: req_data = json.loads(unzip) - self.logger.info( - f"v{version} {endpoint} request from {client_ip}" - ) + self.logger.info(f"v{version} {endpoint} request from {client_ip}") self.logger.debug(req_data) func_to_find = "handle_" + inflection.underscore(endpoint) + "_request" diff --git a/titles/chuni/newplus.py b/titles/chuni/newplus.py index 422d57a..4faf47a 100644 --- a/titles/chuni/newplus.py +++ b/titles/chuni/newplus.py @@ -13,8 +13,12 @@ class ChuniNewPlus(ChuniNew): def handle_get_game_setting_api_request(self, data: Dict) -> Dict: ret = super().handle_get_game_setting_api_request(data) - ret["gameSetting"]["romVersion"] = self.game_cfg.version.version(self.version)["rom"] - ret["gameSetting"]["dataVersion"] = self.game_cfg.version.version(self.version)["data"] + ret["gameSetting"]["romVersion"] = self.game_cfg.version.version(self.version)[ + "rom" + ] + ret["gameSetting"]["dataVersion"] = self.game_cfg.version.version(self.version)[ + "data" + ] ret["gameSetting"][ "matchingUri" ] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/205/ChuniServlet/" diff --git a/titles/chuni/schema/static.py b/titles/chuni/schema/static.py index bef58c0..4537518 100644 --- a/titles/chuni/schema/static.py +++ b/titles/chuni/schema/static.py @@ -200,7 +200,9 @@ class ChuniStaticData(BaseData): return result.lastrowid def get_login_bonus( - self, version: int, preset_id: int, + self, + version: int, + preset_id: int, ) -> Optional[List[Row]]: sql = login_bonus.select( and_( diff --git a/titles/cm/index.py b/titles/cm/index.py index 3a56566..74d3a0d 100644 --- a/titles/cm/index.py +++ b/titles/cm/index.py @@ -109,9 +109,7 @@ class CardMakerServlet: req_data = json.loads(unzip) - self.logger.info( - f"v{version} {endpoint} request from {client_ip}" - ) + self.logger.info(f"v{version} {endpoint} request from {client_ip}") self.logger.debug(req_data) func_to_find = "handle_" + inflection.underscore(endpoint) + "_request" diff --git a/titles/cm/read.py b/titles/cm/read.py index 93ff203..109483c 100644 --- a/titles/cm/read.py +++ b/titles/cm/read.py @@ -90,7 +90,7 @@ class CardMakerReader(BaseReader): "v2_00": ChuniConstants.VER_CHUNITHM_NEW, "v2_05": ChuniConstants.VER_CHUNITHM_NEW_PLUS, # Chunithm SUN, ignore for now - "v2_10": ChuniConstants.VER_CHUNITHM_NEW_PLUS + 1 + "v2_10": ChuniConstants.VER_CHUNITHM_NEW_PLUS + 1, } for root, dirs, files in os.walk(base_dir): diff --git a/titles/cxb/index.py b/titles/cxb/index.py index 36c762e..0c38d55 100644 --- a/titles/cxb/index.py +++ b/titles/cxb/index.py @@ -101,9 +101,7 @@ class CxbServlet(resource.Resource): f"Ready on ports {self.game_cfg.server.port} & {self.game_cfg.server.port_secure}" ) else: - self.logger.info( - f"Ready on port {self.game_cfg.server.port}" - ) + self.logger.info(f"Ready on port {self.game_cfg.server.port}") def render_POST(self, request: Request): version = 0 diff --git a/titles/idz/config.py b/titles/idz/config.py index 2ff4e5c..f7af4fd 100644 --- a/titles/idz/config.py +++ b/titles/idz/config.py @@ -46,15 +46,21 @@ class IDZPortsConfig: @property def userdb(self) -> int: - return CoreConfig.get_config_field(self.__config, "idz", "ports", "userdb", default=10000) + return CoreConfig.get_config_field( + self.__config, "idz", "ports", "userdb", default=10000 + ) @property def match(self) -> int: - return CoreConfig.get_config_field(self.__config, "idz", "ports", "match", default=10010) + return CoreConfig.get_config_field( + self.__config, "idz", "ports", "match", default=10010 + ) @property def echo(self) -> int: - return CoreConfig.get_config_field(self.__config, "idz", "ports", "echo", default=10020) + return CoreConfig.get_config_field( + self.__config, "idz", "ports", "echo", default=10020 + ) class IDZConfig(dict): @@ -64,4 +70,4 @@ class IDZConfig(dict): @property def rsa_keys(self) -> List[Dict]: - return CoreConfig.get_config_field(self, "idz", "rsa_keys", default=[]) \ No newline at end of file + return CoreConfig.get_config_field(self, "idz", "rsa_keys", default=[]) diff --git a/titles/idz/const.py b/titles/idz/const.py index 164c940..93cbb4c 100644 --- a/titles/idz/const.py +++ b/titles/idz/const.py @@ -1,5 +1,6 @@ from enum import Enum + class IDZConstants: GAME_CODE = "SDDF" @@ -22,14 +23,26 @@ class IDZConstants: LOCKED = 0 UNLOCKED = 1 OLD = 2 - + HASH_LUT = [ # No clue - 0x9C82E674, 0x5A4738D9, 0x8B8D7AE0, 0x29EC9D81, + 0x9C82E674, + 0x5A4738D9, + 0x8B8D7AE0, + 0x29EC9D81, # These three are from AES TE0 - 0x1209091B, 0x1D83839E, 0x582C2C74, 0x341A1A2E, - 0x361B1B2D, 0xDC6E6EB2, 0xB45A5AEE, 0x5BA0A0FB, - 0xA45252F6, 0x763B3B4D, 0xB7D6D661, 0x7DB3B3CE, + 0x1209091B, + 0x1D83839E, + 0x582C2C74, + 0x341A1A2E, + 0x361B1B2D, + 0xDC6E6EB2, + 0xB45A5AEE, + 0x5BA0A0FB, + 0xA45252F6, + 0x763B3B4D, + 0xB7D6D661, + 0x7DB3B3CE, ] HASH_NUM = 0 HASH_MUL = [5, 7, 11, 12][HASH_NUM] diff --git a/titles/idz/database.py b/titles/idz/database.py index 1f5a8e2..525b1c1 100644 --- a/titles/idz/database.py +++ b/titles/idz/database.py @@ -1,6 +1,7 @@ from core.data import Data from core.config import CoreConfig + class IDZData(Data): def __init__(self, cfg: CoreConfig) -> None: super().__init__(cfg) diff --git a/titles/idz/echo.py b/titles/idz/echo.py index 4414b70..979fd19 100644 --- a/titles/idz/echo.py +++ b/titles/idz/echo.py @@ -4,6 +4,7 @@ import logging from core.config import CoreConfig from .config import IDZConfig + class IDZEcho(DatagramProtocol): def __init__(self, cfg: CoreConfig, game_cfg: IDZConfig) -> None: super().__init__() @@ -12,5 +13,7 @@ class IDZEcho(DatagramProtocol): self.logger = logging.getLogger("idz") def datagramReceived(self, data, addr): - self.logger.debug(f"Echo from from {addr[0]}:{addr[1]} -> {self.transport.getHost().port} - {data.hex()}") - self.transport.write(data, addr) \ No newline at end of file + self.logger.debug( + f"Echo from from {addr[0]}:{addr[1]} -> {self.transport.getHost().port} - {data.hex()}" + ) + self.transport.write(data, addr) diff --git a/titles/idz/handlers/base.py b/titles/idz/handlers/base.py index 720f097..6b1e5d5 100644 --- a/titles/idz/handlers/base.py +++ b/titles/idz/handlers/base.py @@ -5,7 +5,8 @@ from core.config import CoreConfig from ..config import IDZConfig from ..const import IDZConstants -class IDZHandlerBase(): + +class IDZHandlerBase: name = "generic" cmd_codes = [0x0000] * IDZConstants.NUM_VERS rsp_codes = [0x0001] * IDZConstants.NUM_VERS @@ -18,8 +19,8 @@ class IDZHandlerBase(): self.game = IDZConstants.GAME_CODE self.version = version self.size = 0x30 - + def handle(self, data: bytes) -> bytearray: ret = bytearray([0] * self.size) struct.pack_into(" None: super().__init__(core_cfg, game_cfg, version) self.size = 0x0010 - + def handle(self, data: bytes) -> bytearray: ret = super().handle(data) struct.pack_into(" None: super().__init__(core_cfg, game_cfg, version) - self.size = 0x0ca0 - + self.size = 0x0CA0 + def handle(self, data: bytes) -> bytearray: ret = super().handle(data) aime_id = struct.unpack_from(" None: super().__init__(core_cfg, game_cfg, version) self.size = 0x0020 - + def handle(self, data: bytes) -> bytearray: ret = super().handle(data) aime_id = struct.unpack_from(" 2: struct.pack_into(" None: super().__init__(core_cfg, game_cfg, version) self.size = 0x0010 - + def handle(self, data: bytes) -> bytearray: ret = super().handle(data) user_id = struct.unpack_from(" None: super().__init__(core_cfg, game_cfg, version) - self.size = 0x04c0 + self.size = 0x04C0 if version >= IDZConstants.VER_IDZ_210: self.size = 0x1290 - + def handle(self, data: bytes) -> bytearray: return super().handle(data) + class IDZHandlerLoad2on2B(IDZHandlerBase): cmd_codes = [0x0132] * 4 rsp_codes = [0x0133] * 4 @@ -27,10 +29,10 @@ class IDZHandlerLoad2on2B(IDZHandlerBase): def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None: super().__init__(core_cfg, game_cfg, version) - self.size = 0x04c0 + self.size = 0x04C0 if version >= IDZConstants.VER_IDZ_210: self.size = 0x0540 - + def handle(self, data: bytes) -> bytearray: return super().handle(data) diff --git a/titles/idz/handlers/load_config.py b/titles/idz/handlers/load_config.py index 3ae57f5..b3ceb0d 100644 --- a/titles/idz/handlers/load_config.py +++ b/titles/idz/handlers/load_config.py @@ -5,6 +5,7 @@ from core.config import CoreConfig from ..config import IDZConfig from ..const import IDZConstants + class IDZHandlerLoadConfigA(IDZHandlerBase): cmd_codes = [0x0004] * IDZConstants.NUM_VERS rsp_codes = [0x0005] * IDZConstants.NUM_VERS @@ -12,29 +13,30 @@ class IDZHandlerLoadConfigA(IDZHandlerBase): def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None: super().__init__(core_cfg, game_cfg, version) - self.size = 0x01a0 - + self.size = 0x01A0 + if self.version > 1: - self.size = 0x05e0 - + self.size = 0x05E0 + def handle(self, data: bytes) -> bytearray: ret = super().handle(data) struct.pack_into(" None: super().__init__(core_cfg, game_cfg, version) self.size = 0x0230 - + if self.version > 1: self.size = 0x0240 - + def handle(self, data: bytes) -> bytearray: ret = super().handle(data) struct.pack_into(" None: super().__init__(core_cfg, game_cfg, version) self.size = 0x0070 - + def handle(self, data: bytes) -> bytearray: ret = super().handle(data) struct.pack_into(" None: super().__init__(core_cfg, game_cfg, version) if self.version == IDZConstants.VER_IDZ_110: - self.size = 0x0d30 + self.size = 0x0D30 elif self.version == IDZConstants.VER_IDZ_130: - self.size = 0x0ea0 + self.size = 0x0EA0 elif self.version == IDZConstants.VER_IDZ_210: self.size = 0x1360 elif self.version == IDZConstants.VER_IDZ_230: self.size = 0x1640 - + def handle(self, data: bytes) -> bytearray: ret = super().handle(data) aime_id = struct.unpack_from(" None: super().__init__(core_cfg, game_cfg, version) - self.size = 0x01c0 - + self.size = 0x01C0 + def handle(self, data: bytes) -> bytearray: return super().handle(data) diff --git a/titles/idz/handlers/load_server_info.py b/titles/idz/handlers/load_server_info.py index 3b60f00..ef6e81c 100644 --- a/titles/idz/handlers/load_server_info.py +++ b/titles/idz/handlers/load_server_info.py @@ -5,6 +5,7 @@ from core.config import CoreConfig from ..config import IDZConfig from ..const import IDZConstants + class IDZHandlerLoadServerInfo(IDZHandlerBase): cmd_codes = [0x0006] * IDZConstants.NUM_VERS rsp_codes = [0x0007] * IDZConstants.NUM_VERS @@ -12,14 +13,14 @@ class IDZHandlerLoadServerInfo(IDZHandlerBase): def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None: super().__init__(core_cfg, game_cfg, version) - self.size = 0x04b0 - + self.size = 0x04B0 + def handle(self, data: bytes) -> bytearray: ret = super().handle(data) offset = 0 - if self.version >= IDZConstants.VER_IDZ_210: + if self.version >= IDZConstants.VER_IDZ_210: offset = 2 - + news_str = f"http://{self.core_config.title.hostname}:{self.core_config.title.port}/SDDF/230/news/news80**.txt" err_str = f"http://{self.core_config.title.hostname}:{self.core_config.title.port}/SDDF/230/error" @@ -27,34 +28,69 @@ class IDZHandlerLoadServerInfo(IDZHandlerBase): len_news = len(news_str) len_error = len(err_str) - struct.pack_into(" None: super().__init__(core_cfg, game_cfg, version) - self.size = 0x0ba0 - + self.size = 0x0BA0 + def handle(self, data: bytes) -> bytearray: return super().handle(data) + class IDZHandlerLoadTeamRankingB(IDZHandlerBase): - cmd_codes = [0x00bb, 0x00bb, 0x00a9, 0x00a9] - rsp_codes = [0x00a8] * 4 + cmd_codes = [0x00BB, 0x00BB, 0x00A9, 0x00A9] + rsp_codes = [0x00A8] * 4 name = "load_team_ranking_b" def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None: super().__init__(core_cfg, game_cfg, version) - self.size = 0x0ba0 - + self.size = 0x0BA0 + def handle(self, data: bytes) -> bytearray: return super().handle(data) diff --git a/titles/idz/handlers/load_top_ten.py b/titles/idz/handlers/load_top_ten.py index ef2a158..09a9f5f 100644 --- a/titles/idz/handlers/load_top_ten.py +++ b/titles/idz/handlers/load_top_ten.py @@ -5,9 +5,10 @@ from .base import IDZHandlerBase from core.config import CoreConfig from ..config import IDZConfig + class IDZHandlerLoadTopTen(IDZHandlerBase): - cmd_codes = [0x012c] * 4 - rsp_codes = [0x00ce] * 4 + cmd_codes = [0x012C] * 4 + rsp_codes = [0x00CE] * 4 name = "load_top_ten" def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None: @@ -19,12 +20,16 @@ class IDZHandlerLoadTopTen(IDZHandlerBase): tracks_dates: List[Tuple[int, int]] = [] for i in range(32): tracks_dates.append( - (struct.unpack_from(" None: super().__init__(core_cfg, game_cfg, version) self.size = 0x0020 - + def handle(self, data: bytes) -> bytearray: ret = super().handle(data) profile_data = { - "status": IDZConstants.PROFILE_STATUS.UNLOCKED.value, - "expire_time": int((datetime.now() + timedelta(hours=1)).timestamp() / 1000) + "status": IDZConstants.PROFILE_STATUS.UNLOCKED.value, + "expire_time": int( + (datetime.now() + timedelta(hours=1)).timestamp() / 1000 + ), } user_id = struct.unpack_from(" bytearray: struct.pack_into(" None: super().__init__(core_cfg, game_cfg, version) - + def handle(self, data: bytes) -> bytearray: return super().handle(data) diff --git a/titles/idz/handlers/save_profile.py b/titles/idz/handlers/save_profile.py index 652dbd6..3f5311d 100644 --- a/titles/idz/handlers/save_profile.py +++ b/titles/idz/handlers/save_profile.py @@ -4,12 +4,13 @@ from .base import IDZHandlerBase from core.config import CoreConfig from ..config import IDZConfig + class IDZHandlerSaveProfile(IDZHandlerBase): cmd_codes = [0x0068, 0x0138, 0x0138, 0x0143] name = "save_profile" def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None: super().__init__(core_cfg, game_cfg, version) - + def handle(self, data: bytes) -> bytearray: return super().handle(data) diff --git a/titles/idz/handlers/save_time_attack.py b/titles/idz/handlers/save_time_attack.py index a28a598..bea83af 100644 --- a/titles/idz/handlers/save_time_attack.py +++ b/titles/idz/handlers/save_time_attack.py @@ -5,18 +5,19 @@ from core.config import CoreConfig from ..config import IDZConfig from ..const import IDZConstants + class IDZHandlerSaveTimeAttack(IDZHandlerBase): cmd_codes = [0x00CD, 0x0136, 0x0136, 0x0136] - rsp_codes = [0x00ce, 0x00ce, 0x00cd, 0x00cd] + rsp_codes = [0x00CE, 0x00CE, 0x00CD, 0x00CD] name = "save_time_attack" def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None: super().__init__(core_cfg, game_cfg, version) - self.size = 0x00b0 + self.size = 0x00B0 if self.version > IDZConstants.VER_IDZ_130: - self.size = 0x00f0 - + self.size = 0x00F0 + def handle(self, data: bytes) -> bytearray: ret = super().handle(data) - return ret \ No newline at end of file + return ret diff --git a/titles/idz/handlers/save_topic.py b/titles/idz/handlers/save_topic.py index 9499385..090ce52 100644 --- a/titles/idz/handlers/save_topic.py +++ b/titles/idz/handlers/save_topic.py @@ -4,6 +4,7 @@ from .base import IDZHandlerBase from core.config import CoreConfig from ..config import IDZConfig + class IDZHandlerSaveTopic(IDZHandlerBase): cmd_codes = [0x009A, 0x009A, 0x0091, 0x0091] rsp_codes = [0x009B, 0x009B, 0x0092, 0x0092] @@ -11,7 +12,7 @@ class IDZHandlerSaveTopic(IDZHandlerBase): def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None: super().__init__(core_cfg, game_cfg, version) - self.size = 0x05d0 - + self.size = 0x05D0 + def handle(self, data: bytes) -> bytearray: return super().handle(data) diff --git a/titles/idz/handlers/unknown.py b/titles/idz/handlers/unknown.py index ee3046a..8998d81 100644 --- a/titles/idz/handlers/unknown.py +++ b/titles/idz/handlers/unknown.py @@ -4,8 +4,9 @@ from .base import IDZHandlerBase from core.config import CoreConfig from ..config import IDZConfig + class IDZHandlerUnknown(IDZHandlerBase): - cmd_codes = [0x00ad, 0x00ad, 0x00a2, 0x00a2] + cmd_codes = [0x00AD, 0x00AD, 0x00A2, 0x00A2] name = "unknown" def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None: diff --git a/titles/idz/handlers/unlock_profile.py b/titles/idz/handlers/unlock_profile.py index a5c8310..1be50f5 100644 --- a/titles/idz/handlers/unlock_profile.py +++ b/titles/idz/handlers/unlock_profile.py @@ -5,16 +5,17 @@ from core.config import CoreConfig from ..config import IDZConfig from ..const import IDZConstants + class IDZHandlerUnlockProfile(IDZHandlerBase): - cmd_codes = [0x006f, 0x006f, 0x006b, 0x006b] - rsp_codes = [0x0070, 0x0070, 0x006c, 0x006c] + cmd_codes = [0x006F, 0x006F, 0x006B, 0x006B] + rsp_codes = [0x0070, 0x0070, 0x006C, 0x006C] name = "unlock_profile" def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None: super().__init__(core_cfg, game_cfg, version) self.size = 0x0010 - + def handle(self, data: bytes) -> bytearray: - ret = super().handle(data) + ret = super().handle(data) struct.pack_into(" None: super().__init__(core_cfg, game_cfg, version) - self.size = 0x02b0 - + self.size = 0x02B0 + def handle(self, data: bytes) -> bytearray: - return super().handle(data) - + return super().handle(data) + def handle_common(cls, aime_id: int, ret: bytearray) -> bytearray: pass diff --git a/titles/idz/handlers/update_story_clear_num.py b/titles/idz/handlers/update_story_clear_num.py index 46f2689..bcf44a5 100644 --- a/titles/idz/handlers/update_story_clear_num.py +++ b/titles/idz/handlers/update_story_clear_num.py @@ -5,22 +5,23 @@ from core.config import CoreConfig from ..config import IDZConfig from ..const import IDZConstants + class IDZHandlerUpdateStoryClearNum(IDZHandlerBase): - cmd_codes = [0x007f, 0x097f, 0x013d, 0x0144] - rsp_codes = [0x0080, 0x013e, 0x013e, 0x0145] + cmd_codes = [0x007F, 0x097F, 0x013D, 0x0144] + rsp_codes = [0x0080, 0x013E, 0x013E, 0x0145] name = "update_story_clear_num" - + def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None: super().__init__(core_cfg, game_cfg, version) if self.version == IDZConstants.VER_IDZ_110: self.size = 0x0220 elif self.version == IDZConstants.VER_IDZ_130: - self.size = 0x04f0 + self.size = 0x04F0 elif self.version == IDZConstants.VER_IDZ_210: self.size = 0x0510 elif self.version == IDZConstants.VER_IDZ_230: self.size = 0x0800 - + def handle(self, data: bytes) -> bytearray: return super().handle(data) diff --git a/titles/idz/handlers/update_team_points.py b/titles/idz/handlers/update_team_points.py index c1f69ca..a23d843 100644 --- a/titles/idz/handlers/update_team_points.py +++ b/titles/idz/handlers/update_team_points.py @@ -5,13 +5,14 @@ from core.config import CoreConfig from ..config import IDZConfig from ..const import IDZConstants + class IDZHandleUpdateTeamPoints(IDZHandlerBase): - cmd_codes = [0x0081, 0x0081, 0x007b, 0x007b] + cmd_codes = [0x0081, 0x0081, 0x007B, 0x007B] name = "unlock_profile" def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None: super().__init__(core_cfg, game_cfg, version) - + def handle(self, data: bytes) -> bytearray: - ret = super().handle(data) + ret = super().handle(data) return ret diff --git a/titles/idz/handlers/update_ui_report.py b/titles/idz/handlers/update_ui_report.py index b77b4ea..7e99b40 100644 --- a/titles/idz/handlers/update_ui_report.py +++ b/titles/idz/handlers/update_ui_report.py @@ -5,13 +5,14 @@ from core.config import CoreConfig from ..config import IDZConfig from ..const import IDZConstants + class IDZHandleUpdateUIReport(IDZHandlerBase): - cmd_codes = [0x0084, 0x0084, 0x007e, 0x007e] + cmd_codes = [0x0084, 0x0084, 0x007E, 0x007E] name = "update_ui_report" def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None: super().__init__(core_cfg, game_cfg, version) - + def handle(self, data: bytes) -> bytearray: - ret = super().handle(data) + ret = super().handle(data) return ret diff --git a/titles/idz/handlers/update_user_log.py b/titles/idz/handlers/update_user_log.py index 78679eb..c862f52 100644 --- a/titles/idz/handlers/update_user_log.py +++ b/titles/idz/handlers/update_user_log.py @@ -5,13 +5,14 @@ from core.config import CoreConfig from ..config import IDZConfig from ..const import IDZConstants + class IDZHandleUpdateUserLog(IDZHandlerBase): - cmd_codes = [0x00bd, 0x00bd, 0x00ab, 0x00b3] + cmd_codes = [0x00BD, 0x00BD, 0x00AB, 0x00B3] name = "update_user_log" def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None: super().__init__(core_cfg, game_cfg, version) - + def handle(self, data: bytes) -> bytearray: - ret = super().handle(data) + ret = super().handle(data) return ret diff --git a/titles/idz/index.py b/titles/idz/index.py index 5b60e5a..0f26a30 100644 --- a/titles/idz/index.py +++ b/titles/idz/index.py @@ -16,6 +16,7 @@ from .userdb import IDZUserDBFactory, IDZUserDBWeb, IDZKey from .echo import IDZEcho from .handlers import IDZHandlerLoadConfigB + class IDZServlet: def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None: self.core_cfg = core_cfg @@ -51,13 +52,16 @@ class IDZServlet: level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str ) self.logger.inited = True - + @classmethod def rsaHashKeyN(cls, data): hash_ = 0 for i in data: - hash_ = hash_ * IDZConstants.HASH_MUL + (i ^ IDZConstants.HASH_XOR) ^ IDZConstants.HASH_LUT[i & 0xf] - hash_ &= 0xffffffff + hash_ = ( + hash_ * IDZConstants.HASH_MUL + (i ^ IDZConstants.HASH_XOR) + ^ IDZConstants.HASH_LUT[i & 0xF] + ) + hash_ &= 0xFFFFFFFF return hash_ @classmethod @@ -77,14 +81,18 @@ class IDZServlet: logging.getLogger("idz").error("IDZ: No RSA/AES keys! IDZ cannot start") return (False, "", "") - hostname = core_cfg.title.hostname if not game_cfg.server.hostname else game_cfg.server.hostname + hostname = ( + core_cfg.title.hostname + if not game_cfg.server.hostname + else game_cfg.server.hostname + ) return ( True, f"", f"{hostname}:{game_cfg.ports.userdb}", ) - def setup(self): + def setup(self): for key in self.game_cfg.rsa_keys: if "N" not in key or "d" not in key or "e" not in key: self.logger.error(f"Invalid IDZ key {key}") @@ -92,14 +100,14 @@ class IDZServlet: hashN = self.rsaHashKeyN(str(key["N"]).encode()) self.rsa_keys.append(IDZKey(key["N"], key["d"], key["e"], hashN)) - + if len(self.rsa_keys) <= 0: self.logger.error("No valid RSA keys provided! IDZ cannot start!") return handler_map = [{} for _ in range(IDZConstants.NUM_VERS)] handler_mod = mod = importlib.import_module(f"titles.idz.handlers") - + for cls_name in dir(handler_mod): if cls_name.startswith("__"): continue @@ -109,7 +117,7 @@ class IDZServlet: mod_cmds: List = getattr(mod, "cmd_codes") while len(mod_cmds) < IDZConstants.NUM_VERS: mod_cmds.append(None) - + for i in range(len(mod_cmds)): if mod_cmds[i] is None: mod_cmds[i] = mod_cmds[i - 1] @@ -119,27 +127,47 @@ class IDZServlet: except AttributeError as e: continue - endpoints.serverFromString(reactor, f"tcp:{self.game_cfg.ports.userdb}:interface={self.core_cfg.server.listen_address}")\ - .listen(IDZUserDBFactory(self.core_cfg, self.game_cfg, self.rsa_keys, handler_map)) - - reactor.listenUDP(self.game_cfg.ports.echo, IDZEcho(self.core_cfg, self.game_cfg)) - reactor.listenUDP(self.game_cfg.ports.echo + 1, IDZEcho(self.core_cfg, self.game_cfg)) - reactor.listenUDP(self.game_cfg.ports.match, IDZEcho(self.core_cfg, self.game_cfg)) - reactor.listenUDP(self.game_cfg.ports.userdb + 1, IDZEcho(self.core_cfg, self.game_cfg)) - + endpoints.serverFromString( + reactor, + f"tcp:{self.game_cfg.ports.userdb}:interface={self.core_cfg.server.listen_address}", + ).listen( + IDZUserDBFactory(self.core_cfg, self.game_cfg, self.rsa_keys, handler_map) + ) + + reactor.listenUDP( + self.game_cfg.ports.echo, IDZEcho(self.core_cfg, self.game_cfg) + ) + reactor.listenUDP( + self.game_cfg.ports.echo + 1, IDZEcho(self.core_cfg, self.game_cfg) + ) + reactor.listenUDP( + self.game_cfg.ports.match, IDZEcho(self.core_cfg, self.game_cfg) + ) + reactor.listenUDP( + self.game_cfg.ports.userdb + 1, IDZEcho(self.core_cfg, self.game_cfg) + ) + self.logger.info(f"UserDB Listening on port {self.game_cfg.ports.userdb}") def render_POST(self, request: Request, version: int, url_path: str) -> bytes: - req_raw = request.content.getvalue() + req_raw = request.content.getvalue() self.logger.info(f"IDZ POST request: {url_path} - {req_raw}") return b"" def render_GET(self, request: Request, version: int, url_path: str) -> bytes: - self.logger.info(f"IDZ GET request: {url_path}") - request.responseHeaders.setRawHeaders('Content-Type', [b"text/plain; charset=utf-8"]) - request.responseHeaders.setRawHeaders("Last-Modified", [b"Sun, 23 Apr 2023 05:33:20 GMT"]) - - news = self.game_cfg.server.news if self.game_cfg.server.news else f"Welcome to Initial D Arcade Stage Zero on {self.core_cfg.server.name}!" + self.logger.info(f"IDZ GET request: {url_path}") + request.responseHeaders.setRawHeaders( + "Content-Type", [b"text/plain; charset=utf-8"] + ) + request.responseHeaders.setRawHeaders( + "Last-Modified", [b"Sun, 23 Apr 2023 05:33:20 GMT"] + ) + + news = ( + self.game_cfg.server.news + if self.game_cfg.server.news + else f"Welcome to Initial D Arcade Stage Zero on {self.core_cfg.server.name}!" + ) news += "\r\n" news = "1979/01/01 00:00:00 2099/12/31 23:59:59 " + news diff --git a/titles/idz/userdb.py b/titles/idz/userdb.py index 95a2eb9..2f70ba4 100644 --- a/titles/idz/userdb.py +++ b/titles/idz/userdb.py @@ -19,6 +19,7 @@ from .handlers import IDZHandlerBase HANDLER_MAP: List[Dict] + class IDZKey: def __init__(self, n, d, e, hashN: int) -> None: self.N = n @@ -26,9 +27,16 @@ class IDZKey: self.e = e self.hashN = hashN + class IDZUserDBProtocol(Protocol): - def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, keys: List[IDZKey], handlers: List[Dict]) -> None: - self.logger = logging.getLogger('idz') + def __init__( + self, + core_cfg: CoreConfig, + game_cfg: IDZConfig, + keys: List[IDZKey], + handlers: List[Dict], + ) -> None: + self.logger = logging.getLogger("idz") self.core_config = core_cfg self.game_config = game_cfg self.rsa_keys = keys @@ -37,14 +45,14 @@ class IDZUserDBProtocol(Protocol): self.version = None self.version_internal = None self.skip_next = False - + def append_padding(self, data: bytes): """Appends 0s to the end of the data until it's at the correct size""" length = struct.unpack_from(" None: self.logger.debug(f"{self.transport.getPeer().host} Connected") base = 0 @@ -57,13 +65,17 @@ class IDZUserDBProtocol(Protocol): rsa_key = random.choice(self.rsa_keys) key_enc: int = pow(base, rsa_key.e, rsa_key.N) - result = key_enc.to_bytes(0x40, "little") + struct.pack(" None: + def connectionLost(self, reason) -> None: self.logger.debug( f"{self.transport.getPeer().host} Disconnected - {reason.value}" ) @@ -84,7 +96,7 @@ class IDZUserDBProtocol(Protocol): self.transport.write(b"\x00") return - elif magic == 0x01020304: + elif magic == 0x01020304: self.version = int(data_dec[16:19].decode()) if self.version == 110: @@ -99,10 +111,12 @@ class IDZUserDBProtocol(Protocol): self.logger.warn(f"Bad version v{self.version}") self.version = None self.version_internal = None - - self.logger.debug(f"Userdb v{self.version} handshake response from {self.transport.getPeer().host}") + + self.logger.debug( + f"Userdb v{self.version} handshake response from {self.transport.getPeer().host}" + ) return - + elif self.skip_next: self.skip_next = False self.transport.write(b"\x00") @@ -110,19 +124,25 @@ class IDZUserDBProtocol(Protocol): elif self.version is None: # We didn't get a handshake before, and this isn't one now, so we're up the creek - self.logger.info(f"Bad UserDB request from from {self.transport.getPeer().host}") + self.logger.info( + f"Bad UserDB request from from {self.transport.getPeer().host}" + ) self.transport.write(b"\x00") return cmd = struct.unpack_from(" None: + def __init__( + self, + cfg: CoreConfig, + game_cfg: IDZConfig, + keys: List[IDZKey], + handlers: List[Dict], + ) -> None: self.core_config = cfg self.game_config = game_cfg self.keys = keys self.handlers = handlers def buildProtocol(self, addr): - return IDZUserDBProtocol(self.core_config, self.game_config, self.keys, self.handlers) + return IDZUserDBProtocol( + self.core_config, self.game_config, self.keys, self.handlers + ) + class IDZUserDBWeb(resource.Resource): def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig): @@ -151,12 +180,16 @@ class IDZUserDBWeb(resource.Resource): self.isLeaf = True self.core_config = core_cfg self.game_config = game_cfg - self.logger = logging.getLogger('idz') - + self.logger = logging.getLogger("idz") + def render_POST(self, request: Request) -> bytes: - self.logger.info(f"IDZUserDBWeb POST from {request.getClientAddress().host} to {request.uri} with data {request.content.getvalue()}") + self.logger.info( + f"IDZUserDBWeb POST from {request.getClientAddress().host} to {request.uri} with data {request.content.getvalue()}" + ) return b"" def render_GET(self, request: Request) -> bytes: - self.logger.info(f"IDZUserDBWeb GET from {request.getClientAddress().host} to {request.uri}") - return b"" \ No newline at end of file + self.logger.info( + f"IDZUserDBWeb GET from {request.getClientAddress().host} to {request.uri}" + ) + return b"" diff --git a/titles/mai2/base.py b/titles/mai2/base.py index 9f5c8af..171378c 100644 --- a/titles/mai2/base.py +++ b/titles/mai2/base.py @@ -300,7 +300,9 @@ class Mai2Base: ): for fsr in upsert["userFriendSeasonRankingList"]: fsr["recordDate"] = ( - datetime.strptime(fsr["recordDate"], f"{Mai2Constants.DATE_TIME_FORMAT}.0"), + datetime.strptime( + fsr["recordDate"], f"{Mai2Constants.DATE_TIME_FORMAT}.0" + ), ) self.data.item.put_friend_season_ranking(user_id, fsr) diff --git a/titles/mai2/const.py b/titles/mai2/const.py index 9694aba..dcc7e29 100644 --- a/titles/mai2/const.py +++ b/titles/mai2/const.py @@ -39,7 +39,7 @@ class Mai2Constants: "maimai DX Splash PLUS", "maimai DX Universe", "maimai DX Universe PLUS", - "maimai DX Festival" + "maimai DX Festival", ) @classmethod diff --git a/titles/mai2/index.py b/titles/mai2/index.py index 3618f4d..1b92842 100644 --- a/titles/mai2/index.py +++ b/titles/mai2/index.py @@ -38,7 +38,7 @@ class Mai2Servlet: Mai2SplashPlus, Mai2Universe, Mai2UniversePlus, - Mai2Festival + Mai2Festival, ] self.logger = logging.getLogger("mai2") @@ -134,9 +134,7 @@ class Mai2Servlet: req_data = json.loads(unzip) - self.logger.info( - f"v{version} {endpoint} request from {client_ip}" - ) + self.logger.info(f"v{version} {endpoint} request from {client_ip}") self.logger.debug(req_data) func_to_find = "handle_" + inflection.underscore(endpoint) + "_request" diff --git a/titles/mai2/schema/item.py b/titles/mai2/schema/item.py index 980623e..6280bbb 100644 --- a/titles/mai2/schema/item.py +++ b/titles/mai2/schema/item.py @@ -402,7 +402,7 @@ class Mai2ItemData(BaseData): if result is None: self.logger.warn( f"put_friend_season_ranking: failed to insert", - f"friend_season_ranking! aime_id: {aime_id}" + f"friend_season_ranking! aime_id: {aime_id}", ) return None return result.lastrowid diff --git a/titles/mai2/schema/profile.py b/titles/mai2/schema/profile.py index b3802e5..3cb42d1 100644 --- a/titles/mai2/schema/profile.py +++ b/titles/mai2/schema/profile.py @@ -299,9 +299,11 @@ class Mai2ProfileData(BaseData): return result.lastrowid def get_profile_detail(self, user_id: int, version: int) -> Optional[Row]: - sql = select(detail).where( - and_(detail.c.user == user_id, detail.c.version <= version) - ).order_by(detail.c.version.desc()) + sql = ( + select(detail) + .where(and_(detail.c.user == user_id, detail.c.version <= version)) + .order_by(detail.c.version.desc()) + ) result = self.execute(sql) if result is None: @@ -324,9 +326,11 @@ class Mai2ProfileData(BaseData): return result.lastrowid def get_profile_ghost(self, user_id: int, version: int) -> Optional[Row]: - sql = select(ghost).where( - and_(ghost.c.user == user_id, ghost.c.version_int <= version) - ).order_by(ghost.c.version.desc()) + sql = ( + select(ghost) + .where(and_(ghost.c.user == user_id, ghost.c.version_int <= version)) + .order_by(ghost.c.version.desc()) + ) result = self.execute(sql) if result is None: @@ -349,9 +353,11 @@ class Mai2ProfileData(BaseData): return result.lastrowid def get_profile_extend(self, user_id: int, version: int) -> Optional[Row]: - sql = select(extend).where( - and_(extend.c.user == user_id, extend.c.version <= version) - ).order_by(extend.c.version.desc()) + sql = ( + select(extend) + .where(and_(extend.c.user == user_id, extend.c.version <= version)) + .order_by(extend.c.version.desc()) + ) result = self.execute(sql) if result is None: @@ -374,9 +380,11 @@ class Mai2ProfileData(BaseData): return result.lastrowid def get_profile_option(self, user_id: int, version: int) -> Optional[Row]: - sql = select(option).where( - and_(option.c.user == user_id, option.c.version <= version) - ).order_by(option.c.version.desc()) + sql = ( + select(option) + .where(and_(option.c.user == user_id, option.c.version <= version)) + .order_by(option.c.version.desc()) + ) result = self.execute(sql) if result is None: @@ -399,9 +407,11 @@ class Mai2ProfileData(BaseData): return result.lastrowid def get_profile_rating(self, user_id: int, version: int) -> Optional[Row]: - sql = select(rating).where( - and_(rating.c.user == user_id, rating.c.version <= version) - ).order_by(rating.c.version.desc()) + sql = ( + select(rating) + .where(and_(rating.c.user == user_id, rating.c.version <= version)) + .order_by(rating.c.version.desc()) + ) result = self.execute(sql) if result is None: diff --git a/titles/ongeki/base.py b/titles/ongeki/base.py index 10bb1a8..4f7619c 100644 --- a/titles/ongeki/base.py +++ b/titles/ongeki/base.py @@ -452,8 +452,7 @@ class OngekiBase: tmp.pop("id") items.append(tmp) - xout = kind * 10000000000 + \ - (data["nextIndex"] % 10000000000) + len(items) + xout = kind * 10000000000 + (data["nextIndex"] % 10000000000) + len(items) if len(items) < data["maxCount"] or data["maxCount"] == 0: nextIndex = 0 @@ -852,8 +851,7 @@ class OngekiBase: ) if "userOption" in upsert and len(upsert["userOption"]) > 0: - self.data.profile.put_profile_options( - user_id, upsert["userOption"][0]) + self.data.profile.put_profile_options(user_id, upsert["userOption"][0]) if "userPlaylogList" in upsert: for playlog in upsert["userPlaylogList"]: diff --git a/titles/ongeki/bright.py b/titles/ongeki/bright.py index 23eeb6c..06155a1 100644 --- a/titles/ongeki/bright.py +++ b/titles/ongeki/bright.py @@ -97,7 +97,7 @@ class OngekiBright(OngekiBase): "userId": data["userId"], "length": 0, "nextIndex": 0, - "userCharacterList": [] + "userCharacterList": [], } max_ct = data["maxCount"] @@ -548,7 +548,7 @@ class OngekiBright(OngekiBase): "returnCode": 1, "orderId": 0, "serialId": "11111111111111111111", - "apiName": "CMUpsertUserPrintPlaylogApi" + "apiName": "CMUpsertUserPrintPlaylogApi", } def handle_cm_upsert_user_printlog_api_request(self, data: Dict) -> Dict: @@ -556,7 +556,7 @@ class OngekiBright(OngekiBase): "returnCode": 1, "orderId": 0, "serialId": "11111111111111111111", - "apiName": "CMUpsertUserPrintlogApi" + "apiName": "CMUpsertUserPrintlogApi", } def handle_cm_upsert_user_print_api_request(self, data: Dict) -> Dict: diff --git a/titles/ongeki/schema/item.py b/titles/ongeki/schema/item.py index d826fba..27d90f8 100644 --- a/titles/ongeki/schema/item.py +++ b/titles/ongeki/schema/item.py @@ -705,9 +705,7 @@ class OngekiItemData(BaseData): user=aime_id, serialId=serial_id, **user_print_data ) - conflict = sql.on_duplicate_key_update( - user=aime_id, **user_print_data - ) + conflict = sql.on_duplicate_key_update(user=aime_id, **user_print_data) result = self.execute(conflict) if result is None: diff --git a/titles/pokken/base.py b/titles/pokken/base.py index 0e87397..40e6444 100644 --- a/titles/pokken/base.py +++ b/titles/pokken/base.py @@ -74,27 +74,21 @@ class PokkenBase: return res.SerializeToString() - def handle_save_client_log( - self, request: jackal_pb2.Request - ) -> bytes: + def handle_save_client_log(self, request: jackal_pb2.Request) -> bytes: res = jackal_pb2.Response() res.result = 1 res.type = jackal_pb2.MessageType.SAVE_CLIENT_LOG return res.SerializeToString() - def handle_check_diagnosis( - self, request: jackal_pb2.Request - ) -> bytes: + def handle_check_diagnosis(self, request: jackal_pb2.Request) -> bytes: res = jackal_pb2.Response() res.result = 1 res.type = jackal_pb2.MessageType.CHECK_DIAGNOSIS return res.SerializeToString() - def handle_load_client_settings( - self, request: jackal_pb2.Request - ) -> bytes: + def handle_load_client_settings(self, request: jackal_pb2.Request) -> bytes: res = jackal_pb2.Response() res.result = 1 res.type = jackal_pb2.MessageType.LOAD_CLIENT_SETTINGS @@ -129,26 +123,28 @@ class PokkenBase: ranking.modify_date = int(datetime.now().timestamp() / 1000) res.load_ranking.CopyFrom(ranking) return res.SerializeToString() - + def handle_load_user(self, request: jackal_pb2.Request) -> bytes: res = jackal_pb2.Response() res.result = 1 res.type = jackal_pb2.MessageType.LOAD_USER - access_code = request.load_user.access_code + access_code = request.load_user.access_code load_usr = jackal_pb2.LoadUserResponseData() user_id = self.data.card.get_user_id_from_card(access_code) if user_id is None and self.game_cfg.server.auto_register: user_id = self.data.user.create_user() card_id = self.data.card.create_card(user_id, access_code) - - self.logger.info(f"Register new card {access_code} (UserId {user_id}, CardId {card_id})") - + + self.logger.info( + f"Register new card {access_code} (UserId {user_id}, CardId {card_id})" + ) + elif user_id is None: self.logger.info(f"Registration of card {access_code} blocked!") res.load_user.CopyFrom(load_usr) return res.SerializeToString() - + """ TODO: Add repeated values tutorial_progress_flag @@ -168,12 +164,12 @@ class PokkenBase: load_usr.load_hash = 1 load_usr.cardlock_status = False load_usr.banapass_id = user_id - load_usr.access_code = access_code + load_usr.access_code = access_code load_usr.precedent_release_flag = 0xFFFFFFFF - + if profile is None: - profile_id = self.data.profile.create_profile(user_id) - profile_dict = {'id': profile_id, 'user': user_id} + profile_id = self.data.profile.create_profile(user_id) + profile_dict = {"id": profile_id, "user": user_id} pokemon_data = [] tutorial_progress = [] rankmatch_progress = [] @@ -181,10 +177,12 @@ class PokkenBase: event_achievement_flag = [] event_achievement_param = [] 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', '')})") + 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', '')})" + ) pokemon_data = self.data.profile.get_all_pokemon_data(user_id) tutorial_progress = [] rankmatch_progress = [] @@ -193,76 +191,78 @@ class PokkenBase: event_achievement_param = [] load_usr.new_card_flag = False - load_usr.navi_newbie_flag = profile_dict.get('navi_newbie_flag', True) - load_usr.navi_enable_flag = profile_dict.get('navi_enable_flag', True) - load_usr.pad_vibrate_flag = profile_dict.get('pad_vibrate_flag', True) - load_usr.home_region_code = profile_dict.get('home_region_code', 0) - load_usr.home_loc_name = profile_dict.get('home_loc_name', "") - load_usr.pref_code = profile_dict.get('pref_code', 0) - load_usr.trainer_name = profile_dict.get('trainer_name', "Newb" + str(random.randint(1111,999999))) - 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.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) - load_usr.total_play_days = profile_dict.get('total_play_days', 0) - load_usr.play_date_time = profile_dict.get('play_date_time', 0) - load_usr.lucky_box_fail_num = profile_dict.get('lucky_box_fail_num', 0) - load_usr.event_reward_get_flag = profile_dict.get('event_reward_get_flag', 0) - load_usr.rank_pvp_all = profile_dict.get('rank_pvp_all', 0) - load_usr.rank_pvp_loc = profile_dict.get('rank_pvp_loc', 0) - load_usr.rank_cpu_all = profile_dict.get('rank_cpu_all', 0) - load_usr.rank_cpu_loc = profile_dict.get('rank_cpu_loc', 0) - 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_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.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.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) - load_usr.timeup_win_num = profile_dict.get('timeup_win_num', 0) - 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.continue_num = profile_dict.get('continue_num', 0) + load_usr.navi_newbie_flag = profile_dict.get("navi_newbie_flag", True) + load_usr.navi_enable_flag = profile_dict.get("navi_enable_flag", True) + load_usr.pad_vibrate_flag = profile_dict.get("pad_vibrate_flag", True) + load_usr.home_region_code = profile_dict.get("home_region_code", 0) + load_usr.home_loc_name = profile_dict.get("home_loc_name", "") + load_usr.pref_code = profile_dict.get("pref_code", 0) + load_usr.trainer_name = profile_dict.get( + "trainer_name", "Newb" + str(random.randint(1111, 999999)) + ) + 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.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) + load_usr.total_play_days = profile_dict.get("total_play_days", 0) + load_usr.play_date_time = profile_dict.get("play_date_time", 0) + load_usr.lucky_box_fail_num = profile_dict.get("lucky_box_fail_num", 0) + load_usr.event_reward_get_flag = profile_dict.get("event_reward_get_flag", 0) + load_usr.rank_pvp_all = profile_dict.get("rank_pvp_all", 0) + load_usr.rank_pvp_loc = profile_dict.get("rank_pvp_loc", 0) + load_usr.rank_cpu_all = profile_dict.get("rank_cpu_all", 0) + load_usr.rank_cpu_loc = profile_dict.get("rank_cpu_loc", 0) + 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_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.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.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) + load_usr.timeup_win_num = profile_dict.get("timeup_win_num", 0) + 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.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_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) - load_usr.avatar_face0 = profile_dict.get('avatar_face0', 0) - load_usr.avatar_face1 = profile_dict.get('avatar_face1', 0) - load_usr.avatar_face2 = profile_dict.get('avatar_face2', 0) - load_usr.avatar_bodyall = profile_dict.get('avatar_bodyall', 0) - load_usr.avatar_wear = profile_dict.get('avatar_wear', 0) - load_usr.avatar_accessory = profile_dict.get('avatar_accessory', 0) - load_usr.avatar_stamp = profile_dict.get('avatar_stamp', 0) + load_usr.avatar_body = profile_dict.get("avatar_body", 0) + load_usr.avatar_gender = profile_dict.get("avatar_gender", 0) + 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) + load_usr.avatar_face0 = profile_dict.get("avatar_face0", 0) + load_usr.avatar_face1 = profile_dict.get("avatar_face1", 0) + load_usr.avatar_face2 = profile_dict.get("avatar_face2", 0) + load_usr.avatar_bodyall = profile_dict.get("avatar_bodyall", 0) + load_usr.avatar_wear = profile_dict.get("avatar_wear", 0) + load_usr.avatar_accessory = profile_dict.get("avatar_accessory", 0) + load_usr.avatar_stamp = profile_dict.get("avatar_stamp", 0) - load_usr.event_state = profile_dict.get('event_state', 0) - load_usr.event_id = profile_dict.get('event_id', 0) - load_usr.sp_bonus_category_id_1 = profile_dict.get('sp_bonus_category_id_1', 0) - load_usr.sp_bonus_key_value_1 = profile_dict.get('sp_bonus_key_value_1', 0) - load_usr.sp_bonus_category_id_2 = profile_dict.get('sp_bonus_category_id_2', 0) - load_usr.sp_bonus_key_value_2 = profile_dict.get('sp_bonus_key_value_2', 0) - load_usr.last_play_event_id = profile_dict.get('last_play_event_id', 0) + load_usr.event_state = profile_dict.get("event_state", 0) + load_usr.event_id = profile_dict.get("event_id", 0) + load_usr.sp_bonus_category_id_1 = profile_dict.get("sp_bonus_category_id_1", 0) + load_usr.sp_bonus_key_value_1 = profile_dict.get("sp_bonus_key_value_1", 0) + load_usr.sp_bonus_category_id_2 = profile_dict.get("sp_bonus_category_id_2", 0) + load_usr.sp_bonus_key_value_2 = profile_dict.get("sp_bonus_key_value_2", 0) + load_usr.last_play_event_id = profile_dict.get("last_play_event_id", 0) res.load_user.CopyFrom(load_usr) return res.SerializeToString() - + def handle_set_bnpassid_lock(self, data: jackal_pb2.Request) -> bytes: res = jackal_pb2.Response() res.result = 1 @@ -288,22 +288,30 @@ class PokkenBase: res.type = jackal_pb2.MessageType.SAVE_CHARGE return res.SerializeToString() - def handle_matching_noop(self, data: Dict = {}, client_ip: str = "127.0.0.1") -> Dict: - return {} - - def handle_matching_start_matching(self, data: Dict = {}, client_ip: str = "127.0.0.1") -> Dict: + def handle_matching_noop( + self, data: Dict = {}, client_ip: str = "127.0.0.1" + ) -> Dict: return {} - def handle_matching_is_matching(self, data: Dict = {}, client_ip: str = "127.0.0.1") -> Dict: + def handle_matching_start_matching( + self, data: Dict = {}, client_ip: str = "127.0.0.1" + ) -> Dict: + return {} + + 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 - }, + }, "list":[] """ return {} - - def handle_matching_stop_matching(self, data: Dict = {}, client_ip: str = "127.0.0.1") -> Dict: - return {} \ No newline at end of file + + def handle_matching_stop_matching( + self, data: Dict = {}, client_ip: str = "127.0.0.1" + ) -> Dict: + return {} diff --git a/titles/pokken/config.py b/titles/pokken/config.py index e25a8c7..84da8d2 100644 --- a/titles/pokken/config.py +++ b/titles/pokken/config.py @@ -59,6 +59,7 @@ class PokkenServerConfig: self.__config, "pokken", "server", "auto_register", default=True ) + class PokkenConfig(dict): def __init__(self) -> None: self.server = PokkenServerConfig(self) diff --git a/titles/pokken/const.py b/titles/pokken/const.py index 81b2ebc..2eb5357 100644 --- a/titles/pokken/const.py +++ b/titles/pokken/const.py @@ -1,5 +1,6 @@ from enum import Enum + class PokkenConstants: GAME_CODE = "SDAK" diff --git a/titles/pokken/database.py b/titles/pokken/database.py index c940d83..272cfd8 100644 --- a/titles/pokken/database.py +++ b/titles/pokken/database.py @@ -3,6 +3,7 @@ from core.config import CoreConfig from .schema import * + class PokkenData(Data): def __init__(self, cfg: CoreConfig) -> None: super().__init__(cfg) diff --git a/titles/pokken/index.py b/titles/pokken/index.py index adfde88..bccdcaf 100644 --- a/titles/pokken/index.py +++ b/titles/pokken/index.py @@ -114,19 +114,19 @@ class PokkenServlet(resource.Resource): endpoint = jackal_pb2.MessageType.DESCRIPTOR.values_by_number[ pokken_request.type ].name.lower() - + self.logger.debug(pokken_request) handler = getattr(self.base, f"handle_{endpoint}", None) if handler is None: self.logger.warn(f"No handler found for message type {endpoint}") return self.base.handle_noop(pokken_request) - + self.logger.info(f"{endpoint} request from {Utils.get_ip_addr(request)}") - + ret = handler(pokken_request) return ret - + def handle_matching(self, request: Request) -> bytes: content = request.content.getvalue() client_ip = Utils.get_ip_addr(request) @@ -135,26 +135,37 @@ class PokkenServlet(resource.Resource): self.logger.info("Empty matching request") return json.dumps(self.base.handle_matching_noop()).encode() - json_content = ast.literal_eval(content.decode().replace('null', 'None').replace('true', 'True').replace('false', 'False')) + json_content = ast.literal_eval( + content.decode() + .replace("null", "None") + .replace("true", "True") + .replace("false", "False") + ) self.logger.info(f"Matching {json_content['call']} request") self.logger.debug(json_content) - handler = getattr(self.base, f"handle_matching_{inflection.underscore(json_content['call'])}", None) + handler = getattr( + self.base, + f"handle_matching_{inflection.underscore(json_content['call'])}", + None, + ) if handler is None: - self.logger.warn(f"No handler found for message type {json_content['call']}") + self.logger.warn( + f"No handler found for message type {json_content['call']}" + ) return json.dumps(self.base.handle_matching_noop()).encode() - + ret = handler(json_content, client_ip) - + if ret is None: - ret = {} + ret = {} if "result" not in ret: ret["result"] = "true" if "data" not in ret: ret["data"] = {} if "timestamp" not in ret: ret["timestamp"] = int(datetime.now().timestamp() * 1000) - + self.logger.debug(f"Response {ret}") return json.dumps(ret).encode() diff --git a/titles/pokken/schema/item.py b/titles/pokken/schema/item.py index 686b32f..4919ea0 100644 --- a/titles/pokken/schema/item.py +++ b/titles/pokken/schema/item.py @@ -9,19 +9,26 @@ from sqlalchemy.dialects.mysql import insert from core.data.schema import BaseData, metadata item = Table( - 'pokken_item', + "pokken_item", metadata, - Column('id', Integer, primary_key=True, nullable=False), - Column('user', ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False, unique=True), - Column('category', Integer), - Column('content', Integer), - Column('type', Integer), - UniqueConstraint('user', 'category', 'content', 'type', name='pokken_item_uk'), + Column("id", Integer, primary_key=True, nullable=False), + Column( + "user", + ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), + nullable=False, + unique=True, + ), + Column("category", Integer), + Column("content", Integer), + Column("type", Integer), + UniqueConstraint("user", "category", "content", "type", name="pokken_item_uk"), mysql_charset="utf8mb4", ) + class PokkenItemData(BaseData): """ Items obtained as rewards """ + pass diff --git a/titles/pokken/schema/match.py b/titles/pokken/schema/match.py index aec6bd3..c84ec63 100644 --- a/titles/pokken/schema/match.py +++ b/titles/pokken/schema/match.py @@ -10,29 +10,35 @@ from core.data.schema import BaseData, metadata # Pokken sends depressingly little match data... match_data = Table( - 'pokken_match_data', + "pokken_match_data", metadata, - Column('id', Integer, primary_key=True, nullable=False), - Column('user', ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), - Column('num_games', Integer), - Column('play_modes', JSON), - Column('results', JSON), - Column('ex_ko_num', Integer), - Column('wko_num', Integer), - Column('timeup_win_num', Integer), - Column('cool_ko_num', Integer), - Column('perfect_ko_num', Integer), - Column('use_navi', Integer), - Column('use_navi_cloth', Integer), - Column('use_aid_skill', Integer), - Column('play_date', TIMESTAMP), + Column("id", Integer, primary_key=True, nullable=False), + Column( + "user", + ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), + nullable=False, + ), + Column("num_games", Integer), + Column("play_modes", JSON), + Column("results", JSON), + Column("ex_ko_num", Integer), + Column("wko_num", Integer), + Column("timeup_win_num", Integer), + Column("cool_ko_num", Integer), + Column("perfect_ko_num", Integer), + Column("use_navi", Integer), + Column("use_navi_cloth", Integer), + Column("use_aid_skill", Integer), + Column("play_date", TIMESTAMP), mysql_charset="utf8mb4", ) + class PokkenMatchData(BaseData): """ Match logs """ + def save_match(self, user_id: int, match_data: Dict) -> Optional[int]: pass @@ -43,4 +49,4 @@ class PokkenMatchData(BaseData): pass def get_matches(self, limit: int = 20) -> Optional[List[Row]]: - pass \ No newline at end of file + pass diff --git a/titles/pokken/schema/profile.py b/titles/pokken/schema/profile.py index 871ff9d..8e536f1 100644 --- a/titles/pokken/schema/profile.py +++ b/titles/pokken/schema/profile.py @@ -12,150 +12,179 @@ from ..const import PokkenConstants # Some more of the repeated fields could probably be their own tables, for now I just did the ones that made sense to me # Having the profile table be this massive kinda blows for updates but w/e, **kwargs to the rescue profile = Table( - 'pokken_profile', + "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('home_region_code', Integer), - Column('home_loc_name', String(255)), - Column('pref_code', Integer), - Column('navi_newbie_flag', Boolean), - Column('navi_enable_flag', Boolean), - Column('pad_vibrate_flag', Boolean), - Column('trainer_rank_point', Integer), - Column('wallet', Integer), - Column('fight_money', Integer), - Column('score_point', Integer), - Column('grade_max_num', Integer), - Column('extra_counter', Integer), # Optional - Column('tutorial_progress_flag', JSON), # Repeated, Integer - Column('total_play_days', Integer), - Column('play_date_time', Integer), - Column('achievement_flag', JSON), # Repeated, Integer - Column('lucky_box_fail_num', Integer), - Column('event_reward_get_flag', Integer), - Column('rank_pvp_all', Integer), - Column('rank_pvp_loc', Integer), - Column('rank_cpu_all', Integer), - Column('rank_cpu_loc', Integer), - Column('rank_event', Integer), - Column('awake_num', Integer), - Column('use_support_num', Integer), - Column('rankmatch_flag', Integer), - Column('rankmatch_max', Integer), # Optional - Column('rankmatch_progress', JSON), # Repeated, Integer - Column('rankmatch_success', Integer), # Optional - Column('beat_num', Integer), # Optional - Column('title_text_id', Integer), - Column('title_plate_id', Integer), - Column('title_decoration_id', Integer), - Column('support_pokemon_list', JSON), # Repeated, Integer - Column('support_set_1_1', Integer), # Repeated, Integer - Column('support_set_1_2', Integer), - Column('support_set_2_1', Integer), # Repeated, Integer - Column('support_set_2_2', Integer), - Column('support_set_3_1', Integer), # Repeated, Integer - Column('support_set_3_2', Integer), - Column('navi_trainer', Integer), - Column('navi_version_id', Integer), - Column('aid_skill_list', JSON), # Repeated, Integer - Column('aid_skill', Integer), - Column('comment_text_id', Integer), - Column('comment_word_id', Integer), - Column('latest_use_pokemon', Integer), - Column('ex_ko_num', Integer), - Column('wko_num', Integer), - Column('timeup_win_num', Integer), - Column('cool_ko_num', Integer), - Column('perfect_ko_num', Integer), - Column('record_flag', Integer), - Column('continue_num', Integer), - Column('avatar_body', Integer), # Optional - Column('avatar_gender', Integer), # Optional - Column('avatar_background', Integer), # Optional - Column('avatar_head', Integer), # Optional - Column('avatar_battleglass', Integer), # Optional - Column('avatar_face0', Integer), # Optional - Column('avatar_face1', Integer), # Optional - Column('avatar_face2', Integer), # Optional - Column('avatar_bodyall', Integer), # Optional - Column('avatar_wear', Integer), # Optional - Column('avatar_accessory', Integer), # Optional - Column('avatar_stamp', Integer), # Optional - Column('event_state', Integer), - Column('event_id', Integer), - Column('sp_bonus_category_id_1', Integer), - Column('sp_bonus_key_value_1', Integer), - Column('sp_bonus_category_id_2', Integer), - Column('sp_bonus_key_value_2', Integer), - Column('last_play_event_id', Integer), # Optional - Column('event_achievement_flag', JSON), # Repeated, Integer - Column('event_achievement_param', JSON), # Repeated, Integer - Column('battle_num_vs_wan', Integer), # 4? - Column('win_vs_wan', Integer), - Column('battle_num_vs_lan', Integer), # 3? - Column('win_vs_lan', Integer), - Column('battle_num_vs_cpu', Integer), # 2 - Column('win_cpu', Integer), - Column('battle_num_tutorial', Integer), # 1? - mysql_charset="utf8mb4" + 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("home_region_code", Integer), + Column("home_loc_name", String(255)), + Column("pref_code", Integer), + Column("navi_newbie_flag", Boolean), + Column("navi_enable_flag", Boolean), + Column("pad_vibrate_flag", Boolean), + Column("trainer_rank_point", Integer), + Column("wallet", Integer), + Column("fight_money", Integer), + Column("score_point", Integer), + Column("grade_max_num", Integer), + Column("extra_counter", Integer), # Optional + Column("tutorial_progress_flag", JSON), # Repeated, Integer + Column("total_play_days", Integer), + Column("play_date_time", Integer), + Column("achievement_flag", JSON), # Repeated, Integer + Column("lucky_box_fail_num", Integer), + Column("event_reward_get_flag", Integer), + Column("rank_pvp_all", Integer), + Column("rank_pvp_loc", Integer), + Column("rank_cpu_all", Integer), + Column("rank_cpu_loc", Integer), + Column("rank_event", Integer), + Column("awake_num", Integer), + Column("use_support_num", Integer), + Column("rankmatch_flag", Integer), + Column("rankmatch_max", Integer), # Optional + Column("rankmatch_progress", JSON), # Repeated, Integer + Column("rankmatch_success", Integer), # Optional + Column("beat_num", Integer), # Optional + Column("title_text_id", Integer), + Column("title_plate_id", Integer), + Column("title_decoration_id", Integer), + Column("support_pokemon_list", JSON), # Repeated, Integer + Column("support_set_1_1", Integer), # Repeated, Integer + Column("support_set_1_2", Integer), + Column("support_set_2_1", Integer), # Repeated, Integer + Column("support_set_2_2", Integer), + Column("support_set_3_1", Integer), # Repeated, Integer + Column("support_set_3_2", Integer), + Column("navi_trainer", Integer), + Column("navi_version_id", Integer), + Column("aid_skill_list", JSON), # Repeated, Integer + Column("aid_skill", Integer), + Column("comment_text_id", Integer), + Column("comment_word_id", Integer), + Column("latest_use_pokemon", Integer), + Column("ex_ko_num", Integer), + Column("wko_num", Integer), + Column("timeup_win_num", Integer), + Column("cool_ko_num", Integer), + Column("perfect_ko_num", Integer), + Column("record_flag", Integer), + Column("continue_num", Integer), + Column("avatar_body", Integer), # Optional + Column("avatar_gender", Integer), # Optional + Column("avatar_background", Integer), # Optional + Column("avatar_head", Integer), # Optional + Column("avatar_battleglass", Integer), # Optional + Column("avatar_face0", Integer), # Optional + Column("avatar_face1", Integer), # Optional + Column("avatar_face2", Integer), # Optional + Column("avatar_bodyall", Integer), # Optional + Column("avatar_wear", Integer), # Optional + Column("avatar_accessory", Integer), # Optional + Column("avatar_stamp", Integer), # Optional + Column("event_state", Integer), + Column("event_id", Integer), + Column("sp_bonus_category_id_1", Integer), + Column("sp_bonus_key_value_1", Integer), + Column("sp_bonus_category_id_2", Integer), + Column("sp_bonus_key_value_2", Integer), + Column("last_play_event_id", Integer), # Optional + Column("event_achievement_flag", JSON), # Repeated, Integer + Column("event_achievement_param", JSON), # Repeated, Integer + Column("battle_num_vs_wan", Integer), # 4? + Column("win_vs_wan", Integer), + Column("battle_num_vs_lan", Integer), # 3? + Column("win_vs_lan", Integer), + 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_data", 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('pokemon_exp', Integer), - Column('battle_num_vs_wan', Integer), # 4? - Column('win_vs_wan', Integer), - Column('battle_num_vs_lan', Integer), # 3? - Column('win_vs_lan', Integer), - Column('battle_num_vs_cpu', Integer), # 2 - Column('win_cpu', Integer), - Column('battle_all_num_tutorial', Integer), - Column('battle_num_tutorial', Integer), # 1? - Column('bp_point_atk', Integer), - 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" + 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("pokemon_exp", Integer), + Column("battle_num_vs_wan", Integer), # 4? + Column("win_vs_wan", Integer), + Column("battle_num_vs_lan", Integer), # 3? + Column("win_vs_lan", Integer), + Column("battle_num_vs_cpu", Integer), # 2 + Column("win_cpu", Integer), + Column("battle_all_num_tutorial", Integer), + Column("battle_num_tutorial", Integer), # 1? + Column("bp_point_atk", Integer), + 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", ) + class PokkenProfileData(BaseData): def create_profile(self, user_id: int) -> Optional[int]: - sql = insert(profile).values(user = user_id) - conflict = sql.on_duplicate_key_update(user = user_id) - + sql = insert(profile).values(user=user_id) + conflict = sql.on_duplicate_key_update(user=user_id) + result = self.execute(conflict) if result is None: self.logger.error(f"Failed to create pokken profile for user {user_id}!") return None return result.lastrowid - + def set_profile_name(self, user_id: int, new_name: str) -> None: - sql = update(profile).where(profile.c.user == user_id).values(trainer_name = new_name) + sql = ( + update(profile) + .where(profile.c.user == user_id) + .values(trainer_name=new_name) + ) result = self.execute(sql) if result is None: - self.logger.error(f"Failed to update pokken profile name for user {user_id}!") - + self.logger.error( + f"Failed to update pokken profile name for user {user_id}!" + ) + def update_profile_tutorial_flags(self, user_id: int, tutorial_flags: Dict) -> None: pass - def add_profile_points(self, user_id: int, rank_pts: int, money: int, score_pts: int) -> None: + def add_profile_points( + self, user_id: int, rank_pts: int, money: int, score_pts: int + ) -> None: pass def get_profile(self, user_id: int) -> Optional[Row]: sql = profile.select(profile.c.user == user_id) result = self.execute(sql) - if result is None: return None + if result is None: + return None return result.fetchone() - def put_pokemon_data(self, user_id: int, pokemon_id: int, illust_no: int, get_exp: int, atk: int, res: int, defe: int, sp: int) -> Optional[int]: + def put_pokemon_data( + self, + user_id: int, + pokemon_id: int, + illust_no: int, + get_exp: int, + atk: int, + res: int, + defe: int, + sp: int, + ) -> Optional[int]: pass def get_pokemon_data(self, user_id: int, pokemon_id: int) -> Optional[Row]: @@ -164,13 +193,24 @@ class PokkenProfileData(BaseData): def get_all_pokemon_data(self, user_id: int) -> Optional[List[Row]]: pass - def put_results(self, user_id: int, pokemon_id: int, match_type: int, match_result: int) -> None: + def put_results( + self, user_id: int, pokemon_id: int, match_type: int, match_result: int + ) -> None: """ Records the match stats (type and win/loss) for the pokemon and profile """ pass - def put_stats(self, user_id: int, exkos: int, wkos: int, timeout_wins: int, cool_kos: int, perfects: int, continues: int) -> None: + def put_stats( + self, + user_id: int, + exkos: int, + wkos: int, + timeout_wins: int, + cool_kos: int, + perfects: int, + continues: int, + ) -> None: """ Records profile stats """ diff --git a/titles/pokken/schema/static.py b/titles/pokken/schema/static.py index 63aa2dd..121ebc4 100644 --- a/titles/pokken/schema/static.py +++ b/titles/pokken/schema/static.py @@ -8,5 +8,6 @@ from sqlalchemy.dialects.mysql import insert from core.data.schema import BaseData, metadata + class PokkenStaticData(BaseData): - pass \ No newline at end of file + pass diff --git a/titles/wacca/base.py b/titles/wacca/base.py index 35a6efc..ada40c6 100644 --- a/titles/wacca/base.py +++ b/titles/wacca/base.py @@ -200,18 +200,24 @@ class WaccaBase: self.logger.info(f"User {req.userId} login on {req.chipId}") last_login_time = int(profile["last_login_date"].timestamp()) resp.lastLoginDate = last_login_time - midnight_today_ts = int(datetime.now().replace(hour=0, minute=0, second=0, microsecond=0).timestamp()) + midnight_today_ts = int( + datetime.now() + .replace(hour=0, minute=0, second=0, microsecond=0) + .timestamp() + ) # If somebodies login timestamp < midnight of current day, then they are logging in for the first time today if last_login_time < midnight_today_ts: resp.firstLoginDaily = True - - # If the difference between midnight today and their last login is greater then 1 day (86400 seconds) they've broken their streak + + # If the difference between midnight today and their last login is greater then 1 day (86400 seconds) they've broken their streak if midnight_today_ts - last_login_time > 86400: is_consec_day = False - self.data.profile.session_login(req.userId, resp.firstLoginDaily, is_consec_day) - + self.data.profile.session_login( + req.userId, resp.firstLoginDaily, is_consec_day + ) + if resp.firstLoginDaily: # TODO: Daily bonus pass @@ -624,17 +630,25 @@ class WaccaBase: new_tickets.append([ticket["id"], ticket["ticket_id"], 9999999999]) for item in req.itemsUsed: - if item.itemType == WaccaConstants.ITEM_TYPES["wp"] and not self.game_config.mods.infinite_wp: + if ( + item.itemType == WaccaConstants.ITEM_TYPES["wp"] + and not self.game_config.mods.infinite_wp + ): if current_wp >= item.quantity: current_wp -= item.quantity self.data.profile.spend_wp(req.profileId, item.quantity) else: return BaseResponse().make() - elif item.itemType == WaccaConstants.ITEM_TYPES["ticket"] and not self.game_config.mods.infinite_tickets: + elif ( + item.itemType == WaccaConstants.ITEM_TYPES["ticket"] + and not self.game_config.mods.infinite_tickets + ): for x in range(len(new_tickets)): if new_tickets[x][1] == item.itemId: - self.logger.debug(f"Remove ticket ID {new_tickets[x][0]} type {new_tickets[x][1]} from {user_id}") + self.logger.debug( + f"Remove ticket ID {new_tickets[x][0]} type {new_tickets[x][1]} from {user_id}" + ) self.data.item.spend_ticket(new_tickets[x][0]) new_tickets.pop(x) break @@ -865,7 +879,10 @@ class WaccaBase: user_id = profile["user"] resp.currentWp = profile["wp"] - if req.purchaseType == PurchaseType.PurchaseTypeWP and not self.game_config.mods.infinite_wp: + if ( + req.purchaseType == PurchaseType.PurchaseTypeWP + and not self.game_config.mods.infinite_wp + ): resp.currentWp -= req.cost self.data.profile.spend_wp(req.profileId, req.cost) @@ -1055,11 +1072,18 @@ class WaccaBase: ): if item.quantity > WaccaConstants.Difficulty.HARD.value: old_score = self.data.score.get_best_score( - user_id, item.itemId, item.quantity - ) + user_id, item.itemId, item.quantity + ) if not old_score: self.data.score.put_best_score( - user_id, item.itemId, item.quantity, 0, [0] * 5, [0] * 13, 0, 0 + user_id, + item.itemId, + item.quantity, + 0, + [0] * 5, + [0] * 13, + 0, + 0, ) if item.quantity == 0: diff --git a/titles/wacca/handlers/helpers.py b/titles/wacca/handlers/helpers.py index b17602a..025c161 100644 --- a/titles/wacca/handlers/helpers.py +++ b/titles/wacca/handlers/helpers.py @@ -366,7 +366,7 @@ class UserEventInfo: conditions = [] for x in self.conditionInfo: conditions.append(x.make()) - + return [self.eventId, conditions] @@ -374,7 +374,7 @@ class UserEventConditionInfo: def __init__(self) -> None: self.achievementCondition = 0 self.progress = 0 - + def make(self) -> List: return [self.achievementCondition, self.progress] @@ -835,11 +835,13 @@ class LastSongDetail: class FriendScoreDetail: - def __init__(self, song_id: int = 0, difficulty: int = 1, best_score: int = 0) -> None: + def __init__( + self, song_id: int = 0, difficulty: int = 1, best_score: int = 0 + ) -> None: self.songId = song_id self.difficulty = difficulty self.bestScore = best_score - + def make(self) -> List: return [self.songId, self.difficulty, self.bestScore] diff --git a/titles/wacca/handlers/user_info.py b/titles/wacca/handlers/user_info.py index bf6b74b..b70ac35 100644 --- a/titles/wacca/handlers/user_info.py +++ b/titles/wacca/handlers/user_info.py @@ -11,7 +11,7 @@ class UserInfoUpdateRequest(BaseRequest): self.profileId = int(self.params[0]) self.optsUpdated: List[UserOption] = [] self.unknown2: List = self.params[2] - self.datesUpdated: List[DateUpdate] = [] + self.datesUpdated: List[DateUpdate] = [] self.favoritesRemoved: List[int] = self.params[4] self.favoritesAdded: List[int] = self.params[5] diff --git a/titles/wacca/handlers/user_status.py b/titles/wacca/handlers/user_status.py index b7ed1fc..6eef16a 100644 --- a/titles/wacca/handlers/user_status.py +++ b/titles/wacca/handlers/user_status.py @@ -168,7 +168,7 @@ class UserStatusGetDetailResponseV2(UserStatusGetDetailResponseV1): while len(tut_flg) < 5: flag_id = len(tut_flg) + 1 tut_flg.append([flag_id, 0]) - + for x in self.eventInfo: evts.append(x.make()) @@ -267,7 +267,9 @@ class UserStatusLoginResponseV3(UserStatusLoginResponseV2): self, is_first_login_daily: bool = False, last_login_date: int = 0 ) -> None: super().__init__(is_first_login_daily, last_login_date) - self.unk: List = [] # Ticket info, item info, message, title, voice name (not sure how they fit...) + self.unk: List = ( + [] + ) # Ticket info, item info, message, title, voice name (not sure how they fit...) def make(self) -> Dict: super().make() diff --git a/titles/wacca/lily.py b/titles/wacca/lily.py index 4ff942b..6ac60de 100644 --- a/titles/wacca/lily.py +++ b/titles/wacca/lily.py @@ -73,7 +73,7 @@ class WaccaLily(WaccaS): self.logger.info(f"No user exists for aime id {req.aimeId}") resp.profileStatus = ProfileStatus.ProfileRegister return resp.make() - + opts = self.data.profile.get_options(req.aimeId) self.logger.info(f"User preview for {req.aimeId} from {req.chipId}") @@ -139,7 +139,7 @@ class WaccaLily(WaccaS): if self.game_config.mods.infinite_wp: resp.userStatus.wp = 999999 - + for opt in opts: resp.options.append(UserOption(opt["opt_id"], opt["value"])) @@ -165,17 +165,23 @@ class WaccaLily(WaccaS): self.logger.info(f"User {req.userId} login on {req.chipId}") last_login_time = int(profile["last_login_date"].timestamp()) resp.lastLoginDate = last_login_time - midnight_today_ts = int(datetime.now().replace(hour=0, minute=0, second=0, microsecond=0).timestamp()) + midnight_today_ts = int( + datetime.now() + .replace(hour=0, minute=0, second=0, microsecond=0) + .timestamp() + ) # If somebodies login timestamp < midnight of current day, then they are logging in for the first time today if last_login_time < midnight_today_ts: resp.firstLoginDaily = True - - # If the difference between midnight today and their last login is greater then 1 day (86400 seconds) they've broken their streak + + # If the difference between midnight today and their last login is greater then 1 day (86400 seconds) they've broken their streak if midnight_today_ts - last_login_time > 86400: is_consec_day = False - self.data.profile.session_login(req.userId, resp.firstLoginDaily, is_consec_day) + self.data.profile.session_login( + req.userId, resp.firstLoginDaily, is_consec_day + ) resp.vipInfo.pageYear = datetime.now().year resp.vipInfo.pageMonth = datetime.now().month resp.vipInfo.pageDay = datetime.now().day diff --git a/titles/wacca/reverse.py b/titles/wacca/reverse.py index bb8a332..1711013 100644 --- a/titles/wacca/reverse.py +++ b/titles/wacca/reverse.py @@ -141,7 +141,7 @@ class WaccaReverse(WaccaLilyR): ) # For some fucking reason if this isn't here time play is disabled - resp.seasonalPlayModeCounts.append(PlayModeCounts(0, 1, 1)) + resp.seasonalPlayModeCounts.append(PlayModeCounts(0, 1, 1)) for opt in profile_options: resp.options.append(UserOption(opt["opt_id"], opt["value"]))