let black do it's magic

This commit is contained in:
Hay1tsme
2023-03-09 11:38:58 -05:00
parent fa7206848c
commit a76bb94eb1
150 changed files with 8474 additions and 4843 deletions
+100 -37
View File
@@ -8,17 +8,18 @@ from logging.handlers import TimedRotatingFileHandler
from core.config import CoreConfig from core.config import CoreConfig
from core.data import Data from core.data import Data
class AimedbProtocol(Protocol): class AimedbProtocol(Protocol):
AIMEDB_RESPONSE_CODES = { AIMEDB_RESPONSE_CODES = {
"felica_lookup": 0x03, "felica_lookup": 0x03,
"lookup": 0x06, "lookup": 0x06,
"log": 0x0a, "log": 0x0A,
"campaign": 0x0c, "campaign": 0x0C,
"touch": 0x0e, "touch": 0x0E,
"lookup2": 0x10, "lookup2": 0x10,
"felica_lookup2": 0x12, "felica_lookup2": 0x12,
"log2": 0x14, "log2": 0x14,
"hello": 0x65 "hello": 0x65,
} }
request_list: Dict[int, Any] = {} request_list: Dict[int, Any] = {}
@@ -35,9 +36,9 @@ class AimedbProtocol(Protocol):
self.request_list[0x04] = self.handle_lookup self.request_list[0x04] = self.handle_lookup
self.request_list[0x05] = self.handle_register self.request_list[0x05] = self.handle_register
self.request_list[0x09] = self.handle_log self.request_list[0x09] = self.handle_log
self.request_list[0x0b] = self.handle_campaign self.request_list[0x0B] = self.handle_campaign
self.request_list[0x0d] = self.handle_touch self.request_list[0x0D] = self.handle_touch
self.request_list[0x0f] = self.handle_lookup2 self.request_list[0x0F] = self.handle_lookup2
self.request_list[0x11] = self.handle_felica_lookup2 self.request_list[0x11] = self.handle_felica_lookup2
self.request_list[0x13] = self.handle_log2 self.request_list[0x13] = self.handle_log2
self.request_list[0x64] = self.handle_hello self.request_list[0x64] = self.handle_hello
@@ -53,7 +54,9 @@ class AimedbProtocol(Protocol):
self.logger.debug(f"{self.transport.getPeer().host} Connected") self.logger.debug(f"{self.transport.getPeer().host} Connected")
def connectionLost(self, reason) -> None: def connectionLost(self, reason) -> None:
self.logger.debug(f"{self.transport.getPeer().host} Disconnected - {reason.value}") self.logger.debug(
f"{self.transport.getPeer().host} Disconnected - {reason.value}"
)
def dataReceived(self, data: bytes) -> None: def dataReceived(self, data: bytes) -> None:
cipher = AES.new(self.config.aimedb.key.encode(), AES.MODE_ECB) cipher = AES.new(self.config.aimedb.key.encode(), AES.MODE_ECB)
@@ -66,7 +69,7 @@ class AimedbProtocol(Protocol):
self.logger.debug(f"{self.transport.getPeer().host} wrote {decrypted.hex()}") self.logger.debug(f"{self.transport.getPeer().host} wrote {decrypted.hex()}")
if not decrypted[1] == 0xa1 and not decrypted[0] == 0x3e: if not decrypted[1] == 0xA1 and not decrypted[0] == 0x3E:
self.logger.error(f"Bad magic") self.logger.error(f"Bad magic")
return None return None
@@ -93,27 +96,43 @@ class AimedbProtocol(Protocol):
def handle_campaign(self, data: bytes) -> bytes: def handle_campaign(self, data: bytes) -> bytes:
self.logger.info(f"campaign from {self.transport.getPeer().host}") self.logger.info(f"campaign from {self.transport.getPeer().host}")
ret = struct.pack("<5H", 0xa13e, 0x3087, self.AIMEDB_RESPONSE_CODES["campaign"], 0x0200, 0x0001) ret = struct.pack(
"<5H",
0xA13E,
0x3087,
self.AIMEDB_RESPONSE_CODES["campaign"],
0x0200,
0x0001,
)
return self.append_padding(ret) return self.append_padding(ret)
def handle_hello(self, data: bytes) -> bytes: def handle_hello(self, data: bytes) -> bytes:
self.logger.info(f"hello from {self.transport.getPeer().host}") self.logger.info(f"hello from {self.transport.getPeer().host}")
ret = struct.pack("<5H", 0xa13e, 0x3087, self.AIMEDB_RESPONSE_CODES["hello"], 0x0020, 0x0001) ret = struct.pack(
"<5H", 0xA13E, 0x3087, self.AIMEDB_RESPONSE_CODES["hello"], 0x0020, 0x0001
)
return self.append_padding(ret) return self.append_padding(ret)
def handle_lookup(self, data: bytes) -> bytes: def handle_lookup(self, data: bytes) -> bytes:
luid = data[0x20: 0x2a].hex() luid = data[0x20:0x2A].hex()
user_id = self.data.card.get_user_id_from_card(access_code=luid) user_id = self.data.card.get_user_id_from_card(access_code=luid)
if user_id is None: user_id = -1 if user_id is None:
user_id = -1
self.logger.info(f"lookup from {self.transport.getPeer().host}: luid {luid} -> user_id {user_id}") self.logger.info(
f"lookup from {self.transport.getPeer().host}: luid {luid} -> user_id {user_id}"
)
ret = struct.pack("<5H", 0xa13e, 0x3087, self.AIMEDB_RESPONSE_CODES["lookup"], 0x0130, 0x0001) ret = struct.pack(
"<5H", 0xA13E, 0x3087, self.AIMEDB_RESPONSE_CODES["lookup"], 0x0130, 0x0001
)
ret += bytes(0x20 - len(ret)) ret += bytes(0x20 - len(ret))
if user_id is None: ret += struct.pack("<iH", -1, 0) if user_id is None:
else: ret += struct.pack("<l", user_id) ret += struct.pack("<iH", -1, 0)
else:
ret += struct.pack("<l", user_id)
return self.append_padding(ret) return self.append_padding(ret)
def handle_lookup2(self, data: bytes) -> bytes: def handle_lookup2(self, data: bytes) -> bytes:
@@ -125,30 +144,49 @@ class AimedbProtocol(Protocol):
return bytes(ret) return bytes(ret)
def handle_felica_lookup(self, data: bytes) -> bytes: def handle_felica_lookup(self, data: bytes) -> bytes:
idm = data[0x20: 0x28].hex() idm = data[0x20:0x28].hex()
pmm = data[0x28: 0x30].hex() pmm = data[0x28:0x30].hex()
access_code = self.data.card.to_access_code(idm) access_code = self.data.card.to_access_code(idm)
self.logger.info(f"felica_lookup from {self.transport.getPeer().host}: idm {idm} pmm {pmm} -> access_code {access_code}") self.logger.info(
f"felica_lookup from {self.transport.getPeer().host}: idm {idm} pmm {pmm} -> access_code {access_code}"
)
ret = struct.pack("<5H", 0xa13e, 0x3087, self.AIMEDB_RESPONSE_CODES["felica_lookup"], 0x0030, 0x0001) ret = struct.pack(
"<5H",
0xA13E,
0x3087,
self.AIMEDB_RESPONSE_CODES["felica_lookup"],
0x0030,
0x0001,
)
ret += bytes(26) ret += bytes(26)
ret += bytes.fromhex(access_code) ret += bytes.fromhex(access_code)
return self.append_padding(ret) return self.append_padding(ret)
def handle_felica_lookup2(self, data: bytes) -> bytes: def handle_felica_lookup2(self, data: bytes) -> bytes:
idm = data[0x30: 0x38].hex() idm = data[0x30:0x38].hex()
pmm = data[0x38: 0x40].hex() pmm = data[0x38:0x40].hex()
access_code = self.data.card.to_access_code(idm) access_code = self.data.card.to_access_code(idm)
user_id = self.data.card.get_user_id_from_card(access_code=access_code) user_id = self.data.card.get_user_id_from_card(access_code=access_code)
if user_id is None: user_id = -1 if user_id is None:
user_id = -1
self.logger.info(f"felica_lookup2 from {self.transport.getPeer().host}: idm {idm} ipm {pmm} -> access_code {access_code} user_id {user_id}") self.logger.info(
f"felica_lookup2 from {self.transport.getPeer().host}: idm {idm} ipm {pmm} -> access_code {access_code} user_id {user_id}"
)
ret = struct.pack("<5H", 0xa13e, 0x3087, self.AIMEDB_RESPONSE_CODES["felica_lookup2"], 0x0140, 0x0001) ret = struct.pack(
"<5H",
0xA13E,
0x3087,
self.AIMEDB_RESPONSE_CODES["felica_lookup2"],
0x0140,
0x0001,
)
ret += bytes(22) ret += bytes(22)
ret += struct.pack("<lq", user_id, -1) # first -1 is ext_id, 3rd is access code ret += struct.pack("<lq", user_id, -1) # first -1 is ext_id, 3rd is access code
ret += bytes.fromhex(access_code) ret += bytes.fromhex(access_code)
ret += struct.pack("<l", 1) ret += struct.pack("<l", 1)
@@ -156,14 +194,16 @@ class AimedbProtocol(Protocol):
def handle_touch(self, data: bytes) -> bytes: def handle_touch(self, data: bytes) -> bytes:
self.logger.info(f"touch from {self.transport.getPeer().host}") self.logger.info(f"touch from {self.transport.getPeer().host}")
ret = struct.pack("<5H", 0xa13e, 0x3087, self.AIMEDB_RESPONSE_CODES["touch"], 0x0050, 0x0001) ret = struct.pack(
"<5H", 0xA13E, 0x3087, self.AIMEDB_RESPONSE_CODES["touch"], 0x0050, 0x0001
)
ret += bytes(5) ret += bytes(5)
ret += struct.pack("<3H", 0x6f, 0, 1) ret += struct.pack("<3H", 0x6F, 0, 1)
return self.append_padding(ret) return self.append_padding(ret)
def handle_register(self, data: bytes) -> bytes: def handle_register(self, data: bytes) -> bytes:
luid = data[0x20: 0x2a].hex() luid = data[0x20:0x2A].hex()
if self.config.server.allow_user_registration: if self.config.server.allow_user_registration:
user_id = self.data.user.create_user() user_id = self.data.user.create_user()
@@ -178,13 +218,24 @@ class AimedbProtocol(Protocol):
user_id = -1 user_id = -1
self.logger.error("Failed to register card!") self.logger.error("Failed to register card!")
self.logger.info(f"register from {self.transport.getPeer().host}: luid {luid} -> user_id {user_id}") self.logger.info(
f"register from {self.transport.getPeer().host}: luid {luid} -> user_id {user_id}"
)
else: else:
self.logger.info(f"register from {self.transport.getPeer().host} blocked!: luid {luid}") self.logger.info(
f"register from {self.transport.getPeer().host} blocked!: luid {luid}"
)
user_id = -1 user_id = -1
ret = struct.pack("<5H", 0xa13e, 0x3087, self.AIMEDB_RESPONSE_CODES["lookup"], 0x0030, 0x0001 if user_id > -1 else 0) ret = struct.pack(
"<5H",
0xA13E,
0x3087,
self.AIMEDB_RESPONSE_CODES["lookup"],
0x0030,
0x0001 if user_id > -1 else 0,
)
ret += bytes(0x20 - len(ret)) ret += bytes(0x20 - len(ret))
ret += struct.pack("<l", user_id) ret += struct.pack("<l", user_id)
@@ -193,26 +244,36 @@ class AimedbProtocol(Protocol):
def handle_log(self, data: bytes) -> bytes: def handle_log(self, data: bytes) -> bytes:
# TODO: Save aimedb logs # TODO: Save aimedb logs
self.logger.info(f"log from {self.transport.getPeer().host}") self.logger.info(f"log from {self.transport.getPeer().host}")
ret = struct.pack("<5H", 0xa13e, 0x3087, self.AIMEDB_RESPONSE_CODES["log"], 0x0020, 0x0001) ret = struct.pack(
"<5H", 0xA13E, 0x3087, self.AIMEDB_RESPONSE_CODES["log"], 0x0020, 0x0001
)
return self.append_padding(ret) return self.append_padding(ret)
def handle_log2(self, data: bytes) -> bytes: def handle_log2(self, data: bytes) -> bytes:
self.logger.info(f"log2 from {self.transport.getPeer().host}") self.logger.info(f"log2 from {self.transport.getPeer().host}")
ret = struct.pack("<5H", 0xa13e, 0x3087, self.AIMEDB_RESPONSE_CODES["log2"], 0x0040, 0x0001) ret = struct.pack(
"<5H", 0xA13E, 0x3087, self.AIMEDB_RESPONSE_CODES["log2"], 0x0040, 0x0001
)
ret += bytes(22) ret += bytes(22)
ret += struct.pack("H", 1) ret += struct.pack("H", 1)
return self.append_padding(ret) return self.append_padding(ret)
class AimedbFactory(Factory): class AimedbFactory(Factory):
protocol = AimedbProtocol protocol = AimedbProtocol
def __init__(self, cfg: CoreConfig) -> None: def __init__(self, cfg: CoreConfig) -> None:
self.config = cfg self.config = cfg
log_fmt_str = "[%(asctime)s] Aimedb | %(levelname)s | %(message)s" log_fmt_str = "[%(asctime)s] Aimedb | %(levelname)s | %(message)s"
log_fmt = logging.Formatter(log_fmt_str) log_fmt = logging.Formatter(log_fmt_str)
self.logger = logging.getLogger("aimedb") self.logger = logging.getLogger("aimedb")
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.config.server.log_dir, "aimedb"), when="d", backupCount=10) fileHandler = TimedRotatingFileHandler(
"{0}/{1}.log".format(self.config.server.log_dir, "aimedb"),
when="d",
backupCount=10,
)
fileHandler.setFormatter(log_fmt) fileHandler.setFormatter(log_fmt)
consoleHandler = logging.StreamHandler() consoleHandler = logging.StreamHandler()
@@ -222,7 +283,9 @@ class AimedbFactory(Factory):
self.logger.addHandler(consoleHandler) self.logger.addHandler(consoleHandler)
self.logger.setLevel(self.config.aimedb.loglevel) self.logger.setLevel(self.config.aimedb.loglevel)
coloredlogs.install(level=cfg.aimedb.loglevel, logger=self.logger, fmt=log_fmt_str) coloredlogs.install(
level=cfg.aimedb.loglevel, logger=self.logger, fmt=log_fmt_str
)
if self.config.aimedb.key == "": if self.config.aimedb.key == "":
self.logger.error("Please set 'key' field in your config file.") self.logger.error("Please set 'key' field in your config file.")
+102 -38
View File
@@ -16,6 +16,7 @@ from core.data import Data
from core.utils import Utils from core.utils import Utils
from core.const import * from core.const import *
class AllnetServlet: class AllnetServlet:
def __init__(self, core_cfg: CoreConfig, cfg_folder: str): def __init__(self, core_cfg: CoreConfig, cfg_folder: str):
super().__init__() super().__init__()
@@ -29,7 +30,11 @@ class AllnetServlet:
log_fmt_str = "[%(asctime)s] Allnet | %(levelname)s | %(message)s" log_fmt_str = "[%(asctime)s] Allnet | %(levelname)s | %(message)s"
log_fmt = logging.Formatter(log_fmt_str) log_fmt = logging.Formatter(log_fmt_str)
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.config.server.log_dir, "allnet"), when="d", backupCount=10) fileHandler = TimedRotatingFileHandler(
"{0}/{1}.log".format(self.config.server.log_dir, "allnet"),
when="d",
backupCount=10,
)
fileHandler.setFormatter(log_fmt) fileHandler.setFormatter(log_fmt)
consoleHandler = logging.StreamHandler() consoleHandler = logging.StreamHandler()
@@ -39,7 +44,9 @@ class AllnetServlet:
self.logger.addHandler(consoleHandler) self.logger.addHandler(consoleHandler)
self.logger.setLevel(core_cfg.allnet.loglevel) self.logger.setLevel(core_cfg.allnet.loglevel)
coloredlogs.install(level=core_cfg.allnet.loglevel, logger=self.logger, fmt=log_fmt_str) coloredlogs.install(
level=core_cfg.allnet.loglevel, logger=self.logger, fmt=log_fmt_str
)
self.logger.initialized = True self.logger.initialized = True
plugins = Utils.get_all_titles() plugins = Utils.get_all_titles()
@@ -50,12 +57,16 @@ class AllnetServlet:
for _, mod in plugins.items(): for _, mod in plugins.items():
if hasattr(mod.index, "get_allnet_info"): if hasattr(mod.index, "get_allnet_info"):
for code in mod.game_codes: for code in mod.game_codes:
enabled, uri, host = mod.index.get_allnet_info(code, self.config, self.config_folder) enabled, uri, host = mod.index.get_allnet_info(
code, self.config, self.config_folder
)
if enabled: if enabled:
self.uri_registry[code] = (uri, host) self.uri_registry[code] = (uri, host)
self.logger.info(f"Allnet serving {len(self.uri_registry)} games on port {core_cfg.allnet.port}") self.logger.info(
f"Allnet serving {len(self.uri_registry)} games on port {core_cfg.allnet.port}"
)
def handle_poweron(self, request: Request, _: Dict): def handle_poweron(self, request: Request, _: Dict):
request_ip = request.getClientAddress().host request_ip = request.getClientAddress().host
@@ -67,8 +78,16 @@ class AllnetServlet:
req = AllnetPowerOnRequest(req_dict[0]) req = AllnetPowerOnRequest(req_dict[0])
# Validate the request. Currently we only validate the fields we plan on using # Validate the request. Currently we only validate the fields we plan on using
if not req.game_id or not req.ver or not req.token or not req.serial or not req.ip: if (
raise AllnetRequestException(f"Bad auth request params from {request_ip} - {vars(req)}") not req.game_id
or not req.ver
or not req.token
or not req.serial
or not req.ip
):
raise AllnetRequestException(
f"Bad auth request params from {request_ip} - {vars(req)}"
)
except AllnetRequestException as e: except AllnetRequestException as e:
if e.message != "": if e.message != "":
@@ -83,7 +102,9 @@ class AllnetServlet:
self.logger.debug(f"Allnet request: {vars(req)}") self.logger.debug(f"Allnet request: {vars(req)}")
if req.game_id not in self.uri_registry: if req.game_id not in self.uri_registry:
msg = f"Unrecognised game {req.game_id} attempted allnet auth from {request_ip}." msg = f"Unrecognised game {req.game_id} attempted allnet auth from {request_ip}."
self.data.base.log_event("allnet", "ALLNET_AUTH_UNKNOWN_GAME", logging.WARN, msg) self.data.base.log_event(
"allnet", "ALLNET_AUTH_UNKNOWN_GAME", logging.WARN, msg
)
self.logger.warn(msg) self.logger.warn(msg)
resp.stat = 0 resp.stat = 0
@@ -94,7 +115,9 @@ class AllnetServlet:
machine = self.data.arcade.get_machine(req.serial) machine = self.data.arcade.get_machine(req.serial)
if machine is None and not self.config.server.allow_unregistered_serials: if machine is None and not self.config.server.allow_unregistered_serials:
msg = f"Unrecognised serial {req.serial} attempted allnet auth from {request_ip}." msg = f"Unrecognised serial {req.serial} attempted allnet auth from {request_ip}."
self.data.base.log_event("allnet", "ALLNET_AUTH_UNKNOWN_SERIAL", logging.WARN, msg) self.data.base.log_event(
"allnet", "ALLNET_AUTH_UNKNOWN_SERIAL", logging.WARN, msg
)
self.logger.warn(msg) self.logger.warn(msg)
resp.stat = 0 resp.stat = 0
@@ -102,7 +125,9 @@ class AllnetServlet:
if machine is not None: if machine is not None:
arcade = self.data.arcade.get_arcade(machine["arcade"]) arcade = self.data.arcade.get_arcade(machine["arcade"])
country = arcade["country"] if machine["country"] is None else machine["country"] country = (
arcade["country"] if machine["country"] is None else machine["country"]
)
if country is None: if country is None:
country = AllnetCountryCode.JAPAN.value country = AllnetCountryCode.JAPAN.value
@@ -111,11 +136,25 @@ class AllnetServlet:
resp.allnet_id = machine["id"] resp.allnet_id = machine["id"]
resp.name = arcade["name"] if arcade["name"] is not None else "" resp.name = arcade["name"] if arcade["name"] is not None else ""
resp.nickname = arcade["nickname"] if arcade["nickname"] is not None else "" resp.nickname = arcade["nickname"] if arcade["nickname"] is not None else ""
resp.region0 = arcade["region_id"] if arcade["region_id"] is not None else AllnetJapanRegionId.AICHI.value resp.region0 = (
resp.region_name0 = arcade["country"] if arcade["country"] is not None else AllnetCountryCode.JAPAN.value arcade["region_id"]
resp.region_name1 = arcade["state"] if arcade["state"] is not None else AllnetJapanRegionId.AICHI.name if arcade["region_id"] is not None
else AllnetJapanRegionId.AICHI.value
)
resp.region_name0 = (
arcade["country"]
if arcade["country"] is not None
else AllnetCountryCode.JAPAN.value
)
resp.region_name1 = (
arcade["state"]
if arcade["state"] is not None
else AllnetJapanRegionId.AICHI.name
)
resp.region_name2 = arcade["city"] if arcade["city"] is not None else "" resp.region_name2 = arcade["city"] if arcade["city"] is not None else ""
resp.client_timezone = arcade["timezone"] if arcade["timezone"] is not None else "+0900" resp.client_timezone = (
arcade["timezone"] if arcade["timezone"] is not None else "+0900"
)
int_ver = req.ver.replace(".", "") int_ver = req.ver.replace(".", "")
resp.uri = resp.uri.replace("$v", int_ver) resp.uri = resp.uri.replace("$v", int_ver)
@@ -139,7 +178,9 @@ class AllnetServlet:
# Validate the request. Currently we only validate the fields we plan on using # Validate the request. Currently we only validate the fields we plan on using
if not req.game_id or not req.ver or not req.serial: if not req.game_id or not req.ver or not req.serial:
raise AllnetRequestException(f"Bad download request params from {request_ip} - {vars(req)}") raise AllnetRequestException(
f"Bad download request params from {request_ip} - {vars(req)}"
)
except AllnetRequestException as e: except AllnetRequestException as e:
if e.message != "": if e.message != "":
@@ -150,7 +191,7 @@ class AllnetServlet:
if not self.config.allnet.allow_online_updates: if not self.config.allnet.allow_online_updates:
return self.dict_to_http_form_string([vars(resp)]) return self.dict_to_http_form_string([vars(resp)])
else: # TODO: Actual dlorder response else: # TODO: Actual dlorder response
return self.dict_to_http_form_string([vars(resp)]) return self.dict_to_http_form_string([vars(resp)])
def handle_billing_request(self, request: Request, _: Dict): def handle_billing_request(self, request: Request, _: Dict):
@@ -162,7 +203,7 @@ class AllnetServlet:
self.logger.debug(f"request {req_dict}") self.logger.debug(f"request {req_dict}")
rsa = RSA.import_key(open(self.config.billing.signing_key, 'rb').read()) rsa = RSA.import_key(open(self.config.billing.signing_key, "rb").read())
signer = PKCS1_v1_5.new(rsa) signer = PKCS1_v1_5.new(rsa)
digest = SHA.new() digest = SHA.new()
@@ -178,17 +219,21 @@ class AllnetServlet:
machine = self.data.arcade.get_machine(kc_serial) machine = self.data.arcade.get_machine(kc_serial)
if machine is None and not self.config.server.allow_unregistered_serials: if machine is None and not self.config.server.allow_unregistered_serials:
msg = f"Unrecognised serial {kc_serial} attempted billing checkin from {request_ip} for game {kc_game}." msg = f"Unrecognised serial {kc_serial} attempted billing checkin from {request_ip} for game {kc_game}."
self.data.base.log_event("allnet", "BILLING_CHECKIN_NG_SERIAL", logging.WARN, msg) self.data.base.log_event(
"allnet", "BILLING_CHECKIN_NG_SERIAL", logging.WARN, msg
)
self.logger.warn(msg) self.logger.warn(msg)
resp = BillingResponse("", "", "", "") resp = BillingResponse("", "", "", "")
resp.result = "1" resp.result = "1"
return self.dict_to_http_form_string([vars(resp)]) return self.dict_to_http_form_string([vars(resp)])
msg = f"Billing checkin from {request.getClientIP()}: game {kc_game} keychip {kc_serial} playcount " \ msg = (
f"Billing checkin from {request.getClientIP()}: game {kc_game} keychip {kc_serial} playcount "
f"{kc_playcount} billing_type {kc_billigtype} nearfull {kc_nearfull} playlimit {kc_playlimit}" f"{kc_playcount} billing_type {kc_billigtype} nearfull {kc_nearfull} playlimit {kc_playlimit}"
)
self.logger.info(msg) self.logger.info(msg)
self.data.base.log_event('billing', 'BILLING_CHECKIN_OK', logging.INFO, msg) self.data.base.log_event("billing", "BILLING_CHECKIN_OK", logging.INFO, msg)
while kc_playcount > kc_playlimit: while kc_playcount > kc_playlimit:
kc_playlimit += 1024 kc_playlimit += 1024
@@ -197,11 +242,11 @@ class AllnetServlet:
playlimit = kc_playlimit playlimit = kc_playlimit
nearfull = kc_nearfull + (kc_billigtype * 0x00010000) nearfull = kc_nearfull + (kc_billigtype * 0x00010000)
digest.update(playlimit.to_bytes(4, 'little') + kc_serial_bytes) digest.update(playlimit.to_bytes(4, "little") + kc_serial_bytes)
playlimit_sig = signer.sign(digest).hex() playlimit_sig = signer.sign(digest).hex()
digest = SHA.new() digest = SHA.new()
digest.update(nearfull.to_bytes(4, 'little') + kc_serial_bytes) digest.update(nearfull.to_bytes(4, "little") + kc_serial_bytes)
nearfull_sig = signer.sign(digest).hex() nearfull_sig = signer.sign(digest).hex()
# TODO: playhistory # TODO: playhistory
@@ -222,11 +267,11 @@ class AllnetServlet:
def kvp_to_dict(self, kvp: List[str]) -> List[Dict[str, Any]]: def kvp_to_dict(self, kvp: List[str]) -> List[Dict[str, Any]]:
ret: List[Dict[str, Any]] = [] ret: List[Dict[str, Any]] = []
for x in kvp: for x in kvp:
items = x.split('&') items = x.split("&")
tmp = {} tmp = {}
for item in items: for item in items:
kvp = item.split('=') kvp = item.split("=")
if len(kvp) == 2: if len(kvp) == 2:
tmp[kvp[0]] = kvp[1] tmp[kvp[0]] = kvp[1]
@@ -241,7 +286,7 @@ class AllnetServlet:
try: try:
decomp = zlib.decompressobj(-zlib.MAX_WBITS) decomp = zlib.decompressobj(-zlib.MAX_WBITS)
unzipped = decomp.decompress(data) unzipped = decomp.decompress(data)
sections = unzipped.decode('ascii').split('\r\n') sections = unzipped.decode("ascii").split("\r\n")
return self.kvp_to_dict(sections) return self.kvp_to_dict(sections)
@@ -256,7 +301,7 @@ class AllnetServlet:
try: try:
zipped = base64.b64decode(data) zipped = base64.b64decode(data)
unzipped = zlib.decompress(zipped) unzipped = zlib.decompress(zipped)
sections = unzipped.decode('utf-8').split('\r\n') sections = unzipped.decode("utf-8").split("\r\n")
return self.kvp_to_dict(sections) return self.kvp_to_dict(sections)
@@ -264,14 +309,19 @@ class AllnetServlet:
self.logger.error(f"allnet_req_to_dict: {e} while parsing {data}") self.logger.error(f"allnet_req_to_dict: {e} while parsing {data}")
return None return None
def dict_to_http_form_string(self, data:List[Dict[str, Any]], crlf: bool = False, trailing_newline: bool = True) -> Optional[str]: def dict_to_http_form_string(
self,
data: List[Dict[str, Any]],
crlf: bool = False,
trailing_newline: bool = True,
) -> Optional[str]:
""" """
Takes a python dictionary and parses it into an allnet response string Takes a python dictionary and parses it into an allnet response string
""" """
try: try:
urlencode = "" urlencode = ""
for item in data: for item in data:
for k,v in item.items(): for k, v in item.items():
urlencode += f"{k}={v}&" urlencode += f"{k}={v}&"
if crlf: if crlf:
@@ -291,14 +341,15 @@ class AllnetServlet:
self.logger.error(f"dict_to_http_form_string: {e} while parsing {data}") self.logger.error(f"dict_to_http_form_string: {e} while parsing {data}")
return None return None
class AllnetPowerOnRequest():
class AllnetPowerOnRequest:
def __init__(self, req: Dict) -> None: def __init__(self, req: Dict) -> None:
if req is None: if req is None:
raise AllnetRequestException("Request processing failed") raise AllnetRequestException("Request processing failed")
self.game_id: str = req["game_id"] if "game_id" in req else "" self.game_id: str = req["game_id"] if "game_id" in req else ""
self.ver: str = req["ver"] if "ver" in req else "" self.ver: str = req["ver"] if "ver" in req else ""
self.serial: str = req["serial"] if "serial" in req else "" self.serial: str = req["serial"] if "serial" in req else ""
self.ip: str = req["ip"] if "ip" in req else "" self.ip: str = req["ip"] if "ip" in req else ""
self.firm_ver: str = req["firm_ver"] if "firm_ver" in req else "" self.firm_ver: str = req["firm_ver"] if "firm_ver" in req else ""
self.boot_ver: str = req["boot_ver"] if "boot_ver" in req else "" self.boot_ver: str = req["boot_ver"] if "boot_ver" in req else ""
self.encode: str = req["encode"] if "encode" in req else "" self.encode: str = req["encode"] if "encode" in req else ""
@@ -310,7 +361,8 @@ class AllnetPowerOnRequest():
except ValueError as e: except ValueError as e:
raise AllnetRequestException(f"Failed to parse int: {e}") raise AllnetRequestException(f"Failed to parse int: {e}")
class AllnetPowerOnResponse3():
class AllnetPowerOnResponse3:
def __init__(self, token) -> None: def __init__(self, token) -> None:
self.stat = 1 self.stat = 1
self.uri = "" self.uri = ""
@@ -326,12 +378,15 @@ class AllnetPowerOnResponse3():
self.country = "JPN" self.country = "JPN"
self.allnet_id = "123" self.allnet_id = "123"
self.client_timezone = "+0900" self.client_timezone = "+0900"
self.utc_time = datetime.now(tz=pytz.timezone('UTC')).strftime("%Y-%m-%dT%H:%M:%SZ") self.utc_time = datetime.now(tz=pytz.timezone("UTC")).strftime(
"%Y-%m-%dT%H:%M:%SZ"
)
self.setting = "" self.setting = ""
self.res_ver = "3" self.res_ver = "3"
self.token = str(token) self.token = str(token)
class AllnetPowerOnResponse2():
class AllnetPowerOnResponse2:
def __init__(self) -> None: def __init__(self) -> None:
self.stat = 1 self.stat = 1
self.uri = "" self.uri = ""
@@ -355,23 +410,31 @@ class AllnetPowerOnResponse2():
self.timezone = "+0900" self.timezone = "+0900"
self.res_class = "PowerOnResponseV2" self.res_class = "PowerOnResponseV2"
class AllnetDownloadOrderRequest():
class AllnetDownloadOrderRequest:
def __init__(self, req: Dict) -> None: def __init__(self, req: Dict) -> None:
self.game_id = req["game_id"] if "game_id" in req else "" self.game_id = req["game_id"] if "game_id" in req else ""
self.ver = req["ver"] if "ver" in req else "" self.ver = req["ver"] if "ver" in req else ""
self.serial = req["serial"] if "serial" in req else "" self.serial = req["serial"] if "serial" in req else ""
self.encode = req["encode"] if "encode" in req else "" self.encode = req["encode"] if "encode" in req else ""
class AllnetDownloadOrderResponse():
class AllnetDownloadOrderResponse:
def __init__(self, stat: int = 1, serial: str = "", uri: str = "null") -> None: def __init__(self, stat: int = 1, serial: str = "", uri: str = "null") -> None:
self.stat = stat self.stat = stat
self.serial = serial self.serial = serial
self.uri = uri self.uri = uri
class BillingResponse():
def __init__(self, playlimit: str = "", playlimit_sig: str = "", nearfull: str = "", nearfull_sig: str = "",
playhistory: str = "000000/0:000000/0:000000/0") -> None:
class BillingResponse:
def __init__(
self,
playlimit: str = "",
playlimit_sig: str = "",
nearfull: str = "",
nearfull_sig: str = "",
playhistory: str = "000000/0:000000/0:000000/0",
) -> None:
self.result = "0" self.result = "0"
self.waitime = "100" self.waitime = "100"
self.linelimit = "1" self.linelimit = "1"
@@ -387,6 +450,7 @@ class BillingResponse():
# playhistory -> YYYYMM/C:... # playhistory -> YYYYMM/C:...
# YYYY -> 4 digit year, MM -> 2 digit month, C -> Playcount during that period # YYYY -> 4 digit year, MM -> 2 digit month, C -> Playcount during that period
class AllnetRequestException(Exception): class AllnetRequestException(Exception):
def __init__(self, message="") -> None: def __init__(self, message="") -> None:
self.message = message self.message = message
+147 -44
View File
@@ -1,33 +1,47 @@
import logging, os import logging, os
from typing import Any from typing import Any
class ServerConfig: class ServerConfig:
def __init__(self, parent_config: "CoreConfig") -> None: def __init__(self, parent_config: "CoreConfig") -> None:
self.__config = parent_config self.__config = parent_config
@property @property
def listen_address(self) -> str: def listen_address(self) -> str:
return CoreConfig.get_config_field(self.__config, 'core', 'server', 'listen_address', default='127.0.0.1') return CoreConfig.get_config_field(
self.__config, "core", "server", "listen_address", default="127.0.0.1"
)
@property @property
def allow_user_registration(self) -> bool: def allow_user_registration(self) -> bool:
return CoreConfig.get_config_field(self.__config, 'core', 'server', 'allow_user_registration', default=True) return CoreConfig.get_config_field(
self.__config, "core", "server", "allow_user_registration", default=True
)
@property @property
def allow_unregistered_serials(self) -> bool: def allow_unregistered_serials(self) -> bool:
return CoreConfig.get_config_field(self.__config, 'core', 'server', 'allow_unregistered_serials', default=True) return CoreConfig.get_config_field(
self.__config, "core", "server", "allow_unregistered_serials", default=True
)
@property @property
def name(self) -> str: def name(self) -> str:
return CoreConfig.get_config_field(self.__config, 'core', 'server', 'name', default="ARTEMiS") return CoreConfig.get_config_field(
self.__config, "core", "server", "name", default="ARTEMiS"
)
@property @property
def is_develop(self) -> bool: def is_develop(self) -> bool:
return CoreConfig.get_config_field(self.__config, 'core', 'server', 'is_develop', default=True) return CoreConfig.get_config_field(
self.__config, "core", "server", "is_develop", default=True
)
@property @property
def log_dir(self) -> str: def log_dir(self) -> str:
return CoreConfig.get_config_field(self.__config, 'core', 'server', 'log_dir', default='logs') return CoreConfig.get_config_field(
self.__config, "core", "server", "log_dir", default="logs"
)
class TitleConfig: class TitleConfig:
def __init__(self, parent_config: "CoreConfig") -> None: def __init__(self, parent_config: "CoreConfig") -> None:
@@ -35,15 +49,24 @@ class TitleConfig:
@property @property
def loglevel(self) -> int: def loglevel(self) -> int:
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'core', 'title', 'loglevel', default="info")) return CoreConfig.str_to_loglevel(
CoreConfig.get_config_field(
self.__config, "core", "title", "loglevel", default="info"
)
)
@property @property
def hostname(self) -> str: def hostname(self) -> str:
return CoreConfig.get_config_field(self.__config, 'core', 'title', 'hostname', default="localhost") return CoreConfig.get_config_field(
self.__config, "core", "title", "hostname", default="localhost"
)
@property @property
def port(self) -> int: def port(self) -> int:
return CoreConfig.get_config_field(self.__config, 'core', 'title', 'port', default=8080) return CoreConfig.get_config_field(
self.__config, "core", "title", "port", default=8080
)
class DatabaseConfig: class DatabaseConfig:
def __init__(self, parent_config: "CoreConfig") -> None: def __init__(self, parent_config: "CoreConfig") -> None:
@@ -51,43 +74,70 @@ class DatabaseConfig:
@property @property
def host(self) -> str: def host(self) -> str:
return CoreConfig.get_config_field(self.__config, 'core', 'database', 'host', default="localhost") return CoreConfig.get_config_field(
self.__config, "core", "database", "host", default="localhost"
)
@property @property
def username(self) -> str: def username(self) -> str:
return CoreConfig.get_config_field(self.__config, 'core', 'database', 'username', default='aime') return CoreConfig.get_config_field(
self.__config, "core", "database", "username", default="aime"
)
@property @property
def password(self) -> str: def password(self) -> str:
return CoreConfig.get_config_field(self.__config, 'core', 'database', 'password', default='aime') return CoreConfig.get_config_field(
self.__config, "core", "database", "password", default="aime"
)
@property @property
def name(self) -> str: def name(self) -> str:
return CoreConfig.get_config_field(self.__config, 'core', 'database', 'name', default='aime') return CoreConfig.get_config_field(
self.__config, "core", "database", "name", default="aime"
)
@property @property
def port(self) -> int: def port(self) -> int:
return CoreConfig.get_config_field(self.__config, 'core', 'database', 'port', default=3306) return CoreConfig.get_config_field(
self.__config, "core", "database", "port", default=3306
)
@property @property
def protocol(self) -> str: def protocol(self) -> str:
return CoreConfig.get_config_field(self.__config, 'core', 'database', 'type', default="mysql") return CoreConfig.get_config_field(
self.__config, "core", "database", "type", default="mysql"
)
@property @property
def sha2_password(self) -> bool: def sha2_password(self) -> bool:
return CoreConfig.get_config_field(self.__config, 'core', 'database', 'sha2_password', default=False) return CoreConfig.get_config_field(
self.__config, "core", "database", "sha2_password", default=False
)
@property @property
def loglevel(self) -> int: def loglevel(self) -> int:
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'core', 'database', 'loglevel', default="info")) return CoreConfig.str_to_loglevel(
CoreConfig.get_config_field(
self.__config, "core", "database", "loglevel", default="info"
)
)
@property @property
def user_table_autoincrement_start(self) -> int: def user_table_autoincrement_start(self) -> int:
return CoreConfig.get_config_field(self.__config, 'core', 'database', 'user_table_autoincrement_start', default=10000) return CoreConfig.get_config_field(
self.__config,
"core",
"database",
"user_table_autoincrement_start",
default=10000,
)
@property @property
def memcached_host(self) -> str: def memcached_host(self) -> str:
return CoreConfig.get_config_field(self.__config, 'core', 'database', 'memcached_host', default="localhost") return CoreConfig.get_config_field(
self.__config, "core", "database", "memcached_host", default="localhost"
)
class FrontendConfig: class FrontendConfig:
def __init__(self, parent_config: "CoreConfig") -> None: def __init__(self, parent_config: "CoreConfig") -> None:
@@ -95,15 +145,24 @@ class FrontendConfig:
@property @property
def enable(self) -> int: def enable(self) -> int:
return CoreConfig.get_config_field(self.__config, 'core', 'frontend', 'enable', default=False) return CoreConfig.get_config_field(
self.__config, "core", "frontend", "enable", default=False
)
@property @property
def port(self) -> int: def port(self) -> int:
return CoreConfig.get_config_field(self.__config, 'core', 'frontend', 'port', default=8090) return CoreConfig.get_config_field(
self.__config, "core", "frontend", "port", default=8090
)
@property @property
def loglevel(self) -> int: def loglevel(self) -> int:
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'core', 'frontend', 'loglevel', default="info")) return CoreConfig.str_to_loglevel(
CoreConfig.get_config_field(
self.__config, "core", "frontend", "loglevel", default="info"
)
)
class AllnetConfig: class AllnetConfig:
def __init__(self, parent_config: "CoreConfig") -> None: def __init__(self, parent_config: "CoreConfig") -> None:
@@ -111,15 +170,24 @@ class AllnetConfig:
@property @property
def loglevel(self) -> int: def loglevel(self) -> int:
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'core', 'allnet', 'loglevel', default="info")) return CoreConfig.str_to_loglevel(
CoreConfig.get_config_field(
self.__config, "core", "allnet", "loglevel", default="info"
)
)
@property @property
def port(self) -> int: def port(self) -> int:
return CoreConfig.get_config_field(self.__config, 'core', 'allnet', 'port', default=80) return CoreConfig.get_config_field(
self.__config, "core", "allnet", "port", default=80
)
@property @property
def allow_online_updates(self) -> int: def allow_online_updates(self) -> int:
return CoreConfig.get_config_field(self.__config, 'core', 'allnet', 'allow_online_updates', default=False) return CoreConfig.get_config_field(
self.__config, "core", "allnet", "allow_online_updates", default=False
)
class BillingConfig: class BillingConfig:
def __init__(self, parent_config: "CoreConfig") -> None: def __init__(self, parent_config: "CoreConfig") -> None:
@@ -127,19 +195,28 @@ class BillingConfig:
@property @property
def port(self) -> int: def port(self) -> int:
return CoreConfig.get_config_field(self.__config, 'core', 'billing', 'port', default=8443) return CoreConfig.get_config_field(
self.__config, "core", "billing", "port", default=8443
)
@property @property
def ssl_key(self) -> str: def ssl_key(self) -> str:
return CoreConfig.get_config_field(self.__config, 'core', 'billing', 'ssl_key', default="cert/server.key") return CoreConfig.get_config_field(
self.__config, "core", "billing", "ssl_key", default="cert/server.key"
)
@property @property
def ssl_cert(self) -> str: def ssl_cert(self) -> str:
return CoreConfig.get_config_field(self.__config, 'core', 'billing', 'ssl_cert', default="cert/server.pem") return CoreConfig.get_config_field(
self.__config, "core", "billing", "ssl_cert", default="cert/server.pem"
)
@property @property
def signing_key(self) -> str: def signing_key(self) -> str:
return CoreConfig.get_config_field(self.__config, 'core', 'billing', 'signing_key', default="cert/billing.key") return CoreConfig.get_config_field(
self.__config, "core", "billing", "signing_key", default="cert/billing.key"
)
class AimedbConfig: class AimedbConfig:
def __init__(self, parent_config: "CoreConfig") -> None: def __init__(self, parent_config: "CoreConfig") -> None:
@@ -147,15 +224,24 @@ class AimedbConfig:
@property @property
def loglevel(self) -> int: def loglevel(self) -> int:
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'core', 'aimedb', 'loglevel', default="info")) return CoreConfig.str_to_loglevel(
CoreConfig.get_config_field(
self.__config, "core", "aimedb", "loglevel", default="info"
)
)
@property @property
def port(self) -> int: def port(self) -> int:
return CoreConfig.get_config_field(self.__config, 'core', 'aimedb', 'port', default=22345) return CoreConfig.get_config_field(
self.__config, "core", "aimedb", "port", default=22345
)
@property @property
def key(self) -> str: def key(self) -> str:
return CoreConfig.get_config_field(self.__config, 'core', 'aimedb', 'key', default="") return CoreConfig.get_config_field(
self.__config, "core", "aimedb", "key", default=""
)
class MuchaConfig: class MuchaConfig:
def __init__(self, parent_config: "CoreConfig") -> None: def __init__(self, parent_config: "CoreConfig") -> None:
@@ -163,27 +249,42 @@ class MuchaConfig:
@property @property
def enable(self) -> int: def enable(self) -> int:
return CoreConfig.get_config_field(self.__config, 'core', 'mucha', 'enable', default=False) return CoreConfig.get_config_field(
self.__config, "core", "mucha", "enable", default=False
)
@property @property
def loglevel(self) -> int: def loglevel(self) -> int:
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'core', 'mucha', 'loglevel', default="info")) return CoreConfig.str_to_loglevel(
CoreConfig.get_config_field(
self.__config, "core", "mucha", "loglevel", default="info"
)
)
@property @property
def hostname(self) -> str: def hostname(self) -> str:
return CoreConfig.get_config_field(self.__config, 'core', 'mucha', 'hostname', default="localhost") return CoreConfig.get_config_field(
self.__config, "core", "mucha", "hostname", default="localhost"
)
@property @property
def port(self) -> int: def port(self) -> int:
return CoreConfig.get_config_field(self.__config, 'core', 'mucha', 'port', default=8444) return CoreConfig.get_config_field(
self.__config, "core", "mucha", "port", default=8444
)
@property @property
def ssl_cert(self) -> str: def ssl_cert(self) -> str:
return CoreConfig.get_config_field(self.__config, 'core', 'mucha', 'ssl_cert', default="cert/server.pem") return CoreConfig.get_config_field(
self.__config, "core", "mucha", "ssl_cert", default="cert/server.pem"
)
@property @property
def signing_key(self) -> str: def signing_key(self) -> str:
return CoreConfig.get_config_field(self.__config, 'core', 'mucha', 'signing_key', default="cert/billing.key") return CoreConfig.get_config_field(
self.__config, "core", "mucha", "signing_key", default="cert/billing.key"
)
class CoreConfig(dict): class CoreConfig(dict):
def __init__(self) -> None: def __init__(self) -> None:
@@ -200,20 +301,22 @@ class CoreConfig(dict):
def str_to_loglevel(cls, level_str: str): def str_to_loglevel(cls, level_str: str):
if level_str.lower() == "error": if level_str.lower() == "error":
return logging.ERROR return logging.ERROR
elif level_str.lower().startswith("warn"): # Fits warn or warning elif level_str.lower().startswith("warn"): # Fits warn or warning
return logging.WARN return logging.WARN
elif level_str.lower() == "debug": elif level_str.lower() == "debug":
return logging.DEBUG return logging.DEBUG
else: else:
return logging.INFO return logging.INFO
@classmethod @classmethod
def get_config_field(cls, __config: dict, module, *path: str, default: Any = "") -> Any: def get_config_field(
envKey = f'CFG_{module}_' cls, __config: dict, module, *path: str, default: Any = ""
) -> Any:
envKey = f"CFG_{module}_"
for arg in path: for arg in path:
envKey += arg + '_' envKey += arg + "_"
if envKey.endswith('_'): if envKey.endswith("_"):
envKey = envKey[:-1] envKey = envKey[:-1]
if envKey in os.environ: if envKey in os.environ:
+8 -3
View File
@@ -1,6 +1,7 @@
from enum import Enum from enum import Enum
class MainboardPlatformCodes():
class MainboardPlatformCodes:
RINGEDGE = "AALE" RINGEDGE = "AALE"
RINGWIDE = "AAML" RINGWIDE = "AAML"
NU = "AAVE" NU = "AAVE"
@@ -8,7 +9,8 @@ class MainboardPlatformCodes():
ALLS_UX = "ACAE" ALLS_UX = "ACAE"
ALLS_HX = "ACAX" ALLS_HX = "ACAX"
class MainboardRevisions():
class MainboardRevisions:
RINGEDGE = 1 RINGEDGE = 1
RINGEDGE2 = 2 RINGEDGE2 = 2
@@ -26,12 +28,14 @@ class MainboardRevisions():
ALLS_UX2 = 2 ALLS_UX2 = 2
ALLS_HX2 = 12 ALLS_HX2 = 12
class KeychipPlatformsCodes():
class KeychipPlatformsCodes:
RING = "A72E" RING = "A72E"
NU = ("A60E", "A60E", "A60E") NU = ("A60E", "A60E", "A60E")
NUSX = ("A61X", "A69X") NUSX = ("A61X", "A69X")
ALLS = "A63E" ALLS = "A63E"
class AllnetCountryCode(Enum): class AllnetCountryCode(Enum):
JAPAN = "JPN" JAPAN = "JPN"
UNITED_STATES = "USA" UNITED_STATES = "USA"
@@ -41,6 +45,7 @@ class AllnetCountryCode(Enum):
TAIWAN = "TWN" TAIWAN = "TWN"
CHINA = "CHN" CHINA = "CHN"
class AllnetJapanRegionId(Enum): class AllnetJapanRegionId(Enum):
NONE = 0 NONE = 0
AICHI = 1 AICHI = 1
+6 -4
View File
@@ -1,4 +1,3 @@
from typing import Any, Callable from typing import Any, Callable
from functools import wraps from functools import wraps
import hashlib import hashlib
@@ -6,15 +5,17 @@ import pickle
import logging import logging
from core.config import CoreConfig from core.config import CoreConfig
cfg:CoreConfig = None # type: ignore cfg: CoreConfig = None # type: ignore
# Make memcache optional # Make memcache optional
try: try:
import pylibmc # type: ignore import pylibmc # type: ignore
has_mc = True has_mc = True
except ModuleNotFoundError: except ModuleNotFoundError:
has_mc = False has_mc = False
def cached(lifetime: int=10, extra_key: Any=None) -> Callable:
def cached(lifetime: int = 10, extra_key: Any = None) -> Callable:
def _cached(func: Callable) -> Callable: def _cached(func: Callable) -> Callable:
if has_mc: if has_mc:
hostname = "127.0.0.1" hostname = "127.0.0.1"
@@ -26,7 +27,6 @@ def cached(lifetime: int=10, extra_key: Any=None) -> Callable:
@wraps(func) @wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any: def wrapper(*args: Any, **kwargs: Any) -> Any:
if lifetime is not None: if lifetime is not None:
# Hash function args # Hash function args
items = kwargs.items() items = kwargs.items()
hashable_args = (args[1:], sorted(list(items))) hashable_args = (args[1:], sorted(list(items)))
@@ -55,7 +55,9 @@ def cached(lifetime: int=10, extra_key: Any=None) -> Callable:
memcache.set(cache_key, result, lifetime) memcache.set(cache_key, result, lifetime)
return result return result
else: else:
@wraps(func) @wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any: def wrapper(*args: Any, **kwargs: Any) -> Any:
return func(*args, **kwargs) return func(*args, **kwargs)
+63 -24
View File
@@ -13,6 +13,7 @@ from core.config import CoreConfig
from core.data.schema import * from core.data.schema import *
from core.utils import Utils from core.utils import Utils
class Data: class Data:
def __init__(self, cfg: CoreConfig) -> None: def __init__(self, cfg: CoreConfig) -> None:
self.config = cfg self.config = cfg
@@ -38,9 +39,13 @@ class Data:
self.logger = logging.getLogger("database") self.logger = logging.getLogger("database")
# Prevent the logger from adding handlers multiple times # Prevent the logger from adding handlers multiple times
if not getattr(self.logger, 'handler_set', None): if not getattr(self.logger, "handler_set", None):
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.config.server.log_dir, "db"), encoding="utf-8", fileHandler = TimedRotatingFileHandler(
when="d", backupCount=10) "{0}/{1}.log".format(self.config.server.log_dir, "db"),
encoding="utf-8",
when="d",
backupCount=10,
)
fileHandler.setFormatter(log_fmt) fileHandler.setFormatter(log_fmt)
consoleHandler = logging.StreamHandler() consoleHandler = logging.StreamHandler()
@@ -50,8 +55,10 @@ class Data:
self.logger.addHandler(consoleHandler) self.logger.addHandler(consoleHandler)
self.logger.setLevel(self.config.database.loglevel) self.logger.setLevel(self.config.database.loglevel)
coloredlogs.install(cfg.database.loglevel, logger=self.logger, fmt=log_fmt_str) coloredlogs.install(
self.logger.handler_set = True # type: ignore cfg.database.loglevel, logger=self.logger, fmt=log_fmt_str
)
self.logger.handler_set = True # type: ignore
def create_database(self): def create_database(self):
self.logger.info("Creating databases...") self.logger.info("Creating databases...")
@@ -67,16 +74,24 @@ class Data:
title_db = game_mod.database(self.config) title_db = game_mod.database(self.config)
metadata.create_all(self.__engine.connect()) metadata.create_all(self.__engine.connect())
self.base.set_schema_ver(game_mod.current_schema_version, game_mod.game_codes[0]) self.base.set_schema_ver(
game_mod.current_schema_version, game_mod.game_codes[0]
)
except Exception as e: except Exception as e:
self.logger.warning(f"Could not load database schema from {game_dir} - {e}") self.logger.warning(
f"Could not load database schema from {game_dir} - {e}"
)
self.logger.info(f"Setting base_schema_ver to {self.schema_ver_latest}") self.logger.info(f"Setting base_schema_ver to {self.schema_ver_latest}")
self.base.set_schema_ver(self.schema_ver_latest) self.base.set_schema_ver(self.schema_ver_latest)
self.logger.info(f"Setting user auto_incrememnt to {self.config.database.user_table_autoincrement_start}") self.logger.info(
self.user.reset_autoincrement(self.config.database.user_table_autoincrement_start) f"Setting user auto_incrememnt to {self.config.database.user_table_autoincrement_start}"
)
self.user.reset_autoincrement(
self.config.database.user_table_autoincrement_start
)
def recreate_database(self): def recreate_database(self):
self.logger.info("Dropping all databases...") self.logger.info("Dropping all databases...")
@@ -98,10 +113,14 @@ class Data:
metadata.drop_all(self.__engine.connect()) metadata.drop_all(self.__engine.connect())
except Exception as e: except Exception as e:
self.logger.warning(f"Could not load database schema from {dir} - {e}") self.logger.warning(
f"Could not load database schema from {dir} - {e}"
)
except ImportError as e: except ImportError as e:
self.logger.warning(f"Failed to load database schema dir {dir} - {e}") self.logger.warning(
f"Failed to load database schema dir {dir} - {e}"
)
break break
self.base.execute("SET FOREIGN_KEY_CHECKS=1") self.base.execute("SET FOREIGN_KEY_CHECKS=1")
@@ -113,18 +132,30 @@ class Data:
sql = "" sql = ""
if old_ver is None: if old_ver is None:
self.logger.error(f"Schema for game {game} does not exist, did you run the creation script?") self.logger.error(
f"Schema for game {game} does not exist, did you run the creation script?"
)
return return
if old_ver == version: if old_ver == version:
self.logger.info(f"Schema for game {game} is already version {old_ver}, nothing to do") self.logger.info(
f"Schema for game {game} is already version {old_ver}, nothing to do"
)
return return
if not os.path.exists(f"core/data/schema/versions/{game.upper()}_{version}_{action}.sql"): if not os.path.exists(
self.logger.error(f"Could not find {action} script {game.upper()}_{version}_{action}.sql in core/data/schema/versions folder") f"core/data/schema/versions/{game.upper()}_{version}_{action}.sql"
):
self.logger.error(
f"Could not find {action} script {game.upper()}_{version}_{action}.sql in core/data/schema/versions folder"
)
return return
with open(f"core/data/schema/versions/{game.upper()}_{version}_{action}.sql", "r", encoding="utf-8") as f: with open(
f"core/data/schema/versions/{game.upper()}_{version}_{action}.sql",
"r",
encoding="utf-8",
) as f:
sql = f.read() sql = f.read()
result = self.base.execute(sql) result = self.base.execute(sql)
@@ -140,7 +171,9 @@ class Data:
self.logger.info(f"Successfully migrated {game} to schema version {version}") self.logger.info(f"Successfully migrated {game} to schema version {version}")
def create_owner(self, email: Optional[str] = None) -> None: def create_owner(self, email: Optional[str] = None) -> None:
pw = ''.join(secrets.choice(string.ascii_letters + string.digits) for i in range(20)) pw = "".join(
secrets.choice(string.ascii_letters + string.digits) for i in range(20)
)
hash = bcrypt.hashpw(pw.encode(), bcrypt.gensalt()) hash = bcrypt.hashpw(pw.encode(), bcrypt.gensalt())
user_id = self.user.create_user(email=email, permission=255, password=hash) user_id = self.user.create_user(email=email, permission=255, password=hash)
@@ -153,7 +186,9 @@ class Data:
self.logger.error(f"Failed to create card for owner with id {user_id}") self.logger.error(f"Failed to create card for owner with id {user_id}")
return return
self.logger.warn(f"Successfully created owner with email {email}, access code 00000000000000000000, and password {pw} Make sure to change this password and assign a real card ASAP!") self.logger.warn(
f"Successfully created owner with email {email}, access code 00000000000000000000, and password {pw} Make sure to change this password and assign a real card ASAP!"
)
def migrate_card(self, old_ac: str, new_ac: str, should_force: bool) -> None: def migrate_card(self, old_ac: str, new_ac: str, should_force: bool) -> None:
if old_ac == new_ac: if old_ac == new_ac:
@@ -166,18 +201,22 @@ class Data:
return return
if not should_force: if not should_force:
self.logger.warn(f"Card already exists for access code {new_ac} (id {new_card['id']}). If you wish to continue, rerun with the '--force' flag."\ self.logger.warn(
f" All exiting data on the target card {new_ac} will be perminently erased and replaced with data from card {old_ac}.") f"Card already exists for access code {new_ac} (id {new_card['id']}). If you wish to continue, rerun with the '--force' flag."
f" All exiting data on the target card {new_ac} will be perminently erased and replaced with data from card {old_ac}."
)
return return
self.logger.info(f"All exiting data on the target card {new_ac} will be perminently erased and replaced with data from card {old_ac}.") self.logger.info(
f"All exiting data on the target card {new_ac} will be perminently erased and replaced with data from card {old_ac}."
)
self.card.delete_card(new_card["id"]) self.card.delete_card(new_card["id"])
self.card.update_access_code(old_ac, new_ac) self.card.update_access_code(old_ac, new_ac)
hanging_user = self.user.get_user(new_card["user"]) hanging_user = self.user.get_user(new_card["user"])
if hanging_user["password"] is None: if hanging_user["password"] is None:
self.logger.info(f"Delete hanging user {hanging_user['id']}") self.logger.info(f"Delete hanging user {hanging_user['id']}")
self.user.delete_user(hanging_user['id']) self.user.delete_user(hanging_user["id"])
def delete_hanging_users(self) -> None: def delete_hanging_users(self) -> None:
""" """
@@ -188,11 +227,11 @@ class Data:
self.logger.error("Error occoured finding unregistered users") self.logger.error("Error occoured finding unregistered users")
for user in unreg_users: for user in unreg_users:
cards = self.card.get_user_cards(user['id']) cards = self.card.get_user_cards(user["id"])
if cards is None: if cards is None:
self.logger.error(f"Error getting cards for user {user['id']}") self.logger.error(f"Error getting cards for user {user['id']}")
continue continue
if not cards: if not cards:
self.logger.info(f"Delete hanging user {user['id']}") self.logger.info(f"Delete hanging user {user['id']}")
self.user.delete_user(user['id']) self.user.delete_user(user["id"])
+105 -46
View File
@@ -21,34 +21,49 @@ arcade = Table(
Column("city", String(255)), Column("city", String(255)),
Column("region_id", Integer), Column("region_id", Integer),
Column("timezone", String(255)), Column("timezone", String(255)),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
machine = Table( machine = Table(
"machine", "machine",
metadata, metadata,
Column("id", Integer, primary_key=True, nullable=False), Column("id", Integer, primary_key=True, nullable=False),
Column("arcade", ForeignKey("arcade.id", ondelete="cascade", onupdate="cascade"), nullable=False), Column(
"arcade",
ForeignKey("arcade.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("serial", String(15), nullable=False), Column("serial", String(15), nullable=False),
Column("board", String(15)), Column("board", String(15)),
Column("game", String(4)), Column("game", String(4)),
Column("country", String(3)), # overwrites if not null Column("country", String(3)), # overwrites if not null
Column("timezone", String(255)), Column("timezone", String(255)),
Column("ota_enable", Boolean), Column("ota_enable", Boolean),
Column("is_cab", Boolean), Column("is_cab", Boolean),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
arcade_owner = Table( arcade_owner = Table(
'arcade_owner', "arcade_owner",
metadata, metadata,
Column('user', Integer, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), Column(
Column('arcade', Integer, ForeignKey("arcade.id", ondelete="cascade", onupdate="cascade"), nullable=False), "user",
Column('permissions', Integer, nullable=False), Integer,
PrimaryKeyConstraint('user', 'arcade', name='arcade_owner_pk'), ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
mysql_charset='utf8mb4' nullable=False,
),
Column(
"arcade",
Integer,
ForeignKey("arcade.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("permissions", Integer, nullable=False),
PrimaryKeyConstraint("user", "arcade", name="arcade_owner_pk"),
mysql_charset="utf8mb4",
) )
class ArcadeData(BaseData): class ArcadeData(BaseData):
def get_machine(self, serial: str = None, id: int = None) -> Optional[Dict]: def get_machine(self, serial: str = None, id: int = None) -> Optional[Dict]:
if serial is not None: if serial is not None:
@@ -71,72 +86,112 @@ class ArcadeData(BaseData):
return None return None
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchone() return result.fetchone()
def put_machine(self, arcade_id: int, serial: str = "", board: str = None, game: str = None, is_cab: bool = False) -> Optional[int]: def put_machine(
self,
arcade_id: int,
serial: str = "",
board: str = None,
game: str = None,
is_cab: bool = False,
) -> Optional[int]:
if arcade_id: if arcade_id:
self.logger.error(f"{__name__ }: Need arcade id!") self.logger.error(f"{__name__ }: Need arcade id!")
return None return None
sql = machine.insert().values(arcade = arcade_id, keychip = serial, board = board, game = game, is_cab = is_cab) sql = machine.insert().values(
arcade=arcade_id, keychip=serial, board=board, game=game, is_cab=is_cab
)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.lastrowid return result.lastrowid
def set_machine_serial(self, machine_id: int, serial: str) -> None: def set_machine_serial(self, machine_id: int, serial: str) -> None:
result = self.execute(machine.update(machine.c.id == machine_id).values(keychip = serial)) result = self.execute(
machine.update(machine.c.id == machine_id).values(keychip=serial)
)
if result is None: if result is None:
self.logger.error(f"Failed to update serial for machine {machine_id} -> {serial}") self.logger.error(
f"Failed to update serial for machine {machine_id} -> {serial}"
)
return result.lastrowid return result.lastrowid
def set_machine_boardid(self, machine_id: int, boardid: str) -> None: def set_machine_boardid(self, machine_id: int, boardid: str) -> None:
result = self.execute(machine.update(machine.c.id == machine_id).values(board = boardid)) result = self.execute(
machine.update(machine.c.id == machine_id).values(board=boardid)
)
if result is None: if result is None:
self.logger.error(f"Failed to update board id for machine {machine_id} -> {boardid}") self.logger.error(
f"Failed to update board id for machine {machine_id} -> {boardid}"
)
def get_arcade(self, id: int) -> Optional[Dict]: def get_arcade(self, id: int) -> Optional[Dict]:
sql = arcade.select(arcade.c.id == id) sql = arcade.select(arcade.c.id == id)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchone() return result.fetchone()
def put_arcade(self, name: str, nickname: str = None, country: str = "JPN", country_id: int = 1, def put_arcade(
state: str = "", city: str = "", regional_id: int = 1) -> Optional[int]: self,
if nickname is None: nickname = name name: str,
nickname: str = None,
country: str = "JPN",
country_id: int = 1,
state: str = "",
city: str = "",
regional_id: int = 1,
) -> Optional[int]:
if nickname is None:
nickname = name
sql = arcade.insert().values(name = name, nickname = nickname, country = country, country_id = country_id, sql = arcade.insert().values(
state = state, city = city, regional_id = regional_id) name=name,
nickname=nickname,
result = self.execute(sql) country=country,
if result is None: return None country_id=country_id,
return result.lastrowid state=state,
city=city,
def get_arcade_owners(self, arcade_id: int) -> Optional[Dict]: regional_id=regional_id,
sql = select(arcade_owner).where(arcade_owner.c.arcade==arcade_id)
result = self.execute(sql)
if result is None: return None
return result.fetchall()
def add_arcade_owner(self, arcade_id: int, user_id: int) -> None:
sql = insert(arcade_owner).values(
arcade=arcade_id,
user=user_id
) )
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.lastrowid return result.lastrowid
def format_serial(self, platform_code: str, platform_rev: int, serial_num: int, append: int = 4152) -> str: def get_arcade_owners(self, arcade_id: int) -> Optional[Dict]:
return f"{platform_code}{platform_rev:02d}A{serial_num:04d}{append:04d}" # 0x41 = A, 0x52 = R sql = select(arcade_owner).where(arcade_owner.c.arcade == arcade_id)
result = self.execute(sql)
if result is None:
return None
return result.fetchall()
def add_arcade_owner(self, arcade_id: int, user_id: int) -> None:
sql = insert(arcade_owner).values(arcade=arcade_id, user=user_id)
result = self.execute(sql)
if result is None:
return None
return result.lastrowid
def format_serial(
self, platform_code: str, platform_rev: int, serial_num: int, append: int = 4152
) -> str:
return f"{platform_code}{platform_rev:02d}A{serial_num:04d}{append:04d}" # 0x41 = A, 0x52 = R
def validate_keychip_format(self, serial: str) -> bool: def validate_keychip_format(self, serial: str) -> bool:
serial = serial.replace("-", "") serial = serial.replace("-", "")
if len(serial) != 11 or len(serial) != 15: if len(serial) != 11 or len(serial) != 15:
self.logger.error(f"Serial validate failed: Incorrect length for {serial} (len {len(serial)})") self.logger.error(
f"Serial validate failed: Incorrect length for {serial} (len {len(serial)})"
)
return False return False
platform_code = serial[:4] platform_code = serial[:4]
@@ -150,11 +205,15 @@ class ArcadeData(BaseData):
return False return False
if len(append) != 0 or len(append) != 4: if len(append) != 0 or len(append) != 4:
self.logger.error(f"Serial validate failed: {serial} had malformed append {append}") self.logger.error(
f"Serial validate failed: {serial} had malformed append {append}"
)
return False return False
if len(num) != 4: if len(num) != 4:
self.logger.error(f"Serial validate failed: {serial} had malformed number {num}") self.logger.error(
f"Serial validate failed: {serial} had malformed number {num}"
)
return False return False
return True return True
+26 -12
View File
@@ -19,7 +19,7 @@ schema_ver = Table(
metadata, metadata,
Column("game", String(4), primary_key=True, nullable=False), Column("game", String(4), primary_key=True, nullable=False),
Column("version", Integer, nullable=False, server_default="1"), Column("version", Integer, nullable=False, server_default="1"),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
event_log = Table( event_log = Table(
@@ -32,16 +32,17 @@ event_log = Table(
Column("message", String(1000), nullable=False), Column("message", String(1000), nullable=False),
Column("details", JSON, nullable=False), Column("details", JSON, nullable=False),
Column("when_logged", TIMESTAMP, nullable=False, server_default=func.now()), Column("when_logged", TIMESTAMP, nullable=False, server_default=func.now()),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
class BaseData():
class BaseData:
def __init__(self, cfg: CoreConfig, conn: Connection) -> None: def __init__(self, cfg: CoreConfig, conn: Connection) -> None:
self.config = cfg self.config = cfg
self.conn = conn self.conn = conn
self.logger = logging.getLogger("database") self.logger = logging.getLogger("database")
def execute(self, sql: str, opts: Dict[str, Any]={}) -> Optional[CursorResult]: def execute(self, sql: str, opts: Dict[str, Any] = {}) -> Optional[CursorResult]:
res = None res = None
try: try:
@@ -94,21 +95,33 @@ class BaseData():
return row["version"] return row["version"]
def set_schema_ver(self, ver: int, game: str = "CORE") -> Optional[int]: def set_schema_ver(self, ver: int, game: str = "CORE") -> Optional[int]:
sql = insert(schema_ver).values(game = game, version = ver) sql = insert(schema_ver).values(game=game, version=ver)
conflict = sql.on_duplicate_key_update(version = ver) conflict = sql.on_duplicate_key_update(version=ver)
result = self.execute(conflict) result = self.execute(conflict)
if result is None: if result is None:
self.logger.error(f"Failed to update schema version for game {game} (v{ver})") self.logger.error(
f"Failed to update schema version for game {game} (v{ver})"
)
return None return None
return result.lastrowid return result.lastrowid
def log_event(self, system: str, type: str, severity: int, message: str, details: Dict = {}) -> Optional[int]: def log_event(
sql = event_log.insert().values(system = system, type = type, severity = severity, message = message, details = json.dumps(details)) self, system: str, type: str, severity: int, message: str, details: Dict = {}
) -> Optional[int]:
sql = event_log.insert().values(
system=system,
type=type,
severity=severity,
message=message,
details=json.dumps(details),
)
result = self.execute(sql) result = self.execute(sql)
if result is None: if result is None:
self.logger.error(f"{__name__}: Failed to insert event into event log! system = {system}, type = {type}, severity = {severity}, message = {message}") self.logger.error(
f"{__name__}: Failed to insert event into event log! system = {system}, type = {type}, severity = {severity}, message = {message}"
)
return None return None
return result.lastrowid return result.lastrowid
@@ -117,11 +130,12 @@ class BaseData():
sql = event_log.select().limit(entries).all() sql = event_log.select().limit(entries).all()
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchall() return result.fetchall()
def fix_bools(self, data: Dict) -> Dict: def fix_bools(self, data: Dict) -> Dict:
for k,v in data.items(): for k, v in data.items():
if type(v) == str and v.lower() == "true": if type(v) == str and v.lower() == "true":
data[k] = True data[k] = True
elif type(v) == str and v.lower() == "false": elif type(v) == str and v.lower() == "false":
+25 -12
View File
@@ -8,47 +8,59 @@ from sqlalchemy.engine import Row
from core.data.schema.base import BaseData, metadata from core.data.schema.base import BaseData, metadata
aime_card = Table( aime_card = Table(
'aime_card', "aime_card",
metadata, metadata,
Column("id", Integer, primary_key=True, nullable=False), Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("access_code", String(20)), Column("access_code", String(20)),
Column("created_date", TIMESTAMP, server_default=func.now()), Column("created_date", TIMESTAMP, server_default=func.now()),
Column("last_login_date", TIMESTAMP, onupdate=func.now()), Column("last_login_date", TIMESTAMP, onupdate=func.now()),
Column("is_locked", Boolean, server_default="0"), Column("is_locked", Boolean, server_default="0"),
Column("is_banned", Boolean, server_default="0"), Column("is_banned", Boolean, server_default="0"),
UniqueConstraint("user", "access_code", name="aime_card_uk"), UniqueConstraint("user", "access_code", name="aime_card_uk"),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
class CardData(BaseData): class CardData(BaseData):
def get_card_by_access_code(self, access_code: str) -> Optional[Row]: def get_card_by_access_code(self, access_code: str) -> Optional[Row]:
sql = aime_card.select(aime_card.c.access_code == access_code) sql = aime_card.select(aime_card.c.access_code == access_code)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchone() return result.fetchone()
def get_card_by_id(self, card_id: int) -> Optional[Row]: def get_card_by_id(self, card_id: int) -> Optional[Row]:
sql = aime_card.select(aime_card.c.id == card_id) sql = aime_card.select(aime_card.c.id == card_id)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchone() return result.fetchone()
def update_access_code(self, old_ac: str, new_ac: str) -> None: def update_access_code(self, old_ac: str, new_ac: str) -> None:
sql = aime_card.update(aime_card.c.access_code == old_ac).values(access_code = new_ac) sql = aime_card.update(aime_card.c.access_code == old_ac).values(
access_code=new_ac
)
result = self.execute(sql) result = self.execute(sql)
if result is None: if result is None:
self.logger.error(f"Failed to change card access code from {old_ac} to {new_ac}") self.logger.error(
f"Failed to change card access code from {old_ac} to {new_ac}"
)
def get_user_id_from_card(self, access_code: str) -> Optional[int]: def get_user_id_from_card(self, access_code: str) -> Optional[int]:
""" """
Given a 20 digit access code as a string, get the user id associated with that card Given a 20 digit access code as a string, get the user id associated with that card
""" """
card = self.get_card_by_access_code(access_code) card = self.get_card_by_access_code(access_code)
if card is None: return None if card is None:
return None
return int(card["user"]) return int(card["user"])
@@ -65,17 +77,18 @@ class CardData(BaseData):
""" """
sql = aime_card.select(aime_card.c.user == aime_id) sql = aime_card.select(aime_card.c.user == aime_id)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchall() return result.fetchall()
def create_card(self, user_id: int, access_code: str) -> Optional[int]: def create_card(self, user_id: int, access_code: str) -> Optional[int]:
""" """
Given a aime_user id and a 20 digit access code as a string, create a card and return the ID if successful Given a aime_user id and a 20 digit access code as a string, create a card and return the ID if successful
""" """
sql = aime_card.insert().values(user=user_id, access_code=access_code) sql = aime_card.insert().values(user=user_id, access_code=access_code)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.lastrowid return result.lastrowid
def to_access_code(self, luid: str) -> str: def to_access_code(self, luid: str) -> str:
@@ -88,4 +101,4 @@ class CardData(BaseData):
""" """
Given a 20 digit access code as a string, return the 16 hex character luid Given a 20 digit access code as a string, return the 16 hex character luid
""" """
return f'{int(access_code):0{16}x}' return f"{int(access_code):0{16}x}"
+23 -14
View File
@@ -21,22 +21,31 @@ aime_user = Table(
Column("created_date", TIMESTAMP, server_default=func.now()), Column("created_date", TIMESTAMP, server_default=func.now()),
Column("last_login_date", TIMESTAMP, onupdate=func.now()), Column("last_login_date", TIMESTAMP, onupdate=func.now()),
Column("suspend_expire_time", TIMESTAMP), Column("suspend_expire_time", TIMESTAMP),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
class PermissionBits(Enum): class PermissionBits(Enum):
PermUser = 1 PermUser = 1
PermMod = 2 PermMod = 2
PermSysAdmin = 4 PermSysAdmin = 4
class UserData(BaseData): class UserData(BaseData):
def create_user(self, id: int = None, username: str = None, email: str = None, password: str = None, permission: int = 1) -> Optional[int]: def create_user(
self,
id: int = None,
username: str = None,
email: str = None,
password: str = None,
permission: int = 1,
) -> Optional[int]:
if id is None: if id is None:
sql = insert(aime_user).values( sql = insert(aime_user).values(
username=username, username=username,
email=email, email=email,
password=password, password=password,
permissions=permission permissions=permission,
) )
else: else:
sql = insert(aime_user).values( sql = insert(aime_user).values(
@@ -44,34 +53,34 @@ class UserData(BaseData):
username=username, username=username,
email=email, email=email,
password=password, password=password,
permissions=permission permissions=permission,
) )
conflict = sql.on_duplicate_key_update( conflict = sql.on_duplicate_key_update(
username=username, username=username, email=email, password=password, permissions=permission
email=email,
password=password,
permissions=permission
) )
result = self.execute(conflict) result = self.execute(conflict)
if result is None: return None if result is None:
return None
return result.lastrowid return result.lastrowid
def get_user(self, user_id: int) -> Optional[Row]: def get_user(self, user_id: int) -> Optional[Row]:
sql = select(aime_user).where(aime_user.c.id == user_id) sql = select(aime_user).where(aime_user.c.id == user_id)
result = self.execute(sql) result = self.execute(sql)
if result is None: return False if result is None:
return False
return result.fetchone() return result.fetchone()
def check_password(self, user_id: int, passwd: bytes = None) -> bool: def check_password(self, user_id: int, passwd: bytes = None) -> bool:
usr = self.get_user(user_id) usr = self.get_user(user_id)
if usr is None: return False if usr is None:
if usr['password'] is None:
return False return False
return bcrypt.checkpw(passwd, usr['password'].encode()) if usr["password"] is None:
return False
return bcrypt.checkpw(passwd, usr["password"].encode())
def reset_autoincrement(self, ai_value: int) -> None: def reset_autoincrement(self, ai_value: int) -> None:
# ALTER TABLE isn't in sqlalchemy so we do this the ugly way # ALTER TABLE isn't in sqlalchemy so we do this the ugly way
+54 -17
View File
@@ -14,11 +14,13 @@ from core.config import CoreConfig
from core.data import Data from core.data import Data
from core.utils import Utils from core.utils import Utils
class IUserSession(Interface): class IUserSession(Interface):
userId = Attribute("User's ID") userId = Attribute("User's ID")
current_ip = Attribute("User's current ip address") current_ip = Attribute("User's current ip address")
permissions = Attribute("User's permission level") permissions = Attribute("User's permission level")
@implementer(IUserSession) @implementer(IUserSession)
class UserSession(object): class UserSession(object):
def __init__(self, session): def __init__(self, session):
@@ -26,10 +28,11 @@ class UserSession(object):
self.current_ip = "0.0.0.0" self.current_ip = "0.0.0.0"
self.permissions = 0 self.permissions = 0
class FrontendServlet(resource.Resource): class FrontendServlet(resource.Resource):
def getChild(self, name: bytes, request: Request): def getChild(self, name: bytes, request: Request):
self.logger.debug(f"{request.getClientIP()} -> {name.decode()}") self.logger.debug(f"{request.getClientIP()} -> {name.decode()}")
if name == b'': if name == b"":
return self return self
return resource.Resource.getChild(self, name, request) return resource.Resource.getChild(self, name, request)
@@ -42,7 +45,11 @@ class FrontendServlet(resource.Resource):
self.game_list: List[Dict[str, str]] = [] self.game_list: List[Dict[str, str]] = []
self.children: Dict[str, Any] = {} self.children: Dict[str, Any] = {}
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.config.server.log_dir, "frontend"), when="d", backupCount=10) fileHandler = TimedRotatingFileHandler(
"{0}/{1}.log".format(self.config.server.log_dir, "frontend"),
when="d",
backupCount=10,
)
fileHandler.setFormatter(log_fmt) fileHandler.setFormatter(log_fmt)
consoleHandler = logging.StreamHandler() consoleHandler = logging.StreamHandler()
@@ -52,7 +59,9 @@ class FrontendServlet(resource.Resource):
self.logger.addHandler(consoleHandler) self.logger.addHandler(consoleHandler)
self.logger.setLevel(cfg.frontend.loglevel) self.logger.setLevel(cfg.frontend.loglevel)
coloredlogs.install(level=cfg.frontend.loglevel, logger=self.logger, fmt=log_fmt_str) coloredlogs.install(
level=cfg.frontend.loglevel, logger=self.logger, fmt=log_fmt_str
)
registerAdapter(UserSession, Session, IUserSession) registerAdapter(UserSession, Session, IUserSession)
fe_game = FE_Game(cfg, self.environment) fe_game = FE_Game(cfg, self.environment)
@@ -71,12 +80,20 @@ class FrontendServlet(resource.Resource):
self.putChild(b"user", FE_User(cfg, self.environment)) self.putChild(b"user", FE_User(cfg, self.environment))
self.putChild(b"game", fe_game) self.putChild(b"game", fe_game)
self.logger.info(f"Ready on port {self.config.frontend.port} serving {len(fe_game.children)} games") self.logger.info(
f"Ready on port {self.config.frontend.port} serving {len(fe_game.children)} games"
)
def render_GET(self, request): def render_GET(self, request):
self.logger.debug(f"{request.getClientIP()} -> {request.uri.decode()}") self.logger.debug(f"{request.getClientIP()} -> {request.uri.decode()}")
template = self.environment.get_template("core/frontend/index.jinja") template = self.environment.get_template("core/frontend/index.jinja")
return template.render(server_name=self.config.server.name, title=self.config.server.name, game_list=self.game_list, sesh=vars(IUserSession(request.getSession()))).encode("utf-16") return template.render(
server_name=self.config.server.name,
title=self.config.server.name,
game_list=self.game_list,
sesh=vars(IUserSession(request.getSession())),
).encode("utf-16")
class FE_Base(resource.Resource): class FE_Base(resource.Resource):
""" """
@@ -84,14 +101,17 @@ class FE_Base(resource.Resource):
Initializes the environment, data, logger, config, and sets isLeaf to true Initializes the environment, data, logger, config, and sets isLeaf to true
It is expected that game implementations of this class overwrite many of these It is expected that game implementations of this class overwrite many of these
""" """
isLeaf = True isLeaf = True
def __init__(self, cfg: CoreConfig, environment: jinja2.Environment) -> None: def __init__(self, cfg: CoreConfig, environment: jinja2.Environment) -> None:
self.core_config = cfg self.core_config = cfg
self.data = Data(cfg) self.data = Data(cfg)
self.logger = logging.getLogger('frontend') self.logger = logging.getLogger("frontend")
self.environment = environment self.environment = environment
self.nav_name = "nav_name" self.nav_name = "nav_name"
class FE_Gate(FE_Base): class FE_Gate(FE_Base):
def render_GET(self, request: Request): def render_GET(self, request: Request):
self.logger.debug(f"{request.getClientIP()} -> {request.uri.decode()}") self.logger.debug(f"{request.getClientIP()} -> {request.uri.decode()}")
@@ -105,16 +125,21 @@ class FE_Gate(FE_Base):
if uri.startswith("/gate/create"): if uri.startswith("/gate/create"):
return self.create_user(request) return self.create_user(request)
if b'e' in request.args: if b"e" in request.args:
try: try:
err = int(request.args[b'e'][0].decode()) err = int(request.args[b"e"][0].decode())
except: except:
err = 0 err = 0
else: err = 0 else:
err = 0
template = self.environment.get_template("core/frontend/gate/gate.jinja") template = self.environment.get_template("core/frontend/gate/gate.jinja")
return template.render(title=f"{self.core_config.server.name} | Login Gate", error=err, sesh=vars(usr_sesh)).encode("utf-16") return template.render(
title=f"{self.core_config.server.name} | Login Gate",
error=err,
sesh=vars(usr_sesh),
).encode("utf-16")
def render_POST(self, request: Request): def render_POST(self, request: Request):
uri = request.uri.decode() uri = request.uri.decode()
@@ -134,7 +159,9 @@ class FE_Gate(FE_Base):
sesh = self.data.user.check_password(uid) sesh = self.data.user.check_password(uid)
if sesh is not None: if sesh is not None:
return redirectTo(f"/gate/create?ac={access_code}".encode(), request) return redirectTo(
f"/gate/create?ac={access_code}".encode(), request
)
return redirectTo(b"/gate?e=1", request) return redirectTo(b"/gate?e=1", request)
if not self.data.user.check_password(uid, passwd): if not self.data.user.check_password(uid, passwd):
@@ -162,7 +189,9 @@ class FE_Gate(FE_Base):
salt = bcrypt.gensalt() salt = bcrypt.gensalt()
hashed = bcrypt.hashpw(passwd, salt) hashed = bcrypt.hashpw(passwd, salt)
result = self.data.user.create_user(uid, username, email, hashed.decode(), 1) result = self.data.user.create_user(
uid, username, email, hashed.decode(), 1
)
if result is None: if result is None:
return redirectTo(b"/gate?e=3", request) return redirectTo(b"/gate?e=3", request)
@@ -175,13 +204,18 @@ class FE_Gate(FE_Base):
return b"" return b""
def create_user(self, request: Request): def create_user(self, request: Request):
if b'ac' not in request.args or len(request.args[b'ac'][0].decode()) != 20: if b"ac" not in request.args or len(request.args[b"ac"][0].decode()) != 20:
return redirectTo(b"/gate?e=2", request) return redirectTo(b"/gate?e=2", request)
ac = request.args[b'ac'][0].decode() ac = request.args[b"ac"][0].decode()
template = self.environment.get_template("core/frontend/gate/create.jinja") template = self.environment.get_template("core/frontend/gate/create.jinja")
return template.render(title=f"{self.core_config.server.name} | Create User", code=ac, sesh={"userId": 0}).encode("utf-16") return template.render(
title=f"{self.core_config.server.name} | Create User",
code=ac,
sesh={"userId": 0},
).encode("utf-16")
class FE_User(FE_Base): class FE_User(FE_Base):
def render_GET(self, request: Request): def render_GET(self, request: Request):
@@ -192,14 +226,17 @@ class FE_User(FE_Base):
if usr_sesh.userId == 0: if usr_sesh.userId == 0:
return redirectTo(b"/gate", request) return redirectTo(b"/gate", request)
return template.render(title=f"{self.core_config.server.name} | Account", sesh=vars(usr_sesh)).encode("utf-16") return template.render(
title=f"{self.core_config.server.name} | Account", sesh=vars(usr_sesh)
).encode("utf-16")
class FE_Game(FE_Base): class FE_Game(FE_Base):
isLeaf = False isLeaf = False
children: Dict[str, Any] = {} children: Dict[str, Any] = {}
def getChild(self, name: bytes, request: Request): def getChild(self, name: bytes, request: Request):
if name == b'': if name == b"":
return self return self
return resource.Resource.getChild(self, name, request) return resource.Resource.getChild(self, name, request)
+53 -21
View File
@@ -9,17 +9,22 @@ import pytz
from core.config import CoreConfig from core.config import CoreConfig
from core.utils import Utils from core.utils import Utils
class MuchaServlet: class MuchaServlet:
def __init__(self, cfg: CoreConfig, cfg_dir: str) -> None: def __init__(self, cfg: CoreConfig, cfg_dir: str) -> None:
self.config = cfg self.config = cfg
self.config_dir = cfg_dir self.config_dir = cfg_dir
self.mucha_registry: List[str] = [] self.mucha_registry: List[str] = []
self.logger = logging.getLogger('mucha') self.logger = logging.getLogger("mucha")
log_fmt_str = "[%(asctime)s] Mucha | %(levelname)s | %(message)s" log_fmt_str = "[%(asctime)s] Mucha | %(levelname)s | %(message)s"
log_fmt = logging.Formatter(log_fmt_str) log_fmt = logging.Formatter(log_fmt_str)
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.config.server.log_dir, "mucha"), when="d", backupCount=10) fileHandler = TimedRotatingFileHandler(
"{0}/{1}.log".format(self.config.server.log_dir, "mucha"),
when="d",
backupCount=10,
)
fileHandler.setFormatter(log_fmt) fileHandler.setFormatter(log_fmt)
consoleHandler = logging.StreamHandler() consoleHandler = logging.StreamHandler()
@@ -35,21 +40,29 @@ class MuchaServlet:
for _, mod in all_titles.items(): for _, mod in all_titles.items():
if hasattr(mod, "index") and hasattr(mod.index, "get_mucha_info"): if hasattr(mod, "index") and hasattr(mod.index, "get_mucha_info"):
enabled, game_cd = mod.index.get_mucha_info(self.config, self.config_dir) enabled, game_cd = mod.index.get_mucha_info(
self.config, self.config_dir
)
if enabled: if enabled:
self.mucha_registry.append(game_cd) self.mucha_registry.append(game_cd)
self.logger.info(f"Serving {len(self.mucha_registry)} games on port {self.config.mucha.port}") self.logger.info(
f"Serving {len(self.mucha_registry)} games on port {self.config.mucha.port}"
)
def handle_boardauth(self, request: Request, _: Dict) -> bytes: def handle_boardauth(self, request: Request, _: Dict) -> bytes:
req_dict = self.mucha_preprocess(request.content.getvalue()) req_dict = self.mucha_preprocess(request.content.getvalue())
if req_dict is None: if req_dict is None:
self.logger.error(f"Error processing mucha request {request.content.getvalue()}") self.logger.error(
f"Error processing mucha request {request.content.getvalue()}"
)
return b"" return b""
req = MuchaAuthRequest(req_dict) req = MuchaAuthRequest(req_dict)
self.logger.debug(f"Mucha request {vars(req)}") self.logger.debug(f"Mucha request {vars(req)}")
self.logger.info(f"Boardauth request from {request.getClientAddress().host} for {req.gameVer}") self.logger.info(
f"Boardauth request from {request.getClientAddress().host} for {req.gameVer}"
)
if req.gameCd not in self.mucha_registry: if req.gameCd not in self.mucha_registry:
self.logger.warn(f"Unknown gameCd {req.gameCd}") self.logger.warn(f"Unknown gameCd {req.gameCd}")
@@ -57,7 +70,9 @@ class MuchaServlet:
# TODO: Decrypt S/N # TODO: Decrypt S/N
resp = MuchaAuthResponse(f"{self.config.mucha.hostname}{':' + self.config.mucha.port if self.config.server.is_develop else ''}") resp = MuchaAuthResponse(
f"{self.config.mucha.hostname}{':' + self.config.mucha.port if self.config.server.is_develop else ''}"
)
self.logger.debug(f"Mucha response {vars(resp)}") self.logger.debug(f"Mucha response {vars(resp)}")
@@ -66,12 +81,16 @@ class MuchaServlet:
def handle_updatecheck(self, request: Request, _: Dict) -> bytes: def handle_updatecheck(self, request: Request, _: Dict) -> bytes:
req_dict = self.mucha_preprocess(request.content.getvalue()) req_dict = self.mucha_preprocess(request.content.getvalue())
if req_dict is None: if req_dict is None:
self.logger.error(f"Error processing mucha request {request.content.getvalue()}") self.logger.error(
f"Error processing mucha request {request.content.getvalue()}"
)
return b"" return b""
req = MuchaUpdateRequest(req_dict) req = MuchaUpdateRequest(req_dict)
self.logger.debug(f"Mucha request {vars(req)}") self.logger.debug(f"Mucha request {vars(req)}")
self.logger.info(f"Updatecheck request from {request.getClientAddress().host} for {req.gameVer}") self.logger.info(
f"Updatecheck request from {request.getClientAddress().host} for {req.gameVer}"
)
if req.gameCd not in self.mucha_registry: if req.gameCd not in self.mucha_registry:
self.logger.warn(f"Unknown gameCd {req.gameCd}") self.logger.warn(f"Unknown gameCd {req.gameCd}")
@@ -87,8 +106,8 @@ class MuchaServlet:
try: try:
ret: Dict[str, Any] = {} ret: Dict[str, Any] = {}
for x in data.decode().split('&'): for x in data.decode().split("&"):
kvp = x.split('=') kvp = x.split("=")
if len(kvp) == 2: if len(kvp) == 2:
ret[kvp[0]] = kvp[1] ret[kvp[0]] = kvp[1]
@@ -101,7 +120,7 @@ class MuchaServlet:
def mucha_postprocess(self, data: dict) -> Optional[bytes]: def mucha_postprocess(self, data: dict) -> Optional[bytes]:
try: try:
urlencode = "" urlencode = ""
for k,v in data.items(): for k, v in data.items():
urlencode += f"{k}={v}&" urlencode += f"{k}={v}&"
return urlencode.encode() return urlencode.encode()
@@ -110,22 +129,30 @@ class MuchaServlet:
self.logger.error("Error processing mucha response") self.logger.error("Error processing mucha response")
return None return None
class MuchaAuthRequest():
class MuchaAuthRequest:
def __init__(self, request: Dict) -> None: def __init__(self, request: Dict) -> None:
self.gameVer = "" if "gameVer" not in request else request["gameVer"] # gameCd + boardType + countryCd + version self.gameVer = (
self.sendDate = "" if "sendDate" not in request else request["sendDate"] # %Y%m%d "" if "gameVer" not in request else request["gameVer"]
) # gameCd + boardType + countryCd + version
self.sendDate = (
"" if "sendDate" not in request else request["sendDate"]
) # %Y%m%d
self.serialNum = "" if "serialNum" not in request else request["serialNum"] self.serialNum = "" if "serialNum" not in request else request["serialNum"]
self.gameCd = "" if "gameCd" not in request else request["gameCd"] self.gameCd = "" if "gameCd" not in request else request["gameCd"]
self.boardType = "" if "boardType" not in request else request["boardType"] self.boardType = "" if "boardType" not in request else request["boardType"]
self.boardId = "" if "boardId" not in request else request["boardId"] self.boardId = "" if "boardId" not in request else request["boardId"]
self.mac = "" if "mac" not in request else request["mac"] self.mac = "" if "mac" not in request else request["mac"]
self.placeId = "" if "placeId" not in request else request["placeId"] self.placeId = "" if "placeId" not in request else request["placeId"]
self.storeRouterIp = "" if "storeRouterIp" not in request else request["storeRouterIp"] self.storeRouterIp = (
"" if "storeRouterIp" not in request else request["storeRouterIp"]
)
self.countryCd = "" if "countryCd" not in request else request["countryCd"] self.countryCd = "" if "countryCd" not in request else request["countryCd"]
self.useToken = "" if "useToken" not in request else request["useToken"] self.useToken = "" if "useToken" not in request else request["useToken"]
self.allToken = "" if "allToken" not in request else request["allToken"] self.allToken = "" if "allToken" not in request else request["allToken"]
class MuchaAuthResponse():
class MuchaAuthResponse:
def __init__(self, mucha_url: str) -> None: def __init__(self, mucha_url: str) -> None:
self.RESULTS = "001" self.RESULTS = "001"
self.AUTH_INTERVAL = "86400" self.AUTH_INTERVAL = "86400"
@@ -169,16 +196,20 @@ class MuchaAuthResponse():
self.DONGLE_FLG = "1" self.DONGLE_FLG = "1"
self.FORCE_BOOT = "0" self.FORCE_BOOT = "0"
class MuchaUpdateRequest():
class MuchaUpdateRequest:
def __init__(self, request: Dict) -> None: def __init__(self, request: Dict) -> None:
self.gameVer = "" if "gameVer" not in request else request["gameVer"] self.gameVer = "" if "gameVer" not in request else request["gameVer"]
self.gameCd = "" if "gameCd" not in request else request["gameCd"] self.gameCd = "" if "gameCd" not in request else request["gameCd"]
self.serialNum = "" if "serialNum" not in request else request["serialNum"] self.serialNum = "" if "serialNum" not in request else request["serialNum"]
self.countryCd = "" if "countryCd" not in request else request["countryCd"] self.countryCd = "" if "countryCd" not in request else request["countryCd"]
self.placeId = "" if "placeId" not in request else request["placeId"] self.placeId = "" if "placeId" not in request else request["placeId"]
self.storeRouterIp = "" if "storeRouterIp" not in request else request["storeRouterIp"] self.storeRouterIp = (
"" if "storeRouterIp" not in request else request["storeRouterIp"]
)
class MuchaUpdateResponse():
class MuchaUpdateResponse:
def __init__(self, game_ver: str, mucha_url: str) -> None: def __init__(self, game_ver: str, mucha_url: str) -> None:
self.RESULTS = "001" self.RESULTS = "001"
self.UPDATE_VER_1 = game_ver self.UPDATE_VER_1 = game_ver
@@ -194,7 +225,8 @@ class MuchaUpdateResponse():
self.USER_ID = "" self.USER_ID = ""
self.PASSWORD = "" self.PASSWORD = ""
class MuchaUpdateResponseStub():
class MuchaUpdateResponseStub:
def __init__(self, game_ver: str) -> None: def __init__(self, game_ver: str) -> None:
self.RESULTS = "001" self.RESULTS = "001"
self.UPDATE_VER_1 = game_ver self.UPDATE_VER_1 = game_ver
+19 -6
View File
@@ -7,7 +7,8 @@ from core.config import CoreConfig
from core.data import Data from core.data import Data
from core.utils import Utils from core.utils import Utils
class TitleServlet():
class TitleServlet:
def __init__(self, core_cfg: CoreConfig, cfg_folder: str): def __init__(self, core_cfg: CoreConfig, cfg_folder: str):
super().__init__() super().__init__()
self.config = core_cfg self.config = core_cfg
@@ -20,7 +21,11 @@ class TitleServlet():
log_fmt_str = "[%(asctime)s] Title | %(levelname)s | %(message)s" log_fmt_str = "[%(asctime)s] Title | %(levelname)s | %(message)s"
log_fmt = logging.Formatter(log_fmt_str) log_fmt = logging.Formatter(log_fmt_str)
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.config.server.log_dir, "title"), when="d", backupCount=10) fileHandler = TimedRotatingFileHandler(
"{0}/{1}.log".format(self.config.server.log_dir, "title"),
when="d",
backupCount=10,
)
fileHandler.setFormatter(log_fmt) fileHandler.setFormatter(log_fmt)
consoleHandler = logging.StreamHandler() consoleHandler = logging.StreamHandler()
@@ -30,7 +35,9 @@ class TitleServlet():
self.logger.addHandler(consoleHandler) self.logger.addHandler(consoleHandler)
self.logger.setLevel(core_cfg.title.loglevel) self.logger.setLevel(core_cfg.title.loglevel)
coloredlogs.install(level=core_cfg.title.loglevel, logger=self.logger, fmt=log_fmt_str) coloredlogs.install(
level=core_cfg.title.loglevel, logger=self.logger, fmt=log_fmt_str
)
self.logger.initialized = True self.logger.initialized = True
plugins = Utils.get_all_titles() plugins = Utils.get_all_titles()
@@ -41,7 +48,9 @@ class TitleServlet():
if hasattr(mod.index, "get_allnet_info"): if hasattr(mod.index, "get_allnet_info"):
for code in mod.game_codes: for code in mod.game_codes:
enabled, _, _ = mod.index.get_allnet_info(code, self.config, self.config_folder) enabled, _, _ = mod.index.get_allnet_info(
code, self.config, self.config_folder
)
if enabled: if enabled:
handler_cls = mod.index(self.config, self.config_folder) handler_cls = mod.index(self.config, self.config_folder)
@@ -58,7 +67,9 @@ class TitleServlet():
else: else:
self.logger.error(f"{folder} missing game_code or index in __init__.py") self.logger.error(f"{folder} missing game_code or index in __init__.py")
self.logger.info(f"Serving {len(self.title_registry)} game codes on port {core_cfg.title.port}") self.logger.info(
f"Serving {len(self.title_registry)} game codes on port {core_cfg.title.port}"
)
def render_GET(self, request: Request, endpoints: dict) -> bytes: def render_GET(self, request: Request, endpoints: dict) -> bytes:
code = endpoints["game"] code = endpoints["game"]
@@ -88,4 +99,6 @@ class TitleServlet():
request.setResponseCode(405) request.setResponseCode(405)
return b"" return b""
return index.render_POST(request, int(endpoints["version"]), endpoints["endpoint"]) return index.render_POST(
request, int(endpoints["version"]), endpoints["endpoint"]
)
+1
View File
@@ -4,6 +4,7 @@ import logging
import importlib import importlib
from os import walk from os import walk
class Utils: class Utils:
@classmethod @classmethod
def get_all_titles(cls) -> Dict[str, ModuleType]: def get_all_titles(cls) -> Dict[str, ModuleType]:
+19 -5
View File
@@ -4,16 +4,30 @@ from core.config import CoreConfig
from core.data import Data from core.data import Data
from os import path from os import path
if __name__=='__main__': if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Database utilities") parser = argparse.ArgumentParser(description="Database utilities")
parser.add_argument("--config", "-c", type=str, help="Config folder to use", default="config") parser.add_argument(
parser.add_argument("--version", "-v", type=str, help="Version of the database to upgrade/rollback to") "--config", "-c", type=str, help="Config folder to use", default="config"
parser.add_argument("--game", "-g", type=str, help="Game code of the game who's schema will be updated/rolled back. Ex. SDFE") )
parser.add_argument(
"--version",
"-v",
type=str,
help="Version of the database to upgrade/rollback to",
)
parser.add_argument(
"--game",
"-g",
type=str,
help="Game code of the game who's schema will be updated/rolled back. Ex. SDFE",
)
parser.add_argument("--email", "-e", type=str, help="Email for the new user") parser.add_argument("--email", "-e", type=str, help="Email for the new user")
parser.add_argument("--old_ac", "-o", type=str, help="Access code to transfer from") parser.add_argument("--old_ac", "-o", type=str, help="Access code to transfer from")
parser.add_argument("--new_ac", "-n", type=str, help="Access code to transfer to") parser.add_argument("--new_ac", "-n", type=str, help="Access code to transfer to")
parser.add_argument("--force", "-f", type=bool, help="Force the action to happen") parser.add_argument("--force", "-f", type=bool, help="Force the action to happen")
parser.add_argument("action", type=str, help="DB Action, create, recreate, upgrade, or rollback") parser.add_argument(
"action", type=str, help="DB Action, create, recreate, upgrade, or rollback"
)
args = parser.parse_args() args = parser.parse_args()
cfg = CoreConfig() cfg = CoreConfig()
+113 -25
View File
@@ -12,6 +12,7 @@ from twisted.internet import reactor, endpoints
from twisted.web.http import Request from twisted.web.http import Request
from routes import Mapper from routes import Mapper
class HttpDispatcher(resource.Resource): class HttpDispatcher(resource.Resource):
def __init__(self, cfg: CoreConfig, config_dir: str): def __init__(self, cfg: CoreConfig, config_dir: str):
super().__init__() super().__init__()
@@ -25,22 +26,80 @@ class HttpDispatcher(resource.Resource):
self.title = TitleServlet(cfg, config_dir) self.title = TitleServlet(cfg, config_dir)
self.mucha = MuchaServlet(cfg, config_dir) self.mucha = MuchaServlet(cfg, config_dir)
self.map_post.connect('allnet_ping', '/naomitest.html', controller="allnet", action='handle_naomitest', conditions=dict(method=['GET'])) self.map_post.connect(
self.map_post.connect('allnet_poweron', '/sys/servlet/PowerOn', controller="allnet", action='handle_poweron', conditions=dict(method=['POST'])) "allnet_ping",
self.map_post.connect('allnet_downloadorder', '/sys/servlet/DownloadOrder', controller="allnet", action='handle_dlorder', conditions=dict(method=['POST'])) "/naomitest.html",
self.map_post.connect('allnet_billing', '/request', controller="allnet", action='handle_billing_request', conditions=dict(method=['POST'])) controller="allnet",
self.map_post.connect('allnet_billing', '/request/', controller="allnet", action='handle_billing_request', conditions=dict(method=['POST'])) action="handle_naomitest",
conditions=dict(method=["GET"]),
)
self.map_post.connect(
"allnet_poweron",
"/sys/servlet/PowerOn",
controller="allnet",
action="handle_poweron",
conditions=dict(method=["POST"]),
)
self.map_post.connect(
"allnet_downloadorder",
"/sys/servlet/DownloadOrder",
controller="allnet",
action="handle_dlorder",
conditions=dict(method=["POST"]),
)
self.map_post.connect(
"allnet_billing",
"/request",
controller="allnet",
action="handle_billing_request",
conditions=dict(method=["POST"]),
)
self.map_post.connect(
"allnet_billing",
"/request/",
controller="allnet",
action="handle_billing_request",
conditions=dict(method=["POST"]),
)
self.map_post.connect('mucha_boardauth', '/mucha/boardauth.do', controller="mucha", action='handle_boardauth', conditions=dict(method=['POST'])) self.map_post.connect(
self.map_post.connect('mucha_updatacheck', '/mucha/updatacheck.do', controller="mucha", action='handle_updatacheck', conditions=dict(method=['POST'])) "mucha_boardauth",
"/mucha/boardauth.do",
controller="mucha",
action="handle_boardauth",
conditions=dict(method=["POST"]),
)
self.map_post.connect(
"mucha_updatacheck",
"/mucha/updatacheck.do",
controller="mucha",
action="handle_updatacheck",
conditions=dict(method=["POST"]),
)
self.map_get.connect("title_get", "/{game}/{version}/{endpoint:.*?}", controller="title", action="render_GET", conditions=dict(method=['GET']), requirements=dict(game=R"S...")) self.map_get.connect(
self.map_post.connect("title_post", "/{game}/{version}/{endpoint:.*?}", controller="title", action="render_POST", conditions=dict(method=['POST']), requirements=dict(game=R"S...")) "title_get",
"/{game}/{version}/{endpoint:.*?}",
controller="title",
action="render_GET",
conditions=dict(method=["GET"]),
requirements=dict(game=R"S..."),
)
self.map_post.connect(
"title_post",
"/{game}/{version}/{endpoint:.*?}",
controller="title",
action="render_POST",
conditions=dict(method=["POST"]),
requirements=dict(game=R"S..."),
)
def render_GET(self, request: Request) -> bytes: def render_GET(self, request: Request) -> bytes:
test = self.map_get.match(request.uri.decode()) test = self.map_get.match(request.uri.decode())
if test is None: if test is None:
self.logger.debug(f"Unknown GET endpoint {request.uri.decode()} from {request.getClientAddress().host} to port {request.getHost().port}") self.logger.debug(
f"Unknown GET endpoint {request.uri.decode()} from {request.getClientAddress().host} to port {request.getHost().port}"
)
request.setResponseCode(404) request.setResponseCode(404)
return b"Endpoint not found." return b"Endpoint not found."
@@ -49,7 +108,9 @@ class HttpDispatcher(resource.Resource):
def render_POST(self, request: Request) -> bytes: def render_POST(self, request: Request) -> bytes:
test = self.map_post.match(request.uri.decode()) test = self.map_post.match(request.uri.decode())
if test is None: if test is None:
self.logger.debug(f"Unknown POST endpoint {request.uri.decode()} from {request.getClientAddress().host} to port {request.getHost().port}") self.logger.debug(
f"Unknown POST endpoint {request.uri.decode()} from {request.getClientAddress().host} to port {request.getHost().port}"
)
request.setResponseCode(404) request.setResponseCode(404)
return b"Endpoint not found." return b"Endpoint not found."
@@ -58,13 +119,17 @@ class HttpDispatcher(resource.Resource):
def dispatch(self, matcher: Dict, request: Request) -> bytes: def dispatch(self, matcher: Dict, request: Request) -> bytes:
controller = getattr(self, matcher["controller"], None) controller = getattr(self, matcher["controller"], None)
if controller is None: if controller is None:
self.logger.error(f"Controller {matcher['controller']} not found via endpoint {request.uri.decode()}") self.logger.error(
f"Controller {matcher['controller']} not found via endpoint {request.uri.decode()}"
)
request.setResponseCode(404) request.setResponseCode(404)
return b"Endpoint not found." return b"Endpoint not found."
handler = getattr(controller, matcher["action"], None) handler = getattr(controller, matcher["action"], None)
if handler is None: if handler is None:
self.logger.error(f"Action {matcher['action']} not found in controller {matcher['controller']} via endpoint {request.uri.decode()}") self.logger.error(
f"Action {matcher['action']} not found in controller {matcher['controller']} via endpoint {request.uri.decode()}"
)
request.setResponseCode(404) request.setResponseCode(404)
return b"Endpoint not found." return b"Endpoint not found."
@@ -80,13 +145,18 @@ class HttpDispatcher(resource.Resource):
else: else:
return b"" return b""
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser(description="ARTEMiS main entry point") parser = argparse.ArgumentParser(description="ARTEMiS main entry point")
parser.add_argument("--config", "-c", type=str, default="config", help="Configuration folder") parser.add_argument(
"--config", "-c", type=str, default="config", help="Configuration folder"
)
args = parser.parse_args() args = parser.parse_args()
if not path.exists(f"{args.config}/core.yaml"): if not path.exists(f"{args.config}/core.yaml"):
print(f"The config folder you specified ({args.config}) does not exist or does not contain core.yaml.\nDid you copy the example folder?") print(
f"The config folder you specified ({args.config}) does not exist or does not contain core.yaml.\nDid you copy the example folder?"
)
exit(1) exit(1)
cfg: CoreConfig = CoreConfig() cfg: CoreConfig = CoreConfig()
@@ -97,7 +167,9 @@ if __name__ == "__main__":
log_fmt_str = "[%(asctime)s] Core | %(levelname)s | %(message)s" log_fmt_str = "[%(asctime)s] Core | %(levelname)s | %(message)s"
log_fmt = logging.Formatter(log_fmt_str) log_fmt = logging.Formatter(log_fmt_str)
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(cfg.server.log_dir, "core"), when="d", backupCount=10) fileHandler = TimedRotatingFileHandler(
"{0}/{1}.log".format(cfg.server.log_dir, "core"), when="d", backupCount=10
)
fileHandler.setFormatter(log_fmt) fileHandler.setFormatter(log_fmt)
consoleHandler = logging.StreamHandler() consoleHandler = logging.StreamHandler()
@@ -114,37 +186,53 @@ if __name__ == "__main__":
mkdir(cfg.server.log_dir) mkdir(cfg.server.log_dir)
if not access(cfg.server.log_dir, W_OK): if not access(cfg.server.log_dir, W_OK):
logger.error(f"Log directory {cfg.server.log_dir} NOT writable, please check permissions") logger.error(
f"Log directory {cfg.server.log_dir} NOT writable, please check permissions"
)
exit(1) exit(1)
if not cfg.aimedb.key: if not cfg.aimedb.key:
logger.error("!!AIMEDB KEY BLANK, SET KEY IN CORE.YAML!!") logger.error("!!AIMEDB KEY BLANK, SET KEY IN CORE.YAML!!")
exit(1) exit(1)
logger.info(f"ARTEMiS starting in {'develop' if cfg.server.is_develop else 'production'} mode") logger.info(
f"ARTEMiS starting in {'develop' if cfg.server.is_develop else 'production'} mode"
)
allnet_server_str = f"tcp:{cfg.allnet.port}:interface={cfg.server.listen_address}" allnet_server_str = f"tcp:{cfg.allnet.port}:interface={cfg.server.listen_address}"
title_server_str = f"tcp:{cfg.title.port}:interface={cfg.server.listen_address}" title_server_str = f"tcp:{cfg.title.port}:interface={cfg.server.listen_address}"
adb_server_str = f"tcp:{cfg.aimedb.port}:interface={cfg.server.listen_address}" adb_server_str = f"tcp:{cfg.aimedb.port}:interface={cfg.server.listen_address}"
frontend_server_str = f"tcp:{cfg.frontend.port}:interface={cfg.server.listen_address}" frontend_server_str = (
f"tcp:{cfg.frontend.port}:interface={cfg.server.listen_address}"
)
billing_server_str = f"tcp:{cfg.billing.port}:interface={cfg.server.listen_address}" billing_server_str = f"tcp:{cfg.billing.port}:interface={cfg.server.listen_address}"
if cfg.server.is_develop: if cfg.server.is_develop:
billing_server_str = f"ssl:{cfg.billing.port}:interface={cfg.server.listen_address}"\ billing_server_str = (
f"ssl:{cfg.billing.port}:interface={cfg.server.listen_address}"
f":privateKey={cfg.billing.ssl_key}:certKey={cfg.billing.ssl_cert}" f":privateKey={cfg.billing.ssl_key}:certKey={cfg.billing.ssl_cert}"
)
dispatcher = HttpDispatcher(cfg, args.config) dispatcher = HttpDispatcher(cfg, args.config)
endpoints.serverFromString(reactor, allnet_server_str).listen(server.Site(dispatcher)) endpoints.serverFromString(reactor, allnet_server_str).listen(
server.Site(dispatcher)
)
endpoints.serverFromString(reactor, adb_server_str).listen(AimedbFactory(cfg)) endpoints.serverFromString(reactor, adb_server_str).listen(AimedbFactory(cfg))
if cfg.frontend.enable: if cfg.frontend.enable:
endpoints.serverFromString(reactor, frontend_server_str).listen(server.Site(FrontendServlet(cfg, args.config))) endpoints.serverFromString(reactor, frontend_server_str).listen(
server.Site(FrontendServlet(cfg, args.config))
)
if cfg.billing.port > 0: if cfg.billing.port > 0:
endpoints.serverFromString(reactor, billing_server_str).listen(server.Site(dispatcher)) endpoints.serverFromString(reactor, billing_server_str).listen(
server.Site(dispatcher)
)
if cfg.title.port > 0: if cfg.title.port > 0:
endpoints.serverFromString(reactor, title_server_str).listen(server.Site(dispatcher)) endpoints.serverFromString(reactor, title_server_str).listen(
server.Site(dispatcher)
)
reactor.run() # type: ignore reactor.run() # type: ignore
+33 -23
View File
@@ -12,8 +12,16 @@ from typing import List, Optional
from core import CoreConfig from core import CoreConfig
from core.utils import Utils from core.utils import Utils
class BaseReader():
def __init__(self, config: CoreConfig, version: int, bin_dir: Optional[str], opt_dir: Optional[str], extra: Optional[str]) -> None: class BaseReader:
def __init__(
self,
config: CoreConfig,
version: int,
bin_dir: Optional[str],
opt_dir: Optional[str],
extra: Optional[str],
) -> None:
self.logger = logging.getLogger("reader") self.logger = logging.getLogger("reader")
self.config = config self.config = config
self.bin_dir = bin_dir self.bin_dir = bin_dir
@@ -21,47 +29,47 @@ class BaseReader():
self.version = version self.version = version
self.extra = extra self.extra = extra
def get_data_directories(self, directory: str) -> List[str]: def get_data_directories(self, directory: str) -> List[str]:
ret: List[str] = [] ret: List[str] = []
for root, dirs, files in os.walk(directory): for root, dirs, files in os.walk(directory):
for dir in dirs: for dir in dirs:
if re.fullmatch("[A-Z0-9]{4,4}", dir) is not None: if re.fullmatch("[A-Z0-9]{4,4}", dir) is not None:
ret.append(f"{root}/{dir}") ret.append(f"{root}/{dir}")
return ret return ret
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Import Game Information') parser = argparse.ArgumentParser(description="Import Game Information")
parser.add_argument( parser.add_argument(
'--series', "--series",
action='store', action="store",
type=str, type=str,
required=True, required=True,
help='The game series we are importing.', help="The game series we are importing.",
) )
parser.add_argument( parser.add_argument(
'--version', "--version",
dest='version', dest="version",
action='store', action="store",
type=int, type=int,
required=True, required=True,
help='The game version we are importing.', help="The game version we are importing.",
) )
parser.add_argument( parser.add_argument(
'--binfolder', "--binfolder",
dest='bin', dest="bin",
action='store', action="store",
type=str, type=str,
help='Folder containing A000 base data', help="Folder containing A000 base data",
) )
parser.add_argument( parser.add_argument(
'--optfolder', "--optfolder",
dest='opt', dest="opt",
action='store', action="store",
type=str, type=str,
help='Folder containing Option data folders', help="Folder containing Option data folders",
) )
parser.add_argument( parser.add_argument(
"--config", "--config",
@@ -86,7 +94,9 @@ if __name__ == "__main__":
log_fmt = logging.Formatter(log_fmt_str) log_fmt = logging.Formatter(log_fmt_str)
logger = logging.getLogger("reader") logger = logging.getLogger("reader")
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(config.server.log_dir, "reader"), when="d", backupCount=10) fileHandler = TimedRotatingFileHandler(
"{0}/{1}.log".format(config.server.log_dir, "reader"), when="d", backupCount=10
)
fileHandler.setFormatter(log_fmt) fileHandler.setFormatter(log_fmt)
consoleHandler = logging.StreamHandler() consoleHandler = logging.StreamHandler()
+2 -1
View File
@@ -5,12 +5,13 @@ from titles.chuni.base import ChuniBase
from titles.chuni.const import ChuniConstants from titles.chuni.const import ChuniConstants
from titles.chuni.config import ChuniConfig from titles.chuni.config import ChuniConfig
class ChuniAir(ChuniBase): class ChuniAir(ChuniBase):
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None: def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
super().__init__(core_cfg, game_cfg) super().__init__(core_cfg, game_cfg)
self.version = ChuniConstants.VER_CHUNITHM_AIR self.version = ChuniConstants.VER_CHUNITHM_AIR
def handle_get_game_setting_api_request(self, data: Dict) -> Dict: def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = super().handle_get_game_setting_api_request(data) ret = super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.10.00" ret["gameSetting"]["dataVersion"] = "1.10.00"
return ret return ret
+2 -1
View File
@@ -5,12 +5,13 @@ from titles.chuni.base import ChuniBase
from titles.chuni.const import ChuniConstants from titles.chuni.const import ChuniConstants
from titles.chuni.config import ChuniConfig from titles.chuni.config import ChuniConfig
class ChuniAirPlus(ChuniBase): class ChuniAirPlus(ChuniBase):
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None: def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
super().__init__(core_cfg, game_cfg) super().__init__(core_cfg, game_cfg)
self.version = ChuniConstants.VER_CHUNITHM_AIR_PLUS self.version = ChuniConstants.VER_CHUNITHM_AIR_PLUS
def handle_get_game_setting_api_request(self, data: Dict) -> Dict: def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = super().handle_get_game_setting_api_request(data) ret = super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.15.00" ret["gameSetting"]["dataVersion"] = "1.15.00"
return ret return ret
+2 -1
View File
@@ -7,12 +7,13 @@ from titles.chuni.base import ChuniBase
from titles.chuni.const import ChuniConstants from titles.chuni.const import ChuniConstants
from titles.chuni.config import ChuniConfig from titles.chuni.config import ChuniConfig
class ChuniAmazon(ChuniBase): class ChuniAmazon(ChuniBase):
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None: def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
super().__init__(core_cfg, game_cfg) super().__init__(core_cfg, game_cfg)
self.version = ChuniConstants.VER_CHUNITHM_AMAZON self.version = ChuniConstants.VER_CHUNITHM_AMAZON
def handle_get_game_setting_api_request(self, data: Dict) -> Dict: def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = super().handle_get_game_setting_api_request(data) ret = super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.30.00" ret["gameSetting"]["dataVersion"] = "1.30.00"
return ret return ret
+2 -1
View File
@@ -7,12 +7,13 @@ from titles.chuni.base import ChuniBase
from titles.chuni.const import ChuniConstants from titles.chuni.const import ChuniConstants
from titles.chuni.config import ChuniConfig from titles.chuni.config import ChuniConfig
class ChuniAmazonPlus(ChuniBase): class ChuniAmazonPlus(ChuniBase):
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None: def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
super().__init__(core_cfg, game_cfg) super().__init__(core_cfg, game_cfg)
self.version = ChuniConstants.VER_CHUNITHM_AMAZON_PLUS self.version = ChuniConstants.VER_CHUNITHM_AMAZON_PLUS
def handle_get_game_setting_api_request(self, data: Dict) -> Dict: def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = super().handle_get_game_setting_api_request(data) ret = super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.35.00" ret["gameSetting"]["dataVersion"] = "1.35.00"
return ret return ret
+123 -105
View File
@@ -11,7 +11,8 @@ from titles.chuni.const import ChuniConstants
from titles.chuni.database import ChuniData from titles.chuni.database import ChuniData
from titles.chuni.config import ChuniConfig from titles.chuni.config import ChuniConfig
class ChuniBase():
class ChuniBase:
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None: def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
self.core_cfg = core_cfg self.core_cfg = core_cfg
self.game_cfg = game_cfg self.game_cfg = game_cfg
@@ -22,32 +23,31 @@ class ChuniBase():
self.version = ChuniConstants.VER_CHUNITHM self.version = ChuniConstants.VER_CHUNITHM
def handle_game_login_api_request(self, data: Dict) -> Dict: def handle_game_login_api_request(self, data: Dict) -> Dict:
#self.data.base.log_event("chuni", "login", logging.INFO, {"version": self.version, "user": data["userId"]}) # self.data.base.log_event("chuni", "login", logging.INFO, {"version": self.version, "user": data["userId"]})
return { "returnCode": 1 } return {"returnCode": 1}
def handle_game_logout_api_request(self, data: Dict) -> Dict: def handle_game_logout_api_request(self, data: Dict) -> Dict:
#self.data.base.log_event("chuni", "logout", logging.INFO, {"version": self.version, "user": data["userId"]}) # self.data.base.log_event("chuni", "logout", logging.INFO, {"version": self.version, "user": data["userId"]})
return { "returnCode": 1 } return {"returnCode": 1}
def handle_get_game_charge_api_request(self, data: Dict) -> Dict: def handle_get_game_charge_api_request(self, data: Dict) -> Dict:
game_charge_list = self.data.static.get_enabled_charges(self.version) game_charge_list = self.data.static.get_enabled_charges(self.version)
charges = [] charges = []
for x in range(len(game_charge_list)): for x in range(len(game_charge_list)):
charges.append({ charges.append(
"orderId": x, {
"chargeId": game_charge_list[x]["chargeId"], "orderId": x,
"price": 1, "chargeId": game_charge_list[x]["chargeId"],
"startDate": "2017-12-05 07:00:00.0", "price": 1,
"endDate": "2099-12-31 00:00:00.0", "startDate": "2017-12-05 07:00:00.0",
"salePrice": 1, "endDate": "2099-12-31 00:00:00.0",
"saleStartDate": "2017-12-05 07:00:00.0", "salePrice": 1,
"saleEndDate": "2099-12-31 00:00:00.0" "saleStartDate": "2017-12-05 07:00:00.0",
}) "saleEndDate": "2099-12-31 00:00:00.0",
return { }
"length": len(charges), )
"gameChargeList": charges return {"length": len(charges), "gameChargeList": charges}
}
def handle_get_game_event_api_request(self, data: Dict) -> Dict: def handle_get_game_event_api_request(self, data: Dict) -> Dict:
game_events = self.data.static.get_enabled_events(self.version) game_events = self.data.static.get_enabled_events(self.version)
@@ -64,24 +64,28 @@ class ChuniBase():
return { return {
"type": data["type"], "type": data["type"],
"length": len(event_list), "length": len(event_list),
"gameEventList": event_list "gameEventList": event_list,
} }
def handle_get_game_idlist_api_request(self, data: Dict) -> Dict: def handle_get_game_idlist_api_request(self, data: Dict) -> Dict:
return { "type": data["type"], "length": 0, "gameIdlistList": [] } return {"type": data["type"], "length": 0, "gameIdlistList": []}
def handle_get_game_message_api_request(self, data: Dict) -> Dict: def handle_get_game_message_api_request(self, data: Dict) -> Dict:
return { "type": data["type"], "length": "0", "gameMessageList": [] } return {"type": data["type"], "length": "0", "gameMessageList": []}
def handle_get_game_ranking_api_request(self, data: Dict) -> Dict: def handle_get_game_ranking_api_request(self, data: Dict) -> Dict:
return { "type": data["type"], "gameRankingList": [] } return {"type": data["type"], "gameRankingList": []}
def handle_get_game_sale_api_request(self, data: Dict) -> Dict: def handle_get_game_sale_api_request(self, data: Dict) -> Dict:
return { "type": data["type"], "length": 0, "gameSaleList": [] } return {"type": data["type"], "length": 0, "gameSaleList": []}
def handle_get_game_setting_api_request(self, data: Dict) -> Dict: def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
reboot_start = datetime.strftime(datetime.now() - timedelta(hours=4), self.date_time_format) reboot_start = datetime.strftime(
reboot_end = datetime.strftime(datetime.now() - timedelta(hours=3), self.date_time_format) datetime.now() - timedelta(hours=4), self.date_time_format
)
reboot_end = datetime.strftime(
datetime.now() - timedelta(hours=3), self.date_time_format
)
return { return {
"gameSetting": { "gameSetting": {
"dataVersion": "1.00.00", "dataVersion": "1.00.00",
@@ -94,12 +98,14 @@ class ChuniBase():
"maxCountItem": 300, "maxCountItem": 300,
"maxCountMusic": 300, "maxCountMusic": 300,
}, },
"isDumpUpload": "false", "isDumpUpload": "false",
"isAou": "false", "isAou": "false",
} }
def handle_get_user_activity_api_request(self, data: Dict) -> Dict: def handle_get_user_activity_api_request(self, data: Dict) -> Dict:
user_activity_list = self.data.profile.get_profile_activity(data["userId"], data["kind"]) user_activity_list = self.data.profile.get_profile_activity(
data["userId"], data["kind"]
)
activity_list = [] activity_list = []
@@ -111,15 +117,16 @@ class ChuniBase():
activity_list.append(tmp) activity_list.append(tmp)
return { return {
"userId": data["userId"], "userId": data["userId"],
"length": len(activity_list), "length": len(activity_list),
"kind": data["kind"], "kind": data["kind"],
"userActivityList": activity_list "userActivityList": activity_list,
} }
def handle_get_user_character_api_request(self, data: Dict) -> Dict: def handle_get_user_character_api_request(self, data: Dict) -> Dict:
characters = self.data.item.get_characters(data["userId"]) characters = self.data.item.get_characters(data["userId"])
if characters is None: return {} if characters is None:
return {}
next_idx = -1 next_idx = -1
characterList = [] characterList = []
@@ -132,14 +139,16 @@ class ChuniBase():
if len(characterList) >= int(data["maxCount"]): if len(characterList) >= int(data["maxCount"]):
break break
if len(characterList) >= int(data["maxCount"]) and len(characters) > int(data["maxCount"]) + int(data["nextIndex"]): if len(characterList) >= int(data["maxCount"]) and len(characters) > int(
data["maxCount"]
) + int(data["nextIndex"]):
next_idx = int(data["maxCount"]) + int(data["nextIndex"]) + 1 next_idx = int(data["maxCount"]) + int(data["nextIndex"]) + 1
return { return {
"userId": data["userId"], "userId": data["userId"],
"length": len(characterList), "length": len(characterList),
"nextIndex": next_idx, "nextIndex": next_idx,
"userCharacterList": characterList "userCharacterList": characterList,
} }
def handle_get_user_charge_api_request(self, data: Dict) -> Dict: def handle_get_user_charge_api_request(self, data: Dict) -> Dict:
@@ -155,7 +164,7 @@ class ChuniBase():
return { return {
"userId": data["userId"], "userId": data["userId"],
"length": len(charge_list), "length": len(charge_list),
"userChargeList": charge_list "userChargeList": charge_list,
} }
def handle_get_user_course_api_request(self, data: Dict) -> Dict: def handle_get_user_course_api_request(self, data: Dict) -> Dict:
@@ -165,7 +174,7 @@ class ChuniBase():
"userId": data["userId"], "userId": data["userId"],
"length": 0, "length": 0,
"nextIndex": -1, "nextIndex": -1,
"userCourseList": [] "userCourseList": [],
} }
course_list = [] course_list = []
@@ -190,40 +199,37 @@ class ChuniBase():
"userId": data["userId"], "userId": data["userId"],
"length": len(course_list), "length": len(course_list),
"nextIndex": next_idx, "nextIndex": next_idx,
"userCourseList": course_list "userCourseList": course_list,
} }
def handle_get_user_data_api_request(self, data: Dict) -> Dict: def handle_get_user_data_api_request(self, data: Dict) -> Dict:
p = self.data.profile.get_profile_data(data["userId"], self.version) p = self.data.profile.get_profile_data(data["userId"], self.version)
if p is None: return {} if p is None:
return {}
profile = p._asdict() profile = p._asdict()
profile.pop("id") profile.pop("id")
profile.pop("user") profile.pop("user")
profile.pop("version") profile.pop("version")
return { return {"userId": data["userId"], "userData": profile}
"userId": data["userId"],
"userData": profile
}
def handle_get_user_data_ex_api_request(self, data: Dict) -> Dict: def handle_get_user_data_ex_api_request(self, data: Dict) -> Dict:
p = self.data.profile.get_profile_data_ex(data["userId"], self.version) p = self.data.profile.get_profile_data_ex(data["userId"], self.version)
if p is None: return {} if p is None:
return {}
profile = p._asdict() profile = p._asdict()
profile.pop("id") profile.pop("id")
profile.pop("user") profile.pop("user")
profile.pop("version") profile.pop("version")
return { return {"userId": data["userId"], "userDataEx": profile}
"userId": data["userId"],
"userDataEx": profile
}
def handle_get_user_duel_api_request(self, data: Dict) -> Dict: def handle_get_user_duel_api_request(self, data: Dict) -> Dict:
user_duel_list = self.data.item.get_duels(data["userId"]) user_duel_list = self.data.item.get_duels(data["userId"])
if user_duel_list is None: return {} if user_duel_list is None:
return {}
duel_list = [] duel_list = []
for duel in user_duel_list: for duel in user_duel_list:
@@ -235,7 +241,7 @@ class ChuniBase():
return { return {
"userId": data["userId"], "userId": data["userId"],
"length": len(duel_list), "length": len(duel_list),
"userDuelList": duel_list "userDuelList": duel_list,
} }
def handle_get_user_favorite_item_api_request(self, data: Dict) -> Dict: def handle_get_user_favorite_item_api_request(self, data: Dict) -> Dict:
@@ -244,7 +250,7 @@ class ChuniBase():
"length": 0, "length": 0,
"kind": data["kind"], "kind": data["kind"],
"nextIndex": -1, "nextIndex": -1,
"userFavoriteItemList": [] "userFavoriteItemList": [],
} }
def handle_get_user_favorite_music_api_request(self, data: Dict) -> Dict: def handle_get_user_favorite_music_api_request(self, data: Dict) -> Dict:
@@ -252,11 +258,7 @@ class ChuniBase():
This is handled via the webui, which we don't have right now This is handled via the webui, which we don't have right now
""" """
return { return {"userId": data["userId"], "length": 0, "userFavoriteMusicList": []}
"userId": data["userId"],
"length": 0,
"userFavoriteMusicList": []
}
def handle_get_user_item_api_request(self, data: Dict) -> Dict: def handle_get_user_item_api_request(self, data: Dict) -> Dict:
kind = int(int(data["nextIndex"]) / 10000000000) kind = int(int(data["nextIndex"]) / 10000000000)
@@ -264,7 +266,12 @@ class ChuniBase():
user_item_list = self.data.item.get_items(data["userId"], kind) user_item_list = self.data.item.get_items(data["userId"], kind)
if user_item_list is None or len(user_item_list) == 0: if user_item_list is None or len(user_item_list) == 0:
return {"userId": data["userId"], "nextIndex": -1, "itemKind": kind, "userItemList": []} return {
"userId": data["userId"],
"nextIndex": -1,
"itemKind": kind,
"userItemList": [],
}
items: list[Dict[str, Any]] = [] items: list[Dict[str, Any]] = []
for i in range(next_idx, len(user_item_list)): for i in range(next_idx, len(user_item_list)):
@@ -277,10 +284,18 @@ class ChuniBase():
xout = kind * 10000000000 + next_idx + len(items) xout = kind * 10000000000 + next_idx + len(items)
if len(items) < int(data["maxCount"]): nextIndex = 0 if len(items) < int(data["maxCount"]):
else: nextIndex = xout nextIndex = 0
else:
nextIndex = xout
return {"userId": data["userId"], "nextIndex": nextIndex, "itemKind": kind, "length": len(items), "userItemList": items} return {
"userId": data["userId"],
"nextIndex": nextIndex,
"itemKind": kind,
"length": len(items),
"userItemList": items,
}
def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict: def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict:
""" """
@@ -291,23 +306,24 @@ class ChuniBase():
"length": 2, "length": 2,
"userLoginBonusList": [ "userLoginBonusList": [
{ {
"presetId": '10', "presetId": "10",
"bonusCount": '0', "bonusCount": "0",
"lastUpdateDate": "1970-01-01 09:00:00", "lastUpdateDate": "1970-01-01 09:00:00",
"isWatched": "true" "isWatched": "true",
}, },
{ {
"presetId": '20', "presetId": "20",
"bonusCount": '0', "bonusCount": "0",
"lastUpdateDate": "1970-01-01 09:00:00", "lastUpdateDate": "1970-01-01 09:00:00",
"isWatched": "true" "isWatched": "true",
}, },
] ],
} }
def handle_get_user_map_api_request(self, data: Dict) -> Dict: def handle_get_user_map_api_request(self, data: Dict) -> Dict:
user_map_list = self.data.item.get_maps(data["userId"]) user_map_list = self.data.item.get_maps(data["userId"])
if user_map_list is None: return {} if user_map_list is None:
return {}
map_list = [] map_list = []
for map in user_map_list: for map in user_map_list:
@@ -319,7 +335,7 @@ class ChuniBase():
return { return {
"userId": data["userId"], "userId": data["userId"],
"length": len(map_list), "length": len(map_list),
"userMapList": map_list "userMapList": map_list,
} }
def handle_get_user_music_api_request(self, data: Dict) -> Dict: def handle_get_user_music_api_request(self, data: Dict) -> Dict:
@@ -329,7 +345,7 @@ class ChuniBase():
"userId": data["userId"], "userId": data["userId"],
"length": 0, "length": 0,
"nextIndex": -1, "nextIndex": -1,
"userMusicList": [] #240 "userMusicList": [], # 240
} }
song_list = [] song_list = []
next_idx = int(data["nextIndex"]) next_idx = int(data["nextIndex"])
@@ -348,10 +364,7 @@ class ChuniBase():
song["length"] = len(song["userMusicDetailList"]) song["length"] = len(song["userMusicDetailList"])
if not found: if not found:
song_list.append({ song_list.append({"length": 1, "userMusicDetailList": [tmp]})
"length": 1,
"userMusicDetailList": [tmp]
})
if len(song_list) >= max_ct: if len(song_list) >= max_ct:
break break
@@ -365,7 +378,7 @@ class ChuniBase():
"userId": data["userId"], "userId": data["userId"],
"length": len(song_list), "length": len(song_list),
"nextIndex": next_idx, "nextIndex": next_idx,
"userMusicList": song_list #240 "userMusicList": song_list, # 240
} }
def handle_get_user_option_api_request(self, data: Dict) -> Dict: def handle_get_user_option_api_request(self, data: Dict) -> Dict:
@@ -375,10 +388,7 @@ class ChuniBase():
option.pop("id") option.pop("id")
option.pop("user") option.pop("user")
return { return {"userId": data["userId"], "userGameOption": option}
"userId": data["userId"],
"userGameOption": option
}
def handle_get_user_option_ex_api_request(self, data: Dict) -> Dict: def handle_get_user_option_ex_api_request(self, data: Dict) -> Dict:
p = self.data.profile.get_profile_option_ex(data["userId"]) p = self.data.profile.get_profile_option_ex(data["userId"])
@@ -387,18 +397,18 @@ class ChuniBase():
option.pop("id") option.pop("id")
option.pop("user") option.pop("user")
return { return {"userId": data["userId"], "userGameOptionEx": option}
"userId": data["userId"],
"userGameOptionEx": option
}
def read_wtf8(self, src): def read_wtf8(self, src):
return bytes([ord(c) for c in src]).decode("utf-8") return bytes([ord(c) for c in src]).decode("utf-8")
def handle_get_user_preview_api_request(self, data: Dict) -> Dict: def handle_get_user_preview_api_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile_preview(data["userId"], self.version) profile = self.data.profile.get_profile_preview(data["userId"], self.version)
if profile is None: return None if profile is None:
profile_character = self.data.item.get_character(data["userId"], profile["characterId"]) return None
profile_character = self.data.item.get_character(
data["userId"], profile["characterId"]
)
if profile_character is None: if profile_character is None:
chara = {} chara = {}
@@ -459,10 +469,7 @@ class ChuniBase():
def handle_get_user_team_api_request(self, data: Dict) -> Dict: def handle_get_user_team_api_request(self, data: Dict) -> Dict:
# TODO: Team # TODO: Team
return { return {"userId": data["userId"], "teamId": 0}
"userId": data["userId"],
"teamId": 0
}
def handle_get_team_course_setting_api_request(self, data: Dict) -> Dict: def handle_get_team_course_setting_api_request(self, data: Dict) -> Dict:
return { return {
@@ -486,18 +493,29 @@ class ChuniBase():
if "userData" in upsert: if "userData" in upsert:
try: try:
upsert["userData"][0]["userName"] = self.read_wtf8(upsert["userData"][0]["userName"]) upsert["userData"][0]["userName"] = self.read_wtf8(
except: pass upsert["userData"][0]["userName"]
)
except:
pass
self.data.profile.put_profile_data(user_id, self.version, upsert["userData"][0]) self.data.profile.put_profile_data(
user_id, self.version, upsert["userData"][0]
)
if "userDataEx" in upsert: if "userDataEx" in upsert:
self.data.profile.put_profile_data_ex(user_id, self.version, upsert["userDataEx"][0]) self.data.profile.put_profile_data_ex(
user_id, self.version, upsert["userDataEx"][0]
)
if "userGameOption" in upsert: if "userGameOption" in upsert:
self.data.profile.put_profile_option(user_id, upsert["userGameOption"][0]) self.data.profile.put_profile_option(user_id, upsert["userGameOption"][0])
if "userGameOptionEx" in upsert: if "userGameOptionEx" in upsert:
self.data.profile.put_profile_option_ex(user_id, upsert["userGameOptionEx"][0]) self.data.profile.put_profile_option_ex(
user_id, upsert["userGameOptionEx"][0]
)
if "userRecentRatingList" in upsert: if "userRecentRatingList" in upsert:
self.data.profile.put_profile_recent_rating(user_id, upsert["userRecentRatingList"]) self.data.profile.put_profile_recent_rating(
user_id, upsert["userRecentRatingList"]
)
if "userCharacterList" in upsert: if "userCharacterList" in upsert:
for character in upsert["userCharacterList"]: for character in upsert["userCharacterList"]:
@@ -551,22 +569,22 @@ class ChuniBase():
for emoney in upsert["userEmoneyList"]: for emoney in upsert["userEmoneyList"]:
self.data.profile.put_profile_emoney(user_id, emoney) self.data.profile.put_profile_emoney(user_id, emoney)
return { "returnCode": "1" } return {"returnCode": "1"}
def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict: def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict:
return { "returnCode": "1" } return {"returnCode": "1"}
def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict: def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict:
return { "returnCode": "1" } return {"returnCode": "1"}
def handle_upsert_client_develop_api_request(self, data: Dict) -> Dict: def handle_upsert_client_develop_api_request(self, data: Dict) -> Dict:
return { "returnCode": "1" } return {"returnCode": "1"}
def handle_upsert_client_error_api_request(self, data: Dict) -> Dict: def handle_upsert_client_error_api_request(self, data: Dict) -> Dict:
return { "returnCode": "1" } return {"returnCode": "1"}
def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict: def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict:
return { "returnCode": "1" } return {"returnCode": "1"}
def handle_upsert_client_testmode_api_request(self, data: Dict) -> Dict: def handle_upsert_client_testmode_api_request(self, data: Dict) -> Dict:
return { "returnCode": "1" } return {"returnCode": "1"}
+19 -6
View File
@@ -1,19 +1,27 @@
from core.config import CoreConfig from core.config import CoreConfig
from typing import Dict from typing import Dict
class ChuniServerConfig():
class ChuniServerConfig:
def __init__(self, parent_config: "ChuniConfig") -> None: def __init__(self, parent_config: "ChuniConfig") -> None:
self.__config = parent_config self.__config = parent_config
@property @property
def enable(self) -> bool: def enable(self) -> bool:
return CoreConfig.get_config_field(self.__config, 'chuni', 'server', 'enable', default=True) return CoreConfig.get_config_field(
self.__config, "chuni", "server", "enable", default=True
)
@property @property
def loglevel(self) -> int: def loglevel(self) -> int:
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'chuni', 'server', 'loglevel', default="info")) return CoreConfig.str_to_loglevel(
CoreConfig.get_config_field(
self.__config, "chuni", "server", "loglevel", default="info"
)
)
class ChuniCryptoConfig():
class ChuniCryptoConfig:
def __init__(self, parent_config: "ChuniConfig") -> None: def __init__(self, parent_config: "ChuniConfig") -> None:
self.__config = parent_config self.__config = parent_config
@@ -24,11 +32,16 @@ class ChuniCryptoConfig():
internal_version: [key, iv] internal_version: [key, iv]
all values are hex strings all values are hex strings
""" """
return CoreConfig.get_config_field(self.__config, 'chuni', 'crypto', 'keys', default={}) return CoreConfig.get_config_field(
self.__config, "chuni", "crypto", "keys", default={}
)
@property @property
def encrypted_only(self) -> bool: def encrypted_only(self) -> bool:
return CoreConfig.get_config_field(self.__config, 'chuni', 'crypto', 'encrypted_only', default=False) return CoreConfig.get_config_field(
self.__config, "chuni", "crypto", "encrypted_only", default=False
)
class ChuniConfig(dict): class ChuniConfig(dict):
def __init__(self) -> None: def __init__(self) -> None:
+16 -3
View File
@@ -1,4 +1,4 @@
class ChuniConstants(): class ChuniConstants:
GAME_CODE = "SDBT" GAME_CODE = "SDBT"
GAME_CODE_NEW = "SDHD" GAME_CODE_NEW = "SDHD"
@@ -18,8 +18,21 @@ class ChuniConstants():
VER_CHUNITHM_NEW = 11 VER_CHUNITHM_NEW = 11
VER_CHUNITHM_NEW_PLUS = 12 VER_CHUNITHM_NEW_PLUS = 12
VERSION_NAMES = ["Chunithm", "Chunithm+", "Chunithm Air", "Chunithm Air+", "Chunithm Star", "Chunithm Star+", "Chunithm Amazon", VERSION_NAMES = [
"Chunithm Amazon+", "Chunithm Crystal", "Chunithm Crystal+", "Chunithm Paradise", "Chunithm New!!", "Chunithm New!!+"] "Chunithm",
"Chunithm+",
"Chunithm Air",
"Chunithm Air+",
"Chunithm Star",
"Chunithm Star+",
"Chunithm Amazon",
"Chunithm Amazon+",
"Chunithm Crystal",
"Chunithm Crystal+",
"Chunithm Paradise",
"Chunithm New!!",
"Chunithm New!!+",
]
@classmethod @classmethod
def game_ver_to_string(cls, ver: int): def game_ver_to_string(cls, ver: int):
+2 -1
View File
@@ -7,12 +7,13 @@ from titles.chuni.base import ChuniBase
from titles.chuni.const import ChuniConstants from titles.chuni.const import ChuniConstants
from titles.chuni.config import ChuniConfig from titles.chuni.config import ChuniConfig
class ChuniCrystal(ChuniBase): class ChuniCrystal(ChuniBase):
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None: def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
super().__init__(core_cfg, game_cfg) super().__init__(core_cfg, game_cfg)
self.version = ChuniConstants.VER_CHUNITHM_CRYSTAL self.version = ChuniConstants.VER_CHUNITHM_CRYSTAL
def handle_get_game_setting_api_request(self, data: Dict) -> Dict: def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = super().handle_get_game_setting_api_request(data) ret = super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.40.00" ret["gameSetting"]["dataVersion"] = "1.40.00"
return ret return ret
+2 -1
View File
@@ -7,12 +7,13 @@ from titles.chuni.base import ChuniBase
from titles.chuni.const import ChuniConstants from titles.chuni.const import ChuniConstants
from titles.chuni.config import ChuniConfig from titles.chuni.config import ChuniConfig
class ChuniCrystalPlus(ChuniBase): class ChuniCrystalPlus(ChuniBase):
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None: def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
super().__init__(core_cfg, game_cfg) super().__init__(core_cfg, game_cfg)
self.version = ChuniConstants.VER_CHUNITHM_CRYSTAL_PLUS self.version = ChuniConstants.VER_CHUNITHM_CRYSTAL_PLUS
def handle_get_game_setting_api_request(self, data: Dict) -> Dict: def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = super().handle_get_game_setting_api_request(data) ret = super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.45.00" ret["gameSetting"]["dataVersion"] = "1.45.00"
return ret return ret
+1
View File
@@ -2,6 +2,7 @@ from core.data import Data
from core.config import CoreConfig from core.config import CoreConfig
from titles.chuni.schema import * from titles.chuni.schema import *
class ChuniData(Data): class ChuniData(Data):
def __init__(self, cfg: CoreConfig) -> None: def __init__(self, cfg: CoreConfig) -> None:
super().__init__(cfg) super().__init__(cfg)
+53 -29
View File
@@ -28,12 +28,15 @@ from titles.chuni.paradise import ChuniParadise
from titles.chuni.new import ChuniNew from titles.chuni.new import ChuniNew
from titles.chuni.newplus import ChuniNewPlus from titles.chuni.newplus import ChuniNewPlus
class ChuniServlet():
class ChuniServlet:
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None: def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
self.core_cfg = core_cfg self.core_cfg = core_cfg
self.game_cfg = ChuniConfig() self.game_cfg = ChuniConfig()
if path.exists(f"{cfg_dir}/{ChuniConstants.CONFIG_NAME}"): if path.exists(f"{cfg_dir}/{ChuniConstants.CONFIG_NAME}"):
self.game_cfg.update(yaml.safe_load(open(f"{cfg_dir}/{ChuniConstants.CONFIG_NAME}"))) self.game_cfg.update(
yaml.safe_load(open(f"{cfg_dir}/{ChuniConstants.CONFIG_NAME}"))
)
self.versions = [ self.versions = [
ChuniBase(core_cfg, self.game_cfg), ChuniBase(core_cfg, self.game_cfg),
@@ -56,8 +59,12 @@ class ChuniServlet():
if not hasattr(self.logger, "inited"): if not hasattr(self.logger, "inited"):
log_fmt_str = "[%(asctime)s] Chunithm | %(levelname)s | %(message)s" log_fmt_str = "[%(asctime)s] Chunithm | %(levelname)s | %(message)s"
log_fmt = logging.Formatter(log_fmt_str) log_fmt = logging.Formatter(log_fmt_str)
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.core_cfg.server.log_dir, "chuni"), encoding='utf8', fileHandler = TimedRotatingFileHandler(
when="d", backupCount=10) "{0}/{1}.log".format(self.core_cfg.server.log_dir, "chuni"),
encoding="utf8",
when="d",
backupCount=10,
)
fileHandler.setFormatter(log_fmt) fileHandler.setFormatter(log_fmt)
@@ -68,20 +75,30 @@ class ChuniServlet():
self.logger.addHandler(consoleHandler) self.logger.addHandler(consoleHandler)
self.logger.setLevel(self.game_cfg.server.loglevel) self.logger.setLevel(self.game_cfg.server.loglevel)
coloredlogs.install(level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str) coloredlogs.install(
level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str
)
self.logger.inited = True self.logger.inited = True
@classmethod @classmethod
def get_allnet_info(cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str) -> Tuple[bool, str, str]: def get_allnet_info(
cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str
) -> Tuple[bool, str, str]:
game_cfg = ChuniConfig() game_cfg = ChuniConfig()
if path.exists(f"{cfg_dir}/{ChuniConstants.CONFIG_NAME}"): if path.exists(f"{cfg_dir}/{ChuniConstants.CONFIG_NAME}"):
game_cfg.update(yaml.safe_load(open(f"{cfg_dir}/{ChuniConstants.CONFIG_NAME}"))) game_cfg.update(
yaml.safe_load(open(f"{cfg_dir}/{ChuniConstants.CONFIG_NAME}"))
)
if not game_cfg.server.enable: if not game_cfg.server.enable:
return (False, "", "") return (False, "", "")
if core_cfg.server.is_develop: if core_cfg.server.is_develop:
return (True, f"http://{core_cfg.title.hostname}:{core_cfg.title.port}/{game_code}/$v/", "") return (
True,
f"http://{core_cfg.title.hostname}:{core_cfg.title.port}/{game_code}/$v/",
"",
)
return (True, f"http://{core_cfg.title.hostname}/{game_code}/$v/", "") return (True, f"http://{core_cfg.title.hostname}/{game_code}/$v/", "")
@@ -95,31 +112,31 @@ class ChuniServlet():
internal_ver = 0 internal_ver = 0
endpoint = url_split[len(url_split) - 1] endpoint = url_split[len(url_split) - 1]
if version < 105: # 1.0 if version < 105: # 1.0
internal_ver = ChuniConstants.VER_CHUNITHM internal_ver = ChuniConstants.VER_CHUNITHM
elif version >= 105 and version < 110: # Plus elif version >= 105 and version < 110: # Plus
internal_ver = ChuniConstants.VER_CHUNITHM_PLUS internal_ver = ChuniConstants.VER_CHUNITHM_PLUS
elif version >= 110 and version < 115: # Air elif version >= 110 and version < 115: # Air
internal_ver = ChuniConstants.VER_CHUNITHM_AIR internal_ver = ChuniConstants.VER_CHUNITHM_AIR
elif version >= 115 and version < 120: # Air Plus elif version >= 115 and version < 120: # Air Plus
internal_ver = ChuniConstants.VER_CHUNITHM_AIR_PLUS internal_ver = ChuniConstants.VER_CHUNITHM_AIR_PLUS
elif version >= 120 and version < 125: # Star elif version >= 120 and version < 125: # Star
internal_ver = ChuniConstants.VER_CHUNITHM_STAR internal_ver = ChuniConstants.VER_CHUNITHM_STAR
elif version >= 125 and version < 130: # Star Plus elif version >= 125 and version < 130: # Star Plus
internal_ver = ChuniConstants.VER_CHUNITHM_STAR_PLUS internal_ver = ChuniConstants.VER_CHUNITHM_STAR_PLUS
elif version >= 130 and version < 135: # Amazon elif version >= 130 and version < 135: # Amazon
internal_ver = ChuniConstants.VER_CHUNITHM_AMAZON internal_ver = ChuniConstants.VER_CHUNITHM_AMAZON
elif version >= 135 and version < 140: # Amazon Plus elif version >= 135 and version < 140: # Amazon Plus
internal_ver = ChuniConstants.VER_CHUNITHM_AMAZON_PLUS internal_ver = ChuniConstants.VER_CHUNITHM_AMAZON_PLUS
elif version >= 140 and version < 145: # Crystal elif version >= 140 and version < 145: # Crystal
internal_ver = ChuniConstants.VER_CHUNITHM_CRYSTAL internal_ver = ChuniConstants.VER_CHUNITHM_CRYSTAL
elif version >= 145 and version < 150: # Crystal Plus elif version >= 145 and version < 150: # Crystal Plus
internal_ver = ChuniConstants.VER_CHUNITHM_CRYSTAL_PLUS internal_ver = ChuniConstants.VER_CHUNITHM_CRYSTAL_PLUS
elif version >= 150 and version < 200: # Paradise elif version >= 150 and version < 200: # Paradise
internal_ver = ChuniConstants.VER_CHUNITHM_PARADISE internal_ver = ChuniConstants.VER_CHUNITHM_PARADISE
elif version >= 200 and version < 205: # New elif version >= 200 and version < 205: # New
internal_ver = ChuniConstants.VER_CHUNITHM_NEW internal_ver = ChuniConstants.VER_CHUNITHM_NEW
elif version >= 205 and version < 210: # New Plus elif version >= 205 and version < 210: # New Plus
internal_ver = ChuniConstants.VER_CHUNITHM_NEW_PLUS internal_ver = ChuniConstants.VER_CHUNITHM_NEW_PLUS
if all(c in string.hexdigits for c in endpoint) and len(endpoint) == 32: if all(c in string.hexdigits for c in endpoint) and len(endpoint) == 32:
@@ -131,31 +148,39 @@ class ChuniServlet():
crypt = AES.new( crypt = AES.new(
bytes.fromhex(self.game_cfg.crypto.keys[str(internal_ver)][0]), bytes.fromhex(self.game_cfg.crypto.keys[str(internal_ver)][0]),
AES.MODE_CBC, AES.MODE_CBC,
bytes.fromhex(self.game_cfg.crypto.keys[str(internal_ver)][1]) bytes.fromhex(self.game_cfg.crypto.keys[str(internal_ver)][1]),
) )
req_raw = crypt.decrypt(req_raw) req_raw = crypt.decrypt(req_raw)
except: except:
self.logger.error(f"Failed to decrypt v{version} request to {endpoint} -> {req_raw}") self.logger.error(
f"Failed to decrypt v{version} request to {endpoint} -> {req_raw}"
)
return zlib.compress(b'{"stat": "0"}') return zlib.compress(b'{"stat": "0"}')
encrtped = True encrtped = True
if not encrtped and self.game_cfg.crypto.encrypted_only: if not encrtped and self.game_cfg.crypto.encrypted_only:
self.logger.error(f"Unencrypted v{version} {endpoint} request, but config is set to encrypted only: {req_raw}") self.logger.error(
f"Unencrypted v{version} {endpoint} request, but config is set to encrypted only: {req_raw}"
)
return zlib.compress(b'{"stat": "0"}') return zlib.compress(b'{"stat": "0"}')
try: try:
unzip = zlib.decompress(req_raw) unzip = zlib.decompress(req_raw)
except zlib.error as e: except zlib.error as e:
self.logger.error(f"Failed to decompress v{version} {endpoint} request -> {e}") self.logger.error(
f"Failed to decompress v{version} {endpoint} request -> {e}"
)
return b"" return b""
req_data = json.loads(unzip) req_data = json.loads(unzip)
self.logger.info(f"v{version} {endpoint} request from {request.getClientAddress().host}") self.logger.info(
f"v{version} {endpoint} request from {request.getClientAddress().host}"
)
self.logger.debug(req_data) self.logger.debug(req_data)
func_to_find = "handle_" + inflection.underscore(endpoint) + "_request" func_to_find = "handle_" + inflection.underscore(endpoint) + "_request"
@@ -165,7 +190,6 @@ class ChuniServlet():
resp = {"returnCode": 1} resp = {"returnCode": 1}
else: else:
try: try:
handler = getattr(self.versions[internal_ver], func_to_find) handler = getattr(self.versions[internal_ver], func_to_find)
resp = handler(req_data) resp = handler(req_data)
@@ -175,7 +199,7 @@ class ChuniServlet():
return zlib.compress(b'{"stat": "0"}') return zlib.compress(b'{"stat": "0"}')
if resp == None: if resp == None:
resp = {'returnCode': 1} resp = {"returnCode": 1}
self.logger.debug(f"Response {resp}") self.logger.debug(f"Response {resp}")
@@ -189,7 +213,7 @@ class ChuniServlet():
crypt = AES.new( crypt = AES.new(
bytes.fromhex(self.game_cfg.crypto.keys[str(internal_ver)][0]), bytes.fromhex(self.game_cfg.crypto.keys[str(internal_ver)][0]),
AES.MODE_CBC, AES.MODE_CBC,
bytes.fromhex(self.game_cfg.crypto.keys[str(internal_ver)][1]) bytes.fromhex(self.game_cfg.crypto.keys[str(internal_ver)][1]),
) )
return crypt.encrypt(padded) return crypt.encrypt(padded)
+25 -24
View File
@@ -9,13 +9,9 @@ from titles.chuni.database import ChuniData
from titles.chuni.base import ChuniBase from titles.chuni.base import ChuniBase
from titles.chuni.config import ChuniConfig from titles.chuni.config import ChuniConfig
class ChuniNew(ChuniBase):
ITEM_TYPE = { class ChuniNew(ChuniBase):
"character": 20, ITEM_TYPE = {"character": 20, "story": 21, "card": 22}
"story": 21,
"card": 22
}
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None: def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
self.core_cfg = core_cfg self.core_cfg = core_cfg
@@ -27,10 +23,18 @@ class ChuniNew(ChuniBase):
self.version = ChuniConstants.VER_CHUNITHM_NEW self.version = ChuniConstants.VER_CHUNITHM_NEW
def handle_get_game_setting_api_request(self, data: Dict) -> Dict: def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
match_start = datetime.strftime(datetime.now() - timedelta(hours=10), self.date_time_format) match_start = datetime.strftime(
match_end = datetime.strftime(datetime.now() + timedelta(hours=10), self.date_time_format) datetime.now() - timedelta(hours=10), self.date_time_format
reboot_start = datetime.strftime(datetime.now() - timedelta(hours=11), self.date_time_format) )
reboot_end = datetime.strftime(datetime.now() - timedelta(hours=10), self.date_time_format) match_end = datetime.strftime(
datetime.now() + timedelta(hours=10), self.date_time_format
)
reboot_start = datetime.strftime(
datetime.now() - timedelta(hours=11), self.date_time_format
)
reboot_end = datetime.strftime(
datetime.now() - timedelta(hours=10), self.date_time_format
)
return { return {
"gameSetting": { "gameSetting": {
"isMaintenance": "false", "isMaintenance": "false",
@@ -52,15 +56,15 @@ class ChuniNew(ChuniBase):
"udpHolePunchUri": f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/200/ChuniServlet/", "udpHolePunchUri": f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/200/ChuniServlet/",
"reflectorUri": f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/200/ChuniServlet/", "reflectorUri": f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/200/ChuniServlet/",
}, },
"isDumpUpload": "false", "isDumpUpload": "false",
"isAou": "false", "isAou": "false",
} }
def handle_delete_token_api_request(self, data: Dict) -> Dict: def handle_delete_token_api_request(self, data: Dict) -> Dict:
return { "returnCode": "1" } return {"returnCode": "1"}
def handle_create_token_api_request(self, data: Dict) -> Dict: def handle_create_token_api_request(self, data: Dict) -> Dict:
return { "returnCode": "1" } return {"returnCode": "1"}
def handle_get_user_map_area_api_request(self, data: Dict) -> Dict: def handle_get_user_map_area_api_request(self, data: Dict) -> Dict:
user_map_areas = self.data.item.get_map_areas(data["userId"]) user_map_areas = self.data.item.get_map_areas(data["userId"])
@@ -72,21 +76,18 @@ class ChuniNew(ChuniBase):
tmp.pop("user") tmp.pop("user")
map_areas.append(tmp) map_areas.append(tmp)
return { return {"userId": data["userId"], "userMapAreaList": map_areas}
"userId": data["userId"],
"userMapAreaList": map_areas
}
def handle_get_user_symbol_chat_setting_api_request(self, data: Dict) -> Dict: def handle_get_user_symbol_chat_setting_api_request(self, data: Dict) -> Dict:
return { return {"userId": data["userId"], "symbolCharInfoList": []}
"userId": data["userId"],
"symbolCharInfoList": []
}
def handle_get_user_preview_api_request(self, data: Dict) -> Dict: def handle_get_user_preview_api_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile_preview(data["userId"], self.version) profile = self.data.profile.get_profile_preview(data["userId"], self.version)
if profile is None: return None if profile is None:
profile_character = self.data.item.get_character(data["userId"], profile["characterId"]) return None
profile_character = self.data.item.get_character(
data["userId"], profile["characterId"]
)
if profile_character is None: if profile_character is None:
chara = {} chara = {}
+13 -4
View File
@@ -7,6 +7,7 @@ from titles.chuni.new import ChuniNew
from titles.chuni.const import ChuniConstants from titles.chuni.const import ChuniConstants
from titles.chuni.config import ChuniConfig from titles.chuni.config import ChuniConfig
class ChuniNewPlus(ChuniNew): class ChuniNewPlus(ChuniNew):
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None: def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
super().__init__(core_cfg, game_cfg) super().__init__(core_cfg, game_cfg)
@@ -16,8 +17,16 @@ class ChuniNewPlus(ChuniNew):
ret = super().handle_get_game_setting_api_request(data) ret = super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["romVersion"] = "2.05.00" ret["gameSetting"]["romVersion"] = "2.05.00"
ret["gameSetting"]["dataVersion"] = "2.05.00" ret["gameSetting"]["dataVersion"] = "2.05.00"
ret["gameSetting"]["matchingUri"] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/205/ChuniServlet/" ret["gameSetting"][
ret["gameSetting"]["matchingUriX"] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/205/ChuniServlet/" "matchingUri"
ret["gameSetting"]["udpHolePunchUri"] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/205/ChuniServlet/" ] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/205/ChuniServlet/"
ret["gameSetting"]["reflectorUri"] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/205/ChuniServlet/" ret["gameSetting"][
"matchingUriX"
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/205/ChuniServlet/"
ret["gameSetting"][
"udpHolePunchUri"
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/205/ChuniServlet/"
ret["gameSetting"][
"reflectorUri"
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/205/ChuniServlet/"
return ret return ret
+2 -1
View File
@@ -7,12 +7,13 @@ from titles.chuni.base import ChuniBase
from titles.chuni.const import ChuniConstants from titles.chuni.const import ChuniConstants
from titles.chuni.config import ChuniConfig from titles.chuni.config import ChuniConfig
class ChuniParadise(ChuniBase): class ChuniParadise(ChuniBase):
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None: def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
super().__init__(core_cfg, game_cfg) super().__init__(core_cfg, game_cfg)
self.version = ChuniConstants.VER_CHUNITHM_PARADISE self.version = ChuniConstants.VER_CHUNITHM_PARADISE
def handle_get_game_setting_api_request(self, data: Dict) -> Dict: def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = super().handle_get_game_setting_api_request(data) ret = super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.50.00" ret["gameSetting"]["dataVersion"] = "1.50.00"
return ret return ret
+2 -1
View File
@@ -5,12 +5,13 @@ from titles.chuni.base import ChuniBase
from titles.chuni.const import ChuniConstants from titles.chuni.const import ChuniConstants
from titles.chuni.config import ChuniConfig from titles.chuni.config import ChuniConfig
class ChuniPlus(ChuniBase): class ChuniPlus(ChuniBase):
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None: def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
super().__init__(core_cfg, game_cfg) super().__init__(core_cfg, game_cfg)
self.version = ChuniConstants.VER_CHUNITHM_PLUS self.version = ChuniConstants.VER_CHUNITHM_PLUS
def handle_get_game_setting_api_request(self, data: Dict) -> Dict: def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = super().handle_get_game_setting_api_request(data) ret = super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.05.00" ret["gameSetting"]["dataVersion"] = "1.05.00"
return ret return ret
+84 -53
View File
@@ -7,13 +7,23 @@ from core.config import CoreConfig
from titles.chuni.database import ChuniData from titles.chuni.database import ChuniData
from titles.chuni.const import ChuniConstants from titles.chuni.const import ChuniConstants
class ChuniReader(BaseReader): class ChuniReader(BaseReader):
def __init__(self, config: CoreConfig, version: int, bin_dir: Optional[str], opt_dir: Optional[str], extra: Optional[str]) -> None: def __init__(
self,
config: CoreConfig,
version: int,
bin_dir: Optional[str],
opt_dir: Optional[str],
extra: Optional[str],
) -> None:
super().__init__(config, version, bin_dir, opt_dir, extra) super().__init__(config, version, bin_dir, opt_dir, extra)
self.data = ChuniData(config) self.data = ChuniData(config)
try: try:
self.logger.info(f"Start importer for {ChuniConstants.game_ver_to_string(version)}") self.logger.info(
f"Start importer for {ChuniConstants.game_ver_to_string(version)}"
)
except IndexError: except IndexError:
self.logger.error(f"Invalid chunithm version {version}") self.logger.error(f"Invalid chunithm version {version}")
exit(1) exit(1)
@@ -24,7 +34,7 @@ class ChuniReader(BaseReader):
data_dirs += self.get_data_directories(self.bin_dir) data_dirs += self.get_data_directories(self.bin_dir)
if self.opt_dir is not None: if self.opt_dir is not None:
data_dirs += self.get_data_directories(self.opt_dir) data_dirs += self.get_data_directories(self.opt_dir)
for dir in data_dirs: for dir in data_dirs:
self.logger.info(f"Read from {dir}") self.logger.info(f"Read from {dir}")
@@ -37,18 +47,20 @@ class ChuniReader(BaseReader):
for root, dirs, files in walk(evt_dir): for root, dirs, files in walk(evt_dir):
for dir in dirs: for dir in dirs:
if path.exists(f"{root}/{dir}/Event.xml"): if path.exists(f"{root}/{dir}/Event.xml"):
with open(f"{root}/{dir}/Event.xml", 'rb') as fp: with open(f"{root}/{dir}/Event.xml", "rb") as fp:
bytedata = fp.read() bytedata = fp.read()
strdata = bytedata.decode('UTF-8') strdata = bytedata.decode("UTF-8")
xml_root = ET.fromstring(strdata) xml_root = ET.fromstring(strdata)
for name in xml_root.findall('name'): for name in xml_root.findall("name"):
id = name.find('id').text id = name.find("id").text
name = name.find('str').text name = name.find("str").text
for substances in xml_root.findall('substances'): for substances in xml_root.findall("substances"):
event_type = substances.find('type').text event_type = substances.find("type").text
result = self.data.static.put_event(self.version, id, event_type, name) result = self.data.static.put_event(
self.version, id, event_type, name
)
if result is not None: if result is not None:
self.logger.info(f"Inserted event {id}") self.logger.info(f"Inserted event {id}")
else: else:
@@ -58,37 +70,43 @@ class ChuniReader(BaseReader):
for root, dirs, files in walk(music_dir): for root, dirs, files in walk(music_dir):
for dir in dirs: for dir in dirs:
if path.exists(f"{root}/{dir}/Music.xml"): if path.exists(f"{root}/{dir}/Music.xml"):
with open(f"{root}/{dir}/Music.xml", 'rb') as fp: with open(f"{root}/{dir}/Music.xml", "rb") as fp:
bytedata = fp.read() bytedata = fp.read()
strdata = bytedata.decode('UTF-8') strdata = bytedata.decode("UTF-8")
xml_root = ET.fromstring(strdata) xml_root = ET.fromstring(strdata)
for name in xml_root.findall('name'): for name in xml_root.findall("name"):
song_id = name.find('id').text song_id = name.find("id").text
title = name.find('str').text title = name.find("str").text
for artistName in xml_root.findall('artistName'): for artistName in xml_root.findall("artistName"):
artist = artistName.find('str').text artist = artistName.find("str").text
for genreNames in xml_root.findall('genreNames'): for genreNames in xml_root.findall("genreNames"):
for list_ in genreNames.findall('list'): for list_ in genreNames.findall("list"):
for StringID in list_.findall('StringID'): for StringID in list_.findall("StringID"):
genre = StringID.find('str').text genre = StringID.find("str").text
for jaketFile in xml_root.findall('jaketFile'): #nice typo, SEGA for jaketFile in xml_root.findall("jaketFile"): # nice typo, SEGA
jacket_path = jaketFile.find('path').text jacket_path = jaketFile.find("path").text
for fumens in xml_root.findall('fumens'): for fumens in xml_root.findall("fumens"):
for MusicFumenData in fumens.findall('MusicFumenData'): for MusicFumenData in fumens.findall("MusicFumenData"):
fumen_path = MusicFumenData.find('file').find("path") fumen_path = MusicFumenData.find("file").find("path")
if fumen_path is not None: if fumen_path is not None:
chart_id = MusicFumenData.find('type').find('id').text chart_id = MusicFumenData.find("type").find("id").text
if chart_id == "4": if chart_id == "4":
level = float(xml_root.find("starDifType").text) level = float(xml_root.find("starDifType").text)
we_chara = xml_root.find("worldsEndTagName").find("str").text we_chara = (
xml_root.find("worldsEndTagName")
.find("str")
.text
)
else: else:
level = float(f"{MusicFumenData.find('level').text}.{MusicFumenData.find('levelDecimal').text}") level = float(
f"{MusicFumenData.find('level').text}.{MusicFumenData.find('levelDecimal').text}"
)
we_chara = None we_chara = None
result = self.data.static.put_music( result = self.data.static.put_music(
@@ -100,31 +118,42 @@ class ChuniReader(BaseReader):
level, level,
genre, genre,
jacket_path, jacket_path,
we_chara we_chara,
) )
if result is not None: if result is not None:
self.logger.info(f"Inserted music {song_id} chart {chart_id}") self.logger.info(
f"Inserted music {song_id} chart {chart_id}"
)
else: else:
self.logger.warn(f"Failed to insert music {song_id} chart {chart_id}") self.logger.warn(
f"Failed to insert music {song_id} chart {chart_id}"
)
def read_charges(self, charge_dir: str) -> None: def read_charges(self, charge_dir: str) -> None:
for root, dirs, files in walk(charge_dir): for root, dirs, files in walk(charge_dir):
for dir in dirs: for dir in dirs:
if path.exists(f"{root}/{dir}/ChargeItem.xml"): if path.exists(f"{root}/{dir}/ChargeItem.xml"):
with open(f"{root}/{dir}/ChargeItem.xml", 'rb') as fp: with open(f"{root}/{dir}/ChargeItem.xml", "rb") as fp:
bytedata = fp.read() bytedata = fp.read()
strdata = bytedata.decode('UTF-8') strdata = bytedata.decode("UTF-8")
xml_root = ET.fromstring(strdata) xml_root = ET.fromstring(strdata)
for name in xml_root.findall('name'): for name in xml_root.findall("name"):
id = name.find('id').text id = name.find("id").text
name = name.find('str').text name = name.find("str").text
expirationDays = xml_root.find('expirationDays').text expirationDays = xml_root.find("expirationDays").text
consumeType = xml_root.find('consumeType').text consumeType = xml_root.find("consumeType").text
sellingAppeal = bool(xml_root.find('sellingAppeal').text) sellingAppeal = bool(xml_root.find("sellingAppeal").text)
result = self.data.static.put_charge(self.version, id, name, expirationDays, consumeType, sellingAppeal) result = self.data.static.put_charge(
self.version,
id,
name,
expirationDays,
consumeType,
sellingAppeal,
)
if result is not None: if result is not None:
self.logger.info(f"Inserted charge {id}") self.logger.info(f"Inserted charge {id}")
@@ -135,21 +164,23 @@ class ChuniReader(BaseReader):
for root, dirs, files in walk(avatar_dir): for root, dirs, files in walk(avatar_dir):
for dir in dirs: for dir in dirs:
if path.exists(f"{root}/{dir}/AvatarAccessory.xml"): if path.exists(f"{root}/{dir}/AvatarAccessory.xml"):
with open(f"{root}/{dir}/AvatarAccessory.xml", 'rb') as fp: with open(f"{root}/{dir}/AvatarAccessory.xml", "rb") as fp:
bytedata = fp.read() bytedata = fp.read()
strdata = bytedata.decode('UTF-8') strdata = bytedata.decode("UTF-8")
xml_root = ET.fromstring(strdata) xml_root = ET.fromstring(strdata)
for name in xml_root.findall('name'): for name in xml_root.findall("name"):
id = name.find('id').text id = name.find("id").text
name = name.find('str').text name = name.find("str").text
category = xml_root.find('category').text category = xml_root.find("category").text
for image in xml_root.findall('image'): for image in xml_root.findall("image"):
iconPath = image.find('path').text iconPath = image.find("path").text
for texture in xml_root.findall('texture'): for texture in xml_root.findall("texture"):
texturePath = texture.find('path').text texturePath = texture.find("path").text
result = self.data.static.put_avatar(self.version, id, name, category, iconPath, texturePath) result = self.data.static.put_avatar(
self.version, id, name, category, iconPath, texturePath
)
if result is not None: if result is not None:
self.logger.info(f"Inserted avatarAccessory {id}") self.logger.info(f"Inserted avatarAccessory {id}")
+59 -29
View File
@@ -13,7 +13,11 @@ character = Table(
"chuni_item_character", "chuni_item_character",
metadata, metadata,
Column("id", Integer, primary_key=True, nullable=False), Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("characterId", Integer), Column("characterId", Integer),
Column("level", Integer), Column("level", Integer),
Column("param1", Integer), Column("param1", Integer),
@@ -26,27 +30,35 @@ character = Table(
Column("assignIllust", Integer), Column("assignIllust", Integer),
Column("exMaxLv", Integer), Column("exMaxLv", Integer),
UniqueConstraint("user", "characterId", name="chuni_item_character_uk"), UniqueConstraint("user", "characterId", name="chuni_item_character_uk"),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
item = Table( item = Table(
"chuni_item_item", "chuni_item_item",
metadata, metadata,
Column("id", Integer, primary_key=True, nullable=False), Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("itemId", Integer), Column("itemId", Integer),
Column("itemKind", Integer), Column("itemKind", Integer),
Column("stock", Integer), Column("stock", Integer),
Column("isValid", Boolean), Column("isValid", Boolean),
UniqueConstraint("user", "itemId", "itemKind", name="chuni_item_item_uk"), UniqueConstraint("user", "itemId", "itemKind", name="chuni_item_item_uk"),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
duel = Table( duel = Table(
"chuni_item_duel", "chuni_item_duel",
metadata, metadata,
Column("id", Integer, primary_key=True, nullable=False), Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("duelId", Integer), Column("duelId", Integer),
Column("progress", Integer), Column("progress", Integer),
Column("point", Integer), Column("point", Integer),
@@ -57,14 +69,18 @@ duel = Table(
Column("param3", Integer), Column("param3", Integer),
Column("param4", Integer), Column("param4", Integer),
UniqueConstraint("user", "duelId", name="chuni_item_duel_uk"), UniqueConstraint("user", "duelId", name="chuni_item_duel_uk"),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
map = Table( map = Table(
"chuni_item_map", "chuni_item_map",
metadata, metadata,
Column("id", Integer, primary_key=True, nullable=False), Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("mapId", Integer), Column("mapId", Integer),
Column("position", Integer), Column("position", Integer),
Column("isClear", Boolean), Column("isClear", Boolean),
@@ -75,14 +91,18 @@ map = Table(
Column("statusCount", Integer), Column("statusCount", Integer),
Column("isValid", Boolean), Column("isValid", Boolean),
UniqueConstraint("user", "mapId", name="chuni_item_map_uk"), UniqueConstraint("user", "mapId", name="chuni_item_map_uk"),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
map_area = Table( map_area = Table(
"chuni_item_map_area", "chuni_item_map_area",
metadata, metadata,
Column("id", Integer, primary_key=True, nullable=False), Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("mapAreaId", Integer), Column("mapAreaId", Integer),
Column("rate", Integer), Column("rate", Integer),
Column("isClear", Boolean), Column("isClear", Boolean),
@@ -91,9 +111,10 @@ map_area = Table(
Column("statusCount", Integer), Column("statusCount", Integer),
Column("remainGridCount", Integer), Column("remainGridCount", Integer),
UniqueConstraint("user", "mapAreaId", name="chuni_item_map_area_uk"), UniqueConstraint("user", "mapAreaId", name="chuni_item_map_area_uk"),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
class ChuniItemData(BaseData): class ChuniItemData(BaseData):
def put_character(self, user_id: int, character_data: Dict) -> Optional[int]: def put_character(self, user_id: int, character_data: Dict) -> Optional[int]:
character_data["user"] = user_id character_data["user"] = user_id
@@ -104,24 +125,26 @@ class ChuniItemData(BaseData):
conflict = sql.on_duplicate_key_update(**character_data) conflict = sql.on_duplicate_key_update(**character_data)
result = self.execute(conflict) result = self.execute(conflict)
if result is None: return None if result is None:
return None
return result.lastrowid return result.lastrowid
def get_character(self, user_id: int, character_id: int) -> Optional[Dict]: def get_character(self, user_id: int, character_id: int) -> Optional[Dict]:
sql = select(character).where(and_( sql = select(character).where(
character.c.user == user_id, and_(character.c.user == user_id, character.c.characterId == character_id)
character.c.characterId == character_id )
))
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchone() return result.fetchone()
def get_characters(self, user_id: int) -> Optional[List[Row]]: def get_characters(self, user_id: int) -> Optional[List[Row]]:
sql = select(character).where(character.c.user == user_id) sql = select(character).where(character.c.user == user_id)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchall() return result.fetchall()
def put_item(self, user_id: int, item_data: Dict) -> Optional[int]: def put_item(self, user_id: int, item_data: Dict) -> Optional[int]:
@@ -133,20 +156,21 @@ class ChuniItemData(BaseData):
conflict = sql.on_duplicate_key_update(**item_data) conflict = sql.on_duplicate_key_update(**item_data)
result = self.execute(conflict) result = self.execute(conflict)
if result is None: return None if result is None:
return None
return result.lastrowid return result.lastrowid
def get_items(self, user_id: int, kind: int = None) -> Optional[List[Row]]: def get_items(self, user_id: int, kind: int = None) -> Optional[List[Row]]:
if kind is None: if kind is None:
sql = select(item).where(item.c.user == user_id) sql = select(item).where(item.c.user == user_id)
else: else:
sql = select(item).where(and_( sql = select(item).where(
item.c.user == user_id, and_(item.c.user == user_id, item.c.itemKind == kind)
item.c.itemKind == kind )
))
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchall() return result.fetchall()
def put_duel(self, user_id: int, duel_data: Dict) -> Optional[int]: def put_duel(self, user_id: int, duel_data: Dict) -> Optional[int]:
@@ -158,14 +182,16 @@ class ChuniItemData(BaseData):
conflict = sql.on_duplicate_key_update(**duel_data) conflict = sql.on_duplicate_key_update(**duel_data)
result = self.execute(conflict) result = self.execute(conflict)
if result is None: return None if result is None:
return None
return result.lastrowid return result.lastrowid
def get_duels(self, user_id: int) -> Optional[List[Row]]: def get_duels(self, user_id: int) -> Optional[List[Row]]:
sql = select(duel).where(duel.c.user == user_id) sql = select(duel).where(duel.c.user == user_id)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchall() return result.fetchall()
def put_map(self, user_id: int, map_data: Dict) -> Optional[int]: def put_map(self, user_id: int, map_data: Dict) -> Optional[int]:
@@ -177,14 +203,16 @@ class ChuniItemData(BaseData):
conflict = sql.on_duplicate_key_update(**map_data) conflict = sql.on_duplicate_key_update(**map_data)
result = self.execute(conflict) result = self.execute(conflict)
if result is None: return None if result is None:
return None
return result.lastrowid return result.lastrowid
def get_maps(self, user_id: int) -> Optional[List[Row]]: def get_maps(self, user_id: int) -> Optional[List[Row]]:
sql = select(map).where(map.c.user == user_id) sql = select(map).where(map.c.user == user_id)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchall() return result.fetchall()
def put_map_area(self, user_id: int, map_area_data: Dict) -> Optional[int]: def put_map_area(self, user_id: int, map_area_data: Dict) -> Optional[int]:
@@ -196,12 +224,14 @@ class ChuniItemData(BaseData):
conflict = sql.on_duplicate_key_update(**map_area_data) conflict = sql.on_duplicate_key_update(**map_area_data)
result = self.execute(conflict) result = self.execute(conflict)
if result is None: return None if result is None:
return None
return result.lastrowid return result.lastrowid
def get_map_areas(self, user_id: int) -> Optional[List[Row]]: def get_map_areas(self, user_id: int) -> Optional[List[Row]]:
sql = select(map_area).where(map_area.c.user == user_id) sql = select(map_area).where(map_area.c.user == user_id)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchall() return result.fetchall()
+150 -65
View File
@@ -13,7 +13,11 @@ profile = Table(
"chuni_profile_data", "chuni_profile_data",
metadata, metadata,
Column("id", Integer, primary_key=True, nullable=False), Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("version", Integer, nullable=False), Column("version", Integer, nullable=False),
Column("exp", Integer), Column("exp", Integer),
Column("level", Integer), Column("level", Integer),
@@ -62,7 +66,7 @@ profile = Table(
Column("firstTutorialCancelNum", Integer), Column("firstTutorialCancelNum", Integer),
Column("totalAdvancedHighScore", Integer), Column("totalAdvancedHighScore", Integer),
Column("masterTutorialCancelNum", Integer), Column("masterTutorialCancelNum", Integer),
Column("ext1", Integer), # Added in chunew Column("ext1", Integer), # Added in chunew
Column("ext2", Integer), Column("ext2", Integer),
Column("ext3", Integer), Column("ext3", Integer),
Column("ext4", Integer), Column("ext4", Integer),
@@ -80,7 +84,11 @@ profile = Table(
Column("compatibleCmVersion", String(25)), Column("compatibleCmVersion", String(25)),
Column("medal", Integer), Column("medal", Integer),
Column("voiceId", Integer), Column("voiceId", Integer),
Column("teamId", Integer, ForeignKey("chuni_profile_team.id", ondelete="SET NULL", onupdate="SET NULL")), Column(
"teamId",
Integer,
ForeignKey("chuni_profile_team.id", ondelete="SET NULL", onupdate="SET NULL"),
),
Column("avatarBack", Integer, server_default="0"), Column("avatarBack", Integer, server_default="0"),
Column("avatarFace", Integer, server_default="0"), Column("avatarFace", Integer, server_default="0"),
Column("eliteRankPoint", Integer, server_default="0"), Column("eliteRankPoint", Integer, server_default="0"),
@@ -121,14 +129,18 @@ profile = Table(
Column("netBattleEndState", Integer, server_default="0"), Column("netBattleEndState", Integer, server_default="0"),
Column("avatarHead", Integer, server_default="0"), Column("avatarHead", Integer, server_default="0"),
UniqueConstraint("user", "version", name="chuni_profile_profile_uk"), UniqueConstraint("user", "version", name="chuni_profile_profile_uk"),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
profile_ex = Table( profile_ex = Table(
"chuni_profile_data_ex", "chuni_profile_data_ex",
metadata, metadata,
Column("id", Integer, primary_key=True, nullable=False), Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("version", Integer, nullable=False), Column("version", Integer, nullable=False),
Column("ext1", Integer), Column("ext1", Integer),
Column("ext2", Integer), Column("ext2", Integer),
@@ -165,14 +177,18 @@ profile_ex = Table(
Column("mapIconId", Integer), Column("mapIconId", Integer),
Column("compatibleCmVersion", String(25)), Column("compatibleCmVersion", String(25)),
UniqueConstraint("user", "version", name="chuni_profile_data_ex_uk"), UniqueConstraint("user", "version", name="chuni_profile_data_ex_uk"),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
option = Table( option = Table(
"chuni_profile_option", "chuni_profile_option",
metadata, metadata,
Column("id", Integer, primary_key=True, nullable=False), Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("speed", Integer), Column("speed", Integer),
Column("bgInfo", Integer), Column("bgInfo", Integer),
Column("rating", Integer), Column("rating", Integer),
@@ -195,7 +211,7 @@ option = Table(
Column("successSkill", Integer), Column("successSkill", Integer),
Column("successSlideHold", Integer), Column("successSlideHold", Integer),
Column("successTapTimbre", Integer), Column("successTapTimbre", Integer),
Column("ext1", Integer), # Added in chunew Column("ext1", Integer), # Added in chunew
Column("ext2", Integer), Column("ext2", Integer),
Column("ext3", Integer), Column("ext3", Integer),
Column("ext4", Integer), Column("ext4", Integer),
@@ -224,14 +240,18 @@ option = Table(
Column("playTimingOffset", Integer, server_default="0"), Column("playTimingOffset", Integer, server_default="0"),
Column("fieldWallPosition_120", Integer, server_default="0"), Column("fieldWallPosition_120", Integer, server_default="0"),
UniqueConstraint("user", name="chuni_profile_option_uk"), UniqueConstraint("user", name="chuni_profile_option_uk"),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
option_ex = Table( option_ex = Table(
"chuni_profile_option_ex", "chuni_profile_option_ex",
metadata, metadata,
Column("id", Integer, primary_key=True, nullable=False), Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("ext1", Integer), Column("ext1", Integer),
Column("ext2", Integer), Column("ext2", Integer),
Column("ext3", Integer), Column("ext3", Integer),
@@ -253,51 +273,69 @@ option_ex = Table(
Column("ext19", Integer), Column("ext19", Integer),
Column("ext20", Integer), Column("ext20", Integer),
UniqueConstraint("user", name="chuni_profile_option_ex_uk"), UniqueConstraint("user", name="chuni_profile_option_ex_uk"),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
recent_rating = Table( recent_rating = Table(
"chuni_profile_recent_rating", "chuni_profile_recent_rating",
metadata, metadata,
Column("id", Integer, primary_key=True, nullable=False), Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("recentRating", JSON), Column("recentRating", JSON),
UniqueConstraint("user", name="chuni_profile_recent_rating_uk"), UniqueConstraint("user", name="chuni_profile_recent_rating_uk"),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
region = Table( region = Table(
"chuni_profile_region", "chuni_profile_region",
metadata, metadata,
Column("id", Integer, primary_key=True, nullable=False), Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("regionId", Integer), Column("regionId", Integer),
Column("playCount", Integer), Column("playCount", Integer),
UniqueConstraint("user", "regionId", name="chuni_profile_region_uk"), UniqueConstraint("user", "regionId", name="chuni_profile_region_uk"),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
activity = Table( activity = Table(
"chuni_profile_activity", "chuni_profile_activity",
metadata, metadata,
Column("id", Integer, primary_key=True, nullable=False), Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("kind", Integer), Column("kind", Integer),
Column("activityId", Integer), # Reminder: Change this to ID in base.py or the game will be sad Column(
"activityId", Integer
), # Reminder: Change this to ID in base.py or the game will be sad
Column("sortNumber", Integer), Column("sortNumber", Integer),
Column("param1", Integer), Column("param1", Integer),
Column("param2", Integer), Column("param2", Integer),
Column("param3", Integer), Column("param3", Integer),
Column("param4", Integer), Column("param4", Integer),
UniqueConstraint("user", "kind", "activityId", name="chuni_profile_activity_uk"), UniqueConstraint("user", "kind", "activityId", name="chuni_profile_activity_uk"),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
charge = Table( charge = Table(
"chuni_profile_charge", "chuni_profile_charge",
metadata, metadata,
Column("id", Integer, primary_key=True, nullable=False), Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("chargeId", Integer), Column("chargeId", Integer),
Column("stock", Integer), Column("stock", Integer),
Column("purchaseDate", String(25)), Column("purchaseDate", String(25)),
@@ -306,14 +344,18 @@ charge = Table(
Column("param2", Integer), Column("param2", Integer),
Column("paramDate", String(25)), Column("paramDate", String(25)),
UniqueConstraint("user", "chargeId", name="chuni_profile_charge_uk"), UniqueConstraint("user", "chargeId", name="chuni_profile_charge_uk"),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
emoney = Table( emoney = Table(
"chuni_profile_emoney", "chuni_profile_emoney",
metadata, metadata,
Column("id", Integer, primary_key=True, nullable=False), Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("ext1", Integer), Column("ext1", Integer),
Column("ext2", Integer), Column("ext2", Integer),
Column("ext3", Integer), Column("ext3", Integer),
@@ -321,20 +363,24 @@ emoney = Table(
Column("emoneyBrand", Integer), Column("emoneyBrand", Integer),
Column("emoneyCredit", Integer), Column("emoneyCredit", Integer),
UniqueConstraint("user", "emoneyBrand", name="chuni_profile_emoney_uk"), UniqueConstraint("user", "emoneyBrand", name="chuni_profile_emoney_uk"),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
overpower = Table( overpower = Table(
"chuni_profile_overpower", "chuni_profile_overpower",
metadata, metadata,
Column("id", Integer, primary_key=True, nullable=False), Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("genreId", Integer), Column("genreId", Integer),
Column("difficulty", Integer), Column("difficulty", Integer),
Column("rate", Integer), Column("rate", Integer),
Column("point", Integer), Column("point", Integer),
UniqueConstraint("user", "genreId", "difficulty", name="chuni_profile_emoney_uk"), UniqueConstraint("user", "genreId", "difficulty", name="chuni_profile_emoney_uk"),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
team = Table( team = Table(
@@ -343,11 +389,14 @@ team = Table(
Column("id", Integer, primary_key=True, nullable=False), Column("id", Integer, primary_key=True, nullable=False),
Column("teamName", String(255)), Column("teamName", String(255)),
Column("teamPoint", Integer), Column("teamPoint", Integer),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
class ChuniProfileData(BaseData): class ChuniProfileData(BaseData):
def put_profile_data(self, aime_id: int, version: int, profile_data: Dict) -> Optional[int]: def put_profile_data(
self, aime_id: int, version: int, profile_data: Dict
) -> Optional[int]:
profile_data["user"] = aime_id profile_data["user"] = aime_id
profile_data["version"] = version profile_data["version"] = version
if "accessCode" in profile_data: if "accessCode" in profile_data:
@@ -365,25 +414,33 @@ class ChuniProfileData(BaseData):
return result.lastrowid return result.lastrowid
def get_profile_preview(self, aime_id: int, version: int) -> Optional[Row]: def get_profile_preview(self, aime_id: int, version: int) -> Optional[Row]:
sql = select([profile, option]).join(option, profile.c.user == option.c.user).filter( sql = (
and_(profile.c.user == aime_id, profile.c.version == version) select([profile, option])
.join(option, profile.c.user == option.c.user)
.filter(and_(profile.c.user == aime_id, profile.c.version == version))
) )
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchone() return result.fetchone()
def get_profile_data(self, aime_id: int, version: int) -> Optional[Row]: def get_profile_data(self, aime_id: int, version: int) -> Optional[Row]:
sql = select(profile).where(and_( sql = select(profile).where(
profile.c.user == aime_id, and_(
profile.c.version == version, profile.c.user == aime_id,
)) profile.c.version == version,
)
)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchone() return result.fetchone()
def put_profile_data_ex(self, aime_id: int, version: int, profile_ex_data: Dict) -> Optional[int]: def put_profile_data_ex(
self, aime_id: int, version: int, profile_ex_data: Dict
) -> Optional[int]:
profile_ex_data["user"] = aime_id profile_ex_data["user"] = aime_id
profile_ex_data["version"] = version profile_ex_data["version"] = version
if "accessCode" in profile_ex_data: if "accessCode" in profile_ex_data:
@@ -394,18 +451,23 @@ class ChuniProfileData(BaseData):
result = self.execute(conflict) result = self.execute(conflict)
if result is None: if result is None:
self.logger.warn(f"put_profile_data_ex: Failed to update! aime_id: {aime_id}") self.logger.warn(
f"put_profile_data_ex: Failed to update! aime_id: {aime_id}"
)
return None return None
return result.lastrowid return result.lastrowid
def get_profile_data_ex(self, aime_id: int, version: int) -> Optional[Row]: def get_profile_data_ex(self, aime_id: int, version: int) -> Optional[Row]:
sql = select(profile_ex).where(and_( sql = select(profile_ex).where(
profile_ex.c.user == aime_id, and_(
profile_ex.c.version == version, profile_ex.c.user == aime_id,
)) profile_ex.c.version == version,
)
)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchone() return result.fetchone()
def put_profile_option(self, aime_id: int, option_data: Dict) -> Optional[int]: def put_profile_option(self, aime_id: int, option_data: Dict) -> Optional[int]:
@@ -416,7 +478,9 @@ class ChuniProfileData(BaseData):
result = self.execute(conflict) result = self.execute(conflict)
if result is None: if result is None:
self.logger.warn(f"put_profile_option: Failed to update! aime_id: {aime_id}") self.logger.warn(
f"put_profile_option: Failed to update! aime_id: {aime_id}"
)
return None return None
return result.lastrowid return result.lastrowid
@@ -424,10 +488,13 @@ class ChuniProfileData(BaseData):
sql = select(option).where(option.c.user == aime_id) sql = select(option).where(option.c.user == aime_id)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchone() return result.fetchone()
def put_profile_option_ex(self, aime_id: int, option_ex_data: Dict) -> Optional[int]: def put_profile_option_ex(
self, aime_id: int, option_ex_data: Dict
) -> Optional[int]:
option_ex_data["user"] = aime_id option_ex_data["user"] = aime_id
sql = insert(option_ex).values(**option_ex_data) sql = insert(option_ex).values(**option_ex_data)
@@ -435,7 +502,9 @@ class ChuniProfileData(BaseData):
result = self.execute(conflict) result = self.execute(conflict)
if result is None: if result is None:
self.logger.warn(f"put_profile_option_ex: Failed to update! aime_id: {aime_id}") self.logger.warn(
f"put_profile_option_ex: Failed to update! aime_id: {aime_id}"
)
return None return None
return result.lastrowid return result.lastrowid
@@ -443,19 +512,23 @@ class ChuniProfileData(BaseData):
sql = select(option_ex).where(option_ex.c.user == aime_id) sql = select(option_ex).where(option_ex.c.user == aime_id)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchone() return result.fetchone()
def put_profile_recent_rating(self, aime_id: int, recent_rating_data: List[Dict]) -> Optional[int]: def put_profile_recent_rating(
self, aime_id: int, recent_rating_data: List[Dict]
) -> Optional[int]:
sql = insert(recent_rating).values( sql = insert(recent_rating).values(
user = aime_id, user=aime_id, recentRating=recent_rating_data
recentRating = recent_rating_data
) )
conflict = sql.on_duplicate_key_update(recentRating = recent_rating_data) conflict = sql.on_duplicate_key_update(recentRating=recent_rating_data)
result = self.execute(conflict) result = self.execute(conflict)
if result is None: if result is None:
self.logger.warn(f"put_profile_recent_rating: Failed to update! aime_id: {aime_id}") self.logger.warn(
f"put_profile_recent_rating: Failed to update! aime_id: {aime_id}"
)
return None return None
return result.lastrowid return result.lastrowid
@@ -463,7 +536,8 @@ class ChuniProfileData(BaseData):
sql = select(recent_rating).where(recent_rating.c.user == aime_id) sql = select(recent_rating).where(recent_rating.c.user == aime_id)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchone() return result.fetchone()
def put_profile_activity(self, aime_id: int, activity_data: Dict) -> Optional[int]: def put_profile_activity(self, aime_id: int, activity_data: Dict) -> Optional[int]:
@@ -477,18 +551,20 @@ class ChuniProfileData(BaseData):
result = self.execute(conflict) result = self.execute(conflict)
if result is None: if result is None:
self.logger.warn(f"put_profile_activity: Failed to update! aime_id: {aime_id}") self.logger.warn(
f"put_profile_activity: Failed to update! aime_id: {aime_id}"
)
return None return None
return result.lastrowid return result.lastrowid
def get_profile_activity(self, aime_id: int, kind: int) -> Optional[List[Row]]: def get_profile_activity(self, aime_id: int, kind: int) -> Optional[List[Row]]:
sql = select(activity).where(and_( sql = select(activity).where(
activity.c.user == aime_id, and_(activity.c.user == aime_id, activity.c.kind == kind)
activity.c.kind == kind )
))
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchall() return result.fetchall()
def put_profile_charge(self, aime_id: int, charge_data: Dict) -> Optional[int]: def put_profile_charge(self, aime_id: int, charge_data: Dict) -> Optional[int]:
@@ -499,7 +575,9 @@ class ChuniProfileData(BaseData):
result = self.execute(conflict) result = self.execute(conflict)
if result is None: if result is None:
self.logger.warn(f"put_profile_charge: Failed to update! aime_id: {aime_id}") self.logger.warn(
f"put_profile_charge: Failed to update! aime_id: {aime_id}"
)
return None return None
return result.lastrowid return result.lastrowid
@@ -507,7 +585,8 @@ class ChuniProfileData(BaseData):
sql = select(charge).where(charge.c.user == aime_id) sql = select(charge).where(charge.c.user == aime_id)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchall() return result.fetchall()
def add_profile_region(self, aime_id: int, region_id: int) -> Optional[int]: def add_profile_region(self, aime_id: int, region_id: int) -> Optional[int]:
@@ -523,29 +602,35 @@ class ChuniProfileData(BaseData):
conflict = sql.on_duplicate_key_update(**emoney_data) conflict = sql.on_duplicate_key_update(**emoney_data)
result = self.execute(conflict) result = self.execute(conflict)
if result is None: return None if result is None:
return None
return result.lastrowid return result.lastrowid
def get_profile_emoney(self, aime_id: int) -> Optional[List[Row]]: def get_profile_emoney(self, aime_id: int) -> Optional[List[Row]]:
sql = select(emoney).where(emoney.c.user == aime_id) sql = select(emoney).where(emoney.c.user == aime_id)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchall() return result.fetchall()
def put_profile_overpower(self, aime_id: int, overpower_data: Dict) -> Optional[int]: def put_profile_overpower(
self, aime_id: int, overpower_data: Dict
) -> Optional[int]:
overpower_data["user"] = aime_id overpower_data["user"] = aime_id
sql = insert(overpower).values(**overpower_data) sql = insert(overpower).values(**overpower_data)
conflict = sql.on_duplicate_key_update(**overpower_data) conflict = sql.on_duplicate_key_update(**overpower_data)
result = self.execute(conflict) result = self.execute(conflict)
if result is None: return None if result is None:
return None
return result.lastrowid return result.lastrowid
def get_profile_overpower(self, aime_id: int) -> Optional[List[Row]]: def get_profile_overpower(self, aime_id: int) -> Optional[List[Row]]:
sql = select(overpower).where(overpower.c.user == aime_id) sql = select(overpower).where(overpower.c.user == aime_id)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchall() return result.fetchall()
+31 -12
View File
@@ -13,7 +13,11 @@ course = Table(
"chuni_score_course", "chuni_score_course",
metadata, metadata,
Column("id", Integer, primary_key=True, nullable=False), Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("courseId", Integer), Column("courseId", Integer),
Column("classId", Integer), Column("classId", Integer),
Column("playCount", Integer), Column("playCount", Integer),
@@ -33,14 +37,18 @@ course = Table(
Column("orderId", Integer), Column("orderId", Integer),
Column("playerRating", Integer), Column("playerRating", Integer),
UniqueConstraint("user", "courseId", name="chuni_score_course_uk"), UniqueConstraint("user", "courseId", name="chuni_score_course_uk"),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
best_score = Table( best_score = Table(
"chuni_score_best", "chuni_score_best",
metadata, metadata,
Column("id", Integer, primary_key=True, nullable=False), Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("musicId", Integer), Column("musicId", Integer),
Column("level", Integer), Column("level", Integer),
Column("playCount", Integer), Column("playCount", Integer),
@@ -60,14 +68,18 @@ best_score = Table(
Column("ext1", Integer), Column("ext1", Integer),
Column("theoryCount", Integer), Column("theoryCount", Integer),
UniqueConstraint("user", "musicId", "level", name="chuni_score_best_uk"), UniqueConstraint("user", "musicId", "level", name="chuni_score_best_uk"),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
playlog = Table( playlog = Table(
"chuni_score_playlog", "chuni_score_playlog",
metadata, metadata,
Column("id", Integer, primary_key=True, nullable=False), Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("orderId", Integer), Column("orderId", Integer),
Column("sortNumber", Integer), Column("sortNumber", Integer),
Column("placeId", Integer), Column("placeId", Integer),
@@ -122,15 +134,17 @@ playlog = Table(
Column("charaIllustId", Integer), Column("charaIllustId", Integer),
Column("romVersion", String(255)), Column("romVersion", String(255)),
Column("judgeHeaven", Integer), Column("judgeHeaven", Integer),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
class ChuniScoreData(BaseData): class ChuniScoreData(BaseData):
def get_courses(self, aime_id: int) -> Optional[Row]: def get_courses(self, aime_id: int) -> Optional[Row]:
sql = select(course).where(course.c.user == aime_id) sql = select(course).where(course.c.user == aime_id)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchall() return result.fetchall()
def put_course(self, aime_id: int, course_data: Dict) -> Optional[int]: def put_course(self, aime_id: int, course_data: Dict) -> Optional[int]:
@@ -141,14 +155,16 @@ class ChuniScoreData(BaseData):
conflict = sql.on_duplicate_key_update(**course_data) conflict = sql.on_duplicate_key_update(**course_data)
result = self.execute(conflict) result = self.execute(conflict)
if result is None: return None if result is None:
return None
return result.lastrowid return result.lastrowid
def get_scores(self, aime_id: int) -> Optional[Row]: def get_scores(self, aime_id: int) -> Optional[Row]:
sql = select(best_score).where(best_score.c.user == aime_id) sql = select(best_score).where(best_score.c.user == aime_id)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchall() return result.fetchall()
def put_score(self, aime_id: int, score_data: Dict) -> Optional[int]: def put_score(self, aime_id: int, score_data: Dict) -> Optional[int]:
@@ -159,14 +175,16 @@ class ChuniScoreData(BaseData):
conflict = sql.on_duplicate_key_update(**score_data) conflict = sql.on_duplicate_key_update(**score_data)
result = self.execute(conflict) result = self.execute(conflict)
if result is None: return None if result is None:
return None
return result.lastrowid return result.lastrowid
def get_playlogs(self, aime_id: int) -> Optional[Row]: def get_playlogs(self, aime_id: int) -> Optional[Row]:
sql = select(playlog).where(playlog.c.user == aime_id) sql = select(playlog).where(playlog.c.user == aime_id)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchall() return result.fetchall()
def put_playlog(self, aime_id: int, playlog_data: Dict) -> Optional[int]: def put_playlog(self, aime_id: int, playlog_data: Dict) -> Optional[int]:
@@ -177,5 +195,6 @@ class ChuniScoreData(BaseData):
conflict = sql.on_duplicate_key_update(**playlog_data) conflict = sql.on_duplicate_key_update(**playlog_data)
result = self.execute(conflict) result = self.execute(conflict)
if result is None: return None if result is None:
return None
return result.lastrowid return result.lastrowid
+126 -82
View File
@@ -19,7 +19,7 @@ events = Table(
Column("name", String(255)), Column("name", String(255)),
Column("enabled", Boolean, server_default="1"), Column("enabled", Boolean, server_default="1"),
UniqueConstraint("version", "eventId", name="chuni_static_events_uk"), UniqueConstraint("version", "eventId", name="chuni_static_events_uk"),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
music = Table( music = Table(
@@ -36,7 +36,7 @@ music = Table(
Column("jacketPath", String(255)), Column("jacketPath", String(255)),
Column("worldsEndTag", String(7)), Column("worldsEndTag", String(7)),
UniqueConstraint("version", "songId", "chartId", name="chuni_static_music_uk"), UniqueConstraint("version", "songId", "chartId", name="chuni_static_music_uk"),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
charge = Table( charge = Table(
@@ -51,7 +51,7 @@ charge = Table(
Column("sellingAppeal", Boolean), Column("sellingAppeal", Boolean),
Column("enabled", Boolean, server_default="1"), Column("enabled", Boolean, server_default="1"),
UniqueConstraint("version", "chargeId", name="chuni_static_charge_uk"), UniqueConstraint("version", "chargeId", name="chuni_static_charge_uk"),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
avatar = Table( avatar = Table(
@@ -65,159 +65,203 @@ avatar = Table(
Column("iconPath", String(255)), Column("iconPath", String(255)),
Column("texturePath", String(255)), Column("texturePath", String(255)),
UniqueConstraint("version", "avatarAccessoryId", name="chuni_static_avatar_uk"), UniqueConstraint("version", "avatarAccessoryId", name="chuni_static_avatar_uk"),
mysql_charset='utf8mb4' mysql_charset="utf8mb4",
) )
class ChuniStaticData(BaseData): class ChuniStaticData(BaseData):
def put_event(self, version: int, event_id: int, type: int, name: str) -> Optional[int]: def put_event(
self, version: int, event_id: int, type: int, name: str
) -> Optional[int]:
sql = insert(events).values( sql = insert(events).values(
version = version, version=version, eventId=event_id, type=type, name=name
eventId = event_id,
type = type,
name = name
) )
conflict = sql.on_duplicate_key_update( conflict = sql.on_duplicate_key_update(name=name)
name = name
)
result = self.execute(conflict) result = self.execute(conflict)
if result is None: return None if result is None:
return None
return result.lastrowid return result.lastrowid
def update_event(self, version: int, event_id: int, enabled: bool) -> Optional[bool]: def update_event(
sql = events.update(and_(events.c.version == version, events.c.eventId == event_id)).values( self, version: int, event_id: int, enabled: bool
enabled = enabled ) -> Optional[bool]:
) sql = events.update(
and_(events.c.version == version, events.c.eventId == event_id)
).values(enabled=enabled)
result = self.execute(sql) result = self.execute(sql)
if result is None: if result is None:
self.logger.warn(f"update_event: failed to update event! version: {version}, event_id: {event_id}, enabled: {enabled}") self.logger.warn(
f"update_event: failed to update event! version: {version}, event_id: {event_id}, enabled: {enabled}"
)
return None return None
event = self.get_event(version, event_id) event = self.get_event(version, event_id)
if event is None: if event is None:
self.logger.warn(f"update_event: failed to fetch event {event_id} after updating") self.logger.warn(
f"update_event: failed to fetch event {event_id} after updating"
)
return None return None
return event["enabled"] return event["enabled"]
def get_event(self, version: int, event_id: int) -> Optional[Row]: def get_event(self, version: int, event_id: int) -> Optional[Row]:
sql = select(events).where(and_(events.c.version == version, events.c.eventId == event_id)) sql = select(events).where(
and_(events.c.version == version, events.c.eventId == event_id)
)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchone() return result.fetchone()
def get_enabled_events(self, version: int) -> Optional[List[Row]]: def get_enabled_events(self, version: int) -> Optional[List[Row]]:
sql = select(events).where(and_(events.c.version == version, events.c.enabled == True)) sql = select(events).where(
and_(events.c.version == version, events.c.enabled == True)
)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchall() return result.fetchall()
def get_events(self, version: int) -> Optional[List[Row]]: def get_events(self, version: int) -> Optional[List[Row]]:
sql = select(events).where(events.c.version == version) sql = select(events).where(events.c.version == version)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchall() return result.fetchall()
def put_music(self, version: int, song_id: int, chart_id: int, title: int, artist: str, def put_music(
level: float, genre: str, jacketPath: str, we_tag: str) -> Optional[int]: self,
version: int,
song_id: int,
chart_id: int,
title: int,
artist: str,
level: float,
genre: str,
jacketPath: str,
we_tag: str,
) -> Optional[int]:
sql = insert(music).values( sql = insert(music).values(
version = version, version=version,
songId = song_id, songId=song_id,
chartId = chart_id, chartId=chart_id,
title = title, title=title,
artist = artist, artist=artist,
level = level, level=level,
genre = genre, genre=genre,
jacketPath = jacketPath, jacketPath=jacketPath,
worldsEndTag = we_tag, worldsEndTag=we_tag,
) )
conflict = sql.on_duplicate_key_update( conflict = sql.on_duplicate_key_update(
title = title, title=title,
artist = artist, artist=artist,
level = level, level=level,
genre = genre, genre=genre,
jacketPath = jacketPath, jacketPath=jacketPath,
worldsEndTag = we_tag, worldsEndTag=we_tag,
) )
result = self.execute(conflict) result = self.execute(conflict)
if result is None: return None if result is None:
return None
return result.lastrowid return result.lastrowid
def put_charge(self, version: int, charge_id: int, name: str, expiration_days: int, def put_charge(
consume_type: int, selling_appeal: bool) -> Optional[int]: self,
version: int,
charge_id: int,
name: str,
expiration_days: int,
consume_type: int,
selling_appeal: bool,
) -> Optional[int]:
sql = insert(charge).values( sql = insert(charge).values(
version = version, version=version,
chargeId = charge_id, chargeId=charge_id,
name = name, name=name,
expirationDays = expiration_days, expirationDays=expiration_days,
consumeType = consume_type, consumeType=consume_type,
sellingAppeal = selling_appeal, sellingAppeal=selling_appeal,
) )
conflict = sql.on_duplicate_key_update( conflict = sql.on_duplicate_key_update(
name = name, name=name,
expirationDays = expiration_days, expirationDays=expiration_days,
consumeType = consume_type, consumeType=consume_type,
sellingAppeal = selling_appeal, sellingAppeal=selling_appeal,
) )
result = self.execute(conflict) result = self.execute(conflict)
if result is None: return None if result is None:
return None
return result.lastrowid return result.lastrowid
def get_enabled_charges(self, version: int) -> Optional[List[Row]]: def get_enabled_charges(self, version: int) -> Optional[List[Row]]:
sql = select(charge).where(and_( sql = select(charge).where(
charge.c.version == version, and_(charge.c.version == version, charge.c.enabled == True)
charge.c.enabled == True )
))
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchall() return result.fetchall()
def get_charges(self, version: int) -> Optional[List[Row]]: def get_charges(self, version: int) -> Optional[List[Row]]:
sql = select(charge).where(charge.c.version == version) sql = select(charge).where(charge.c.version == version)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchall() return result.fetchall()
def get_music_chart(self, version: int, song_id: int, chart_id: int) -> Optional[List[Row]]: def get_music_chart(
sql = select(music).where(and_( self, version: int, song_id: int, chart_id: int
music.c.version == version, ) -> Optional[List[Row]]:
music.c.songId == song_id, sql = select(music).where(
music.c.chartId == chart_id and_(
)) music.c.version == version,
music.c.songId == song_id,
music.c.chartId == chart_id,
)
)
result = self.execute(sql) result = self.execute(sql)
if result is None: return None if result is None:
return None
return result.fetchone() return result.fetchone()
def put_avatar(self, version: int, avatarAccessoryId: int, name: str, category: int, iconPath: str, texturePath: str) -> Optional[int]: def put_avatar(
self,
version: int,
avatarAccessoryId: int,
name: str,
category: int,
iconPath: str,
texturePath: str,
) -> Optional[int]:
sql = insert(avatar).values( sql = insert(avatar).values(
version = version, version=version,
avatarAccessoryId = avatarAccessoryId, avatarAccessoryId=avatarAccessoryId,
name = name, name=name,
category = category, category=category,
iconPath = iconPath, iconPath=iconPath,
texturePath = texturePath, texturePath=texturePath,
) )
conflict = sql.on_duplicate_key_update( conflict = sql.on_duplicate_key_update(
name = name, name=name,
category = category, category=category,
iconPath = iconPath, iconPath=iconPath,
texturePath = texturePath, texturePath=texturePath,
) )
result = self.execute(conflict) result = self.execute(conflict)
if result is None: return None if result is None:
return None
return result.lastrowid return result.lastrowid
+2 -1
View File
@@ -5,12 +5,13 @@ from titles.chuni.base import ChuniBase
from titles.chuni.const import ChuniConstants from titles.chuni.const import ChuniConstants
from titles.chuni.config import ChuniConfig from titles.chuni.config import ChuniConfig
class ChuniStar(ChuniBase): class ChuniStar(ChuniBase):
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None: def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
super().__init__(core_cfg, game_cfg) super().__init__(core_cfg, game_cfg)
self.version = ChuniConstants.VER_CHUNITHM_STAR self.version = ChuniConstants.VER_CHUNITHM_STAR
def handle_get_game_setting_api_request(self, data: Dict) -> Dict: def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = super().handle_get_game_setting_api_request(data) ret = super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.20.00" ret["gameSetting"]["dataVersion"] = "1.20.00"
return ret return ret
+2 -1
View File
@@ -5,12 +5,13 @@ from titles.chuni.base import ChuniBase
from titles.chuni.const import ChuniConstants from titles.chuni.const import ChuniConstants
from titles.chuni.config import ChuniConfig from titles.chuni.config import ChuniConfig
class ChuniStarPlus(ChuniBase): class ChuniStarPlus(ChuniBase):
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None: def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
super().__init__(core_cfg, game_cfg) super().__init__(core_cfg, game_cfg)
self.version = ChuniConstants.VER_CHUNITHM_STAR_PLUS self.version = ChuniConstants.VER_CHUNITHM_STAR_PLUS
def handle_get_game_setting_api_request(self, data: Dict) -> Dict: def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = super().handle_get_game_setting_api_request(data) ret = super().handle_get_game_setting_api_request(data)
ret["gameSetting"]["dataVersion"] = "1.25.00" ret["gameSetting"]["dataVersion"] = "1.25.00"
return ret return ret
+17 -27
View File
@@ -10,12 +10,14 @@ from titles.cm.const import CardMakerConstants
from titles.cm.config import CardMakerConfig from titles.cm.config import CardMakerConfig
class CardMakerBase(): class CardMakerBase:
def __init__(self, core_cfg: CoreConfig, game_cfg: CardMakerConfig) -> None: def __init__(self, core_cfg: CoreConfig, game_cfg: CardMakerConfig) -> None:
self.core_cfg = core_cfg self.core_cfg = core_cfg
self.game_cfg = game_cfg self.game_cfg = game_cfg
self.date_time_format = "%Y-%m-%d %H:%M:%S" self.date_time_format = "%Y-%m-%d %H:%M:%S"
self.date_time_format_ext = "%Y-%m-%d %H:%M:%S.%f" # needs to be lopped off at [:-5] self.date_time_format_ext = (
"%Y-%m-%d %H:%M:%S.%f" # needs to be lopped off at [:-5]
)
self.date_time_format_short = "%Y-%m-%d" self.date_time_format_short = "%Y-%m-%d"
self.logger = logging.getLogger("cardmaker") self.logger = logging.getLogger("cardmaker")
self.game = CardMakerConstants.GAME_CODE self.game = CardMakerConstants.GAME_CODE
@@ -31,27 +33,19 @@ class CardMakerBase():
return { return {
"length": 3, "length": 3,
"gameConnectList": [ "gameConnectList": [
{ {"modelKind": 0, "type": 1, "titleUri": f"{uri}/SDHD/200/"},
"modelKind": 0, {"modelKind": 1, "type": 1, "titleUri": f"{uri}/SDEZ/120/"},
"type": 1, {"modelKind": 2, "type": 1, "titleUri": f"{uri}/SDDT/130/"},
"titleUri": f"{uri}/SDHD/200/" ],
},
{
"modelKind": 1,
"type": 1,
"titleUri": f"{uri}/SDEZ/120/"
},
{
"modelKind": 2,
"type": 1,
"titleUri": f"{uri}/SDDT/130/"
}
]
} }
def handle_get_game_setting_api_request(self, data: Dict) -> Dict: def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
reboot_start = date.strftime(datetime.now() + timedelta(hours=3), self.date_time_format) reboot_start = date.strftime(
reboot_end = date.strftime(datetime.now() + timedelta(hours=4), self.date_time_format) datetime.now() + timedelta(hours=3), self.date_time_format
)
reboot_end = date.strftime(
datetime.now() + timedelta(hours=4), self.date_time_format
)
return { return {
"gameSetting": { "gameSetting": {
@@ -67,18 +61,14 @@ class CardMakerBase():
"maxCountCard": 100, "maxCountCard": 100,
"watermark": False, "watermark": False,
"isMaintenance": False, "isMaintenance": False,
"isBackgroundDistribute": False "isBackgroundDistribute": False,
}, },
"isDumpUpload": False, "isDumpUpload": False,
"isAou": False "isAou": False,
} }
def handle_get_client_bookkeeping_api_request(self, data: Dict) -> Dict: def handle_get_client_bookkeeping_api_request(self, data: Dict) -> Dict:
return { return {"placeId": data["placeId"], "length": 0, "clientBookkeepingList": []}
"placeId": data["placeId"],
"length": 0,
"clientBookkeepingList": []
}
def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict: def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict:
return {"returnCode": 1, "apiName": "UpsertClientSettingApi"} return {"returnCode": 1, "apiName": "UpsertClientSettingApi"}
+9 -3
View File
@@ -1,17 +1,23 @@
from core.config import CoreConfig from core.config import CoreConfig
class CardMakerServerConfig(): class CardMakerServerConfig:
def __init__(self, parent_config: "CardMakerConfig") -> None: def __init__(self, parent_config: "CardMakerConfig") -> None:
self.__config = parent_config self.__config = parent_config
@property @property
def enable(self) -> bool: def enable(self) -> bool:
return CoreConfig.get_config_field(self.__config, 'cardmaker', 'server', 'enable', default=True) return CoreConfig.get_config_field(
self.__config, "cardmaker", "server", "enable", default=True
)
@property @property
def loglevel(self) -> int: def loglevel(self) -> int:
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'cardmaker', 'server', 'loglevel', default="info")) return CoreConfig.str_to_loglevel(
CoreConfig.get_config_field(
self.__config, "cardmaker", "server", "loglevel", default="info"
)
)
class CardMakerConfig(dict): class CardMakerConfig(dict):
+1 -1
View File
@@ -1,4 +1,4 @@
class CardMakerConstants(): class CardMakerConstants:
GAME_CODE = "SDED" GAME_CODE = "SDED"
CONFIG_NAME = "cardmaker.yaml" CONFIG_NAME = "cardmaker.yaml"
+29 -14
View File
@@ -18,23 +18,29 @@ from titles.cm.base import CardMakerBase
from titles.cm.cm136 import CardMaker136 from titles.cm.cm136 import CardMaker136
class CardMakerServlet(): class CardMakerServlet:
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None: def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
self.core_cfg = core_cfg self.core_cfg = core_cfg
self.game_cfg = CardMakerConfig() self.game_cfg = CardMakerConfig()
if path.exists(f"{cfg_dir}/{CardMakerConstants.CONFIG_NAME}"): if path.exists(f"{cfg_dir}/{CardMakerConstants.CONFIG_NAME}"):
self.game_cfg.update(yaml.safe_load(open(f"{cfg_dir}/{CardMakerConstants.CONFIG_NAME}"))) self.game_cfg.update(
yaml.safe_load(open(f"{cfg_dir}/{CardMakerConstants.CONFIG_NAME}"))
)
self.versions = [ self.versions = [
CardMakerBase(core_cfg, self.game_cfg), CardMakerBase(core_cfg, self.game_cfg),
CardMaker136(core_cfg, self.game_cfg) CardMaker136(core_cfg, self.game_cfg),
] ]
self.logger = logging.getLogger("cardmaker") self.logger = logging.getLogger("cardmaker")
log_fmt_str = "[%(asctime)s] Card Maker | %(levelname)s | %(message)s" log_fmt_str = "[%(asctime)s] Card Maker | %(levelname)s | %(message)s"
log_fmt = logging.Formatter(log_fmt_str) log_fmt = logging.Formatter(log_fmt_str)
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.core_cfg.server.log_dir, "cardmaker"), encoding='utf8', fileHandler = TimedRotatingFileHandler(
when="d", backupCount=10) "{0}/{1}.log".format(self.core_cfg.server.log_dir, "cardmaker"),
encoding="utf8",
when="d",
backupCount=10,
)
fileHandler.setFormatter(log_fmt) fileHandler.setFormatter(log_fmt)
@@ -45,20 +51,29 @@ class CardMakerServlet():
self.logger.addHandler(consoleHandler) self.logger.addHandler(consoleHandler)
self.logger.setLevel(self.game_cfg.server.loglevel) self.logger.setLevel(self.game_cfg.server.loglevel)
coloredlogs.install(level=self.game_cfg.server.loglevel, coloredlogs.install(
logger=self.logger, fmt=log_fmt_str) level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str
)
@classmethod @classmethod
def get_allnet_info(cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str) -> Tuple[bool, str, str]: def get_allnet_info(
cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str
) -> Tuple[bool, str, str]:
game_cfg = CardMakerConfig() game_cfg = CardMakerConfig()
if path.exists(f"{cfg_dir}/{CardMakerConstants.CONFIG_NAME}"): if path.exists(f"{cfg_dir}/{CardMakerConstants.CONFIG_NAME}"):
game_cfg.update(yaml.safe_load(open(f"{cfg_dir}/{CardMakerConstants.CONFIG_NAME}"))) game_cfg.update(
yaml.safe_load(open(f"{cfg_dir}/{CardMakerConstants.CONFIG_NAME}"))
)
if not game_cfg.server.enable: if not game_cfg.server.enable:
return (False, "", "") return (False, "", "")
if core_cfg.server.is_develop: if core_cfg.server.is_develop:
return (True, f"http://{core_cfg.title.hostname}:{core_cfg.title.port}/{game_code}/$v/", "") return (
True,
f"http://{core_cfg.title.hostname}:{core_cfg.title.port}/{game_code}/$v/",
"",
)
return (True, f"http://{core_cfg.title.hostname}/{game_code}/$v/", "") return (True, f"http://{core_cfg.title.hostname}/{game_code}/$v/", "")
@@ -86,7 +101,8 @@ class CardMakerServlet():
except zlib.error as e: except zlib.error as e:
self.logger.error( self.logger.error(
f"Failed to decompress v{version} {endpoint} request -> {e}") f"Failed to decompress v{version} {endpoint} request -> {e}"
)
return zlib.compress(b'{"stat": "0"}') return zlib.compress(b'{"stat": "0"}')
req_data = json.loads(unzip) req_data = json.loads(unzip)
@@ -104,12 +120,11 @@ class CardMakerServlet():
resp = handler(req_data) resp = handler(req_data)
except Exception as e: except Exception as e:
self.logger.error( self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
f"Error handling v{version} method {endpoint} - {e}")
return zlib.compress(b'{"stat": "0"}') return zlib.compress(b'{"stat": "0"}')
if resp is None: if resp is None:
resp = {'returnCode': 1} resp = {"returnCode": 1}
self.logger.info(f"Response {resp}") self.logger.info(f"Response {resp}")
+32 -15
View File
@@ -15,14 +15,21 @@ from titles.ongeki.config import OngekiConfig
class CardMakerReader(BaseReader): class CardMakerReader(BaseReader):
def __init__(self, config: CoreConfig, version: int, bin_dir: Optional[str], def __init__(
opt_dir: Optional[str], extra: Optional[str]) -> None: self,
config: CoreConfig,
version: int,
bin_dir: Optional[str],
opt_dir: Optional[str],
extra: Optional[str],
) -> None:
super().__init__(config, version, bin_dir, opt_dir, extra) super().__init__(config, version, bin_dir, opt_dir, extra)
self.ongeki_data = OngekiData(config) self.ongeki_data = OngekiData(config)
try: try:
self.logger.info( self.logger.info(
f"Start importer for {CardMakerConstants.game_ver_to_string(version)}") f"Start importer for {CardMakerConstants.game_ver_to_string(version)}"
)
except IndexError: except IndexError:
self.logger.error(f"Invalid Card Maker version {version}") self.logger.error(f"Invalid Card Maker version {version}")
exit(1) exit(1)
@@ -30,7 +37,7 @@ class CardMakerReader(BaseReader):
def read(self) -> None: def read(self) -> None:
static_datas = { static_datas = {
"static_gachas.csv": "read_ongeki_gacha_csv", "static_gachas.csv": "read_ongeki_gacha_csv",
"static_gacha_cards.csv": "read_ongeki_gacha_card_csv" "static_gacha_cards.csv": "read_ongeki_gacha_card_csv",
} }
data_dirs = [] data_dirs = []
@@ -41,7 +48,9 @@ class CardMakerReader(BaseReader):
read_csv = getattr(CardMakerReader, func) read_csv = getattr(CardMakerReader, func)
read_csv(self, f"{self.bin_dir}/MU3/{file}") read_csv(self, f"{self.bin_dir}/MU3/{file}")
else: else:
self.logger.warn(f"Couldn't find {file} file in {self.bin_dir}, skipping") self.logger.warn(
f"Couldn't find {file} file in {self.bin_dir}, skipping"
)
if self.opt_dir is not None: if self.opt_dir is not None:
data_dirs += self.get_data_directories(self.opt_dir) data_dirs += self.get_data_directories(self.opt_dir)
@@ -64,7 +73,7 @@ class CardMakerReader(BaseReader):
row["kind"], row["kind"],
type=row["type"], type=row["type"],
isCeiling=True if row["isCeiling"] == "1" else False, isCeiling=True if row["isCeiling"] == "1" else False,
maxSelectPoint=row["maxSelectPoint"] maxSelectPoint=row["maxSelectPoint"],
) )
self.logger.info(f"Added gacha {row['gachaId']}") self.logger.info(f"Added gacha {row['gachaId']}")
@@ -81,7 +90,7 @@ class CardMakerReader(BaseReader):
rarity=row["rarity"], rarity=row["rarity"],
weight=row["weight"], weight=row["weight"],
isPickup=True if row["isPickup"] == "1" else False, isPickup=True if row["isPickup"] == "1" else False,
isSelect=True if row["isSelect"] == "1" else False isSelect=True if row["isSelect"] == "1" else False,
) )
self.logger.info(f"Added card {row['cardId']} to gacha") self.logger.info(f"Added card {row['cardId']} to gacha")
@@ -95,7 +104,7 @@ class CardMakerReader(BaseReader):
"Pickup": "Pickup", "Pickup": "Pickup",
"RecoverFiveShotFlag": "BonusRestored", "RecoverFiveShotFlag": "BonusRestored",
"Free": "Free", "Free": "Free",
"FreeSR": "Free" "FreeSR": "Free",
} }
for root, dirs, files in os.walk(base_dir): for root, dirs, files in os.walk(base_dir):
@@ -104,13 +113,19 @@ class CardMakerReader(BaseReader):
with open(f"{root}/{dir}/Gacha.xml", "r", encoding="utf-8") as f: with open(f"{root}/{dir}/Gacha.xml", "r", encoding="utf-8") as f:
troot = ET.fromstring(f.read()) troot = ET.fromstring(f.read())
name = troot.find('Name').find('str').text name = troot.find("Name").find("str").text
gacha_id = int(troot.find('Name').find('id').text) gacha_id = int(troot.find("Name").find("id").text)
# skip already existing gachas # skip already existing gachas
if self.ongeki_data.static.get_gacha( if (
OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY, gacha_id) is not None: self.ongeki_data.static.get_gacha(
self.logger.info(f"Gacha {gacha_id} already added, skipping") OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY, gacha_id
)
is not None
):
self.logger.info(
f"Gacha {gacha_id} already added, skipping"
)
continue continue
# 1140 is the first bright memory gacha # 1140 is the first bright memory gacha
@@ -120,7 +135,8 @@ class CardMakerReader(BaseReader):
version = OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY version = OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY
gacha_kind = OngekiConstants.CM_GACHA_KINDS[ gacha_kind = OngekiConstants.CM_GACHA_KINDS[
type_to_kind[troot.find('Type').text]].value type_to_kind[troot.find("Type").text]
].value
# hardcode which gachas get "Select Gacha" with 33 points # hardcode which gachas get "Select Gacha" with 33 points
is_ceiling, max_select_point = 0, 0 is_ceiling, max_select_point = 0, 0
@@ -134,5 +150,6 @@ class CardMakerReader(BaseReader):
name, name,
gacha_kind, gacha_kind,
isCeiling=is_ceiling, isCeiling=is_ceiling,
maxSelectPoint=max_select_point) maxSelectPoint=max_select_point,
)
self.logger.info(f"Added gacha {gacha_id}") self.logger.info(f"Added gacha {gacha_id}")
+343 -222
View File
@@ -11,48 +11,53 @@ from titles.cxb.config import CxbConfig
from titles.cxb.const import CxbConstants from titles.cxb.const import CxbConstants
from titles.cxb.database import CxbData from titles.cxb.database import CxbData
class CxbBase():
class CxbBase:
def __init__(self, cfg: CoreConfig, game_cfg: CxbConfig) -> None: def __init__(self, cfg: CoreConfig, game_cfg: CxbConfig) -> None:
self.config = cfg # Config file self.config = cfg # Config file
self.game_config = game_cfg self.game_config = game_cfg
self.data = CxbData(cfg) # Database self.data = CxbData(cfg) # Database
self.game = CxbConstants.GAME_CODE self.game = CxbConstants.GAME_CODE
self.logger = logging.getLogger("cxb") self.logger = logging.getLogger("cxb")
self.version = CxbConstants.VER_CROSSBEATS_REV self.version = CxbConstants.VER_CROSSBEATS_REV
def handle_action_rpreq_request(self, data: Dict) -> Dict: def handle_action_rpreq_request(self, data: Dict) -> Dict:
return({}) return {}
def handle_action_hitreq_request(self, data: Dict) -> Dict: def handle_action_hitreq_request(self, data: Dict) -> Dict:
return({"data":[]}) return {"data": []}
def handle_auth_usercheck_request(self, data: Dict) -> Dict: def handle_auth_usercheck_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile_index(0, data["usercheck"]["authid"], self.version) profile = self.data.profile.get_profile_index(
0, data["usercheck"]["authid"], self.version
)
if profile is not None: if profile is not None:
self.logger.info(f"User {data['usercheck']['authid']} has CXB profile") self.logger.info(f"User {data['usercheck']['authid']} has CXB profile")
return({"exist": "true", "logout": "true"}) return {"exist": "true", "logout": "true"}
self.logger.info(f"No profile for aime id {data['usercheck']['authid']}") self.logger.info(f"No profile for aime id {data['usercheck']['authid']}")
return({"exist": "false", "logout": "true"}) return {"exist": "false", "logout": "true"}
def handle_auth_entry_request(self, data: Dict) -> Dict: def handle_auth_entry_request(self, data: Dict) -> Dict:
self.logger.info(f"New profile for {data['entry']['authid']}") self.logger.info(f"New profile for {data['entry']['authid']}")
return({"token": data["entry"]["authid"], "uid": data["entry"]["authid"]}) return {"token": data["entry"]["authid"], "uid": data["entry"]["authid"]}
def handle_auth_login_request(self, data: Dict) -> Dict: def handle_auth_login_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile_index(0, data["login"]["authid"], self.version) profile = self.data.profile.get_profile_index(
0, data["login"]["authid"], self.version
)
if profile is not None: if profile is not None:
self.logger.info(f"Login user {data['login']['authid']}") self.logger.info(f"Login user {data['login']['authid']}")
return({"token": data["login"]["authid"], "uid": data["login"]["authid"]}) return {"token": data["login"]["authid"], "uid": data["login"]["authid"]}
self.logger.warn(f"User {data['login']['authid']} does not have a profile") self.logger.warn(f"User {data['login']['authid']} does not have a profile")
return({}) return {}
def handle_action_loadrange_request(self, data: Dict) -> Dict: def handle_action_loadrange_request(self, data: Dict) -> Dict:
range_start = data['loadrange']['range'][0] range_start = data["loadrange"]["range"][0]
range_end = data['loadrange']['range'][1] range_end = data["loadrange"]["range"][1]
uid = data['loadrange']['uid'] uid = data["loadrange"]["uid"]
self.logger.info(f"Load data for {uid}") self.logger.info(f"Load data for {uid}")
profile = self.data.profile.get_profile(uid, self.version) profile = self.data.profile.get_profile(uid, self.version)
@@ -66,27 +71,31 @@ class CxbBase():
profile_data = profile_index["data"] profile_data = profile_index["data"]
if int(range_start) == 800000: if int(range_start) == 800000:
return({"index":range_start, "data":[], "version":10400}) return {"index": range_start, "data": [], "version": 10400}
if not ( int(range_start) <= int(profile_index[3]) <= int(range_end) ): if not (int(range_start) <= int(profile_index[3]) <= int(range_end)):
continue continue
#Prevent loading of the coupons within the profile to use the force unlock instead # Prevent loading of the coupons within the profile to use the force unlock instead
elif 500 <= int(profile_index[3]) <= 510: elif 500 <= int(profile_index[3]) <= 510:
continue continue
#Prevent loading of songs saved in the profile # Prevent loading of songs saved in the profile
elif 100000 <= int(profile_index[3]) <= 110000: elif 100000 <= int(profile_index[3]) <= 110000:
continue continue
#Prevent loading of the shop list / unlocked titles & icons saved in the profile # Prevent loading of the shop list / unlocked titles & icons saved in the profile
elif 200000 <= int(profile_index[3]) <= 210000: elif 200000 <= int(profile_index[3]) <= 210000:
continue continue
#Prevent loading of stories in the profile # Prevent loading of stories in the profile
elif 900000 <= int(profile_index[3]) <= 900200: elif 900000 <= int(profile_index[3]) <= 900200:
continue continue
else: else:
index.append(profile_index[3]) index.append(profile_index[3])
data1.append(b64encode(bytes(json.dumps(profile_data, separators=(',', ':')), 'utf-8')).decode('utf-8')) data1.append(
b64encode(
bytes(json.dumps(profile_data, separators=(",", ":")), "utf-8")
).decode("utf-8")
)
''' """
100000 = Songs 100000 = Songs
200000 = Shop 200000 = Shop
300000 = Courses 300000 = Courses
@@ -96,101 +105,140 @@ class CxbBase():
700000 = rcLog 700000 = rcLog
800000 = Partners 800000 = Partners
900000 = Stories 900000 = Stories
''' """
# Coupons # Coupons
for i in range(500,510): for i in range(500, 510):
index.append(str(i)) index.append(str(i))
couponid = int(i) - 500 couponid = int(i) - 500
dataValue = [{ dataValue = [
"couponId":str(couponid), {
"couponNum":"1", "couponId": str(couponid),
"couponLog":[], "couponNum": "1",
}] "couponLog": [],
data1.append(b64encode(bytes(json.dumps(dataValue[0], separators=(',', ':')), 'utf-8')).decode('utf-8')) }
]
data1.append(
b64encode(
bytes(json.dumps(dataValue[0], separators=(",", ":")), "utf-8")
).decode("utf-8")
)
# ShopList_Title # ShopList_Title
for i in range(200000,201451): for i in range(200000, 201451):
index.append(str(i)) index.append(str(i))
shopid = int(i) - 200000 shopid = int(i) - 200000
dataValue = [{ dataValue = [
"shopId":shopid, {
"shopState":"2", "shopId": shopid,
"isDisable":"t", "shopState": "2",
"isDeleted":"f", "isDisable": "t",
"isSpecialFlag":"f" "isDeleted": "f",
}] "isSpecialFlag": "f",
data1.append(b64encode(bytes(json.dumps(dataValue[0], separators=(',', ':')), 'utf-8')).decode('utf-8')) }
]
data1.append(
b64encode(
bytes(json.dumps(dataValue[0], separators=(",", ":")), "utf-8")
).decode("utf-8")
)
#ShopList_Icon # ShopList_Icon
for i in range(202000,202264): for i in range(202000, 202264):
index.append(str(i)) index.append(str(i))
shopid = int(i) - 200000 shopid = int(i) - 200000
dataValue = [{ dataValue = [
"shopId":shopid, {
"shopState":"2", "shopId": shopid,
"isDisable":"t", "shopState": "2",
"isDeleted":"f", "isDisable": "t",
"isSpecialFlag":"f" "isDeleted": "f",
}] "isSpecialFlag": "f",
data1.append(b64encode(bytes(json.dumps(dataValue[0], separators=(',', ':')), 'utf-8')).decode('utf-8')) }
]
data1.append(
b64encode(
bytes(json.dumps(dataValue[0], separators=(",", ":")), "utf-8")
).decode("utf-8")
)
#Stories # Stories
for i in range(900000,900003): for i in range(900000, 900003):
index.append(str(i)) index.append(str(i))
storyid = int(i) - 900000 storyid = int(i) - 900000
dataValue = [{ dataValue = [
"storyId":storyid, {
"unlockState1":["t"] * 10, "storyId": storyid,
"unlockState2":["t"] * 10, "unlockState1": ["t"] * 10,
"unlockState3":["t"] * 10, "unlockState2": ["t"] * 10,
"unlockState4":["t"] * 10, "unlockState3": ["t"] * 10,
"unlockState5":["t"] * 10, "unlockState4": ["t"] * 10,
"unlockState6":["t"] * 10, "unlockState5": ["t"] * 10,
"unlockState7":["t"] * 10, "unlockState6": ["t"] * 10,
"unlockState8":["t"] * 10, "unlockState7": ["t"] * 10,
"unlockState9":["t"] * 10, "unlockState8": ["t"] * 10,
"unlockState10":["t"] * 10, "unlockState9": ["t"] * 10,
"unlockState11":["t"] * 10, "unlockState10": ["t"] * 10,
"unlockState12":["t"] * 10, "unlockState11": ["t"] * 10,
"unlockState13":["t"] * 10, "unlockState12": ["t"] * 10,
"unlockState14":["t"] * 10, "unlockState13": ["t"] * 10,
"unlockState15":["t"] * 10, "unlockState14": ["t"] * 10,
"unlockState16":["t"] * 10 "unlockState15": ["t"] * 10,
}] "unlockState16": ["t"] * 10,
data1.append(b64encode(bytes(json.dumps(dataValue[0], separators=(',', ':')), 'utf-8')).decode('utf-8')) }
]
data1.append(
b64encode(
bytes(json.dumps(dataValue[0], separators=(",", ":")), "utf-8")
).decode("utf-8")
)
for song in songs: for song in songs:
song_data = song["data"] song_data = song["data"]
songCode = [] songCode = []
songCode.append({ songCode.append(
"mcode": song_data['mcode'], {
"musicState": song_data['musicState'], "mcode": song_data["mcode"],
"playCount": song_data['playCount'], "musicState": song_data["musicState"],
"totalScore": song_data['totalScore'], "playCount": song_data["playCount"],
"highScore": song_data['highScore'], "totalScore": song_data["totalScore"],
"everHighScore": song_data['everHighScore'] if 'everHighScore' in song_data else ["0","0","0","0","0"], "highScore": song_data["highScore"],
"clearRate": song_data['clearRate'], "everHighScore": song_data["everHighScore"]
"rankPoint": song_data['rankPoint'], if "everHighScore" in song_data
"normalCR": song_data['normalCR'] if 'normalCR' in song_data else ["0","0","0","0","0"], else ["0", "0", "0", "0", "0"],
"survivalCR": song_data['survivalCR'] if 'survivalCR' in song_data else ["0","0","0","0","0"], "clearRate": song_data["clearRate"],
"ultimateCR": song_data['ultimateCR'] if 'ultimateCR' in song_data else ["0","0","0","0","0"], "rankPoint": song_data["rankPoint"],
"nohopeCR": song_data['nohopeCR'] if 'nohopeCR' in song_data else ["0","0","0","0","0"], "normalCR": song_data["normalCR"]
"combo": song_data['combo'], if "normalCR" in song_data
"coupleUserId": song_data['coupleUserId'], else ["0", "0", "0", "0", "0"],
"difficulty": song_data['difficulty'], "survivalCR": song_data["survivalCR"]
"isFullCombo": song_data['isFullCombo'], if "survivalCR" in song_data
"clearGaugeType": song_data['clearGaugeType'], else ["0", "0", "0", "0", "0"],
"fieldType": song_data['fieldType'], "ultimateCR": song_data["ultimateCR"]
"gameType": song_data['gameType'], if "ultimateCR" in song_data
"grade": song_data['grade'], else ["0", "0", "0", "0", "0"],
"unlockState": song_data['unlockState'], "nohopeCR": song_data["nohopeCR"]
"extraState": song_data['extraState'] if "nohopeCR" in song_data
}) else ["0", "0", "0", "0", "0"],
index.append(song_data['index']) "combo": song_data["combo"],
data1.append(b64encode(bytes(json.dumps(songCode[0], separators=(',', ':')), 'utf-8')).decode('utf-8')) "coupleUserId": song_data["coupleUserId"],
"difficulty": song_data["difficulty"],
"isFullCombo": song_data["isFullCombo"],
"clearGaugeType": song_data["clearGaugeType"],
"fieldType": song_data["fieldType"],
"gameType": song_data["gameType"],
"grade": song_data["grade"],
"unlockState": song_data["unlockState"],
"extraState": song_data["extraState"],
}
)
index.append(song_data["index"])
data1.append(
b64encode(
bytes(json.dumps(songCode[0], separators=(",", ":")), "utf-8")
).decode("utf-8")
)
for v in index: for v in index:
try: try:
@@ -198,66 +246,81 @@ class CxbBase():
v_profile_data = v_profile["data"] v_profile_data = v_profile["data"]
versionindex.append(int(v_profile_data["appVersion"])) versionindex.append(int(v_profile_data["appVersion"]))
except: except:
versionindex.append('10400') versionindex.append("10400")
return({"index":index, "data":data1, "version":versionindex}) return {"index": index, "data": data1, "version": versionindex}
def handle_action_saveindex_request(self, data: Dict) -> Dict: def handle_action_saveindex_request(self, data: Dict) -> Dict:
save_data = data['saveindex'] save_data = data["saveindex"]
try: try:
#REV Omnimix Version Fetcher # REV Omnimix Version Fetcher
gameversion = data['saveindex']['data'][0][2] gameversion = data["saveindex"]["data"][0][2]
self.logger.warning(f"Game Version is {gameversion}") self.logger.warning(f"Game Version is {gameversion}")
except: except:
pass pass
if "10205" in gameversion: if "10205" in gameversion:
self.logger.info(f"Saving CrossBeats REV profile for {data['saveindex']['uid']}") self.logger.info(
#Alright.... time to bring the jank code f"Saving CrossBeats REV profile for {data['saveindex']['uid']}"
)
# Alright.... time to bring the jank code
for value in data['saveindex']['data']: for value in data["saveindex"]["data"]:
if "playedUserId" in value[1]:
if 'playedUserId' in value[1]: self.data.profile.put_profile(
self.data.profile.put_profile(data['saveindex']['uid'], self.version, value[0], value[1]) data["saveindex"]["uid"], self.version, value[0], value[1]
if 'mcode' not in value[1]: )
self.data.profile.put_profile(data['saveindex']['uid'], self.version, value[0], value[1]) if "mcode" not in value[1]:
if 'shopId' in value: self.data.profile.put_profile(
data["saveindex"]["uid"], self.version, value[0], value[1]
)
if "shopId" in value:
continue continue
if 'mcode' in value[1] and 'musicState' in value[1]: if "mcode" in value[1] and "musicState" in value[1]:
song_json = json.loads(value[1]) song_json = json.loads(value[1])
songCode = [] songCode = []
songCode.append({ songCode.append(
"mcode": song_json['mcode'], {
"musicState": song_json['musicState'], "mcode": song_json["mcode"],
"playCount": song_json['playCount'], "musicState": song_json["musicState"],
"totalScore": song_json['totalScore'], "playCount": song_json["playCount"],
"highScore": song_json['highScore'], "totalScore": song_json["totalScore"],
"clearRate": song_json['clearRate'], "highScore": song_json["highScore"],
"rankPoint": song_json['rankPoint'], "clearRate": song_json["clearRate"],
"combo": song_json['combo'], "rankPoint": song_json["rankPoint"],
"coupleUserId": song_json['coupleUserId'], "combo": song_json["combo"],
"difficulty": song_json['difficulty'], "coupleUserId": song_json["coupleUserId"],
"isFullCombo": song_json['isFullCombo'], "difficulty": song_json["difficulty"],
"clearGaugeType": song_json['clearGaugeType'], "isFullCombo": song_json["isFullCombo"],
"fieldType": song_json['fieldType'], "clearGaugeType": song_json["clearGaugeType"],
"gameType": song_json['gameType'], "fieldType": song_json["fieldType"],
"grade": song_json['grade'], "gameType": song_json["gameType"],
"unlockState": song_json['unlockState'], "grade": song_json["grade"],
"extraState": song_json['extraState'], "unlockState": song_json["unlockState"],
"index": value[0] "extraState": song_json["extraState"],
}) "index": value[0],
self.data.score.put_best_score(data['saveindex']['uid'], song_json['mcode'], self.version, value[0], songCode[0]) }
return({}) )
self.data.score.put_best_score(
data["saveindex"]["uid"],
song_json["mcode"],
self.version,
value[0],
songCode[0],
)
return {}
else: else:
self.logger.info(f"Saving CrossBeats REV Sunrise profile for {data['saveindex']['uid']}") self.logger.info(
f"Saving CrossBeats REV Sunrise profile for {data['saveindex']['uid']}"
)
#Sunrise # Sunrise
try: try:
profileIndex = save_data['index'].index('0') profileIndex = save_data["index"].index("0")
except: except:
return({"data":""}) #Maybe return {"data": ""} # Maybe
profile = json.loads(save_data["data"][profileIndex]) profile = json.loads(save_data["data"][profileIndex])
aimeId = profile["aimeId"] aimeId = profile["aimeId"]
@@ -265,65 +328,91 @@ class CxbBase():
for index, value in enumerate(data["saveindex"]["data"]): for index, value in enumerate(data["saveindex"]["data"]):
if int(data["saveindex"]["index"][index]) == 101: if int(data["saveindex"]["index"][index]) == 101:
self.data.profile.put_profile(aimeId, self.version, data["saveindex"]["index"][index], value) self.data.profile.put_profile(
if int(data["saveindex"]["index"][index]) >= 700000 and int(data["saveindex"]["index"][index])<= 701000: aimeId, self.version, data["saveindex"]["index"][index], value
self.data.profile.put_profile(aimeId, self.version, data["saveindex"]["index"][index], value) )
if int(data["saveindex"]["index"][index]) >= 500 and int(data["saveindex"]["index"][index]) <= 510: if (
self.data.profile.put_profile(aimeId, self.version, data["saveindex"]["index"][index], value) int(data["saveindex"]["index"][index]) >= 700000
if 'playedUserId' in value: and int(data["saveindex"]["index"][index]) <= 701000
self.data.profile.put_profile(aimeId, self.version, data["saveindex"]["index"][index], json.loads(value)) ):
if 'mcode' not in value and "normalCR" not in value: self.data.profile.put_profile(
self.data.profile.put_profile(aimeId, self.version, data["saveindex"]["index"][index], json.loads(value)) aimeId, self.version, data["saveindex"]["index"][index], value
if 'shopId' in value: )
if (
int(data["saveindex"]["index"][index]) >= 500
and int(data["saveindex"]["index"][index]) <= 510
):
self.data.profile.put_profile(
aimeId, self.version, data["saveindex"]["index"][index], value
)
if "playedUserId" in value:
self.data.profile.put_profile(
aimeId,
self.version,
data["saveindex"]["index"][index],
json.loads(value),
)
if "mcode" not in value and "normalCR" not in value:
self.data.profile.put_profile(
aimeId,
self.version,
data["saveindex"]["index"][index],
json.loads(value),
)
if "shopId" in value:
continue continue
# MusicList Index for the profile # MusicList Index for the profile
indexSongList = [] indexSongList = []
for value in data["saveindex"]["index"]: for value in data["saveindex"]["index"]:
if int(value) in range(100000,110000): if int(value) in range(100000, 110000):
indexSongList.append(value) indexSongList.append(value)
for index, value in enumerate(data["saveindex"]["data"]): for index, value in enumerate(data["saveindex"]["data"]):
if 'mcode' not in value: if "mcode" not in value:
continue continue
if 'playedUserId' in value: if "playedUserId" in value:
continue continue
data1 = json.loads(value) data1 = json.loads(value)
songCode = [] songCode = []
songCode.append({ songCode.append(
"mcode": data1['mcode'], {
"musicState": data1['musicState'], "mcode": data1["mcode"],
"playCount": data1['playCount'], "musicState": data1["musicState"],
"totalScore": data1['totalScore'], "playCount": data1["playCount"],
"highScore": data1['highScore'], "totalScore": data1["totalScore"],
"everHighScore": data1['everHighScore'], "highScore": data1["highScore"],
"clearRate": data1['clearRate'], "everHighScore": data1["everHighScore"],
"rankPoint": data1['rankPoint'], "clearRate": data1["clearRate"],
"normalCR": data1['normalCR'], "rankPoint": data1["rankPoint"],
"survivalCR": data1['survivalCR'], "normalCR": data1["normalCR"],
"ultimateCR": data1['ultimateCR'], "survivalCR": data1["survivalCR"],
"nohopeCR": data1['nohopeCR'], "ultimateCR": data1["ultimateCR"],
"combo": data1['combo'], "nohopeCR": data1["nohopeCR"],
"coupleUserId": data1['coupleUserId'], "combo": data1["combo"],
"difficulty": data1['difficulty'], "coupleUserId": data1["coupleUserId"],
"isFullCombo": data1['isFullCombo'], "difficulty": data1["difficulty"],
"clearGaugeType": data1['clearGaugeType'], "isFullCombo": data1["isFullCombo"],
"fieldType": data1['fieldType'], "clearGaugeType": data1["clearGaugeType"],
"gameType": data1['gameType'], "fieldType": data1["fieldType"],
"grade": data1['grade'], "gameType": data1["gameType"],
"unlockState": data1['unlockState'], "grade": data1["grade"],
"extraState": data1['extraState'], "unlockState": data1["unlockState"],
"index": indexSongList[i] "extraState": data1["extraState"],
}) "index": indexSongList[i],
}
)
self.data.score.put_best_score(aimeId, data1['mcode'], self.version, indexSongList[i], songCode[0]) self.data.score.put_best_score(
aimeId, data1["mcode"], self.version, indexSongList[i], songCode[0]
)
i += 1 i += 1
return({}) return {}
def handle_action_sprankreq_request(self, data: Dict) -> Dict: def handle_action_sprankreq_request(self, data: Dict) -> Dict:
uid = data['sprankreq']['uid'] uid = data["sprankreq"]["uid"]
self.logger.info(f"Get best rankings for {uid}") self.logger.info(f"Get best rankings for {uid}")
p = self.data.score.get_best_rankings(uid) p = self.data.score.get_best_rankings(uid)
@@ -331,55 +420,83 @@ class CxbBase():
for rank in p: for rank in p:
if rank["song_id"] is not None: if rank["song_id"] is not None:
rankList.append({ rankList.append(
"sc": [rank["score"],rank["song_id"]], {
"rid": rank["rev_id"], "sc": [rank["score"], rank["song_id"]],
"clear": rank["clear"] "rid": rank["rev_id"],
}) "clear": rank["clear"],
}
)
else: else:
rankList.append({ rankList.append(
"sc": [rank["score"]], {
"rid": rank["rev_id"], "sc": [rank["score"]],
"clear": rank["clear"] "rid": rank["rev_id"],
}) "clear": rank["clear"],
}
)
return({ return {
"uid": data["sprankreq"]["uid"], "uid": data["sprankreq"]["uid"],
"aid": data["sprankreq"]["aid"], "aid": data["sprankreq"]["aid"],
"rank": rankList, "rank": rankList,
"rankx":[1,1,1] "rankx": [1, 1, 1],
}) }
def handle_action_getadv_request(self, data: Dict) -> Dict: def handle_action_getadv_request(self, data: Dict) -> Dict:
return({"data":[{"r":"1","i":"100300","c":"20"}]}) return {"data": [{"r": "1", "i": "100300", "c": "20"}]}
def handle_action_getmsg_request(self, data: Dict) -> Dict: def handle_action_getmsg_request(self, data: Dict) -> Dict:
return({"msgs":[]}) return {"msgs": []}
def handle_auth_logout_request(self, data: Dict) -> Dict: def handle_auth_logout_request(self, data: Dict) -> Dict:
return({"auth":True}) return {"auth": True}
def handle_action_rankreg_request(self, data: Dict) -> Dict: def handle_action_rankreg_request(self, data: Dict) -> Dict:
uid = data['rankreg']['uid'] uid = data["rankreg"]["uid"]
self.logger.info(f"Put {len(data['rankreg']['data'])} rankings for {uid}") self.logger.info(f"Put {len(data['rankreg']['data'])} rankings for {uid}")
for rid in data['rankreg']['data']: for rid in data["rankreg"]["data"]:
#REV S2 # REV S2
if "clear" in rid: if "clear" in rid:
try: try:
self.data.score.put_ranking(user_id=uid, rev_id=int(rid["rid"]), song_id=int(rid["sc"][1]), score=int(rid["sc"][0]), clear=rid["clear"]) self.data.score.put_ranking(
user_id=uid,
rev_id=int(rid["rid"]),
song_id=int(rid["sc"][1]),
score=int(rid["sc"][0]),
clear=rid["clear"],
)
except: except:
self.data.score.put_ranking(user_id=uid, rev_id=int(rid["rid"]), song_id=0, score=int(rid["sc"][0]), clear=rid["clear"]) self.data.score.put_ranking(
#REV user_id=uid,
rev_id=int(rid["rid"]),
song_id=0,
score=int(rid["sc"][0]),
clear=rid["clear"],
)
# REV
else: else:
try: try:
self.data.score.put_ranking(user_id=uid, rev_id=int(rid["rid"]), song_id=int(rid["sc"][1]), score=int(rid["sc"][0]), clear=0) self.data.score.put_ranking(
user_id=uid,
rev_id=int(rid["rid"]),
song_id=int(rid["sc"][1]),
score=int(rid["sc"][0]),
clear=0,
)
except: except:
self.data.score.put_ranking(user_id=uid, rev_id=int(rid["rid"]), song_id=0, score=int(rid["sc"][0]), clear=0) self.data.score.put_ranking(
return({}) user_id=uid,
rev_id=int(rid["rid"]),
song_id=0,
score=int(rid["sc"][0]),
clear=0,
)
return {}
def handle_action_addenergy_request(self, data: Dict) -> Dict: def handle_action_addenergy_request(self, data: Dict) -> Dict:
uid = data['addenergy']['uid'] uid = data["addenergy"]["uid"]
self.logger.info(f"Add energy to user {uid}") self.logger.info(f"Add energy to user {uid}")
profile = self.data.profile.get_profile_index(0, uid, self.version) profile = self.data.profile.get_profile_index(0, uid, self.version)
data1 = profile["data"] data1 = profile["data"]
@@ -389,12 +506,12 @@ class CxbBase():
if not p: if not p:
self.data.item.put_energy(uid, 5) self.data.item.put_energy(uid, 5)
return({ return {
"class": data1["myClass"], "class": data1["myClass"],
"granted": "5", "granted": "5",
"total": "5", "total": "5",
"threshold": "1000" "threshold": "1000",
}) }
array = [] array = []
@@ -402,19 +519,23 @@ class CxbBase():
self.data.item.put_energy(uid, newenergy) self.data.item.put_energy(uid, newenergy)
if int(energy) <= 995: if int(energy) <= 995:
array.append({ array.append(
"class": data1["myClass"], {
"granted": "5", "class": data1["myClass"],
"total": str(energy), "granted": "5",
"threshold": "1000" "total": str(energy),
}) "threshold": "1000",
}
)
else: else:
array.append({ array.append(
"class": data1["myClass"], {
"granted": "0", "class": data1["myClass"],
"total": str(energy), "granted": "0",
"threshold": "1000" "total": str(energy),
}) "threshold": "1000",
}
)
return array[0] return array[0]
def handle_action_eventreq_request(self, data: Dict) -> Dict: def handle_action_eventreq_request(self, data: Dict) -> Dict:
+29 -9
View File
@@ -1,40 +1,60 @@
from core.config import CoreConfig from core.config import CoreConfig
class CxbServerConfig():
class CxbServerConfig:
def __init__(self, parent_config: "CxbConfig"): def __init__(self, parent_config: "CxbConfig"):
self.__config = parent_config self.__config = parent_config
@property @property
def enable(self) -> bool: def enable(self) -> bool:
return CoreConfig.get_config_field(self.__config, 'cxb', 'server', 'enable', default=True) return CoreConfig.get_config_field(
self.__config, "cxb", "server", "enable", default=True
)
@property @property
def loglevel(self) -> int: def loglevel(self) -> int:
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'cxb', 'server', 'loglevel', default="info")) return CoreConfig.str_to_loglevel(
CoreConfig.get_config_field(
self.__config, "cxb", "server", "loglevel", default="info"
)
)
@property @property
def hostname(self) -> str: def hostname(self) -> str:
return CoreConfig.get_config_field(self.__config, 'cxb', 'server', 'hostname', default="localhost") return CoreConfig.get_config_field(
self.__config, "cxb", "server", "hostname", default="localhost"
)
@property @property
def ssl_enable(self) -> bool: def ssl_enable(self) -> bool:
return CoreConfig.get_config_field(self.__config, 'cxb', 'server', 'ssl_enable', default=False) return CoreConfig.get_config_field(
self.__config, "cxb", "server", "ssl_enable", default=False
)
@property @property
def port(self) -> int: def port(self) -> int:
return CoreConfig.get_config_field(self.__config, 'cxb', 'server', 'port', default=8082) return CoreConfig.get_config_field(
self.__config, "cxb", "server", "port", default=8082
)
@property @property
def port_secure(self) -> int: def port_secure(self) -> int:
return CoreConfig.get_config_field(self.__config, 'cxb', 'server', 'port_secure', default=443) return CoreConfig.get_config_field(
self.__config, "cxb", "server", "port_secure", default=443
)
@property @property
def ssl_cert(self) -> str: def ssl_cert(self) -> str:
return CoreConfig.get_config_field(self.__config, 'cxb', 'server', 'ssl_cert', default="cert/title.crt") return CoreConfig.get_config_field(
self.__config, "cxb", "server", "ssl_cert", default="cert/title.crt"
)
@property @property
def ssl_key(self) -> str: def ssl_key(self) -> str:
return CoreConfig.get_config_field(self.__config, 'cxb', 'server', 'ssl_key', default="cert/title.key") return CoreConfig.get_config_field(
self.__config, "cxb", "server", "ssl_key", default="cert/title.key"
)
class CxbConfig(dict): class CxbConfig(dict):
def __init__(self) -> None: def __init__(self) -> None:
+7 -2
View File
@@ -1,4 +1,4 @@
class CxbConstants(): class CxbConstants:
GAME_CODE = "SDCA" GAME_CODE = "SDCA"
CONFIG_NAME = "cxb.yaml" CONFIG_NAME = "cxb.yaml"
@@ -8,7 +8,12 @@ class CxbConstants():
VER_CROSSBEATS_REV_SUNRISE_S2 = 2 VER_CROSSBEATS_REV_SUNRISE_S2 = 2
VER_CROSSBEATS_REV_SUNRISE_S2_OMNI = 3 VER_CROSSBEATS_REV_SUNRISE_S2_OMNI = 3
VERSION_NAMES = ("crossbeats REV.", "crossbeats REV. SUNRISE", "crossbeats REV. SUNRISE S2", "crossbeats REV. SUNRISE S2 Omnimix") VERSION_NAMES = (
"crossbeats REV.",
"crossbeats REV. SUNRISE",
"crossbeats REV. SUNRISE S2",
"crossbeats REV. SUNRISE S2 Omnimix",
)
@classmethod @classmethod
def game_ver_to_string(cls, ver: int): def game_ver_to_string(cls, ver: int):
+1 -1
View File
@@ -1,8 +1,8 @@
from core.data import Data from core.data import Data
from core.config import CoreConfig from core.config import CoreConfig
from titles.cxb.schema import CxbProfileData, CxbScoreData, CxbItemData, CxbStaticData from titles.cxb.schema import CxbProfileData, CxbScoreData, CxbItemData, CxbStaticData
class CxbData(Data): class CxbData(Data):
def __init__(self, cfg: CoreConfig) -> None: def __init__(self, cfg: CoreConfig) -> None:
super().__init__(cfg) super().__init__(cfg)
+53 -20
View File
@@ -17,6 +17,7 @@ from titles.cxb.rev import CxbRev
from titles.cxb.rss1 import CxbRevSunriseS1 from titles.cxb.rss1 import CxbRevSunriseS1
from titles.cxb.rss2 import CxbRevSunriseS2 from titles.cxb.rss2 import CxbRevSunriseS2
class CxbServlet(resource.Resource): class CxbServlet(resource.Resource):
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None: def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
self.isLeaf = True self.isLeaf = True
@@ -24,14 +25,20 @@ class CxbServlet(resource.Resource):
self.core_cfg = core_cfg self.core_cfg = core_cfg
self.game_cfg = CxbConfig() self.game_cfg = CxbConfig()
if path.exists(f"{cfg_dir}/{CxbConstants.CONFIG_NAME}"): if path.exists(f"{cfg_dir}/{CxbConstants.CONFIG_NAME}"):
self.game_cfg.update(yaml.safe_load(open(f"{cfg_dir}/{CxbConstants.CONFIG_NAME}"))) self.game_cfg.update(
yaml.safe_load(open(f"{cfg_dir}/{CxbConstants.CONFIG_NAME}"))
)
self.logger = logging.getLogger("cxb") self.logger = logging.getLogger("cxb")
if not hasattr(self.logger, "inited"): if not hasattr(self.logger, "inited"):
log_fmt_str = "[%(asctime)s] CXB | %(levelname)s | %(message)s" log_fmt_str = "[%(asctime)s] CXB | %(levelname)s | %(message)s"
log_fmt = logging.Formatter(log_fmt_str) log_fmt = logging.Formatter(log_fmt_str)
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.core_cfg.server.log_dir, "cxb"), encoding='utf8', fileHandler = TimedRotatingFileHandler(
when="d", backupCount=10) "{0}/{1}.log".format(self.core_cfg.server.log_dir, "cxb"),
encoding="utf8",
when="d",
backupCount=10,
)
fileHandler.setFormatter(log_fmt) fileHandler.setFormatter(log_fmt)
@@ -42,7 +49,9 @@ class CxbServlet(resource.Resource):
self.logger.addHandler(consoleHandler) self.logger.addHandler(consoleHandler)
self.logger.setLevel(self.game_cfg.server.loglevel) self.logger.setLevel(self.game_cfg.server.loglevel)
coloredlogs.install(level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str) coloredlogs.install(
level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str
)
self.logger.inited = True self.logger.inited = True
self.versions = [ self.versions = [
@@ -52,34 +61,49 @@ class CxbServlet(resource.Resource):
] ]
@classmethod @classmethod
def get_allnet_info(cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str) -> Tuple[bool, str, str]: def get_allnet_info(
cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str
) -> Tuple[bool, str, str]:
game_cfg = CxbConfig() game_cfg = CxbConfig()
if path.exists(f"{cfg_dir}/{CxbConstants.CONFIG_NAME}"): if path.exists(f"{cfg_dir}/{CxbConstants.CONFIG_NAME}"):
game_cfg.update(yaml.safe_load(open(f"{cfg_dir}/{CxbConstants.CONFIG_NAME}"))) game_cfg.update(
yaml.safe_load(open(f"{cfg_dir}/{CxbConstants.CONFIG_NAME}"))
)
if not game_cfg.server.enable: if not game_cfg.server.enable:
return (False, "", "") return (False, "", "")
if core_cfg.server.is_develop: if core_cfg.server.is_develop:
return (True, f"http://{core_cfg.title.hostname}:{core_cfg.title.port}/{game_code}/$v/", "") return (
True,
f"http://{core_cfg.title.hostname}:{core_cfg.title.port}/{game_code}/$v/",
"",
)
return (True, f"http://{core_cfg.title.hostname}/{game_code}/$v/", "") return (True, f"http://{core_cfg.title.hostname}/{game_code}/$v/", "")
def setup(self): def setup(self):
if self.game_cfg.server.enable: if self.game_cfg.server.enable:
endpoints.serverFromString(reactor, f"tcp:{self.game_cfg.server.port}:interface={self.core_cfg.server.listen_address}")\ endpoints.serverFromString(
.listen(server.Site(CxbServlet(self.core_cfg, self.cfg_dir))) reactor,
f"tcp:{self.game_cfg.server.port}:interface={self.core_cfg.server.listen_address}",
).listen(server.Site(CxbServlet(self.core_cfg, self.cfg_dir)))
if self.core_cfg.server.is_develop and self.game_cfg.server.ssl_enable: if self.core_cfg.server.is_develop and self.game_cfg.server.ssl_enable:
endpoints.serverFromString(reactor, f"ssl:{self.game_cfg.server.port_secure}"\ endpoints.serverFromString(
f":interface={self.core_cfg.server.listen_address}:privateKey={self.game_cfg.server.ssl_key}:"\ reactor,
f"certKey={self.game_cfg.server.ssl_cert}")\ f"ssl:{self.game_cfg.server.port_secure}"
.listen(server.Site(CxbServlet(self.core_cfg, self.cfg_dir))) f":interface={self.core_cfg.server.listen_address}:privateKey={self.game_cfg.server.ssl_key}:"
f"certKey={self.game_cfg.server.ssl_cert}",
).listen(server.Site(CxbServlet(self.core_cfg, self.cfg_dir)))
self.logger.info(f"Crossbeats title server ready on port {self.game_cfg.server.port} & {self.game_cfg.server.port_secure}") self.logger.info(
f"Crossbeats title server ready on port {self.game_cfg.server.port} & {self.game_cfg.server.port_secure}"
)
else: else:
self.logger.info(f"Crossbeats title server ready on port {self.game_cfg.server.port}") self.logger.info(
f"Crossbeats title server ready on port {self.game_cfg.server.port}"
)
def render_POST(self, request: Request): def render_POST(self, request: Request):
version = 0 version = 0
@@ -96,10 +120,14 @@ class CxbServlet(resource.Resource):
except Exception as e: except Exception as e:
try: try:
req_json: Dict = json.loads(req_bytes.decode().replace('"', '\\"').replace("'", '"')) req_json: Dict = json.loads(
req_bytes.decode().replace('"', '\\"').replace("'", '"')
)
except Exception as f: except Exception as f:
self.logger.warn(f"Error decoding json: {e} / {f} - {req_url} - {req_bytes}") self.logger.warn(
f"Error decoding json: {e} / {f} - {req_url} - {req_bytes}"
)
return b"" return b""
if req_json == {}: if req_json == {}:
@@ -110,7 +138,10 @@ class CxbServlet(resource.Resource):
subcmd = list(req_json.keys())[0] subcmd = list(req_json.keys())[0]
if subcmd == "dldate": if subcmd == "dldate":
if not type(req_json["dldate"]) is dict or "filetype" not in req_json["dldate"]: if (
not type(req_json["dldate"]) is dict
or "filetype" not in req_json["dldate"]
):
self.logger.warn(f"Malformed dldate request: {req_url} {req_json}") self.logger.warn(f"Malformed dldate request: {req_url} {req_json}")
return b"" return b""
@@ -119,7 +150,9 @@ class CxbServlet(resource.Resource):
version = int(filetype_split[0]) version = int(filetype_split[0])
filetype_inflect_split = inflection.underscore(filetype).split("/") filetype_inflect_split = inflection.underscore(filetype).split("/")
match = re.match("^([A-Za-z]*)(\d\d\d\d)$", filetype_split[len(filetype_split) - 1]) match = re.match(
"^([A-Za-z]*)(\d\d\d\d)$", filetype_split[len(filetype_split) - 1]
)
if match: if match:
subcmd = f"{inflection.underscore(match.group(1))}xxxx" subcmd = f"{inflection.underscore(match.group(1))}xxxx"
else: else:
+72 -7
View File
@@ -8,13 +8,23 @@ from core.config import CoreConfig
from titles.cxb.database import CxbData from titles.cxb.database import CxbData
from titles.cxb.const import CxbConstants from titles.cxb.const import CxbConstants
class CxbReader(BaseReader): class CxbReader(BaseReader):
def __init__(self, config: CoreConfig, version: int, bin_arg: Optional[str], opt_arg: Optional[str], extra: Optional[str]) -> None: def __init__(
self,
config: CoreConfig,
version: int,
bin_arg: Optional[str],
opt_arg: Optional[str],
extra: Optional[str],
) -> None:
super().__init__(config, version, bin_arg, opt_arg, extra) super().__init__(config, version, bin_arg, opt_arg, extra)
self.data = CxbData(config) self.data = CxbData(config)
try: try:
self.logger.info(f"Start importer for {CxbConstants.game_ver_to_string(version)}") self.logger.info(
f"Start importer for {CxbConstants.game_ver_to_string(version)}"
)
except IndexError: except IndexError:
self.logger.error(f"Invalid project cxb version {version}") self.logger.error(f"Invalid project cxb version {version}")
exit(1) exit(1)
@@ -45,18 +55,73 @@ class CxbReader(BaseReader):
if not "N/A" in row["standard"]: if not "N/A" in row["standard"]:
self.logger.info(f"Added song {song_id} chart 0") self.logger.info(f"Added song {song_id} chart 0")
self.data.static.put_music(self.version, song_id, index, 0, title, artist, genre, int(row["standard"].replace("Standard ","").replace("N/A","0"))) self.data.static.put_music(
self.version,
song_id,
index,
0,
title,
artist,
genre,
int(
row["standard"]
.replace("Standard ", "")
.replace("N/A", "0")
),
)
if not "N/A" in row["hard"]: if not "N/A" in row["hard"]:
self.logger.info(f"Added song {song_id} chart 1") self.logger.info(f"Added song {song_id} chart 1")
self.data.static.put_music(self.version, song_id, index, 1, title, artist, genre, int(row["hard"].replace("Hard ","").replace("N/A","0"))) self.data.static.put_music(
self.version,
song_id,
index,
1,
title,
artist,
genre,
int(row["hard"].replace("Hard ", "").replace("N/A", "0")),
)
if not "N/A" in row["master"]: if not "N/A" in row["master"]:
self.logger.info(f"Added song {song_id} chart 2") self.logger.info(f"Added song {song_id} chart 2")
self.data.static.put_music(self.version, song_id, index, 2, title, artist, genre, int(row["master"].replace("Master ","").replace("N/A","0"))) self.data.static.put_music(
self.version,
song_id,
index,
2,
title,
artist,
genre,
int(
row["master"].replace("Master ", "").replace("N/A", "0")
),
)
if not "N/A" in row["unlimited"]: if not "N/A" in row["unlimited"]:
self.logger.info(f"Added song {song_id} chart 3") self.logger.info(f"Added song {song_id} chart 3")
self.data.static.put_music(self.version, song_id, index, 3, title, artist, genre, int(row["unlimited"].replace("Unlimited ","").replace("N/A","0"))) self.data.static.put_music(
self.version,
song_id,
index,
3,
title,
artist,
genre,
int(
row["unlimited"]
.replace("Unlimited ", "")
.replace("N/A", "0")
),
)
if not "N/A" in row["easy"]: if not "N/A" in row["easy"]:
self.logger.info(f"Added song {song_id} chart 4") self.logger.info(f"Added song {song_id} chart 4")
self.data.static.put_music(self.version, song_id, index, 4, title, artist, genre, int(row["easy"].replace("Easy ","").replace("N/A","0"))) self.data.static.put_music(
self.version,
song_id,
index,
4,
title,
artist,
genre,
int(row["easy"].replace("Easy ", "").replace("N/A", "0")),
)
except: except:
self.logger.warn(f"Couldn't read csv file in {self.bin_dir}, skipping") self.logger.warn(f"Couldn't read csv file in {self.bin_dir}, skipping")

Some files were not shown because too many files have changed in this diff Show More