forked from Hay1tsme/artemis
begin move
This commit is contained in:
parent
b056ff218d
commit
14fa0f5e8e
@ -1,6 +1,6 @@
|
|||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
from core.allnet import AllnetServlet
|
from core.allnet import AllnetServlet, BillingServlet
|
||||||
from core.aimedb import AimedbFactory
|
from core.aimedb import AimedbServlette
|
||||||
from core.title import TitleServlet
|
from core.title import TitleServlet
|
||||||
from core.utils import Utils
|
from core.utils import Utils
|
||||||
from core.mucha import MuchaServlet
|
from core.mucha import MuchaServlet
|
||||||
|
@ -102,7 +102,7 @@ class ADBHeader:
|
|||||||
magic, protocol_ver, cmd, length, status, game_id, store_id, keychip_id = struct.unpack_from("<5H6sI12s", data)
|
magic, protocol_ver, cmd, length, status, game_id, store_id, keychip_id = struct.unpack_from("<5H6sI12s", data)
|
||||||
head = cls(magic, protocol_ver, cmd, length, status, game_id, store_id, keychip_id)
|
head = cls(magic, protocol_ver, cmd, length, status, game_id, store_id, keychip_id)
|
||||||
|
|
||||||
if head.length != len(data):
|
if head.length > len(data):
|
||||||
raise ADBHeaderException(f"Length is incorrect! Expect {head.length}, got {len(data)}")
|
raise ADBHeaderException(f"Length is incorrect! Expect {head.length}, got {len(data)}")
|
||||||
|
|
||||||
return head
|
return head
|
||||||
|
178
core/aimedb.py
178
core/aimedb.py
@ -2,8 +2,8 @@ from twisted.internet.protocol import Factory, Protocol
|
|||||||
import logging, coloredlogs
|
import logging, coloredlogs
|
||||||
from Crypto.Cipher import AES
|
from Crypto.Cipher import AES
|
||||||
import struct
|
import struct
|
||||||
from typing import Dict, Tuple, Callable, Union
|
from typing import Dict, Tuple, Callable, Union, Optional
|
||||||
from typing_extensions import Final
|
import asyncio
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
|
|
||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
@ -11,15 +11,37 @@ from core.utils import create_sega_auth_key
|
|||||||
from core.data import Data
|
from core.data import Data
|
||||||
from .adb_handlers import *
|
from .adb_handlers import *
|
||||||
|
|
||||||
|
class AimedbServlette():
|
||||||
class AimedbProtocol(Protocol):
|
|
||||||
request_list: Dict[int, Tuple[Callable[[bytes, int], Union[ADBBaseResponse, bytes]], int, str]] = {}
|
request_list: Dict[int, Tuple[Callable[[bytes, int], Union[ADBBaseResponse, bytes]], int, str]] = {}
|
||||||
|
def __init__(self, core_cfg: CoreConfig) -> None:
|
||||||
def __init__(self, core_cfg: CoreConfig) -> None:
|
self.config = core_cfg
|
||||||
self.logger = logging.getLogger("aimedb")
|
|
||||||
self.config = core_cfg
|
|
||||||
self.data = Data(core_cfg)
|
self.data = Data(core_cfg)
|
||||||
if core_cfg.aimedb.key == "":
|
|
||||||
|
self.logger = logging.getLogger("aimedb")
|
||||||
|
if not hasattr(self.logger, "initted"):
|
||||||
|
log_fmt_str = "[%(asctime)s] Aimedb | %(levelname)s | %(message)s"
|
||||||
|
log_fmt = logging.Formatter(log_fmt_str)
|
||||||
|
|
||||||
|
fileHandler = TimedRotatingFileHandler(
|
||||||
|
"{0}/{1}.log".format(self.config.server.log_dir, "aimedb"),
|
||||||
|
when="d",
|
||||||
|
backupCount=10,
|
||||||
|
)
|
||||||
|
fileHandler.setFormatter(log_fmt)
|
||||||
|
|
||||||
|
consoleHandler = logging.StreamHandler()
|
||||||
|
consoleHandler.setFormatter(log_fmt)
|
||||||
|
|
||||||
|
self.logger.addHandler(fileHandler)
|
||||||
|
self.logger.addHandler(consoleHandler)
|
||||||
|
|
||||||
|
self.logger.setLevel(self.config.aimedb.loglevel)
|
||||||
|
coloredlogs.install(
|
||||||
|
level=core_cfg.aimedb.loglevel, logger=self.logger, fmt=log_fmt_str
|
||||||
|
)
|
||||||
|
self.logger.initted = True
|
||||||
|
|
||||||
|
if not core_cfg.aimedb.key:
|
||||||
self.logger.error("!!!KEY NOT SET!!!")
|
self.logger.error("!!!KEY NOT SET!!!")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
@ -40,27 +62,30 @@ class AimedbProtocol(Protocol):
|
|||||||
|
|
||||||
self.register_handler(0x13, 0x14, self.handle_log_ex, 'aime_log_ex')
|
self.register_handler(0x13, 0x14, self.handle_log_ex, 'aime_log_ex')
|
||||||
self.register_handler(0x64, 0x65, self.handle_hello, 'hello')
|
self.register_handler(0x64, 0x65, self.handle_hello, 'hello')
|
||||||
self.register_handler(0x66, 0, self.handle_goodbye, 'goodbye')
|
|
||||||
|
|
||||||
def register_handler(self, cmd: int, resp:int, handler: Callable[[bytes, int], Union[ADBBaseResponse, bytes]], name: str) -> None:
|
def register_handler(self, cmd: int, resp:int, handler: Callable[[bytes, int], Union[ADBBaseResponse, bytes]], name: str) -> None:
|
||||||
self.request_list[cmd] = (handler, resp, name)
|
self.request_list[cmd] = (handler, resp, name)
|
||||||
|
|
||||||
|
def start(self) -> None:
|
||||||
|
self.logger.info(f"Start on port {self.config.aimedb.port}")
|
||||||
|
asyncio.create_task(asyncio.start_server(self.dataReceived, self.config.server.listen_address, self.config.aimedb.port))
|
||||||
|
|
||||||
|
async def dataReceived(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
|
||||||
|
self.logger.debug(f"Connection made from {writer.get_extra_info('peername')[0]}")
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
data: bytes = await reader.read()
|
||||||
|
if len(data) == 0:
|
||||||
|
self.logger.debug("Connection closed")
|
||||||
|
return
|
||||||
|
await self.process_data(data, reader, writer)
|
||||||
|
await writer.drain()
|
||||||
|
except ConnectionResetError as e:
|
||||||
|
self.logger.warn("Connection reset, disconnecting")
|
||||||
|
return
|
||||||
|
|
||||||
def append_padding(self, data: bytes):
|
async def process_data(self, data: bytes, reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> Optional[bytes]:
|
||||||
"""Appends 0s to the end of the data until it's at the correct size"""
|
addr = writer.get_extra_info('peername')[0]
|
||||||
length = struct.unpack_from("<H", data, 6)
|
|
||||||
padding_size = length[0] - len(data)
|
|
||||||
data += bytes(padding_size)
|
|
||||||
return data
|
|
||||||
|
|
||||||
def connectionMade(self) -> None:
|
|
||||||
self.logger.debug(f"{self.transport.getPeer().host} Connected")
|
|
||||||
|
|
||||||
def connectionLost(self, reason) -> None:
|
|
||||||
self.logger.debug(
|
|
||||||
f"{self.transport.getPeer().host} Disconnected - {reason.value}"
|
|
||||||
)
|
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -68,9 +93,9 @@ class AimedbProtocol(Protocol):
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Failed to decrypt {data.hex()} because {e}")
|
self.logger.error(f"Failed to decrypt {data.hex()} because {e}")
|
||||||
return None
|
return
|
||||||
|
|
||||||
self.logger.debug(f"{self.transport.getPeer().host} wrote {decrypted.hex()}")
|
self.logger.debug(f"{addr} wrote {decrypted.hex()}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
head = ADBHeader.from_data(decrypted)
|
head = ADBHeader.from_data(decrypted)
|
||||||
@ -79,7 +104,9 @@ class AimedbProtocol(Protocol):
|
|||||||
self.logger.error(f"Error parsing ADB header: {e}")
|
self.logger.error(f"Error parsing ADB header: {e}")
|
||||||
try:
|
try:
|
||||||
encrypted = cipher.encrypt(ADBBaseResponse().make())
|
encrypted = cipher.encrypt(ADBBaseResponse().make())
|
||||||
self.transport.write(encrypted)
|
writer.write(encrypted)
|
||||||
|
await writer.drain()
|
||||||
|
return
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Failed to encrypt default response because {e}")
|
self.logger.error(f"Failed to encrypt default response because {e}")
|
||||||
@ -89,46 +116,51 @@ class AimedbProtocol(Protocol):
|
|||||||
if head.keychip_id == "ABCD1234567" or head.store_id == 0xfff0:
|
if head.keychip_id == "ABCD1234567" or head.store_id == 0xfff0:
|
||||||
self.logger.warning(f"Request from uninitialized AMLib: {vars(head)}")
|
self.logger.warning(f"Request from uninitialized AMLib: {vars(head)}")
|
||||||
|
|
||||||
|
if head.cmd == 0x66:
|
||||||
|
self.logger.info("Goodbye")
|
||||||
|
writer.close()
|
||||||
|
return
|
||||||
|
|
||||||
handler, resp_code, name = self.request_list.get(head.cmd, (self.handle_default, None, 'default'))
|
handler, resp_code, name = self.request_list.get(head.cmd, (self.handle_default, None, 'default'))
|
||||||
|
|
||||||
if resp_code is None:
|
if resp_code is None:
|
||||||
self.logger.warning(f"No handler for cmd {hex(head.cmd)}")
|
self.logger.warning(f"No handler for cmd {hex(head.cmd)}")
|
||||||
|
|
||||||
elif resp_code > 0:
|
elif resp_code > 0:
|
||||||
self.logger.info(f"{name} from {head.keychip_id} ({head.game_id}) @ {self.transport.getPeer().host}")
|
self.logger.info(f"{name} from {head.keychip_id} ({head.game_id}) @ {addr}")
|
||||||
|
|
||||||
resp = handler(decrypted, resp_code)
|
resp = await handler(decrypted, resp_code)
|
||||||
|
|
||||||
if type(resp) == ADBBaseResponse or issubclass(type(resp), ADBBaseResponse):
|
if type(resp) == ADBBaseResponse or issubclass(type(resp), ADBBaseResponse):
|
||||||
resp_bytes = resp.make()
|
resp_bytes = resp.make()
|
||||||
if len(resp_bytes) != resp.head.length:
|
|
||||||
resp_bytes = self.append_padding(resp_bytes)
|
|
||||||
|
|
||||||
elif type(resp) == bytes:
|
elif type(resp) == bytes:
|
||||||
resp_bytes = resp
|
resp_bytes = resp
|
||||||
|
|
||||||
elif resp is None: # Nothing to send, probably a goodbye
|
elif resp is None: # Nothing to send, probably a goodbye
|
||||||
|
self.logger.warn(f"None return by handler for {name}")
|
||||||
return
|
return
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
self.logger.error(f"Unsupported type returned by ADB handler for {name}: {type(resp)}")
|
||||||
raise TypeError(f"Unsupported type returned by ADB handler for {name}: {type(resp)}")
|
raise TypeError(f"Unsupported type returned by ADB handler for {name}: {type(resp)}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
encrypted = cipher.encrypt(resp_bytes)
|
encrypted = cipher.encrypt(resp_bytes)
|
||||||
self.logger.debug(f"Response {resp_bytes.hex()}")
|
self.logger.debug(f"Response {resp_bytes.hex()}")
|
||||||
self.transport.write(encrypted)
|
writer.write(encrypted)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Failed to encrypt {resp_bytes.hex()} because {e}")
|
self.logger.error(f"Failed to encrypt {resp_bytes.hex()} because {e}")
|
||||||
|
|
||||||
def handle_default(self, data: bytes, resp_code: int, length: int = 0x20) -> ADBBaseResponse:
|
async def handle_default(self, data: bytes, resp_code: int, length: int = 0x20) -> ADBBaseResponse:
|
||||||
req = ADBHeader.from_data(data)
|
req = ADBHeader.from_data(data)
|
||||||
return ADBBaseResponse(resp_code, length, 1, req.game_id, req.store_id, req.keychip_id, req.protocol_ver)
|
return ADBBaseResponse(resp_code, length, 1, req.game_id, req.store_id, req.keychip_id, req.protocol_ver)
|
||||||
|
|
||||||
def handle_hello(self, data: bytes, resp_code: int) -> ADBBaseResponse:
|
async def handle_hello(self, data: bytes, resp_code: int) -> ADBBaseResponse:
|
||||||
return self.handle_default(data, resp_code)
|
return await self.handle_default(data, resp_code)
|
||||||
|
|
||||||
def handle_campaign(self, data: bytes, resp_code: int) -> ADBBaseResponse:
|
async def handle_campaign(self, data: bytes, resp_code: int) -> ADBBaseResponse:
|
||||||
h = ADBHeader.from_data(data)
|
h = ADBHeader.from_data(data)
|
||||||
if h.protocol_ver >= 0x3030:
|
if h.protocol_ver >= 0x3030:
|
||||||
req = h
|
req = h
|
||||||
@ -143,26 +175,26 @@ class AimedbProtocol(Protocol):
|
|||||||
# We don't currently support campaigns
|
# We don't currently support campaigns
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
def handle_lookup(self, data: bytes, resp_code: int) -> ADBBaseResponse:
|
async def handle_lookup(self, data: bytes, resp_code: int) -> ADBBaseResponse:
|
||||||
req = ADBLookupRequest(data)
|
req = ADBLookupRequest(data)
|
||||||
user_id = self.data.card.get_user_id_from_card(req.access_code)
|
user_id = self.data.card.get_user_id_from_card(req.access_code)
|
||||||
is_banned = self.data.card.get_card_banned(req.access_code)
|
is_banned = self.data.card.get_card_banned(req.access_code)
|
||||||
is_locked = self.data.card.get_card_locked(req.access_code)
|
is_locked = self.data.card.get_card_locked(req.access_code)
|
||||||
|
|
||||||
|
ret = ADBLookupResponse.from_req(req.head, user_id)
|
||||||
if is_banned and is_locked:
|
if is_banned and is_locked:
|
||||||
ret.head.status = ADBStatus.BAN_SYS_USER
|
ret.head.status = ADBStatus.BAN_SYS_USER
|
||||||
elif is_banned:
|
elif is_banned:
|
||||||
ret.head.status = ADBStatus.BAN_SYS
|
ret.head.status = ADBStatus.BAN_SYS
|
||||||
elif is_locked:
|
elif is_locked:
|
||||||
ret.head.status = ADBStatus.LOCK_USER
|
ret.head.status = ADBStatus.LOCK_USER
|
||||||
ret = ADBLookupResponse.from_req(req.head, user_id)
|
|
||||||
|
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
f"access_code {req.access_code} -> user_id {ret.user_id}"
|
f"access_code {req.access_code} -> user_id {ret.user_id}"
|
||||||
)
|
)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def handle_lookup_ex(self, data: bytes, resp_code: int) -> ADBBaseResponse:
|
async def handle_lookup_ex(self, data: bytes, resp_code: int) -> ADBBaseResponse:
|
||||||
req = ADBLookupRequest(data)
|
req = ADBLookupRequest(data)
|
||||||
user_id = self.data.card.get_user_id_from_card(req.access_code)
|
user_id = self.data.card.get_user_id_from_card(req.access_code)
|
||||||
|
|
||||||
@ -191,7 +223,7 @@ class AimedbProtocol(Protocol):
|
|||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def handle_felica_lookup(self, data: bytes, resp_code: int) -> bytes:
|
async def handle_felica_lookup(self, data: bytes, resp_code: int) -> bytes:
|
||||||
"""
|
"""
|
||||||
On official, I think a card has to be registered for this to actually work, but
|
On official, I think a card has to be registered for this to actually work, but
|
||||||
I'm making the executive decision to not implement that and just kick back our
|
I'm making the executive decision to not implement that and just kick back our
|
||||||
@ -207,7 +239,7 @@ class AimedbProtocol(Protocol):
|
|||||||
)
|
)
|
||||||
return ADBFelicaLookupResponse.from_req(req.head, ac)
|
return ADBFelicaLookupResponse.from_req(req.head, ac)
|
||||||
|
|
||||||
def handle_felica_register(self, data: bytes, resp_code: int) -> bytes:
|
async def handle_felica_register(self, data: bytes, resp_code: int) -> bytes:
|
||||||
"""
|
"""
|
||||||
I've never seen this used.
|
I've never seen this used.
|
||||||
"""
|
"""
|
||||||
@ -239,7 +271,7 @@ class AimedbProtocol(Protocol):
|
|||||||
|
|
||||||
return ADBFelicaLookupResponse.from_req(req.head, ac)
|
return ADBFelicaLookupResponse.from_req(req.head, ac)
|
||||||
|
|
||||||
def handle_felica_lookup_ex(self, data: bytes, resp_code: int) -> bytes:
|
async def handle_felica_lookup_ex(self, data: bytes, resp_code: int) -> bytes:
|
||||||
req = ADBFelicaLookup2Request(data)
|
req = ADBFelicaLookup2Request(data)
|
||||||
access_code = self.data.card.to_access_code(req.idm)
|
access_code = self.data.card.to_access_code(req.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)
|
||||||
@ -263,7 +295,7 @@ class AimedbProtocol(Protocol):
|
|||||||
|
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
def handle_campaign_clear(self, data: bytes, resp_code: int) -> ADBBaseResponse:
|
async def handle_campaign_clear(self, data: bytes, resp_code: int) -> ADBBaseResponse:
|
||||||
req = ADBCampaignClearRequest(data)
|
req = ADBCampaignClearRequest(data)
|
||||||
|
|
||||||
resp = ADBCampaignClearResponse.from_req(req.head)
|
resp = ADBCampaignClearResponse.from_req(req.head)
|
||||||
@ -271,7 +303,7 @@ class AimedbProtocol(Protocol):
|
|||||||
# We don't support campaign stuff
|
# We don't support campaign stuff
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
def handle_register(self, data: bytes, resp_code: int) -> bytes:
|
async def handle_register(self, data: bytes, resp_code: int) -> bytes:
|
||||||
req = ADBLookupRequest(data)
|
req = ADBLookupRequest(data)
|
||||||
user_id = -1
|
user_id = -1
|
||||||
|
|
||||||
@ -305,17 +337,17 @@ class AimedbProtocol(Protocol):
|
|||||||
return resp
|
return resp
|
||||||
|
|
||||||
# TODO: Save these in some capacity, as deemed relevant
|
# TODO: Save these in some capacity, as deemed relevant
|
||||||
def handle_status_log(self, data: bytes, resp_code: int) -> bytes:
|
async def handle_status_log(self, data: bytes, resp_code: int) -> bytes:
|
||||||
req = ADBStatusLogRequest(data)
|
req = ADBStatusLogRequest(data)
|
||||||
self.logger.info(f"User {req.aime_id} logged {req.status.name} event")
|
self.logger.info(f"User {req.aime_id} logged {req.status.name} event")
|
||||||
return ADBBaseResponse(resp_code, 0x20, 1, req.head.game_id, req.head.store_id, req.head.keychip_id, req.head.protocol_ver)
|
return ADBBaseResponse(resp_code, 0x20, 1, req.head.game_id, req.head.store_id, req.head.keychip_id, req.head.protocol_ver)
|
||||||
|
|
||||||
def handle_log(self, data: bytes, resp_code: int) -> bytes:
|
async def handle_log(self, data: bytes, resp_code: int) -> bytes:
|
||||||
req = ADBLogRequest(data)
|
req = ADBLogRequest(data)
|
||||||
self.logger.info(f"User {req.aime_id} logged {req.status.name} event, credit_ct: {req.credit_ct} bet_ct: {req.bet_ct} won_ct: {req.won_ct}")
|
self.logger.info(f"User {req.aime_id} logged {req.status.name} event, credit_ct: {req.credit_ct} bet_ct: {req.bet_ct} won_ct: {req.won_ct}")
|
||||||
return ADBBaseResponse(resp_code, 0x20, 1, req.head.game_id, req.head.store_id, req.head.keychip_id, req.head.protocol_ver)
|
return ADBBaseResponse(resp_code, 0x20, 1, req.head.game_id, req.head.store_id, req.head.keychip_id, req.head.protocol_ver)
|
||||||
|
|
||||||
def handle_log_ex(self, data: bytes, resp_code: int) -> bytes:
|
async def handle_log_ex(self, data: bytes, resp_code: int) -> bytes:
|
||||||
req = ADBLogExRequest(data)
|
req = ADBLogExRequest(data)
|
||||||
strs = []
|
strs = []
|
||||||
self.logger.info(f"Recieved {req.num_logs} or {len(req.logs)} logs")
|
self.logger.info(f"Recieved {req.num_logs} or {len(req.logs)} logs")
|
||||||
@ -324,43 +356,3 @@ class AimedbProtocol(Protocol):
|
|||||||
self.logger.debug(f"User {req.logs[x].aime_id} logged {req.logs[x].status.name} event, credit_ct: {req.logs[x].credit_ct} bet_ct: {req.logs[x].bet_ct} won_ct: {req.logs[x].won_ct}")
|
self.logger.debug(f"User {req.logs[x].aime_id} logged {req.logs[x].status.name} event, credit_ct: {req.logs[x].credit_ct} bet_ct: {req.logs[x].bet_ct} won_ct: {req.logs[x].won_ct}")
|
||||||
return ADBLogExResponse.from_req(req.head)
|
return ADBLogExResponse.from_req(req.head)
|
||||||
|
|
||||||
def handle_goodbye(self, data: bytes, resp_code: int) -> None:
|
|
||||||
self.logger.info(f"goodbye from {self.transport.getPeer().host}")
|
|
||||||
self.transport.loseConnection()
|
|
||||||
return
|
|
||||||
|
|
||||||
class AimedbFactory(Factory):
|
|
||||||
protocol = AimedbProtocol
|
|
||||||
|
|
||||||
def __init__(self, cfg: CoreConfig) -> None:
|
|
||||||
self.config = cfg
|
|
||||||
log_fmt_str = "[%(asctime)s] Aimedb | %(levelname)s | %(message)s"
|
|
||||||
log_fmt = logging.Formatter(log_fmt_str)
|
|
||||||
self.logger = logging.getLogger("aimedb")
|
|
||||||
|
|
||||||
fileHandler = TimedRotatingFileHandler(
|
|
||||||
"{0}/{1}.log".format(self.config.server.log_dir, "aimedb"),
|
|
||||||
when="d",
|
|
||||||
backupCount=10,
|
|
||||||
)
|
|
||||||
fileHandler.setFormatter(log_fmt)
|
|
||||||
|
|
||||||
consoleHandler = logging.StreamHandler()
|
|
||||||
consoleHandler.setFormatter(log_fmt)
|
|
||||||
|
|
||||||
self.logger.addHandler(fileHandler)
|
|
||||||
self.logger.addHandler(consoleHandler)
|
|
||||||
|
|
||||||
self.logger.setLevel(self.config.aimedb.loglevel)
|
|
||||||
coloredlogs.install(
|
|
||||||
level=cfg.aimedb.loglevel, logger=self.logger, fmt=log_fmt_str
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.config.aimedb.key == "":
|
|
||||||
self.logger.error("Please set 'key' field in your config file.")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
self.logger.info(f"Ready on port {self.config.aimedb.port}")
|
|
||||||
|
|
||||||
def buildProtocol(self, addr):
|
|
||||||
return AimedbProtocol(self.config)
|
|
||||||
|
282
core/allnet.py
282
core/allnet.py
@ -1,20 +1,24 @@
|
|||||||
from typing import Dict, List, Any, Optional, Tuple, Union, Final
|
|
||||||
import logging, coloredlogs
|
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
|
||||||
from twisted.web.http import Request
|
|
||||||
from datetime import datetime
|
|
||||||
import pytz
|
import pytz
|
||||||
import base64
|
import base64
|
||||||
import zlib
|
import zlib
|
||||||
import json
|
import json
|
||||||
|
import yaml
|
||||||
|
import logging
|
||||||
|
import coloredlogs
|
||||||
|
import urllib.parse
|
||||||
|
import math
|
||||||
|
from typing import Dict, List, Any, Optional, Union, Final
|
||||||
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
|
from starlette.requests import Request
|
||||||
|
from starlette.responses import PlainTextResponse
|
||||||
|
from starlette.applications import Starlette
|
||||||
|
from starlette.routing import Route
|
||||||
|
from datetime import datetime
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from Crypto.PublicKey import RSA
|
from Crypto.PublicKey import RSA
|
||||||
from Crypto.Hash import SHA
|
from Crypto.Hash import SHA
|
||||||
from Crypto.Signature import PKCS1_v1_5
|
from Crypto.Signature import PKCS1_v1_5
|
||||||
from time import strptime
|
from os import path, environ, mkdir, access, W_OK
|
||||||
from os import path
|
|
||||||
import urllib.parse
|
|
||||||
import math
|
|
||||||
|
|
||||||
from .config import CoreConfig
|
from .config import CoreConfig
|
||||||
from .utils import Utils
|
from .utils import Utils
|
||||||
@ -91,7 +95,6 @@ class DLI_STATUS(Enum):
|
|||||||
|
|
||||||
class AllnetServlet:
|
class AllnetServlet:
|
||||||
def __init__(self, core_cfg: CoreConfig, cfg_folder: str):
|
def __init__(self, core_cfg: CoreConfig, cfg_folder: str):
|
||||||
super().__init__()
|
|
||||||
self.config = core_cfg
|
self.config = core_cfg
|
||||||
self.config_folder = cfg_folder
|
self.config_folder = cfg_folder
|
||||||
self.data = Data(core_cfg)
|
self.data = Data(core_cfg)
|
||||||
@ -126,19 +129,20 @@ class AllnetServlet:
|
|||||||
self.logger.error("No games detected!")
|
self.logger.error("No games detected!")
|
||||||
|
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
f"Serving {len(TitleServlet.title_registry)} game codes port {core_cfg.allnet.port}"
|
f"Serving {len(TitleServlet.title_registry)} game codes"
|
||||||
)
|
)
|
||||||
|
|
||||||
def handle_poweron(self, request: Request, _: Dict):
|
async def handle_poweron(self, request: Request):
|
||||||
request_ip = Utils.get_ip_addr(request)
|
request_ip = Utils.get_ip_addr(request)
|
||||||
pragma_header = request.getHeader('Pragma')
|
pragma_header = request.headers.get('Pragma', "")
|
||||||
is_dfi = pragma_header is not None and pragma_header == "DFI"
|
is_dfi = pragma_header is not None and pragma_header == "DFI"
|
||||||
|
data = await request.body()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if is_dfi:
|
if is_dfi:
|
||||||
req_urlencode = self.from_dfi(request.content.getvalue())
|
req_urlencode = self.from_dfi(data)
|
||||||
else:
|
else:
|
||||||
req_urlencode = request.content.getvalue().decode()
|
req_urlencode = data
|
||||||
|
|
||||||
req_dict = self.allnet_req_to_dict(req_urlencode)
|
req_dict = self.allnet_req_to_dict(req_urlencode)
|
||||||
if req_dict is None:
|
if req_dict is None:
|
||||||
@ -155,7 +159,7 @@ class AllnetServlet:
|
|||||||
except AllnetRequestException as e:
|
except AllnetRequestException as e:
|
||||||
if e.message != "":
|
if e.message != "":
|
||||||
self.logger.error(e)
|
self.logger.error(e)
|
||||||
return b""
|
return PlainTextResponse()
|
||||||
|
|
||||||
if req.format_ver == 3:
|
if req.format_ver == 3:
|
||||||
resp = AllnetPowerOnResponse3(req.token)
|
resp = AllnetPowerOnResponse3(req.token)
|
||||||
@ -176,7 +180,7 @@ class AllnetServlet:
|
|||||||
|
|
||||||
resp.stat = ALLNET_STAT.bad_machine.value
|
resp.stat = ALLNET_STAT.bad_machine.value
|
||||||
resp_dict = {k: v for k, v in vars(resp).items() if v is not None}
|
resp_dict = {k: v for k, v in vars(resp).items() if v is not None}
|
||||||
return (urllib.parse.unquote(urllib.parse.urlencode(resp_dict)) + "\n").encode("utf-8")
|
return PlainTextResponse(urllib.parse.unquote(urllib.parse.urlencode(resp_dict)) + "\n")
|
||||||
|
|
||||||
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"])
|
||||||
@ -190,7 +194,7 @@ class AllnetServlet:
|
|||||||
|
|
||||||
resp.stat = ALLNET_STAT.bad_shop.value
|
resp.stat = ALLNET_STAT.bad_shop.value
|
||||||
resp_dict = {k: v for k, v in vars(resp).items() if v is not None}
|
resp_dict = {k: v for k, v in vars(resp).items() if v is not None}
|
||||||
return (urllib.parse.unquote(urllib.parse.urlencode(resp_dict)) + "\n").encode("utf-8")
|
return PlainTextResponse(urllib.parse.unquote(urllib.parse.urlencode(resp_dict)) + "\n")
|
||||||
|
|
||||||
elif (not arcade["ip"] or arcade["ip"] is None) and self.config.server.strict_ip_checking:
|
elif (not arcade["ip"] or arcade["ip"] is None) and self.config.server.strict_ip_checking:
|
||||||
msg = f"Serial {req.serial} attempted allnet auth from bad IP {req.ip}, but arcade {arcade['id']} has no IP set! (strict checking enabled)."
|
msg = f"Serial {req.serial} attempted allnet auth from bad IP {req.ip}, but arcade {arcade['id']} has no IP set! (strict checking enabled)."
|
||||||
@ -201,7 +205,7 @@ class AllnetServlet:
|
|||||||
|
|
||||||
resp.stat = ALLNET_STAT.bad_shop.value
|
resp.stat = ALLNET_STAT.bad_shop.value
|
||||||
resp_dict = {k: v for k, v in vars(resp).items() if v is not None}
|
resp_dict = {k: v for k, v in vars(resp).items() if v is not None}
|
||||||
return (urllib.parse.unquote(urllib.parse.urlencode(resp_dict)) + "\n").encode("utf-8")
|
return PlainTextResponse(urllib.parse.unquote(urllib.parse.urlencode(resp_dict)) + "\n")
|
||||||
|
|
||||||
|
|
||||||
country = (
|
country = (
|
||||||
@ -245,20 +249,20 @@ class AllnetServlet:
|
|||||||
|
|
||||||
resp.stat = ALLNET_STAT.bad_game.value
|
resp.stat = ALLNET_STAT.bad_game.value
|
||||||
resp_dict = {k: v for k, v in vars(resp).items() if v is not None}
|
resp_dict = {k: v for k, v in vars(resp).items() if v is not None}
|
||||||
return (urllib.parse.unquote(urllib.parse.urlencode(resp_dict)) + "\n").encode("utf-8")
|
return PlainTextResponse(urllib.parse.unquote(urllib.parse.urlencode(resp_dict)) + "\n")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
f"Allowed unknown game {req.game_id} v{req.ver} to authenticate from {request_ip} due to 'is_develop' being enabled. S/N: {req.serial}"
|
f"Allowed unknown game {req.game_id} v{req.ver} to authenticate from {request_ip} due to 'is_develop' being enabled. S/N: {req.serial}"
|
||||||
)
|
)
|
||||||
resp.uri = f"http://{self.config.title.hostname}:{self.config.title.port}/{req.game_id}/{req.ver.replace('.', '')}/"
|
resp.uri = f"http://{self.config.server.hostname}:{self.config.server.port}/{req.game_id}/{req.ver.replace('.', '')}/"
|
||||||
resp.host = f"{self.config.title.hostname}:{self.config.title.port}"
|
resp.host = f"{self.config.server.hostname}:{self.config.server.port}"
|
||||||
|
|
||||||
resp_dict = {k: v for k, v in vars(resp).items() if v is not None}
|
resp_dict = {k: v for k, v in vars(resp).items() if v is not None}
|
||||||
resp_str = urllib.parse.unquote(urllib.parse.urlencode(resp_dict))
|
resp_str = urllib.parse.unquote(urllib.parse.urlencode(resp_dict))
|
||||||
|
|
||||||
self.logger.debug(f"Allnet response: {resp_str}")
|
self.logger.debug(f"Allnet response: {resp_str}")
|
||||||
return (resp_str + "\n").encode("utf-8")
|
return PlainTextResponse(resp_str + "\n")
|
||||||
|
|
||||||
|
|
||||||
int_ver = req.ver.replace(".", "")
|
int_ver = req.ver.replace(".", "")
|
||||||
@ -277,18 +281,19 @@ class AllnetServlet:
|
|||||||
request.responseHeaders.addRawHeader('Pragma', 'DFI')
|
request.responseHeaders.addRawHeader('Pragma', 'DFI')
|
||||||
return self.to_dfi(resp_str)"""
|
return self.to_dfi(resp_str)"""
|
||||||
|
|
||||||
return resp_str.encode("utf-8")
|
return PlainTextResponse(resp_str)
|
||||||
|
|
||||||
def handle_dlorder(self, request: Request, _: Dict):
|
async def handle_dlorder(self, request: Request):
|
||||||
request_ip = Utils.get_ip_addr(request)
|
request_ip = Utils.get_ip_addr(request)
|
||||||
pragma_header = request.getHeader('Pragma')
|
pragma_header = request.headers.get('Pragma', "")
|
||||||
is_dfi = pragma_header is not None and pragma_header == "DFI"
|
is_dfi = pragma_header is not None and pragma_header == "DFI"
|
||||||
|
data = await request.body()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if is_dfi:
|
if is_dfi:
|
||||||
req_urlencode = self.from_dfi(request.content.getvalue())
|
req_urlencode = self.from_dfi(data)
|
||||||
else:
|
else:
|
||||||
req_urlencode = request.content.getvalue().decode()
|
req_urlencode = data.decode()
|
||||||
|
|
||||||
req_dict = self.allnet_req_to_dict(req_urlencode)
|
req_dict = self.allnet_req_to_dict(req_urlencode)
|
||||||
if req_dict is None:
|
if req_dict is None:
|
||||||
@ -305,7 +310,7 @@ class AllnetServlet:
|
|||||||
except AllnetRequestException as e:
|
except AllnetRequestException as e:
|
||||||
if e.message != "":
|
if e.message != "":
|
||||||
self.logger.error(e)
|
self.logger.error(e)
|
||||||
return b""
|
return PlainTextResponse()
|
||||||
|
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
f"DownloadOrder from {request_ip} -> {req.game_id} v{req.ver} serial {req.serial}"
|
f"DownloadOrder from {request_ip} -> {req.game_id} v{req.ver} serial {req.serial}"
|
||||||
@ -316,18 +321,18 @@ class AllnetServlet:
|
|||||||
not self.config.allnet.allow_online_updates
|
not self.config.allnet.allow_online_updates
|
||||||
or not self.config.allnet.update_cfg_folder
|
or not self.config.allnet.update_cfg_folder
|
||||||
):
|
):
|
||||||
return urllib.parse.unquote(urllib.parse.urlencode(vars(resp))) + "\n"
|
return PlainTextResponse(urllib.parse.unquote(urllib.parse.urlencode(vars(resp))) + "\n")
|
||||||
|
|
||||||
else: # TODO: Keychip check
|
else: # TODO: Keychip check
|
||||||
if path.exists(
|
if path.exists(
|
||||||
f"{self.config.allnet.update_cfg_folder}/{req.game_id}-{req.ver.replace('.', '')}-app.ini"
|
f"{self.config.allnet.update_cfg_folder}/{req.game_id}-{req.ver.replace('.', '')}-app.ini"
|
||||||
):
|
):
|
||||||
resp.uri = f"http://{self.config.title.hostname}:{self.config.title.port}/dl/ini/{req.game_id}-{req.ver.replace('.', '')}-app.ini"
|
resp.uri = f"http://{self.config.server.hostname}:{self.config.server.port}/dl/ini/{req.game_id}-{req.ver.replace('.', '')}-app.ini"
|
||||||
|
|
||||||
if path.exists(
|
if path.exists(
|
||||||
f"{self.config.allnet.update_cfg_folder}/{req.game_id}-{req.ver.replace('.', '')}-opt.ini"
|
f"{self.config.allnet.update_cfg_folder}/{req.game_id}-{req.ver.replace('.', '')}-opt.ini"
|
||||||
):
|
):
|
||||||
resp.uri += f"|http://{self.config.title.hostname}:{self.config.title.port}/dl/ini/{req.game_id}-{req.ver.replace('.', '')}-opt.ini"
|
resp.uri += f"|http://{self.config.server.hostname}:{self.config.server.port}/dl/ini/{req.game_id}-{req.ver.replace('.', '')}-opt.ini"
|
||||||
|
|
||||||
self.logger.debug(f"Sending download uri {resp.uri}")
|
self.logger.debug(f"Sending download uri {resp.uri}")
|
||||||
self.data.base.log_event("allnet", "DLORDER_REQ_SUCCESS", logging.INFO, f"{Utils.get_ip_addr(request)} requested DL Order for {req.serial} {req.game_id} v{req.ver}")
|
self.data.base.log_event("allnet", "DLORDER_REQ_SUCCESS", logging.INFO, f"{Utils.get_ip_addr(request)} requested DL Order for {req.serial} {req.game_id} v{req.ver}")
|
||||||
@ -337,33 +342,33 @@ class AllnetServlet:
|
|||||||
request.responseHeaders.addRawHeader('Pragma', 'DFI')
|
request.responseHeaders.addRawHeader('Pragma', 'DFI')
|
||||||
return self.to_dfi(res_str)"""
|
return self.to_dfi(res_str)"""
|
||||||
|
|
||||||
return res_str
|
return PlainTextResponse(res_str)
|
||||||
|
|
||||||
def handle_dlorder_ini(self, request: Request, match: Dict) -> bytes:
|
async def handle_dlorder_ini(self, request: Request) -> bytes:
|
||||||
if "file" not in match:
|
req_file = request.path_params.get("file", "").replace("%0A", "")
|
||||||
return b""
|
|
||||||
|
|
||||||
req_file = match["file"].replace("%0A", "")
|
if not req_file:
|
||||||
|
return PlainTextResponse(status_code=404)
|
||||||
|
|
||||||
if path.exists(f"{self.config.allnet.update_cfg_folder}/{req_file}"):
|
if path.exists(f"{self.config.allnet.update_cfg_folder}/{req_file}"):
|
||||||
self.logger.info(f"Request for DL INI file {req_file} from {Utils.get_ip_addr(request)} successful")
|
self.logger.info(f"Request for DL INI file {req_file} from {Utils.get_ip_addr(request)} successful")
|
||||||
self.data.base.log_event("allnet", "DLORDER_INI_SENT", logging.INFO, f"{Utils.get_ip_addr(request)} successfully recieved {req_file}")
|
self.data.base.log_event("allnet", "DLORDER_INI_SENT", logging.INFO, f"{Utils.get_ip_addr(request)} successfully recieved {req_file}")
|
||||||
|
|
||||||
return open(
|
return PlainTextResponse(open(
|
||||||
f"{self.config.allnet.update_cfg_folder}/{req_file}", "rb"
|
f"{self.config.allnet.update_cfg_folder}/{req_file}", "r"
|
||||||
).read()
|
).read())
|
||||||
|
|
||||||
self.logger.info(f"DL INI File {req_file} not found")
|
self.logger.info(f"DL INI File {req_file} not found")
|
||||||
return b""
|
return PlainTextResponse()
|
||||||
|
|
||||||
def handle_dlorder_report(self, request: Request, match: Dict) -> bytes:
|
async def handle_dlorder_report(self, request: Request) -> bytes:
|
||||||
req_raw = request.content.getvalue()
|
req_raw = await request.body()
|
||||||
client_ip = Utils.get_ip_addr(request)
|
client_ip = Utils.get_ip_addr(request)
|
||||||
try:
|
try:
|
||||||
req_dict: Dict = json.loads(req_raw)
|
req_dict: Dict = json.loads(req_raw)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.warning(f"Failed to parse DL Report: {e}")
|
self.logger.warning(f"Failed to parse DL Report: {e}")
|
||||||
return "NG"
|
return PlainTextResponse("NG")
|
||||||
|
|
||||||
dl_data_type = DLIMG_TYPE.app
|
dl_data_type = DLIMG_TYPE.app
|
||||||
dl_data = req_dict.get("appimage", {})
|
dl_data = req_dict.get("appimage", {})
|
||||||
@ -374,13 +379,13 @@ class AllnetServlet:
|
|||||||
|
|
||||||
if dl_data is None or not dl_data:
|
if dl_data is None or not dl_data:
|
||||||
self.logger.warning(f"Failed to parse DL Report: Invalid format - contains neither appimage nor optimage")
|
self.logger.warning(f"Failed to parse DL Report: Invalid format - contains neither appimage nor optimage")
|
||||||
return "NG"
|
return PlainTextResponse("NG")
|
||||||
|
|
||||||
rep = DLReport(dl_data, dl_data_type)
|
rep = DLReport(dl_data, dl_data_type)
|
||||||
|
|
||||||
if not rep.validate():
|
if not rep.validate():
|
||||||
self.logger.warning(f"Failed to parse DL Report: Invalid format - {rep.err}")
|
self.logger.warning(f"Failed to parse DL Report: Invalid format - {rep.err}")
|
||||||
return "NG"
|
return PlainTextResponse("NG")
|
||||||
|
|
||||||
msg = f"{rep.serial} @ {client_ip} reported {rep.rep_type.name} download state {rep.rf_state.name} for {rep.gd} v{rep.dav}:"\
|
msg = f"{rep.serial} @ {client_ip} reported {rep.rep_type.name} download state {rep.rf_state.name} for {rep.gd} v{rep.dav}:"\
|
||||||
f" {rep.tdsc}/{rep.tsc} segments downloaded for working files {rep.wfl} with {rep.dfl if rep.dfl else 'none'} complete."
|
f" {rep.tdsc}/{rep.tsc} segments downloaded for working files {rep.wfl} with {rep.dfl if rep.dfl else 'none'} complete."
|
||||||
@ -388,10 +393,10 @@ class AllnetServlet:
|
|||||||
self.data.base.log_event("allnet", "DL_REPORT", logging.INFO, msg, dl_data)
|
self.data.base.log_event("allnet", "DL_REPORT", logging.INFO, msg, dl_data)
|
||||||
self.logger.info(msg)
|
self.logger.info(msg)
|
||||||
|
|
||||||
return "OK"
|
return PlainTextResponse("OK")
|
||||||
|
|
||||||
def handle_loaderstaterecorder(self, request: Request, match: Dict) -> bytes:
|
async def handle_loaderstaterecorder(self, request: Request) -> bytes:
|
||||||
req_data = request.content.getvalue()
|
req_data = await request.body()
|
||||||
sections = req_data.decode("utf-8").split("\r\n")
|
sections = req_data.decode("utf-8").split("\r\n")
|
||||||
|
|
||||||
req_dict = dict(urllib.parse.parse_qsl(sections[0]))
|
req_dict = dict(urllib.parse.parse_qsl(sections[0]))
|
||||||
@ -403,18 +408,94 @@ class AllnetServlet:
|
|||||||
ip = Utils.get_ip_addr(request)
|
ip = Utils.get_ip_addr(request)
|
||||||
|
|
||||||
if serial is None or num_files_dld is None or num_files_to_dl is None or dl_state is None:
|
if serial is None or num_files_dld is None or num_files_to_dl is None or dl_state is None:
|
||||||
return "NG".encode()
|
return PlainTextResponse("NG")
|
||||||
|
|
||||||
self.logger.info(f"LoaderStateRecorder Request from {ip} {serial}: {num_files_dld}/{num_files_to_dl} Files download (State: {dl_state})")
|
self.logger.info(f"LoaderStateRecorder Request from {ip} {serial}: {num_files_dld}/{num_files_to_dl} Files download (State: {dl_state})")
|
||||||
return "OK".encode()
|
return PlainTextResponse("OK")
|
||||||
|
|
||||||
def handle_alive(self, request: Request, match: Dict) -> bytes:
|
async def handle_alive(self, request: Request) -> bytes:
|
||||||
return "OK".encode()
|
return PlainTextResponse("OK")
|
||||||
|
|
||||||
def handle_billing_request(self, request: Request, _: Dict):
|
async def handle_naomitest(self, request: Request) -> bytes:
|
||||||
req_raw = request.content.getvalue()
|
self.logger.info(f"Ping from {Utils.get_ip_addr(request)}")
|
||||||
|
return PlainTextResponse("naomi ok")
|
||||||
|
|
||||||
|
def allnet_req_to_dict(self, data: str) -> Optional[List[Dict[str, Any]]]:
|
||||||
|
"""
|
||||||
|
Parses an allnet request string into a python dictionary
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
sections = data.split("\r\n")
|
||||||
|
|
||||||
|
ret = []
|
||||||
|
for x in sections:
|
||||||
|
ret.append(dict(urllib.parse.parse_qsl(x)))
|
||||||
|
return ret
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"allnet_req_to_dict: {e} while parsing {data}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def from_dfi(self, data: bytes) -> str:
|
||||||
|
zipped = base64.b64decode(data)
|
||||||
|
unzipped = zlib.decompress(zipped)
|
||||||
|
return unzipped.decode("utf-8")
|
||||||
|
|
||||||
|
def to_dfi(self, data: str) -> bytes:
|
||||||
|
unzipped = data.encode('utf-8')
|
||||||
|
zipped = zlib.compress(unzipped)
|
||||||
|
return base64.b64encode(zipped)
|
||||||
|
|
||||||
|
class BillingServlet:
|
||||||
|
def __init__(self, core_cfg: CoreConfig, cfg_folder: str) -> None:
|
||||||
|
self.config = core_cfg
|
||||||
|
self.config_folder = cfg_folder
|
||||||
|
self.data = Data(core_cfg)
|
||||||
|
|
||||||
|
self.logger = logging.getLogger("billing")
|
||||||
|
if not hasattr(self.logger, "initialized"):
|
||||||
|
log_fmt_str = "[%(asctime)s] Billing | %(levelname)s | %(message)s"
|
||||||
|
log_fmt = logging.Formatter(log_fmt_str)
|
||||||
|
|
||||||
|
fileHandler = TimedRotatingFileHandler(
|
||||||
|
"{0}/{1}.log".format(self.config.server.log_dir, "billing"),
|
||||||
|
when="d",
|
||||||
|
backupCount=10,
|
||||||
|
)
|
||||||
|
fileHandler.setFormatter(log_fmt)
|
||||||
|
|
||||||
|
consoleHandler = logging.StreamHandler()
|
||||||
|
consoleHandler.setFormatter(log_fmt)
|
||||||
|
|
||||||
|
self.logger.addHandler(fileHandler)
|
||||||
|
self.logger.addHandler(consoleHandler)
|
||||||
|
|
||||||
|
self.logger.setLevel(core_cfg.allnet.loglevel)
|
||||||
|
coloredlogs.install(
|
||||||
|
level=core_cfg.billing.loglevel, logger=self.logger, fmt=log_fmt_str
|
||||||
|
)
|
||||||
|
self.logger.initialized = True
|
||||||
|
|
||||||
|
def billing_req_to_dict(self, data: bytes):
|
||||||
|
"""
|
||||||
|
Parses an billing request string into a python dictionary
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
sections = data.decode("ascii").split("\r\n")
|
||||||
|
|
||||||
|
ret = []
|
||||||
|
for x in sections:
|
||||||
|
ret.append(dict(urllib.parse.parse_qsl(x)))
|
||||||
|
return ret
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"billing_req_to_dict: {e} while parsing {data}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def handle_billing_request(self, request: Request):
|
||||||
|
req_raw = await request.body()
|
||||||
|
|
||||||
if request.getHeader('Content-Type') == "application/octet-stream":
|
if request.headers.get('Content-Type', '') == "application/octet-stream":
|
||||||
req_unzip = zlib.decompressobj(-zlib.MAX_WBITS).decompress(req_raw)
|
req_unzip = zlib.decompressobj(-zlib.MAX_WBITS).decompress(req_raw)
|
||||||
else:
|
else:
|
||||||
req_unzip = req_raw
|
req_unzip = req_raw
|
||||||
@ -423,8 +504,8 @@ class AllnetServlet:
|
|||||||
request_ip = Utils.get_ip_addr(request)
|
request_ip = Utils.get_ip_addr(request)
|
||||||
|
|
||||||
if req_dict is None:
|
if req_dict is None:
|
||||||
self.logger.error(f"Failed to parse request {request.content.getvalue()}")
|
self.logger.error(f"Failed to parse request {req_raw}")
|
||||||
return b""
|
return PlainTextResponse()
|
||||||
|
|
||||||
self.logger.debug(f"request {req_dict}")
|
self.logger.debug(f"request {req_dict}")
|
||||||
|
|
||||||
@ -436,7 +517,7 @@ class AllnetServlet:
|
|||||||
req = BillingInfo(req_dict[0])
|
req = BillingInfo(req_dict[0])
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
self.logger.error(f"Billing request failed to parse: {e}")
|
self.logger.error(f"Billing request failed to parse: {e}")
|
||||||
return f"result=5&linelimit=&message=field is missing or formatting is incorrect\r\n".encode()
|
return PlainTextResponse("result=5&linelimit=&message=field is missing or formatting is incorrect\r\n")
|
||||||
|
|
||||||
for x in range(1, len(req_dict)):
|
for x in range(1, len(req_dict)):
|
||||||
if not req_dict[x]:
|
if not req_dict[x]:
|
||||||
@ -467,7 +548,7 @@ class AllnetServlet:
|
|||||||
)
|
)
|
||||||
self.logger.warning(msg)
|
self.logger.warning(msg)
|
||||||
|
|
||||||
return f"result=1&requestno={req.requestno}&message=Keychip Serial bad\r\n".encode()
|
return PlainTextResponse(f"result=1&requestno={req.requestno}&message=Keychip Serial bad\r\n")
|
||||||
|
|
||||||
msg = (
|
msg = (
|
||||||
f"Billing checkin from {request_ip}: game {req.gameid} ver {req.gamever} keychip {req.keychipid} playcount "
|
f"Billing checkin from {request_ip}: game {req.gameid} ver {req.gamever} keychip {req.keychipid} playcount "
|
||||||
@ -496,7 +577,6 @@ class AllnetServlet:
|
|||||||
|
|
||||||
# TODO: playhistory
|
# TODO: playhistory
|
||||||
|
|
||||||
#resp = BillingResponse(playlimit, playlimit_sig, nearfull, nearfull_sig)
|
|
||||||
resp = BillingResponse(playlimit, playlimit_sig, nearfull, nearfull_sig, req.requestno, req.protocolver)
|
resp = BillingResponse(playlimit, playlimit_sig, nearfull, nearfull_sig, req.requestno, req.protocolver)
|
||||||
|
|
||||||
resp_str = urllib.parse.unquote(urllib.parse.urlencode(vars(resp))) + "\r\n"
|
resp_str = urllib.parse.unquote(urllib.parse.urlencode(vars(resp))) + "\r\n"
|
||||||
@ -504,56 +584,9 @@ class AllnetServlet:
|
|||||||
self.logger.debug(f"response {vars(resp)}")
|
self.logger.debug(f"response {vars(resp)}")
|
||||||
if req.traceleft > 0:
|
if req.traceleft > 0:
|
||||||
self.logger.info(f"Requesting 20 more of {req.traceleft} unsent tracelogs")
|
self.logger.info(f"Requesting 20 more of {req.traceleft} unsent tracelogs")
|
||||||
return f"result=6&waittime=0&linelimit=20\r\n".encode()
|
return PlainTextResponse("result=6&waittime=0&linelimit=20\r\n")
|
||||||
|
|
||||||
return resp_str.encode("utf-8")
|
return PlainTextResponse(resp_str)
|
||||||
|
|
||||||
def handle_naomitest(self, request: Request, _: Dict) -> bytes:
|
|
||||||
self.logger.info(f"Ping from {Utils.get_ip_addr(request)}")
|
|
||||||
return b"naomi ok"
|
|
||||||
|
|
||||||
def billing_req_to_dict(self, data: bytes):
|
|
||||||
"""
|
|
||||||
Parses an billing request string into a python dictionary
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
sections = data.decode("ascii").split("\r\n")
|
|
||||||
|
|
||||||
ret = []
|
|
||||||
for x in sections:
|
|
||||||
ret.append(dict(urllib.parse.parse_qsl(x)))
|
|
||||||
return ret
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(f"billing_req_to_dict: {e} while parsing {data}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def allnet_req_to_dict(self, data: str) -> Optional[List[Dict[str, Any]]]:
|
|
||||||
"""
|
|
||||||
Parses an allnet request string into a python dictionary
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
sections = data.split("\r\n")
|
|
||||||
|
|
||||||
ret = []
|
|
||||||
for x in sections:
|
|
||||||
ret.append(dict(urllib.parse.parse_qsl(x)))
|
|
||||||
return ret
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(f"allnet_req_to_dict: {e} while parsing {data}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def from_dfi(self, data: bytes) -> str:
|
|
||||||
zipped = base64.b64decode(data)
|
|
||||||
unzipped = zlib.decompress(zipped)
|
|
||||||
return unzipped.decode("utf-8")
|
|
||||||
|
|
||||||
def to_dfi(self, data: str) -> bytes:
|
|
||||||
unzipped = data.encode('utf-8')
|
|
||||||
zipped = zlib.compress(unzipped)
|
|
||||||
return base64.b64encode(zipped)
|
|
||||||
|
|
||||||
|
|
||||||
class AllnetPowerOnRequest:
|
class AllnetPowerOnRequest:
|
||||||
def __init__(self, req: Dict) -> None:
|
def __init__(self, req: Dict) -> None:
|
||||||
@ -613,7 +646,6 @@ class AllnetPowerOnResponse3(AllnetPowerOnResponse):
|
|||||||
self.minute = None
|
self.minute = None
|
||||||
self.second = None
|
self.second = None
|
||||||
|
|
||||||
|
|
||||||
class AllnetPowerOnResponse2(AllnetPowerOnResponse):
|
class AllnetPowerOnResponse2(AllnetPowerOnResponse):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -623,7 +655,6 @@ class AllnetPowerOnResponse2(AllnetPowerOnResponse):
|
|||||||
self.timezone = "+09:00"
|
self.timezone = "+09:00"
|
||||||
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.get("game_id", "")
|
self.game_id = req.get("game_id", "")
|
||||||
@ -631,7 +662,6 @@ class AllnetDownloadOrderRequest:
|
|||||||
self.serial = req.get("serial", "")
|
self.serial = req.get("serial", "")
|
||||||
self.encode = req.get("encode", "")
|
self.encode = req.get("encode", "")
|
||||||
|
|
||||||
|
|
||||||
class AllnetDownloadOrderResponse:
|
class AllnetDownloadOrderResponse:
|
||||||
def __init__(self, stat: int = 1, serial: str = "", uri: str = "") -> None:
|
def __init__(self, stat: int = 1, serial: str = "", uri: str = "") -> None:
|
||||||
self.stat = stat
|
self.stat = stat
|
||||||
@ -781,7 +811,6 @@ 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
|
||||||
@ -849,3 +878,26 @@ class DLReport:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
cfg_dir = environ.get("DIANA_CFG_DIR", "config")
|
||||||
|
cfg: CoreConfig = CoreConfig()
|
||||||
|
if path.exists(f"{cfg_dir}/core.yaml"):
|
||||||
|
cfg.update(yaml.safe_load(open(f"{cfg_dir}/core.yaml")))
|
||||||
|
|
||||||
|
if not path.exists(cfg.server.log_dir):
|
||||||
|
mkdir(cfg.server.log_dir)
|
||||||
|
|
||||||
|
if not access(cfg.server.log_dir, W_OK):
|
||||||
|
print(
|
||||||
|
f"Log directory {cfg.server.log_dir} NOT writable, please check permissions"
|
||||||
|
)
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
billing = BillingServlet(cfg, cfg_dir)
|
||||||
|
app = Starlette(
|
||||||
|
cfg.server.is_develop,
|
||||||
|
[
|
||||||
|
Route("/request", billing.handle_billing_request, methods=["POST"]),
|
||||||
|
Route("/request/", billing.handle_billing_request, methods=["POST"]),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
89
core/app.py
Normal file
89
core/app.py
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import yaml
|
||||||
|
import logging
|
||||||
|
import coloredlogs
|
||||||
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
|
from starlette.routing import Route
|
||||||
|
from starlette.requests import Request
|
||||||
|
from starlette.applications import Starlette
|
||||||
|
from starlette.responses import PlainTextResponse
|
||||||
|
from os import environ, path, mkdir, W_OK, access
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from core import CoreConfig, TitleServlet, MuchaServlet, AllnetServlet, BillingServlet, AimedbServlette
|
||||||
|
from core.frontend import FrontendServlet
|
||||||
|
|
||||||
|
async def dummy_rt(request: Request):
|
||||||
|
return PlainTextResponse("Service OK")
|
||||||
|
|
||||||
|
cfg_dir = environ.get("ARTEMIS_CFG_DIR", "config")
|
||||||
|
cfg: CoreConfig = CoreConfig()
|
||||||
|
if path.exists(f"{cfg_dir}/core.yaml"):
|
||||||
|
cfg.update(yaml.safe_load(open(f"{cfg_dir}/core.yaml")))
|
||||||
|
|
||||||
|
if not path.exists(cfg.server.log_dir):
|
||||||
|
mkdir(cfg.server.log_dir)
|
||||||
|
|
||||||
|
if not access(cfg.server.log_dir, W_OK):
|
||||||
|
print(
|
||||||
|
f"Log directory {cfg.server.log_dir} NOT writable, please check permissions"
|
||||||
|
)
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
logger = logging.getLogger("core")
|
||||||
|
log_fmt_str = "[%(asctime)s] Core | %(levelname)s | %(message)s"
|
||||||
|
log_fmt = logging.Formatter(log_fmt_str)
|
||||||
|
|
||||||
|
fileHandler = TimedRotatingFileHandler(
|
||||||
|
"{0}/{1}.log".format(cfg.server.log_dir, "core"), when="d", backupCount=10
|
||||||
|
)
|
||||||
|
fileHandler.setFormatter(log_fmt)
|
||||||
|
|
||||||
|
consoleHandler = logging.StreamHandler()
|
||||||
|
consoleHandler.setFormatter(log_fmt)
|
||||||
|
|
||||||
|
logger.addHandler(fileHandler)
|
||||||
|
logger.addHandler(consoleHandler)
|
||||||
|
|
||||||
|
log_lv = logging.DEBUG if cfg.server.is_develop else logging.INFO
|
||||||
|
logger.setLevel(log_lv)
|
||||||
|
coloredlogs.install(level=log_lv, logger=logger, fmt=log_fmt_str)
|
||||||
|
|
||||||
|
logger.info(f"Artemis starting in {'develop' if cfg.server.is_develop else 'production'} mode")
|
||||||
|
|
||||||
|
title = TitleServlet(cfg, cfg_dir) # This has to be loaded first to load plugins
|
||||||
|
mucha = MuchaServlet(cfg, cfg_dir)
|
||||||
|
allnet = AllnetServlet(cfg, cfg_dir)
|
||||||
|
|
||||||
|
route_lst: List[Route] = [
|
||||||
|
# Allnet
|
||||||
|
Route("/sys/servlet/PowerOn", allnet.handle_poweron, methods=["GET", "POST"]),
|
||||||
|
Route("/sys/servlet/DownloadOrder", allnet.handle_dlorder, methods=["GET", "POST"]),
|
||||||
|
Route("/sys/servlet/LoaderStateRecorder", allnet.handle_loaderstaterecorder, methods=["GET", "POST"]),
|
||||||
|
Route("/sys/servlet/Alive", allnet.handle_alive, methods=["GET", "POST"]),
|
||||||
|
Route("/report-api/Report", allnet.handle_dlorder_report, methods=["POST"]),
|
||||||
|
Route("/dl/ini/{file:str}", allnet.handle_dlorder_ini),
|
||||||
|
Route("/naomitest.html", allnet.handle_naomitest),
|
||||||
|
# Mucha
|
||||||
|
Route("/mucha_front/boardauth.do", mucha.handle_boardauth, methods=["POST"]),
|
||||||
|
Route("/mucha_front/updatacheck.do", mucha.handle_updatecheck, methods=["POST"]),
|
||||||
|
Route("/mucha_front/downloadstate.do", mucha.handle_dlstate, methods=["POST"]),
|
||||||
|
]
|
||||||
|
|
||||||
|
if not cfg.billing.standalone:
|
||||||
|
billing = BillingServlet(cfg, cfg_dir)
|
||||||
|
route_lst += [
|
||||||
|
Route("/request", billing.handle_billing_request, methods=["POST"]),
|
||||||
|
Route("/request/", billing.handle_billing_request, methods=["POST"]),
|
||||||
|
]
|
||||||
|
|
||||||
|
if not cfg.frontend.standalone:
|
||||||
|
frontend = FrontendServlet(cfg, cfg_dir)
|
||||||
|
route_lst += frontend.get_routes()
|
||||||
|
else:
|
||||||
|
route_lst.append(Route("/", dummy_rt))
|
||||||
|
route_lst.append(Route("/robots.txt", FrontendServlet.robots))
|
||||||
|
|
||||||
|
for code, game in title.title_registry.items():
|
||||||
|
route_lst += game.get_routes()
|
||||||
|
|
||||||
|
app = Starlette(cfg.server.is_develop, route_lst)
|
148
core/config.py
148
core/config.py
@ -1,16 +1,48 @@
|
|||||||
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:
|
||||||
|
"""
|
||||||
|
Address Artemis will bind to and listen on
|
||||||
|
"""
|
||||||
return CoreConfig.get_config_field(
|
return CoreConfig.get_config_field(
|
||||||
self.__config, "core", "server", "listen_address", default="127.0.0.1"
|
self.__config, "core", "server", "listen_address", default="127.0.0.1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hostname(self) -> str:
|
||||||
|
"""
|
||||||
|
Hostname sent to games
|
||||||
|
"""
|
||||||
|
return CoreConfig.get_config_field(
|
||||||
|
self.__config, "core", "server", "hostname", default="localhost"
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def port(self) -> int:
|
||||||
|
"""
|
||||||
|
Port the game will listen on
|
||||||
|
"""
|
||||||
|
return CoreConfig.get_config_field(
|
||||||
|
self.__config, "core", "server", "port", default=8080
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ssl_key(self) -> str:
|
||||||
|
return CoreConfig.get_config_field(
|
||||||
|
self.__config, "core", "server", "ssl_key", default="cert/title.key"
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ssl_cert(self) -> str:
|
||||||
|
return CoreConfig.get_config_field(
|
||||||
|
self.__config, "core", "title", "ssl_cert", default="cert/title.pem"
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def allow_user_registration(self) -> bool:
|
def allow_user_registration(self) -> bool:
|
||||||
@ -43,9 +75,13 @@ class ServerConfig:
|
|||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def threading(self) -> bool:
|
def proxy_port(self) -> int:
|
||||||
|
"""
|
||||||
|
What port the proxy is listening on. This will be sent instead of 'port' if
|
||||||
|
is_using_proxy is True and this value is non-zero
|
||||||
|
"""
|
||||||
return CoreConfig.get_config_field(
|
return CoreConfig.get_config_field(
|
||||||
self.__config, "core", "server", "threading", default=False
|
self.__config, "core", "title", "proxy_port", default=0
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -66,7 +102,6 @@ class ServerConfig:
|
|||||||
self.__config, "core", "server", "strict_ip_checking", default=False
|
self.__config, "core", "server", "strict_ip_checking", default=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TitleConfig:
|
class TitleConfig:
|
||||||
def __init__(self, parent_config: "CoreConfig") -> None:
|
def __init__(self, parent_config: "CoreConfig") -> None:
|
||||||
self.__config = parent_config
|
self.__config = parent_config
|
||||||
@ -79,49 +114,18 @@ class TitleConfig:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
|
||||||
def hostname(self) -> str:
|
|
||||||
return CoreConfig.get_config_field(
|
|
||||||
self.__config, "core", "title", "hostname", default="localhost"
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def port(self) -> int:
|
|
||||||
return CoreConfig.get_config_field(
|
|
||||||
self.__config, "core", "title", "port", default=8080
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def port_ssl(self) -> int:
|
|
||||||
return CoreConfig.get_config_field(
|
|
||||||
self.__config, "core", "title", "port_ssl", default=0
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def ssl_key(self) -> str:
|
|
||||||
return CoreConfig.get_config_field(
|
|
||||||
self.__config, "core", "title", "ssl_key", default="cert/title.key"
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def ssl_cert(self) -> str:
|
|
||||||
return CoreConfig.get_config_field(
|
|
||||||
self.__config, "core", "title", "ssl_cert", default="cert/title.pem"
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def reboot_start_time(self) -> str:
|
def reboot_start_time(self) -> str:
|
||||||
return CoreConfig.get_config_field(
|
return CoreConfig.get_config_field(
|
||||||
self.__config, "core", "title", "reboot_start_time", default=""
|
self.__config, "core", "title", "reboot_start_time", default="04:00"
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def reboot_end_time(self) -> str:
|
def reboot_end_time(self) -> str:
|
||||||
return CoreConfig.get_config_field(
|
return CoreConfig.get_config_field(
|
||||||
self.__config, "core", "title", "reboot_end_time", default=""
|
self.__config, "core", "title", "reboot_end_time", default="05:00"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class DatabaseConfig:
|
class DatabaseConfig:
|
||||||
def __init__(self, parent_config: "CoreConfig") -> None:
|
def __init__(self, parent_config: "CoreConfig") -> None:
|
||||||
self.__config = parent_config
|
self.__config = parent_config
|
||||||
@ -176,16 +180,6 @@ class DatabaseConfig:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
|
||||||
def user_table_autoincrement_start(self) -> int:
|
|
||||||
return CoreConfig.get_config_field(
|
|
||||||
self.__config,
|
|
||||||
"core",
|
|
||||||
"database",
|
|
||||||
"user_table_autoincrement_start",
|
|
||||||
default=10000,
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def enable_memcached(self) -> bool:
|
def enable_memcached(self) -> bool:
|
||||||
return CoreConfig.get_config_field(
|
return CoreConfig.get_config_field(
|
||||||
@ -198,21 +192,14 @@ class DatabaseConfig:
|
|||||||
self.__config, "core", "database", "memcached_host", default="localhost"
|
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:
|
||||||
self.__config = parent_config
|
self.__config = parent_config
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def enable(self) -> int:
|
def standalone(self) -> int:
|
||||||
return CoreConfig.get_config_field(
|
return CoreConfig.get_config_field(
|
||||||
self.__config, "core", "frontend", "enable", default=False
|
self.__config, "core", "frontend", "standalone", default=True
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def port(self) -> int:
|
|
||||||
return CoreConfig.get_config_field(
|
|
||||||
self.__config, "core", "frontend", "port", default=8090
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -222,7 +209,12 @@ class FrontendConfig:
|
|||||||
self.__config, "core", "frontend", "loglevel", default="info"
|
self.__config, "core", "frontend", "loglevel", default="info"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def secret(self) -> str:
|
||||||
|
CoreConfig.get_config_field(
|
||||||
|
self.__config, "core", "frontend", "secret", default=""
|
||||||
|
)
|
||||||
|
|
||||||
class AllnetConfig:
|
class AllnetConfig:
|
||||||
def __init__(self, parent_config: "CoreConfig") -> None:
|
def __init__(self, parent_config: "CoreConfig") -> None:
|
||||||
@ -236,18 +228,6 @@ class AllnetConfig:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
|
||||||
def port(self) -> int:
|
|
||||||
return CoreConfig.get_config_field(
|
|
||||||
self.__config, "core", "allnet", "port", default=80
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def ip_check(self) -> bool:
|
|
||||||
return CoreConfig.get_config_field(
|
|
||||||
self.__config, "core", "allnet", "ip_check", default=False
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def allow_online_updates(self) -> int:
|
def allow_online_updates(self) -> int:
|
||||||
return CoreConfig.get_config_field(
|
return CoreConfig.get_config_field(
|
||||||
@ -260,10 +240,23 @@ class AllnetConfig:
|
|||||||
self.__config, "core", "allnet", "update_cfg_folder", default=""
|
self.__config, "core", "allnet", "update_cfg_folder", default=""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class BillingConfig:
|
class BillingConfig:
|
||||||
def __init__(self, parent_config: "CoreConfig") -> None:
|
def __init__(self, parent_config: "CoreConfig") -> None:
|
||||||
self.__config = parent_config
|
self.__config = parent_config
|
||||||
|
|
||||||
|
@property
|
||||||
|
def standalone(self) -> bool:
|
||||||
|
return CoreConfig.get_config_field(
|
||||||
|
self.__config, "core", "billing", "standalone", default=True
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def loglevel(self) -> int:
|
||||||
|
return CoreConfig.str_to_loglevel(
|
||||||
|
CoreConfig.get_config_field(
|
||||||
|
self.__config, "core", "billing", "loglevel", default="info"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def port(self) -> int:
|
def port(self) -> int:
|
||||||
@ -289,7 +282,6 @@ class BillingConfig:
|
|||||||
self.__config, "core", "billing", "signing_key", default="cert/billing.key"
|
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:
|
||||||
self.__config = parent_config
|
self.__config = parent_config
|
||||||
@ -326,17 +318,10 @@ class AimedbConfig:
|
|||||||
self.__config, "core", "aimedb", "id_lifetime_seconds", default=86400
|
self.__config, "core", "aimedb", "id_lifetime_seconds", default=86400
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class MuchaConfig:
|
class MuchaConfig:
|
||||||
def __init__(self, parent_config: "CoreConfig") -> None:
|
def __init__(self, parent_config: "CoreConfig") -> None:
|
||||||
self.__config = parent_config
|
self.__config = parent_config
|
||||||
|
|
||||||
@property
|
|
||||||
def enable(self) -> int:
|
|
||||||
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(
|
return CoreConfig.str_to_loglevel(
|
||||||
@ -345,13 +330,6 @@ class MuchaConfig:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
|
||||||
def hostname(self) -> str:
|
|
||||||
return CoreConfig.get_config_field(
|
|
||||||
self.__config, "core", "mucha", "hostname", default="localhost"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class CoreConfig(dict):
|
class CoreConfig(dict):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.server = ServerConfig(self)
|
self.server = ServerConfig(self)
|
||||||
|
1
core/data/alembic/README
Normal file
1
core/data/alembic/README
Normal file
@ -0,0 +1 @@
|
|||||||
|
Generic single-database configuration.
|
89
core/data/alembic/alembic.ini
Normal file
89
core/data/alembic/alembic.ini
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
# A generic, single database configuration.
|
||||||
|
|
||||||
|
[alembic]
|
||||||
|
# path to migration scripts
|
||||||
|
script_location = .
|
||||||
|
|
||||||
|
# template used to generate migration files
|
||||||
|
# file_template = %%(rev)s_%%(slug)s
|
||||||
|
|
||||||
|
# sys.path path, will be prepended to sys.path if present.
|
||||||
|
# defaults to the current working directory.
|
||||||
|
prepend_sys_path = .
|
||||||
|
|
||||||
|
# timezone to use when rendering the date
|
||||||
|
# within the migration file as well as the filename.
|
||||||
|
# string value is passed to dateutil.tz.gettz()
|
||||||
|
# leave blank for localtime
|
||||||
|
# timezone =
|
||||||
|
|
||||||
|
# max length of characters to apply to the
|
||||||
|
# "slug" field
|
||||||
|
# truncate_slug_length = 40
|
||||||
|
|
||||||
|
# set to 'true' to run the environment during
|
||||||
|
# the 'revision' command, regardless of autogenerate
|
||||||
|
# revision_environment = false
|
||||||
|
|
||||||
|
# set to 'true' to allow .pyc and .pyo files without
|
||||||
|
# a source .py file to be detected as revisions in the
|
||||||
|
# versions/ directory
|
||||||
|
# sourceless = false
|
||||||
|
|
||||||
|
# version location specification; this defaults
|
||||||
|
# to ./versions. When using multiple version
|
||||||
|
# directories, initial revisions must be specified with --version-path
|
||||||
|
# version_locations = %(here)s/bar %(here)s/bat ./versions
|
||||||
|
|
||||||
|
# the output encoding used when revision files
|
||||||
|
# are written from script.py.mako
|
||||||
|
# output_encoding = utf-8
|
||||||
|
|
||||||
|
sqlalchemy.url = driver://user:pass@localhost/dbname
|
||||||
|
|
||||||
|
|
||||||
|
[post_write_hooks]
|
||||||
|
# post_write_hooks defines scripts or Python functions that are run
|
||||||
|
# on newly generated revision scripts. See the documentation for further
|
||||||
|
# detail and examples
|
||||||
|
|
||||||
|
# format using "black" - use the console_scripts runner, against the "black" entrypoint
|
||||||
|
# hooks=black
|
||||||
|
# black.type=console_scripts
|
||||||
|
# black.entrypoint=black
|
||||||
|
# black.options=-l 79
|
||||||
|
|
||||||
|
# Logging configuration
|
||||||
|
[loggers]
|
||||||
|
keys = root,sqlalchemy,alembic
|
||||||
|
|
||||||
|
[handlers]
|
||||||
|
keys = console
|
||||||
|
|
||||||
|
[formatters]
|
||||||
|
keys = generic
|
||||||
|
|
||||||
|
[logger_root]
|
||||||
|
level = WARN
|
||||||
|
handlers = console
|
||||||
|
qualname =
|
||||||
|
|
||||||
|
[logger_sqlalchemy]
|
||||||
|
level = WARN
|
||||||
|
handlers =
|
||||||
|
qualname = sqlalchemy.engine
|
||||||
|
|
||||||
|
[logger_alembic]
|
||||||
|
level = INFO
|
||||||
|
handlers =
|
||||||
|
qualname = alembic
|
||||||
|
|
||||||
|
[handler_console]
|
||||||
|
class = StreamHandler
|
||||||
|
args = (sys.stderr,)
|
||||||
|
level = NOTSET
|
||||||
|
formatter = generic
|
||||||
|
|
||||||
|
[formatter_generic]
|
||||||
|
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||||
|
datefmt = %H:%M:%S
|
77
core/data/alembic/env.py
Normal file
77
core/data/alembic/env.py
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
from logging.config import fileConfig
|
||||||
|
|
||||||
|
from sqlalchemy import engine_from_config
|
||||||
|
from sqlalchemy import pool
|
||||||
|
|
||||||
|
from alembic import context
|
||||||
|
|
||||||
|
# this is the Alembic Config object, which provides
|
||||||
|
# access to the values within the .ini file in use.
|
||||||
|
config = context.config
|
||||||
|
|
||||||
|
# Interpret the config file for Python logging.
|
||||||
|
# This line sets up loggers basically.
|
||||||
|
fileConfig(config.config_file_name)
|
||||||
|
|
||||||
|
# add your model's MetaData object here
|
||||||
|
# for 'autogenerate' support
|
||||||
|
# from myapp import mymodel
|
||||||
|
# target_metadata = mymodel.Base.metadata
|
||||||
|
target_metadata = None
|
||||||
|
|
||||||
|
# other values from the config, defined by the needs of env.py,
|
||||||
|
# can be acquired:
|
||||||
|
# my_important_option = config.get_main_option("my_important_option")
|
||||||
|
# ... etc.
|
||||||
|
|
||||||
|
|
||||||
|
def run_migrations_offline():
|
||||||
|
"""Run migrations in 'offline' mode.
|
||||||
|
|
||||||
|
This configures the context with just a URL
|
||||||
|
and not an Engine, though an Engine is acceptable
|
||||||
|
here as well. By skipping the Engine creation
|
||||||
|
we don't even need a DBAPI to be available.
|
||||||
|
|
||||||
|
Calls to context.execute() here emit the given string to the
|
||||||
|
script output.
|
||||||
|
|
||||||
|
"""
|
||||||
|
url = config.get_main_option("sqlalchemy.url")
|
||||||
|
context.configure(
|
||||||
|
url=url,
|
||||||
|
target_metadata=target_metadata,
|
||||||
|
literal_binds=True,
|
||||||
|
dialect_opts={"paramstyle": "named"},
|
||||||
|
)
|
||||||
|
|
||||||
|
with context.begin_transaction():
|
||||||
|
context.run_migrations()
|
||||||
|
|
||||||
|
|
||||||
|
def run_migrations_online():
|
||||||
|
"""Run migrations in 'online' mode.
|
||||||
|
|
||||||
|
In this scenario we need to create an Engine
|
||||||
|
and associate a connection with the context.
|
||||||
|
|
||||||
|
"""
|
||||||
|
connectable = engine_from_config(
|
||||||
|
config.get_section(config.config_ini_section),
|
||||||
|
prefix="sqlalchemy.",
|
||||||
|
poolclass=pool.NullPool,
|
||||||
|
)
|
||||||
|
|
||||||
|
with connectable.connect() as connection:
|
||||||
|
context.configure(
|
||||||
|
connection=connection, target_metadata=target_metadata
|
||||||
|
)
|
||||||
|
|
||||||
|
with context.begin_transaction():
|
||||||
|
context.run_migrations()
|
||||||
|
|
||||||
|
|
||||||
|
if context.is_offline_mode():
|
||||||
|
run_migrations_offline()
|
||||||
|
else:
|
||||||
|
run_migrations_online()
|
24
core/data/alembic/script.py.mako
Normal file
24
core/data/alembic/script.py.mako
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
"""${message}
|
||||||
|
|
||||||
|
Revision ID: ${up_revision}
|
||||||
|
Revises: ${down_revision | comma,n}
|
||||||
|
Create Date: ${create_date}
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
${imports if imports else ""}
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = ${repr(up_revision)}
|
||||||
|
down_revision = ${repr(down_revision)}
|
||||||
|
branch_labels = ${repr(branch_labels)}
|
||||||
|
depends_on = ${repr(depends_on)}
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
${upgrades if upgrades else "pass"}
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
${downgrades if downgrades else "pass"}
|
@ -2,7 +2,9 @@ import logging, coloredlogs
|
|||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
from twisted.web import resource
|
from twisted.web import resource
|
||||||
from twisted.web.util import redirectTo
|
from twisted.web.util import redirectTo
|
||||||
from twisted.web.http import Request
|
from starlette.requests import Request
|
||||||
|
from starlette.routing import Route
|
||||||
|
from starlette.responses import Response, PlainTextResponse
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
from twisted.web.server import Session
|
from twisted.web.server import Session
|
||||||
from zope.interface import Interface, Attribute, implementer
|
from zope.interface import Interface, Attribute, implementer
|
||||||
@ -98,8 +100,15 @@ class FrontendServlet(resource.Resource):
|
|||||||
self.putChild(b"game", fe_game)
|
self.putChild(b"game", fe_game)
|
||||||
|
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
f"Ready on port {self.config.frontend.port} serving {len(fe_game.children)} games"
|
f"Ready on port {self.config.server.port} serving {len(fe_game.children)} games"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_routes(self) -> List[Route]:
|
||||||
|
return []
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def robots(cls, request: Request) -> PlainTextResponse:
|
||||||
|
return PlainTextResponse("User-agent: *\nDisallow: /\n\nUser-agent: AdsBot-Google\nDisallow: /")
|
||||||
|
|
||||||
def render_GET(self, request):
|
def render_GET(self, request):
|
||||||
self.logger.debug(f"{Utils.get_ip_addr(request)} -> {request.uri.decode()}")
|
self.logger.debug(f"{Utils.get_ip_addr(request)} -> {request.uri.decode()}")
|
||||||
|
@ -2,7 +2,7 @@ from typing import Dict, Any, Optional, List
|
|||||||
import logging, coloredlogs
|
import logging, coloredlogs
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
from twisted.web import resource
|
from twisted.web import resource
|
||||||
from twisted.web.http import Request
|
from starlette.requests import Request
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from Crypto.Cipher import Blowfish
|
from Crypto.Cipher import Blowfish
|
||||||
import pytz
|
import pytz
|
||||||
@ -12,7 +12,7 @@ from .utils import Utils
|
|||||||
from .title import TitleServlet
|
from .title import TitleServlet
|
||||||
|
|
||||||
class MuchaServlet:
|
class MuchaServlet:
|
||||||
mucha_registry: List[str] = []
|
mucha_registry: Dict[str, str] = {}
|
||||||
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
|
||||||
@ -39,11 +39,12 @@ class MuchaServlet:
|
|||||||
|
|
||||||
for _, mod in TitleServlet.title_registry.items():
|
for _, mod in TitleServlet.title_registry.items():
|
||||||
if hasattr(mod, "get_mucha_info"):
|
if hasattr(mod, "get_mucha_info"):
|
||||||
enabled, game_cd = mod.get_mucha_info(
|
enabled, game_cds, netid_prefixes = mod.get_mucha_info(
|
||||||
self.config, self.config_dir
|
self.config, self.config_dir
|
||||||
)
|
)
|
||||||
if enabled:
|
if enabled:
|
||||||
self.mucha_registry.append(game_cd)
|
for x in range(len(game_cds)):
|
||||||
|
self.mucha_registry[game_cds[x]] = netid_prefixes[x]
|
||||||
|
|
||||||
self.logger.info(f"Serving {len(self.mucha_registry)} games")
|
self.logger.info(f"Serving {len(self.mucha_registry)} games")
|
||||||
|
|
||||||
@ -75,7 +76,7 @@ class MuchaServlet:
|
|||||||
self.logger.debug(f"Decrypt SN to {sn_decrypt.hex()}")
|
self.logger.debug(f"Decrypt SN to {sn_decrypt.hex()}")
|
||||||
|
|
||||||
resp = MuchaAuthResponse(
|
resp = MuchaAuthResponse(
|
||||||
f"{self.config.mucha.hostname}{':' + str(self.config.allnet.port) if self.config.server.is_develop else ''}"
|
f"{self.config.server.hostname}{':' + str(self.config.server.port) if self.config.server.is_develop else ''}"
|
||||||
)
|
)
|
||||||
|
|
||||||
self.logger.debug(f"Mucha response {vars(resp)}")
|
self.logger.debug(f"Mucha response {vars(resp)}")
|
||||||
@ -100,7 +101,7 @@ class MuchaServlet:
|
|||||||
self.logger.warning(f"Unknown gameCd {req.gameCd}")
|
self.logger.warning(f"Unknown gameCd {req.gameCd}")
|
||||||
return b"RESULTS=000"
|
return b"RESULTS=000"
|
||||||
|
|
||||||
resp = MuchaUpdateResponse(req.gameVer, f"{self.config.mucha.hostname}{':' + str(self.config.allnet.port) if self.config.server.is_develop else ''}")
|
resp = MuchaUpdateResponse(req.gameVer, f"{self.config.server.hostname}{':' + str(self.config.server.port) if self.config.server.is_develop else ''}")
|
||||||
|
|
||||||
self.logger.debug(f"Mucha response {vars(resp)}")
|
self.logger.debug(f"Mucha response {vars(resp)}")
|
||||||
|
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
from typing import Dict, List, Tuple
|
from typing import Dict, List, Tuple
|
||||||
import logging, coloredlogs
|
import logging, coloredlogs
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
from twisted.web.http import Request
|
from starlette.requests import Request
|
||||||
|
from starlette.responses import Response
|
||||||
|
from starlette.routing import Route
|
||||||
|
|
||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
from core.data import Data
|
from core.data import Data
|
||||||
@ -28,18 +30,16 @@ class BaseServlet:
|
|||||||
"""
|
"""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
|
def get_routes(self) -> List[Route]:
|
||||||
"""Called during boot to get all matcher endpoints this title servlet handles
|
"""Called during boot to get all matcher endpoints this title servlet handles
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]: A 2-length tuple where offset 0 is GET and offset 1 is POST,
|
List[Route]: A list of Routes, WebSocketRoutes, or similar classes
|
||||||
containing a list of 3-length tuples where offset 0 is the name of the function in the handler that should be called, offset 1
|
|
||||||
is the matching string, and offset 2 is a dict containing rules for the matcher.
|
|
||||||
"""
|
"""
|
||||||
return (
|
return [
|
||||||
[("render_GET", "/{game}/{version}/{endpoint}", {'game': R'S...'})],
|
Route("/{game}/{version}/{endpoint}", self.render_POST, methods=["POST"]),
|
||||||
[("render_POST", "/{game}/{version}/{endpoint}", {'game': R'S...'})]
|
Route("/{game}/{version}/{endpoint}", self.render_GET, methods=["GET"]),
|
||||||
)
|
]
|
||||||
|
|
||||||
def setup(self) -> None:
|
def setup(self) -> None:
|
||||||
"""Called once during boot, should contain any additional setup the handler must do, such as starting any sub-services
|
"""Called once during boot, should contain any additional setup the handler must do, such as starting any sub-services
|
||||||
@ -58,11 +58,11 @@ class BaseServlet:
|
|||||||
Tuple[str, str]: A tuple where offset 0 is the allnet uri field, and offset 1 is the allnet host field
|
Tuple[str, str]: A tuple where offset 0 is the allnet uri field, and offset 1 is the allnet host field
|
||||||
"""
|
"""
|
||||||
if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80:
|
if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80:
|
||||||
return (f"http://{self.core_cfg.title.hostname}:{Utils.get_title_port(self.core_cfg)}/{game_code}/{game_ver}/", "")
|
return (f"http://{self.core_cfg.server.hostname}:{Utils.get_title_port(self.core_cfg)}/{game_code}/{game_ver}/", "")
|
||||||
|
|
||||||
return (f"http://{self.core_cfg.title.hostname}/{game_code}/{game_ver}/", "")
|
return (f"http://{self.core_cfg.server.hostname}/{game_code}/{game_ver}/", "")
|
||||||
|
|
||||||
def get_mucha_info(self, core_cfg: CoreConfig, cfg_dir: str) -> Tuple[bool, str]:
|
def get_mucha_info(self, core_cfg: CoreConfig, cfg_dir: str) -> Tuple[bool, List[str], List[str]]:
|
||||||
"""Called once during boot to check if this game is a mucha game
|
"""Called once during boot to check if this game is a mucha game
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -72,15 +72,15 @@ class BaseServlet:
|
|||||||
Returns:
|
Returns:
|
||||||
Tuple[bool, str]: Tuple where offset 0 is true if the game is enabled, false otherwise, and offset 1 is the game CD
|
Tuple[bool, str]: Tuple where offset 0 is true if the game is enabled, false otherwise, and offset 1 is the game CD
|
||||||
"""
|
"""
|
||||||
return (False, "")
|
return (False, [], [])
|
||||||
|
|
||||||
def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
async def render_POST(self, request: Request) -> bytes:
|
||||||
self.logger.warn(f"{game_code} Does not dispatch POST")
|
self.logger.warn(f"Game Does not dispatch POST")
|
||||||
return None
|
return Response()
|
||||||
|
|
||||||
def render_GET(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
async def render_GET(self, request: Request) -> bytes:
|
||||||
self.logger.warn(f"{game_code} Does not dispatch GET")
|
self.logger.warn(f"Game Does not dispatch GET")
|
||||||
return None
|
return Response()
|
||||||
|
|
||||||
class TitleServlet:
|
class TitleServlet:
|
||||||
title_registry: Dict[str, BaseServlet] = {}
|
title_registry: Dict[str, BaseServlet] = {}
|
||||||
@ -136,7 +136,7 @@ class TitleServlet:
|
|||||||
self.logger.error(f"{folder} missing game_code or index in __init__.py, or is_game_enabled in index")
|
self.logger.error(f"{folder} missing game_code or index in __init__.py, or is_game_enabled in index")
|
||||||
|
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
f"Serving {len(self.title_registry)} game codes {'on port ' + str(core_cfg.title.port) if core_cfg.title.port > 0 else ''}"
|
f"Serving {len(self.title_registry)} game codes {'on port ' + str(core_cfg.server.port) if core_cfg.server.port > 0 else ''}"
|
||||||
)
|
)
|
||||||
|
|
||||||
def render_GET(self, request: Request, endpoints: dict) -> bytes:
|
def render_GET(self, request: Request, endpoints: dict) -> bytes:
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from typing import Dict, Any, Optional
|
from typing import Dict, Any, Optional
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
from twisted.web.http import Request
|
from starlette.requests import Request
|
||||||
import logging
|
import logging
|
||||||
import importlib
|
import importlib
|
||||||
from os import walk
|
from os import walk
|
||||||
@ -34,36 +34,16 @@ class Utils:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_ip_addr(cls, req: Request) -> str:
|
def get_ip_addr(cls, req: Request) -> str:
|
||||||
return (
|
return req.headers.get("x-forwarded-for", req.client.host)
|
||||||
req.getAllHeaders()[b"x-forwarded-for"].decode()
|
|
||||||
if b"x-forwarded-for" in req.getAllHeaders()
|
|
||||||
else req.getClientAddress().host
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_title_port(cls, cfg: CoreConfig):
|
def get_title_port(cls, cfg: CoreConfig):
|
||||||
if cls.real_title_port is not None: return cls.real_title_port
|
if cls.real_title_port is not None: return cls.real_title_port
|
||||||
|
|
||||||
if cfg.title.port == 0:
|
cls.real_title_port = cfg.server.proxy_port if cfg.server.is_using_proxy else cfg.server.port
|
||||||
cls.real_title_port = cfg.allnet.port
|
|
||||||
|
|
||||||
else:
|
|
||||||
cls.real_title_port = cfg.title.port
|
|
||||||
|
|
||||||
return cls.real_title_port
|
return cls.real_title_port
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_title_port_ssl(cls, cfg: CoreConfig):
|
|
||||||
if cls.real_title_port_ssl is not None: return cls.real_title_port_ssl
|
|
||||||
|
|
||||||
if cfg.title.port_ssl == 0:
|
|
||||||
cls.real_title_port_ssl = 443
|
|
||||||
|
|
||||||
else:
|
|
||||||
cls.real_title_port_ssl = cfg.title.port_ssl
|
|
||||||
|
|
||||||
return cls.real_title_port_ssl
|
|
||||||
|
|
||||||
def create_sega_auth_key(aime_id: int, game: str, place_id: int, keychip_id: str, b64_secret: str, exp_seconds: int = 86400, err_logger: str = 'aimedb') -> Optional[str]:
|
def create_sega_auth_key(aime_id: int, game: str, place_id: int, keychip_id: str, b64_secret: str, exp_seconds: int = 86400, err_logger: str = 'aimedb') -> Optional[str]:
|
||||||
logger = logging.getLogger(err_logger)
|
logger = logging.getLogger(err_logger)
|
||||||
try:
|
try:
|
||||||
|
67
dbutils.py
67
dbutils.py
@ -1,9 +1,11 @@
|
|||||||
import yaml
|
#!/usr/bin/env python3
|
||||||
import argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
from core.config import CoreConfig
|
from os import mkdir, path, access, W_OK
|
||||||
from core.data import Data
|
import yaml
|
||||||
from os import path, mkdir, access, W_OK
|
import asyncio
|
||||||
|
|
||||||
|
from core import Data, CoreConfig
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(description="Database utilities")
|
parser = argparse.ArgumentParser(description="Database utilities")
|
||||||
@ -16,19 +18,9 @@ if __name__ == "__main__":
|
|||||||
type=str,
|
type=str,
|
||||||
help="Version of the database to upgrade/rollback to",
|
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("--access_code", "-a", type=str, help="Access code for new/transfer user", default="00000000000000000000")
|
||||||
parser.add_argument("--new_ac", "-n", type=str, help="Access code to transfer to")
|
parser.add_argument("action", type=str, help="create, upgrade, create-owner")
|
||||||
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"
|
|
||||||
)
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
cfg = CoreConfig()
|
cfg = CoreConfig()
|
||||||
@ -50,42 +42,13 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
if args.action == "create":
|
if args.action == "create":
|
||||||
data.create_database()
|
data.create_database()
|
||||||
|
|
||||||
elif args.action == "recreate":
|
elif args.action == "upgrade":
|
||||||
data.recreate_database()
|
data.schema_upgrade(args.version)
|
||||||
|
|
||||||
elif args.action == "upgrade" or args.action == "rollback":
|
|
||||||
if args.version is None:
|
|
||||||
data.logger.warning("No version set, upgrading to latest")
|
|
||||||
|
|
||||||
if args.game is None:
|
|
||||||
data.logger.warning("No game set, upgrading core schema")
|
|
||||||
data.migrate_database(
|
|
||||||
"CORE",
|
|
||||||
int(args.version) if args.version is not None else None,
|
|
||||||
args.action,
|
|
||||||
)
|
|
||||||
|
|
||||||
else:
|
|
||||||
data.migrate_database(
|
|
||||||
args.game,
|
|
||||||
int(args.version) if args.version is not None else None,
|
|
||||||
args.action,
|
|
||||||
)
|
|
||||||
|
|
||||||
elif args.action == "autoupgrade":
|
|
||||||
data.autoupgrade()
|
|
||||||
|
|
||||||
elif args.action == "create-owner":
|
elif args.action == "create-owner":
|
||||||
data.create_owner(args.email)
|
loop = asyncio.get_event_loop()
|
||||||
|
loop.run_until_complete(data.create_owner(args.email, args.access_code))
|
||||||
|
|
||||||
elif args.action == "migrate-card":
|
else:
|
||||||
data.migrate_card(args.old_ac, args.new_ac, args.force)
|
logging.getLogger("database").info(f"Unknown action {args.action}")
|
||||||
|
|
||||||
elif args.action == "cleanup":
|
|
||||||
data.delete_hanging_users()
|
|
||||||
|
|
||||||
elif args.action == "version":
|
|
||||||
data.show_versions()
|
|
||||||
|
|
||||||
data.logger.info("Done")
|
|
||||||
|
@ -1,26 +1,24 @@
|
|||||||
server:
|
server:
|
||||||
listen_address: "127.0.0.1"
|
listen_address: "127.0.0.1"
|
||||||
|
hostname: "localhost"
|
||||||
|
port: 8080
|
||||||
|
ssl_key: "cert/title.key"
|
||||||
|
ssl_cert: "cert/title.crt"
|
||||||
allow_user_registration: True
|
allow_user_registration: True
|
||||||
allow_unregistered_serials: True
|
allow_unregistered_serials: True
|
||||||
name: "ARTEMiS"
|
name: "ARTEMiS"
|
||||||
is_develop: True
|
is_develop: True
|
||||||
is_using_proxy: False
|
is_using_proxy: False
|
||||||
threading: False
|
proxy_port: 0
|
||||||
log_dir: "logs"
|
log_dir: "logs"
|
||||||
check_arcade_ip: False
|
check_arcade_ip: False
|
||||||
strict_ip_checking: False
|
strict_ip_checking: False
|
||||||
|
|
||||||
title:
|
title:
|
||||||
loglevel: "info"
|
loglevel: "info"
|
||||||
hostname: "localhost"
|
|
||||||
port: 8080
|
|
||||||
port_ssl: 0
|
|
||||||
ssl_cert: "cert/title.crt"
|
|
||||||
ssl_key: "cert/title.key"
|
|
||||||
reboot_start_time: "04:00"
|
reboot_start_time: "04:00"
|
||||||
reboot_end_time: "05:00"
|
reboot_end_time: "05:00"
|
||||||
|
ssl_key: "cert/title.key"
|
||||||
|
|
||||||
database:
|
database:
|
||||||
host: "localhost"
|
host: "localhost"
|
||||||
username: "aime"
|
username: "aime"
|
||||||
@ -30,23 +28,22 @@ database:
|
|||||||
protocol: "mysql"
|
protocol: "mysql"
|
||||||
sha2_password: False
|
sha2_password: False
|
||||||
loglevel: "warn"
|
loglevel: "warn"
|
||||||
user_table_autoincrement_start: 10000
|
|
||||||
enable_memcached: True
|
enable_memcached: True
|
||||||
memcached_host: "localhost"
|
memcached_host: "localhost"
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
enable: False
|
standalone: True
|
||||||
port: 8090
|
|
||||||
loglevel: "info"
|
loglevel: "info"
|
||||||
|
secret: ""
|
||||||
|
|
||||||
allnet:
|
allnet:
|
||||||
loglevel: "info"
|
loglevel: "info"
|
||||||
port: 80
|
|
||||||
ip_check: False
|
|
||||||
allow_online_updates: False
|
allow_online_updates: False
|
||||||
update_cfg_folder: ""
|
update_cfg_folder: ""
|
||||||
|
|
||||||
billing:
|
billing:
|
||||||
|
standalone: True
|
||||||
|
loglevel: "info"
|
||||||
port: 8443
|
port: 8443
|
||||||
ssl_key: "cert/server.key"
|
ssl_key: "cert/server.key"
|
||||||
ssl_cert: "cert/server.pem"
|
ssl_cert: "cert/server.pem"
|
||||||
@ -60,6 +57,4 @@ aimedb:
|
|||||||
id_lifetime_seconds: 86400
|
id_lifetime_seconds: 86400
|
||||||
|
|
||||||
mucha:
|
mucha:
|
||||||
enable: False
|
|
||||||
hostname: "localhost"
|
|
||||||
loglevel: "info"
|
loglevel: "info"
|
||||||
|
375
index.py
375
index.py
@ -1,335 +1,92 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import argparse
|
import argparse
|
||||||
import logging, coloredlogs
|
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
|
||||||
from typing import Dict
|
|
||||||
import yaml
|
import yaml
|
||||||
from os import path, mkdir, access, W_OK
|
from os import path, mkdir, access, W_OK, environ
|
||||||
from core import *
|
import uvicorn
|
||||||
|
import logging
|
||||||
|
import asyncio
|
||||||
|
|
||||||
from twisted.web import server, resource
|
from core import CoreConfig, AimedbServlette
|
||||||
from twisted.internet import reactor, endpoints
|
|
||||||
from twisted.web.http import Request
|
|
||||||
from routes import Mapper
|
|
||||||
from threading import Thread
|
|
||||||
|
|
||||||
class HttpDispatcher(resource.Resource):
|
async def launch_main(cfg: CoreConfig, ssl: bool) -> None:
|
||||||
def __init__(self, cfg: CoreConfig, config_dir: str):
|
if ssl:
|
||||||
super().__init__()
|
server_cfg = uvicorn.Config(
|
||||||
self.config = cfg
|
"core.app:app",
|
||||||
self.isLeaf = True
|
host=cfg.server.listen_address,
|
||||||
self.map_get = Mapper()
|
port=cfg.server.port if args.port == 0 else args.port,
|
||||||
self.map_post = Mapper()
|
reload=cfg.server.is_develop,
|
||||||
self.logger = logging.getLogger("core")
|
log_level="info" if cfg.server.is_develop else "critical",
|
||||||
|
ssl_version=3,
|
||||||
self.title = TitleServlet(cfg, config_dir)
|
ssl_certfile=cfg.server.ssl_cert,
|
||||||
self.allnet = AllnetServlet(cfg, config_dir)
|
ssl_keyfile=cfg.server.ssl_key
|
||||||
self.mucha = MuchaServlet(cfg, config_dir)
|
)
|
||||||
|
else:
|
||||||
self.map_get.connect(
|
server_cfg = uvicorn.Config(
|
||||||
"allnet_downloadorder_ini",
|
"core.app:app",
|
||||||
"/dl/ini/{file}",
|
host=cfg.server.listen_address,
|
||||||
controller="allnet",
|
port=cfg.server.port if args.port == 0 else args.port,
|
||||||
action="handle_dlorder_ini",
|
reload=cfg.server.is_develop,
|
||||||
conditions=dict(method=["GET"]),
|
log_level="info" if cfg.server.is_develop else "critical"
|
||||||
)
|
)
|
||||||
|
server = uvicorn.Server(server_cfg)
|
||||||
|
await server.serve()
|
||||||
|
|
||||||
self.map_post.connect(
|
async def launch_billing(cfg: CoreConfig) -> None:
|
||||||
"allnet_downloadorder_report",
|
server_cfg = uvicorn.Config(
|
||||||
"/report-api/Report",
|
"core.allnet:app",
|
||||||
controller="allnet",
|
host=cfg.server.listen_address,
|
||||||
action="handle_dlorder_report",
|
port=cfg.billing.port,
|
||||||
conditions=dict(method=["POST"]),
|
reload=cfg.server.is_develop,
|
||||||
|
log_level="info" if cfg.server.is_develop else "critical",
|
||||||
|
ssl_version=3,
|
||||||
|
ssl_certfile=cfg.billing.ssl_cert,
|
||||||
|
ssl_keyfile=cfg.billing.ssl_key
|
||||||
)
|
)
|
||||||
|
server = uvicorn.Server(server_cfg)
|
||||||
|
if cfg.billing.standalone:
|
||||||
|
await server.serve()
|
||||||
|
else:
|
||||||
|
while True:
|
||||||
|
pass
|
||||||
|
|
||||||
self.map_get.connect(
|
|
||||||
"allnet_ping",
|
|
||||||
"/naomitest.html",
|
|
||||||
controller="allnet",
|
|
||||||
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_loaderstaterecorder",
|
|
||||||
"/sys/servlet/LoaderStateRecorder",
|
|
||||||
controller="allnet",
|
|
||||||
action="handle_loaderstaterecorder",
|
|
||||||
conditions=dict(method=["POST"]),
|
|
||||||
)
|
|
||||||
self.map_post.connect(
|
|
||||||
"allnet_alive",
|
|
||||||
"/sys/servlet/Alive",
|
|
||||||
controller="allnet",
|
|
||||||
action="handle_alive",
|
|
||||||
conditions=dict(method=["POST"]),
|
|
||||||
)
|
|
||||||
self.map_get.connect(
|
|
||||||
"allnet_alive",
|
|
||||||
"/sys/servlet/Alive",
|
|
||||||
controller="allnet",
|
|
||||||
action="handle_alive",
|
|
||||||
conditions=dict(method=["GET"]),
|
|
||||||
)
|
|
||||||
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"]),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Maintain compatability
|
async def launcher(cfg: CoreConfig, ssl: bool) -> None:
|
||||||
self.map_post.connect(
|
AimedbServlette(cfg).start()
|
||||||
"mucha_boardauth",
|
done, pending = await asyncio.wait(
|
||||||
"/mucha/boardauth.do",
|
[
|
||||||
controller="mucha",
|
asyncio.create_task(launch_main(cfg, ssl)),
|
||||||
action="handle_boardauth",
|
asyncio.create_task(launch_billing(cfg)),
|
||||||
conditions=dict(method=["POST"]),
|
|
||||||
)
|
|
||||||
self.map_post.connect(
|
|
||||||
"mucha_updatacheck",
|
|
||||||
"/mucha/updatacheck.do",
|
|
||||||
controller="mucha",
|
|
||||||
action="handle_updatecheck",
|
|
||||||
conditions=dict(method=["POST"]),
|
|
||||||
)
|
|
||||||
self.map_post.connect(
|
|
||||||
"mucha_dlstate",
|
|
||||||
"/mucha/downloadstate.do",
|
|
||||||
controller="mucha",
|
|
||||||
action="handle_dlstate",
|
|
||||||
conditions=dict(method=["POST"]),
|
|
||||||
)
|
|
||||||
|
|
||||||
self.map_post.connect(
|
|
||||||
"mucha_boardauth",
|
|
||||||
"/mucha_front/boardauth.do",
|
|
||||||
controller="mucha",
|
|
||||||
action="handle_boardauth",
|
|
||||||
conditions=dict(method=["POST"]),
|
|
||||||
)
|
|
||||||
self.map_post.connect(
|
|
||||||
"mucha_updatacheck",
|
|
||||||
"/mucha_front/updatacheck.do",
|
|
||||||
controller="mucha",
|
|
||||||
action="handle_updatecheck",
|
|
||||||
conditions=dict(method=["POST"]),
|
|
||||||
)
|
|
||||||
self.map_post.connect(
|
|
||||||
"mucha_dlstate",
|
|
||||||
"/mucha_front/downloadstate.do",
|
|
||||||
controller="mucha",
|
|
||||||
action="handle_dlstate",
|
|
||||||
conditions=dict(method=["POST"]),
|
|
||||||
)
|
|
||||||
|
|
||||||
for code, game in self.title.title_registry.items():
|
|
||||||
get_matchers, post_matchers = game.get_endpoint_matchers()
|
|
||||||
|
|
||||||
for m in get_matchers:
|
|
||||||
self.map_get.connect(
|
|
||||||
"title_get",
|
|
||||||
m[1],
|
|
||||||
controller="title",
|
|
||||||
action="render_GET",
|
|
||||||
title=code,
|
|
||||||
subaction=m[0],
|
|
||||||
conditions=dict(method=["GET"]),
|
|
||||||
requirements=m[2],
|
|
||||||
)
|
|
||||||
|
|
||||||
for m in post_matchers:
|
|
||||||
self.map_post.connect(
|
|
||||||
"title_post",
|
|
||||||
m[1],
|
|
||||||
controller="title",
|
|
||||||
action="render_POST",
|
|
||||||
title=code,
|
|
||||||
subaction=m[0],
|
|
||||||
conditions=dict(method=["POST"]),
|
|
||||||
requirements=m[2],
|
|
||||||
)
|
|
||||||
|
|
||||||
def render_GET(self, request: Request) -> bytes:
|
|
||||||
test = self.map_get.match(request.uri.decode())
|
|
||||||
client_ip = Utils.get_ip_addr(request)
|
|
||||||
|
|
||||||
if test is None:
|
|
||||||
self.logger.debug(
|
|
||||||
f"Unknown GET endpoint {request.uri.decode()} from {client_ip} to port {request.getHost().port}"
|
|
||||||
)
|
|
||||||
request.setResponseCode(404)
|
|
||||||
return b"Endpoint not found."
|
|
||||||
|
|
||||||
return self.dispatch(test, request)
|
|
||||||
|
|
||||||
def render_POST(self, request: Request) -> bytes:
|
|
||||||
test = self.map_post.match(request.uri.decode())
|
|
||||||
client_ip = Utils.get_ip_addr(request)
|
|
||||||
|
|
||||||
if test is None:
|
|
||||||
self.logger.debug(
|
|
||||||
f"Unknown POST endpoint {request.uri.decode()} from {client_ip} to port {request.getHost().port}"
|
|
||||||
)
|
|
||||||
request.setResponseCode(404)
|
|
||||||
return b"Endpoint not found."
|
|
||||||
|
|
||||||
return self.dispatch(test, request)
|
|
||||||
|
|
||||||
def dispatch(self, matcher: Dict, request: Request) -> bytes:
|
|
||||||
controller = getattr(self, matcher["controller"], None)
|
|
||||||
if controller is None:
|
|
||||||
self.logger.error(
|
|
||||||
f"Controller {matcher['controller']} not found via endpoint {request.uri.decode()}"
|
|
||||||
)
|
|
||||||
request.setResponseCode(404)
|
|
||||||
return b"Endpoint not found."
|
|
||||||
|
|
||||||
handler = getattr(controller, matcher["action"], None)
|
|
||||||
if handler is None:
|
|
||||||
self.logger.error(
|
|
||||||
f"Action {matcher['action']} not found in controller {matcher['controller']} via endpoint {request.uri.decode()}"
|
|
||||||
)
|
|
||||||
request.setResponseCode(404)
|
|
||||||
return b"Endpoint not found."
|
|
||||||
|
|
||||||
url_vars = matcher
|
|
||||||
url_vars.pop("controller")
|
|
||||||
url_vars.pop("action")
|
|
||||||
ret = handler(request, url_vars)
|
|
||||||
|
|
||||||
if type(ret) == str:
|
|
||||||
return ret.encode()
|
|
||||||
|
|
||||||
elif type(ret) == bytes or type(ret) == tuple: # allow for bytes or tuple (data, response code) responses
|
|
||||||
return ret
|
|
||||||
|
|
||||||
elif ret is None:
|
|
||||||
self.logger.warning(f"None returned by controller for {request.uri.decode()} endpoint")
|
|
||||||
return b""
|
|
||||||
|
|
||||||
else:
|
|
||||||
self.logger.warning(f"Unknown data type returned by controller for {request.uri.decode()} endpoint")
|
|
||||||
return b""
|
|
||||||
|
|
||||||
|
],
|
||||||
|
return_when=asyncio.FIRST_COMPLETED,
|
||||||
|
)
|
||||||
|
|
||||||
|
logging.getLogger("core").info("Shutdown")
|
||||||
|
for pending_task in pending:
|
||||||
|
pending_task.cancel("Another service died, server is shutting down")
|
||||||
|
|
||||||
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(
|
parser.add_argument(
|
||||||
"--config", "-c", type=str, default="config", help="Configuration folder"
|
"--config", "-c", type=str, default="config", help="Configuration folder"
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--port", "-p", type=int, default=0, help="Port override"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--ssl", "-s", type=bool, help="Launch with SSL"
|
||||||
|
)
|
||||||
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(
|
print(
|
||||||
f"The config folder you specified ({args.config}) does not exist or does not contain core.yaml.\nDid you copy the example folder?"
|
f"The config folder you specified ({args.config}) does not exist or does not contain core.yaml. Defaults will be used.\nDid you copy the example folder?"
|
||||||
)
|
)
|
||||||
exit(1)
|
|
||||||
|
|
||||||
cfg: CoreConfig = CoreConfig()
|
cfg: CoreConfig = CoreConfig()
|
||||||
if path.exists(f"{args.config}/core.yaml"):
|
if path.exists(f"{args.config}/core.yaml"):
|
||||||
cfg.update(yaml.safe_load(open(f"{args.config}/core.yaml")))
|
cfg.update(yaml.safe_load(open(f"{args.config}/core.yaml")))
|
||||||
|
|
||||||
if not path.exists(cfg.server.log_dir):
|
environ["ARTEMIS_CFG_DIR"] = args.config
|
||||||
mkdir(cfg.server.log_dir)
|
|
||||||
|
|
||||||
if not access(cfg.server.log_dir, W_OK):
|
asyncio.run(launcher(cfg, args.ssl))
|
||||||
print(
|
|
||||||
f"Log directory {cfg.server.log_dir} NOT writable, please check permissions"
|
|
||||||
)
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
logger = logging.getLogger("core")
|
|
||||||
log_fmt_str = "[%(asctime)s] Core | %(levelname)s | %(message)s"
|
|
||||||
log_fmt = logging.Formatter(log_fmt_str)
|
|
||||||
|
|
||||||
fileHandler = TimedRotatingFileHandler(
|
|
||||||
"{0}/{1}.log".format(cfg.server.log_dir, "core"), when="d", backupCount=10
|
|
||||||
)
|
|
||||||
fileHandler.setFormatter(log_fmt)
|
|
||||||
|
|
||||||
consoleHandler = logging.StreamHandler()
|
|
||||||
consoleHandler.setFormatter(log_fmt)
|
|
||||||
|
|
||||||
logger.addHandler(fileHandler)
|
|
||||||
logger.addHandler(consoleHandler)
|
|
||||||
|
|
||||||
log_lv = logging.DEBUG if cfg.server.is_develop else logging.INFO
|
|
||||||
logger.setLevel(log_lv)
|
|
||||||
coloredlogs.install(level=log_lv, logger=logger, fmt=log_fmt_str)
|
|
||||||
|
|
||||||
if not cfg.aimedb.key:
|
|
||||||
logger.error("!!AIMEDB KEY BLANK, SET KEY IN CORE.YAML!!")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
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}"
|
|
||||||
title_server_str = f"tcp:{cfg.title.port}:interface={cfg.server.listen_address}"
|
|
||||||
title_https_server_str = f"ssl:{cfg.title.port_ssl}:interface={cfg.server.listen_address}:privateKey={cfg.title.ssl_key}:certKey={cfg.title.ssl_cert}"
|
|
||||||
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}"
|
|
||||||
)
|
|
||||||
|
|
||||||
billing_server_str = f"tcp:{cfg.billing.port}:interface={cfg.server.listen_address}"
|
|
||||||
if cfg.server.is_develop:
|
|
||||||
billing_server_str = (
|
|
||||||
f"ssl:{cfg.billing.port}:interface={cfg.server.listen_address}"
|
|
||||||
f":privateKey={cfg.billing.ssl_key}:certKey={cfg.billing.ssl_cert}"
|
|
||||||
)
|
|
||||||
|
|
||||||
dispatcher = HttpDispatcher(cfg, args.config)
|
|
||||||
|
|
||||||
endpoints.serverFromString(reactor, allnet_server_str).listen(
|
|
||||||
server.Site(dispatcher)
|
|
||||||
)
|
|
||||||
endpoints.serverFromString(reactor, adb_server_str).listen(AimedbFactory(cfg))
|
|
||||||
|
|
||||||
if cfg.frontend.enable:
|
|
||||||
endpoints.serverFromString(reactor, frontend_server_str).listen(
|
|
||||||
server.Site(FrontendServlet(cfg, args.config))
|
|
||||||
)
|
|
||||||
|
|
||||||
if cfg.billing.port > 0:
|
|
||||||
endpoints.serverFromString(reactor, billing_server_str).listen(
|
|
||||||
server.Site(dispatcher)
|
|
||||||
)
|
|
||||||
|
|
||||||
if cfg.title.port > 0:
|
|
||||||
endpoints.serverFromString(reactor, title_server_str).listen(
|
|
||||||
server.Site(dispatcher)
|
|
||||||
)
|
|
||||||
|
|
||||||
if cfg.title.port_ssl > 0:
|
|
||||||
endpoints.serverFromString(reactor, title_https_server_str).listen(
|
|
||||||
server.Site(dispatcher)
|
|
||||||
)
|
|
||||||
|
|
||||||
if cfg.server.threading:
|
|
||||||
Thread(target=reactor.run, args=(False,)).start()
|
|
||||||
else:
|
|
||||||
reactor.run()
|
|
||||||
|
2
read.py
2
read.py
@ -1,4 +1,4 @@
|
|||||||
# vim: set fileencoding=utf-8
|
#!/usr/bin/env python3
|
||||||
import argparse
|
import argparse
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
mypy
|
mypy
|
||||||
wheel
|
wheel
|
||||||
twisted
|
|
||||||
pytz
|
pytz
|
||||||
pyyaml
|
pyyaml
|
||||||
sqlalchemy==1.4.46
|
sqlalchemy==1.4.46
|
||||||
@ -12,10 +11,11 @@ inflection
|
|||||||
coloredlogs
|
coloredlogs
|
||||||
pylibmc; platform_system != "Windows"
|
pylibmc; platform_system != "Windows"
|
||||||
wacky
|
wacky
|
||||||
Routes
|
|
||||||
bcrypt
|
bcrypt
|
||||||
jinja2
|
jinja2
|
||||||
protobuf
|
protobuf
|
||||||
autobahn
|
|
||||||
pillow
|
pillow
|
||||||
pyjwt
|
pyjwt==2.8.0
|
||||||
|
websockets
|
||||||
|
starlette
|
||||||
|
asyncio
|
||||||
|
@ -11,7 +11,7 @@ class ChuniAir(ChuniBase):
|
|||||||
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:
|
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||||
ret = super().handle_get_game_setting_api_request(data)
|
ret = await super().handle_get_game_setting_api_request(data)
|
||||||
ret["gameSetting"]["dataVersion"] = "1.10.00"
|
ret["gameSetting"]["dataVersion"] = "1.10.00"
|
||||||
return ret
|
return ret
|
||||||
|
@ -11,7 +11,7 @@ class ChuniAirPlus(ChuniBase):
|
|||||||
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:
|
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||||
ret = super().handle_get_game_setting_api_request(data)
|
ret = await super().handle_get_game_setting_api_request(data)
|
||||||
ret["gameSetting"]["dataVersion"] = "1.15.00"
|
ret["gameSetting"]["dataVersion"] = "1.15.00"
|
||||||
return ret
|
return ret
|
||||||
|
@ -13,7 +13,7 @@ class ChuniAmazon(ChuniBase):
|
|||||||
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:
|
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||||
ret = super().handle_get_game_setting_api_request(data)
|
ret = await super().handle_get_game_setting_api_request(data)
|
||||||
ret["gameSetting"]["dataVersion"] = "1.30.00"
|
ret["gameSetting"]["dataVersion"] = "1.30.00"
|
||||||
return ret
|
return ret
|
||||||
|
@ -13,7 +13,7 @@ class ChuniAmazonPlus(ChuniBase):
|
|||||||
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:
|
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||||
ret = super().handle_get_game_setting_api_request(data)
|
ret = await super().handle_get_game_setting_api_request(data)
|
||||||
ret["gameSetting"]["dataVersion"] = "1.35.00"
|
ret["gameSetting"]["dataVersion"] = "1.35.00"
|
||||||
return ret
|
return ret
|
||||||
|
@ -22,7 +22,7 @@ class ChuniBase:
|
|||||||
self.game = ChuniConstants.GAME_CODE
|
self.game = ChuniConstants.GAME_CODE
|
||||||
self.version = ChuniConstants.VER_CHUNITHM
|
self.version = ChuniConstants.VER_CHUNITHM
|
||||||
|
|
||||||
def handle_game_login_api_request(self, data: Dict) -> Dict:
|
async def handle_game_login_api_request(self, data: Dict) -> Dict:
|
||||||
"""
|
"""
|
||||||
Handles the login bonus logic, required for the game because
|
Handles the login bonus logic, required for the game because
|
||||||
getUserLoginBonus gets called after getUserItem and therefore the
|
getUserLoginBonus gets called after getUserItem and therefore the
|
||||||
@ -119,11 +119,11 @@ class ChuniBase:
|
|||||||
|
|
||||||
return {"returnCode": 1}
|
return {"returnCode": 1}
|
||||||
|
|
||||||
def handle_game_logout_api_request(self, data: Dict) -> Dict:
|
async 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:
|
async 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)
|
||||||
|
|
||||||
if game_charge_list is None or len(game_charge_list) == 0:
|
if game_charge_list is None or len(game_charge_list) == 0:
|
||||||
@ -145,7 +145,7 @@ class ChuniBase:
|
|||||||
)
|
)
|
||||||
return {"length": len(charges), "gameChargeList": charges}
|
return {"length": len(charges), "gameChargeList": charges}
|
||||||
|
|
||||||
def handle_get_game_event_api_request(self, data: Dict) -> Dict:
|
async 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)
|
||||||
|
|
||||||
if game_events is None or len(game_events) == 0:
|
if game_events is None or len(game_events) == 0:
|
||||||
@ -177,10 +177,10 @@ class ChuniBase:
|
|||||||
"gameEventList": event_list,
|
"gameEventList": event_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_game_idlist_api_request(self, data: Dict) -> Dict:
|
async 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:
|
async def handle_get_game_message_api_request(self, data: Dict) -> Dict:
|
||||||
return {
|
return {
|
||||||
"type": data["type"],
|
"type": data["type"],
|
||||||
"length": 1,
|
"length": 1,
|
||||||
@ -193,14 +193,14 @@ class ChuniBase:
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_game_ranking_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_ranking_api_request(self, data: Dict) -> Dict:
|
||||||
rankings = self.data.score.get_rankings(self.version)
|
rankings = self.data.score.get_rankings(self.version)
|
||||||
return {"type": data["type"], "gameRankingList": rankings}
|
return {"type": data["type"], "gameRankingList": rankings}
|
||||||
|
|
||||||
def handle_get_game_sale_api_request(self, data: Dict) -> Dict:
|
async 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:
|
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||||
# if reboot start/end time is not defined use the default behavior of being a few hours ago
|
# if reboot start/end time is not defined use the default behavior of being a few hours ago
|
||||||
if self.core_cfg.title.reboot_start_time == "" or self.core_cfg.title.reboot_end_time == "":
|
if self.core_cfg.title.reboot_start_time == "" or self.core_cfg.title.reboot_end_time == "":
|
||||||
reboot_start = datetime.strftime(
|
reboot_start = datetime.strftime(
|
||||||
@ -240,7 +240,7 @@ class ChuniBase:
|
|||||||
"isDumpUpload": "false",
|
"isDumpUpload": "false",
|
||||||
"isAou": "false",
|
"isAou": "false",
|
||||||
}
|
}
|
||||||
def handle_get_user_activity_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_activity_api_request(self, data: Dict) -> Dict:
|
||||||
user_activity_list = self.data.profile.get_profile_activity(
|
user_activity_list = self.data.profile.get_profile_activity(
|
||||||
data["userId"], data["kind"]
|
data["userId"], data["kind"]
|
||||||
)
|
)
|
||||||
@ -261,7 +261,7 @@ class ChuniBase:
|
|||||||
"userActivityList": activity_list,
|
"userActivityList": activity_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_character_api_request(self, data: Dict) -> Dict:
|
async 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:
|
if characters is None:
|
||||||
return {
|
return {
|
||||||
@ -296,7 +296,7 @@ class ChuniBase:
|
|||||||
"userCharacterList": character_list,
|
"userCharacterList": character_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_charge_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_charge_api_request(self, data: Dict) -> Dict:
|
||||||
user_charge_list = self.data.profile.get_profile_charge(data["userId"])
|
user_charge_list = self.data.profile.get_profile_charge(data["userId"])
|
||||||
|
|
||||||
charge_list = []
|
charge_list = []
|
||||||
@ -312,14 +312,14 @@ class ChuniBase:
|
|||||||
"userChargeList": charge_list,
|
"userChargeList": charge_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_recent_player_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_recent_player_api_request(self, data: Dict) -> Dict:
|
||||||
return {
|
return {
|
||||||
"userId": data["userId"],
|
"userId": data["userId"],
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"userRecentPlayerList": [], # playUserId, playUserName, playDate, friendPoint
|
"userRecentPlayerList": [], # playUserId, playUserName, playDate, friendPoint
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_course_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_course_api_request(self, data: Dict) -> Dict:
|
||||||
user_course_list = self.data.score.get_courses(data["userId"])
|
user_course_list = self.data.score.get_courses(data["userId"])
|
||||||
if user_course_list is None:
|
if user_course_list is None:
|
||||||
return {
|
return {
|
||||||
@ -354,7 +354,7 @@ class ChuniBase:
|
|||||||
"userCourseList": course_list,
|
"userCourseList": course_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_data_api_request(self, data: Dict) -> Dict:
|
async 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:
|
if p is None:
|
||||||
return {}
|
return {}
|
||||||
@ -366,7 +366,7 @@ class ChuniBase:
|
|||||||
|
|
||||||
return {"userId": data["userId"], "userData": profile}
|
return {"userId": data["userId"], "userData": profile}
|
||||||
|
|
||||||
def handle_get_user_data_ex_api_request(self, data: Dict) -> Dict:
|
async 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:
|
if p is None:
|
||||||
return {}
|
return {}
|
||||||
@ -378,7 +378,7 @@ class ChuniBase:
|
|||||||
|
|
||||||
return {"userId": data["userId"], "userDataEx": profile}
|
return {"userId": data["userId"], "userDataEx": profile}
|
||||||
|
|
||||||
def handle_get_user_duel_api_request(self, data: Dict) -> Dict:
|
async 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:
|
if user_duel_list is None:
|
||||||
return {}
|
return {}
|
||||||
@ -396,7 +396,7 @@ class ChuniBase:
|
|||||||
"userDuelList": duel_list,
|
"userDuelList": duel_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_rival_data_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_rival_data_api_request(self, data: Dict) -> Dict:
|
||||||
p = self.data.profile.get_rival(data["rivalId"])
|
p = self.data.profile.get_rival(data["rivalId"])
|
||||||
if p is None:
|
if p is None:
|
||||||
return {}
|
return {}
|
||||||
@ -409,7 +409,7 @@ class ChuniBase:
|
|||||||
"userRivalData": userRivalData
|
"userRivalData": userRivalData
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_rival_music_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_rival_music_api_request(self, data: Dict) -> Dict:
|
||||||
rival_id = data["rivalId"]
|
rival_id = data["rivalId"]
|
||||||
next_index = int(data["nextIndex"])
|
next_index = int(data["nextIndex"])
|
||||||
max_count = int(data["maxCount"])
|
max_count = int(data["maxCount"])
|
||||||
@ -462,7 +462,7 @@ class ChuniBase:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def handle_get_user_favorite_item_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_favorite_item_api_request(self, data: Dict) -> Dict:
|
||||||
user_fav_item_list = []
|
user_fav_item_list = []
|
||||||
|
|
||||||
# still needs to be implemented on WebUI
|
# still needs to be implemented on WebUI
|
||||||
@ -482,14 +482,14 @@ class ChuniBase:
|
|||||||
"userFavoriteItemList": user_fav_item_list,
|
"userFavoriteItemList": user_fav_item_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_favorite_music_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_favorite_music_api_request(self, data: Dict) -> Dict:
|
||||||
"""
|
"""
|
||||||
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 {"userId": data["userId"], "length": 0, "userFavoriteMusicList": []}
|
return {"userId": data["userId"], "length": 0, "userFavoriteMusicList": []}
|
||||||
|
|
||||||
def handle_get_user_item_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_item_api_request(self, data: Dict) -> Dict:
|
||||||
kind = int(int(data["nextIndex"]) / 10000000000)
|
kind = int(int(data["nextIndex"]) / 10000000000)
|
||||||
next_idx = int(int(data["nextIndex"]) % 10000000000)
|
next_idx = int(int(data["nextIndex"]) % 10000000000)
|
||||||
user_item_list = self.data.item.get_items(data["userId"], kind)
|
user_item_list = self.data.item.get_items(data["userId"], kind)
|
||||||
@ -526,7 +526,7 @@ class ChuniBase:
|
|||||||
"userItemList": items,
|
"userItemList": items,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict:
|
||||||
user_id = data["userId"]
|
user_id = data["userId"]
|
||||||
user_login_bonus = self.data.item.get_all_login_bonus(user_id, self.version)
|
user_login_bonus = self.data.item.get_all_login_bonus(user_id, self.version)
|
||||||
# ignore the loginBonus request if its disabled in config
|
# ignore the loginBonus request if its disabled in config
|
||||||
@ -552,7 +552,7 @@ class ChuniBase:
|
|||||||
"userLoginBonusList": user_login_list,
|
"userLoginBonusList": user_login_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_map_api_request(self, data: Dict) -> Dict:
|
async 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:
|
if user_map_list is None:
|
||||||
return {}
|
return {}
|
||||||
@ -570,7 +570,7 @@ class ChuniBase:
|
|||||||
"userMapList": map_list,
|
"userMapList": map_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_music_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_music_api_request(self, data: Dict) -> Dict:
|
||||||
music_detail = self.data.score.get_scores(data["userId"])
|
music_detail = self.data.score.get_scores(data["userId"])
|
||||||
if music_detail is None:
|
if music_detail is None:
|
||||||
return {
|
return {
|
||||||
@ -629,7 +629,7 @@ class ChuniBase:
|
|||||||
"userMusicList": song_list, # 240
|
"userMusicList": song_list, # 240
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_option_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_option_api_request(self, data: Dict) -> Dict:
|
||||||
p = self.data.profile.get_profile_option(data["userId"])
|
p = self.data.profile.get_profile_option(data["userId"])
|
||||||
|
|
||||||
option = p._asdict()
|
option = p._asdict()
|
||||||
@ -638,7 +638,7 @@ class ChuniBase:
|
|||||||
|
|
||||||
return {"userId": data["userId"], "userGameOption": option}
|
return {"userId": data["userId"], "userGameOption": option}
|
||||||
|
|
||||||
def handle_get_user_option_ex_api_request(self, data: Dict) -> Dict:
|
async 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"])
|
||||||
|
|
||||||
option = p._asdict()
|
option = p._asdict()
|
||||||
@ -650,7 +650,7 @@ class ChuniBase:
|
|||||||
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:
|
async 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:
|
if profile is None:
|
||||||
return None
|
return None
|
||||||
@ -692,7 +692,7 @@ class ChuniBase:
|
|||||||
"userNameEx": profile["userName"],
|
"userNameEx": profile["userName"],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_recent_rating_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_recent_rating_api_request(self, data: Dict) -> Dict:
|
||||||
recent_rating_list = self.data.profile.get_profile_recent_rating(data["userId"])
|
recent_rating_list = self.data.profile.get_profile_recent_rating(data["userId"])
|
||||||
if recent_rating_list is None:
|
if recent_rating_list is None:
|
||||||
return {
|
return {
|
||||||
@ -707,7 +707,7 @@ class ChuniBase:
|
|||||||
"userRecentRatingList": recent_rating_list["recentRating"],
|
"userRecentRatingList": recent_rating_list["recentRating"],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_region_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_region_api_request(self, data: Dict) -> Dict:
|
||||||
# TODO: Region
|
# TODO: Region
|
||||||
return {
|
return {
|
||||||
"userId": data["userId"],
|
"userId": data["userId"],
|
||||||
@ -715,7 +715,7 @@ class ChuniBase:
|
|||||||
"userRegionList": [],
|
"userRegionList": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_team_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_team_api_request(self, data: Dict) -> Dict:
|
||||||
# Default values
|
# Default values
|
||||||
team_id = 65535
|
team_id = 65535
|
||||||
team_name = self.game_cfg.team.team_name
|
team_name = self.game_cfg.team.team_name
|
||||||
@ -750,7 +750,7 @@ class ChuniBase:
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_team_course_setting_api_request(self, data: Dict) -> Dict:
|
async def handle_get_team_course_setting_api_request(self, data: Dict) -> Dict:
|
||||||
return {
|
return {
|
||||||
"userId": data["userId"],
|
"userId": data["userId"],
|
||||||
"length": 0,
|
"length": 0,
|
||||||
@ -758,7 +758,7 @@ class ChuniBase:
|
|||||||
"teamCourseSettingList": [],
|
"teamCourseSettingList": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_team_course_setting_api_request_proto(self, data: Dict) -> Dict:
|
async def handle_get_team_course_setting_api_request_proto(self, data: Dict) -> Dict:
|
||||||
return {
|
return {
|
||||||
"userId": data["userId"],
|
"userId": data["userId"],
|
||||||
"length": 1,
|
"length": 1,
|
||||||
@ -782,7 +782,7 @@ class ChuniBase:
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_team_course_rule_api_request(self, data: Dict) -> Dict:
|
async def handle_get_team_course_rule_api_request(self, data: Dict) -> Dict:
|
||||||
return {
|
return {
|
||||||
"userId": data["userId"],
|
"userId": data["userId"],
|
||||||
"length": 0,
|
"length": 0,
|
||||||
@ -790,7 +790,7 @@ class ChuniBase:
|
|||||||
"teamCourseRuleList": []
|
"teamCourseRuleList": []
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_team_course_rule_api_request_proto(self, data: Dict) -> Dict:
|
async def handle_get_team_course_rule_api_request_proto(self, data: Dict) -> Dict:
|
||||||
return {
|
return {
|
||||||
"userId": data["userId"],
|
"userId": data["userId"],
|
||||||
"length": 1,
|
"length": 1,
|
||||||
@ -807,7 +807,7 @@ class ChuniBase:
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_upsert_user_all_api_request(self, data: Dict) -> Dict:
|
async def handle_upsert_user_all_api_request(self, data: Dict) -> Dict:
|
||||||
upsert = data["upsertUserAll"]
|
upsert = data["upsertUserAll"]
|
||||||
user_id = data["userId"]
|
user_id = data["userId"]
|
||||||
|
|
||||||
@ -927,28 +927,28 @@ class ChuniBase:
|
|||||||
|
|
||||||
return {"returnCode": "1"}
|
return {"returnCode": "1"}
|
||||||
|
|
||||||
def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict:
|
async def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict:
|
||||||
# add tickets after they got bought, this makes sure the tickets are
|
# add tickets after they got bought, this makes sure the tickets are
|
||||||
# still valid after an unsuccessful logout
|
# still valid after an unsuccessful logout
|
||||||
self.data.profile.put_profile_charge(data["userId"], data["userCharge"])
|
self.data.profile.put_profile_charge(data["userId"], data["userCharge"])
|
||||||
return {"returnCode": "1"}
|
return {"returnCode": "1"}
|
||||||
|
|
||||||
def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict:
|
async 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:
|
async 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:
|
async 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:
|
async 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:
|
async def handle_upsert_client_testmode_api_request(self, data: Dict) -> Dict:
|
||||||
return {"returnCode": "1"}
|
return {"returnCode": "1"}
|
||||||
|
|
||||||
def handle_get_user_net_battle_data_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_net_battle_data_api_request(self, data: Dict) -> Dict:
|
||||||
return {
|
return {
|
||||||
"userId": data["userId"],
|
"userId": data["userId"],
|
||||||
"userNetBattleData": {"recentNBSelectMusicList": []},
|
"userNetBattleData": {"recentNBSelectMusicList": []},
|
||||||
|
@ -13,7 +13,7 @@ class ChuniCrystal(ChuniBase):
|
|||||||
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:
|
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||||
ret = super().handle_get_game_setting_api_request(data)
|
ret = await super().handle_get_game_setting_api_request(data)
|
||||||
ret["gameSetting"]["dataVersion"] = "1.40.00"
|
ret["gameSetting"]["dataVersion"] = "1.40.00"
|
||||||
return ret
|
return ret
|
||||||
|
@ -13,7 +13,7 @@ class ChuniCrystalPlus(ChuniBase):
|
|||||||
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:
|
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||||
ret = super().handle_get_game_setting_api_request(data)
|
ret = await 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,4 +1,6 @@
|
|||||||
from twisted.web.http import Request
|
from starlette.requests import Request
|
||||||
|
from starlette.routing import Route
|
||||||
|
from starlette.responses import Response
|
||||||
import logging, coloredlogs
|
import logging, coloredlogs
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
import zlib
|
import zlib
|
||||||
@ -33,7 +35,6 @@ from .newplus import ChuniNewPlus
|
|||||||
from .sun import ChuniSun
|
from .sun import ChuniSun
|
||||||
from .sunplus import ChuniSunPlus
|
from .sunplus import ChuniSunPlus
|
||||||
|
|
||||||
|
|
||||||
class ChuniServlet(BaseServlet):
|
class ChuniServlet(BaseServlet):
|
||||||
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
|
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
|
||||||
super().__init__(core_cfg, cfg_dir)
|
super().__init__(core_cfg, cfg_dir)
|
||||||
@ -124,15 +125,6 @@ class ChuniServlet(BaseServlet):
|
|||||||
f"Hashed v{version} method {method_fixed} with {bytes.fromhex(keys[2])} to get {hash.hex()}"
|
f"Hashed v{version} method {method_fixed} with {bytes.fromhex(keys[2])} to get {hash.hex()}"
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
|
|
||||||
return (
|
|
||||||
[],
|
|
||||||
[
|
|
||||||
("render_POST", "/{game}/{version}/ChuniServlet/{endpoint}", {}),
|
|
||||||
("render_POST", "/{game}/{version}/ChuniServlet/MatchingServer/{endpoint}", {})
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_game_enabled(
|
def is_game_enabled(
|
||||||
cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str
|
cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str
|
||||||
@ -150,19 +142,25 @@ class ChuniServlet(BaseServlet):
|
|||||||
|
|
||||||
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
|
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
|
||||||
if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80:
|
if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80:
|
||||||
return (f"http://{self.core_cfg.title.hostname}:{Utils.get_title_port(self.core_cfg)}/{game_code}/{game_ver}/", self.core_cfg.title.hostname)
|
return (f"http://{self.core_cfg.server.hostname}:{Utils.get_title_port(self.core_cfg)}/{game_code}/{game_ver}/", self.core_cfg.server.hostname)
|
||||||
|
|
||||||
return (f"http://{self.core_cfg.title.hostname}/{game_code}/{game_ver}/", self.core_cfg.title.hostname)
|
return (f"http://{self.core_cfg.server.hostname}/{game_code}/{game_ver}/", self.core_cfg.server.hostname)
|
||||||
|
|
||||||
def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
def get_routes(self) -> List[Route]:
|
||||||
endpoint = matchers['endpoint']
|
return [
|
||||||
version = int(matchers['version'])
|
Route("/{game:str}/{version:int}/ChuniServlet/{endpoint:str}", self.render_POST, methods=['POST']),
|
||||||
game_code = matchers['game']
|
Route("/{game:str}/{version:int}/ChuniServlet/MatchingServer/{endpoint:str}", self.render_POST, methods=['POST']),
|
||||||
|
]
|
||||||
|
|
||||||
|
async def render_POST(self, request: Request) -> bytes:
|
||||||
|
endpoint: str = request.path_params.get('endpoint')
|
||||||
|
version: int = request.path_params.get('version')
|
||||||
|
game_code: str = request.path_params.get('game')
|
||||||
|
|
||||||
if endpoint.lower() == "ping":
|
if endpoint.lower() == "ping":
|
||||||
return zlib.compress(b'{"returnCode": "1"}')
|
return Response(zlib.compress(b'{"returnCode": "1"}'))
|
||||||
|
|
||||||
req_raw = request.content.getvalue()
|
req_raw = await request.body()
|
||||||
|
|
||||||
encrtped = False
|
encrtped = False
|
||||||
internal_ver = 0
|
internal_ver = 0
|
||||||
@ -201,7 +199,7 @@ class ChuniServlet(BaseServlet):
|
|||||||
internal_ver = ChuniConstants.VER_CHUNITHM_SUN_PLUS
|
internal_ver = ChuniConstants.VER_CHUNITHM_SUN_PLUS
|
||||||
elif game_code == "SDGS": # Int
|
elif game_code == "SDGS": # Int
|
||||||
if version < 110: # SUPERSTAR
|
if version < 110: # SUPERSTAR
|
||||||
internal_ver = ChuniConstants.PARADISE
|
internal_ver = ChuniConstants.VER_CHUNITHM_PARADISE # FIXME: Not sure what was intended to go here? was just "PARADISE"
|
||||||
elif version >= 110 and version < 115: # NEW
|
elif version >= 110 and version < 115: # NEW
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_NEW
|
internal_ver = ChuniConstants.VER_CHUNITHM_NEW
|
||||||
elif version >= 115 and version < 120: # NEW PLUS!!
|
elif version >= 115 and version < 120: # NEW PLUS!!
|
||||||
@ -216,20 +214,20 @@ class ChuniServlet(BaseServlet):
|
|||||||
# doing encrypted. The likelyhood of false positives is low but
|
# doing encrypted. The likelyhood of false positives is low but
|
||||||
# technically not 0
|
# technically not 0
|
||||||
if internal_ver < ChuniConstants.VER_CHUNITHM_NEW:
|
if internal_ver < ChuniConstants.VER_CHUNITHM_NEW:
|
||||||
endpoint = request.getHeader("User-Agent").split("#")[0]
|
endpoint = request.headers.get("User-Agent").split("#")[0]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if internal_ver not in self.hash_table:
|
if internal_ver not in self.hash_table:
|
||||||
self.logger.error(
|
self.logger.error(
|
||||||
f"v{version} does not support encryption or no keys entered"
|
f"v{version} does not support encryption or no keys entered"
|
||||||
)
|
)
|
||||||
return zlib.compress(b'{"stat": "0"}')
|
return Response(zlib.compress(b'{"stat": "0"}'))
|
||||||
|
|
||||||
elif endpoint.lower() not in self.hash_table[internal_ver]:
|
elif endpoint.lower() not in self.hash_table[internal_ver]:
|
||||||
self.logger.error(
|
self.logger.error(
|
||||||
f"No hash found for v{version} endpoint {endpoint}"
|
f"No hash found for v{version} endpoint {endpoint}"
|
||||||
)
|
)
|
||||||
return zlib.compress(b'{"stat": "0"}')
|
return Response(zlib.compress(b'{"stat": "0"}'))
|
||||||
|
|
||||||
endpoint = self.hash_table[internal_ver][endpoint.lower()]
|
endpoint = self.hash_table[internal_ver][endpoint.lower()]
|
||||||
|
|
||||||
@ -246,7 +244,7 @@ class ChuniServlet(BaseServlet):
|
|||||||
self.logger.error(
|
self.logger.error(
|
||||||
f"Failed to decrypt v{version} request to {endpoint} -> {e}"
|
f"Failed to decrypt v{version} request to {endpoint} -> {e}"
|
||||||
)
|
)
|
||||||
return zlib.compress(b'{"stat": "0"}')
|
return Response(zlib.compress(b'{"stat": "0"}'))
|
||||||
|
|
||||||
encrtped = True
|
encrtped = True
|
||||||
|
|
||||||
@ -258,7 +256,7 @@ class ChuniServlet(BaseServlet):
|
|||||||
self.logger.error(
|
self.logger.error(
|
||||||
f"Unencrypted v{version} {endpoint} request, but config is set to encrypted only: {req_raw}"
|
f"Unencrypted v{version} {endpoint} request, but config is set to encrypted only: {req_raw}"
|
||||||
)
|
)
|
||||||
return zlib.compress(b'{"stat": "0"}')
|
return Response(zlib.compress(b'{"stat": "0"}'))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
unzip = zlib.decompress(req_raw)
|
unzip = zlib.decompress(req_raw)
|
||||||
@ -267,7 +265,7 @@ class ChuniServlet(BaseServlet):
|
|||||||
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 b""
|
return Response(zlib.compress(b'{"stat": "0"}'))
|
||||||
|
|
||||||
req_data = json.loads(unzip)
|
req_data = json.loads(unzip)
|
||||||
|
|
||||||
@ -285,11 +283,11 @@ class ChuniServlet(BaseServlet):
|
|||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
handler = getattr(handler_cls, func_to_find)
|
handler = getattr(handler_cls, func_to_find)
|
||||||
resp = handler(req_data)
|
resp = await handler(req_data)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
|
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
|
||||||
return zlib.compress(b'{"stat": "0"}')
|
return Response(zlib.compress(b'{"stat": "0"}'))
|
||||||
|
|
||||||
if resp == None:
|
if resp == None:
|
||||||
resp = {"returnCode": 1}
|
resp = {"returnCode": 1}
|
||||||
@ -299,7 +297,7 @@ class ChuniServlet(BaseServlet):
|
|||||||
zipped = zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
|
zipped = zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
|
||||||
|
|
||||||
if not encrtped:
|
if not encrtped:
|
||||||
return zipped
|
return Response(zipped)
|
||||||
|
|
||||||
padded = pad(zipped, 16)
|
padded = pad(zipped, 16)
|
||||||
|
|
||||||
@ -309,4 +307,4 @@ class ChuniServlet(BaseServlet):
|
|||||||
bytes.fromhex(self.game_cfg.crypto.keys[internal_ver][1]),
|
bytes.fromhex(self.game_cfg.crypto.keys[internal_ver][1]),
|
||||||
)
|
)
|
||||||
|
|
||||||
return crypt.encrypt(padded)
|
return Response(crypt.encrypt(padded))
|
@ -33,7 +33,7 @@ class ChuniNew(ChuniBase):
|
|||||||
if self.version == ChuniConstants.VER_CHUNITHM_SUN_PLUS:
|
if self.version == ChuniConstants.VER_CHUNITHM_SUN_PLUS:
|
||||||
return "215"
|
return "215"
|
||||||
|
|
||||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||||
# use UTC time and convert it to JST time by adding +9
|
# use UTC time and convert it to JST time by adding +9
|
||||||
# matching therefore starts one hour before and lasts for 8 hours
|
# matching therefore starts one hour before and lasts for 8 hours
|
||||||
match_start = datetime.strftime(
|
match_start = datetime.strftime(
|
||||||
@ -82,26 +82,26 @@ class ChuniNew(ChuniBase):
|
|||||||
"matchErrorLimit": self.game_cfg.matching.match_error_limit,
|
"matchErrorLimit": self.game_cfg.matching.match_error_limit,
|
||||||
"romVersion": self.game_cfg.version.version(self.version)["rom"],
|
"romVersion": self.game_cfg.version.version(self.version)["rom"],
|
||||||
"dataVersion": self.game_cfg.version.version(self.version)["data"],
|
"dataVersion": self.game_cfg.version.version(self.version)["data"],
|
||||||
"matchingUri": f"http://{self.core_cfg.title.hostname}:{t_port}/SDHD/{self._interal_ver_to_intver()}/ChuniServlet/",
|
"matchingUri": f"http://{self.core_cfg.server.hostname}:{t_port}/SDHD/{self._interal_ver_to_intver()}/ChuniServlet/",
|
||||||
"matchingUriX": f"http://{self.core_cfg.title.hostname}:{t_port}/SDHD/{self._interal_ver_to_intver()}/ChuniServlet/",
|
"matchingUriX": f"http://{self.core_cfg.server.hostname}:{t_port}/SDHD/{self._interal_ver_to_intver()}/ChuniServlet/",
|
||||||
# might be really important for online battle to connect the cabs via UDP port 50201
|
# might be really important for online battle to connect the cabs via UDP port 50201
|
||||||
"udpHolePunchUri": f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/{self._interal_ver_to_intver()}/ChuniServlet/",
|
"udpHolePunchUri": f"http://{self.core_cfg.server.hostname}:{self.core_cfg.server.port}/SDHD/{self._interal_ver_to_intver()}/ChuniServlet/",
|
||||||
"reflectorUri": f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/{self._interal_ver_to_intver()}/ChuniServlet/",
|
"reflectorUri": f"http://{self.core_cfg.server.hostname}:{self.core_cfg.server.port}/SDHD/{self._interal_ver_to_intver()}/ChuniServlet/",
|
||||||
},
|
},
|
||||||
"isDumpUpload": False,
|
"isDumpUpload": False,
|
||||||
"isAou": False,
|
"isAou": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_remove_token_api_request(self, data: Dict) -> Dict:
|
async def handle_remove_token_api_request(self, data: Dict) -> Dict:
|
||||||
return {"returnCode": "1"}
|
return {"returnCode": "1"}
|
||||||
|
|
||||||
def handle_delete_token_api_request(self, data: Dict) -> Dict:
|
async 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:
|
async 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:
|
async 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"])
|
||||||
|
|
||||||
map_areas = []
|
map_areas = []
|
||||||
@ -113,10 +113,10 @@ class ChuniNew(ChuniBase):
|
|||||||
|
|
||||||
return {"userId": data["userId"], "userMapAreaList": map_areas}
|
return {"userId": data["userId"], "userMapAreaList": map_areas}
|
||||||
|
|
||||||
def handle_get_user_symbol_chat_setting_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_symbol_chat_setting_api_request(self, data: Dict) -> Dict:
|
||||||
return {"userId": data["userId"], "symbolCharInfoList": []}
|
return {"userId": data["userId"], "symbolCharInfoList": []}
|
||||||
|
|
||||||
def handle_get_user_preview_api_request(self, data: Dict) -> Dict:
|
async 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:
|
if profile is None:
|
||||||
return None
|
return None
|
||||||
@ -164,7 +164,7 @@ class ChuniNew(ChuniBase):
|
|||||||
}
|
}
|
||||||
return data1
|
return data1
|
||||||
|
|
||||||
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_get_user_preview_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:
|
if p is None:
|
||||||
return {}
|
return {}
|
||||||
@ -177,13 +177,13 @@ class ChuniNew(ChuniBase):
|
|||||||
"isLogin": False,
|
"isLogin": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_printer_login_api_request(self, data: Dict) -> Dict:
|
async def handle_printer_login_api_request(self, data: Dict) -> Dict:
|
||||||
return {"returnCode": 1}
|
return {"returnCode": 1}
|
||||||
|
|
||||||
def handle_printer_logout_api_request(self, data: Dict) -> Dict:
|
async def handle_printer_logout_api_request(self, data: Dict) -> Dict:
|
||||||
return {"returnCode": 1}
|
return {"returnCode": 1}
|
||||||
|
|
||||||
def handle_get_game_gacha_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_gacha_api_request(self, data: Dict) -> Dict:
|
||||||
"""
|
"""
|
||||||
returns all current active banners (gachas)
|
returns all current active banners (gachas)
|
||||||
"""
|
"""
|
||||||
@ -213,7 +213,7 @@ class ChuniNew(ChuniBase):
|
|||||||
"registIdList": [],
|
"registIdList": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_game_gacha_card_by_id_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_gacha_card_by_id_api_request(self, data: Dict) -> Dict:
|
||||||
"""
|
"""
|
||||||
returns all valid cards for a given gachaId
|
returns all valid cards for a given gachaId
|
||||||
"""
|
"""
|
||||||
@ -237,7 +237,7 @@ class ChuniNew(ChuniBase):
|
|||||||
"ssrBookCalcList": [],
|
"ssrBookCalcList": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_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:
|
if p is None:
|
||||||
return {}
|
return {}
|
||||||
@ -262,7 +262,7 @@ class ChuniNew(ChuniBase):
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_gacha_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_gacha_api_request(self, data: Dict) -> Dict:
|
||||||
user_gachas = self.data.item.get_user_gachas(data["userId"])
|
user_gachas = self.data.item.get_user_gachas(data["userId"])
|
||||||
if user_gachas is None:
|
if user_gachas is None:
|
||||||
return {"userId": data["userId"], "length": 0, "userGachaList": []}
|
return {"userId": data["userId"], "length": 0, "userGachaList": []}
|
||||||
@ -281,7 +281,7 @@ class ChuniNew(ChuniBase):
|
|||||||
"userGachaList": user_gacha_list,
|
"userGachaList": user_gacha_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_printed_card_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_printed_card_api_request(self, data: Dict) -> Dict:
|
||||||
user_print_list = self.data.item.get_user_print_states(
|
user_print_list = self.data.item.get_user_print_states(
|
||||||
data["userId"], has_completed=True
|
data["userId"], has_completed=True
|
||||||
)
|
)
|
||||||
@ -316,7 +316,7 @@ class ChuniNew(ChuniBase):
|
|||||||
"userPrintedCardList": print_list,
|
"userPrintedCardList": print_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_card_print_error_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_card_print_error_api_request(self, data: Dict) -> Dict:
|
||||||
user_id = data["userId"]
|
user_id = data["userId"]
|
||||||
|
|
||||||
user_print_states = self.data.item.get_user_print_states(
|
user_print_states = self.data.item.get_user_print_states(
|
||||||
@ -338,13 +338,13 @@ class ChuniNew(ChuniBase):
|
|||||||
"userCardPrintStateList": card_print_state_list,
|
"userCardPrintStateList": card_print_state_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_cm_get_user_character_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_get_user_character_api_request(self, data: Dict) -> Dict:
|
||||||
return super().handle_get_user_character_api_request(data)
|
return super().handle_get_user_character_api_request(data)
|
||||||
|
|
||||||
def handle_cm_get_user_item_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_get_user_item_api_request(self, data: Dict) -> Dict:
|
||||||
return super().handle_get_user_item_api_request(data)
|
return super().handle_get_user_item_api_request(data)
|
||||||
|
|
||||||
def handle_roll_gacha_api_request(self, data: Dict) -> Dict:
|
async def handle_roll_gacha_api_request(self, data: Dict) -> Dict:
|
||||||
"""
|
"""
|
||||||
Handle a gacha roll API request, with:
|
Handle a gacha roll API request, with:
|
||||||
gachaId: the gachaId where the cards should be pulled from
|
gachaId: the gachaId where the cards should be pulled from
|
||||||
@ -386,7 +386,7 @@ class ChuniNew(ChuniBase):
|
|||||||
|
|
||||||
return {"length": len(rolled_cards), "gameGachaCardList": rolled_cards}
|
return {"length": len(rolled_cards), "gameGachaCardList": rolled_cards}
|
||||||
|
|
||||||
def handle_cm_upsert_user_gacha_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_upsert_user_gacha_api_request(self, data: Dict) -> Dict:
|
||||||
upsert = data["cmUpsertUserGacha"]
|
upsert = data["cmUpsertUserGacha"]
|
||||||
user_id = data["userId"]
|
user_id = data["userId"]
|
||||||
place_id = data["placeId"]
|
place_id = data["placeId"]
|
||||||
@ -441,7 +441,7 @@ class ChuniNew(ChuniBase):
|
|||||||
"userCardPrintStateList": card_print_state_list,
|
"userCardPrintStateList": card_print_state_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_cm_upsert_user_printlog_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_upsert_user_printlog_api_request(self, data: Dict) -> Dict:
|
||||||
return {
|
return {
|
||||||
"returnCode": 1,
|
"returnCode": 1,
|
||||||
"orderId": 0,
|
"orderId": 0,
|
||||||
@ -449,7 +449,7 @@ class ChuniNew(ChuniBase):
|
|||||||
"apiName": "CMUpsertUserPrintlogApi",
|
"apiName": "CMUpsertUserPrintlogApi",
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_cm_upsert_user_print_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_upsert_user_print_api_request(self, data: Dict) -> Dict:
|
||||||
user_print_detail = data["userPrintDetail"]
|
user_print_detail = data["userPrintDetail"]
|
||||||
user_id = data["userId"]
|
user_id = data["userId"]
|
||||||
|
|
||||||
@ -474,7 +474,7 @@ class ChuniNew(ChuniBase):
|
|||||||
"apiName": "CMUpsertUserPrintApi",
|
"apiName": "CMUpsertUserPrintApi",
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_cm_upsert_user_print_subtract_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_upsert_user_print_subtract_api_request(self, data: Dict) -> Dict:
|
||||||
upsert = data["userCardPrintState"]
|
upsert = data["userCardPrintState"]
|
||||||
user_id = data["userId"]
|
user_id = data["userId"]
|
||||||
place_id = data["placeId"]
|
place_id = data["placeId"]
|
||||||
@ -491,7 +491,7 @@ class ChuniNew(ChuniBase):
|
|||||||
|
|
||||||
return {"returnCode": "1", "apiName": "CMUpsertUserPrintSubtractApi"}
|
return {"returnCode": "1", "apiName": "CMUpsertUserPrintSubtractApi"}
|
||||||
|
|
||||||
def handle_cm_upsert_user_print_cancel_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_upsert_user_print_cancel_api_request(self, data: Dict) -> Dict:
|
||||||
order_ids = data["orderIdList"]
|
order_ids = data["orderIdList"]
|
||||||
user_id = data["userId"]
|
user_id = data["userId"]
|
||||||
|
|
||||||
@ -501,11 +501,11 @@ class ChuniNew(ChuniBase):
|
|||||||
|
|
||||||
return {"returnCode": "1", "apiName": "CMUpsertUserPrintCancelApi"}
|
return {"returnCode": "1", "apiName": "CMUpsertUserPrintCancelApi"}
|
||||||
|
|
||||||
def handle_ping_request(self, data: Dict) -> Dict:
|
async def handle_ping_request(self, data: Dict) -> Dict:
|
||||||
# matchmaking ping request
|
# matchmaking ping request
|
||||||
return {"returnCode": "1"}
|
return {"returnCode": "1"}
|
||||||
|
|
||||||
def handle_begin_matching_api_request(self, data: Dict) -> Dict:
|
async def handle_begin_matching_api_request(self, data: Dict) -> Dict:
|
||||||
room_id = 1
|
room_id = 1
|
||||||
# check if there is a free matching room
|
# check if there is a free matching room
|
||||||
matching_room = self.data.item.get_oldest_free_matching(self.version)
|
matching_room = self.data.item.get_oldest_free_matching(self.version)
|
||||||
@ -554,7 +554,7 @@ class ChuniNew(ChuniBase):
|
|||||||
|
|
||||||
return {"roomId": 1, "matchingWaitState": matching_wait}
|
return {"roomId": 1, "matchingWaitState": matching_wait}
|
||||||
|
|
||||||
def handle_end_matching_api_request(self, data: Dict) -> Dict:
|
async def handle_end_matching_api_request(self, data: Dict) -> Dict:
|
||||||
matching_room = self.data.item.get_matching(self.version, data["roomId"])
|
matching_room = self.data.item.get_matching(self.version, data["roomId"])
|
||||||
members = matching_room["matchingMemberInfoList"]
|
members = matching_room["matchingMemberInfoList"]
|
||||||
|
|
||||||
@ -579,10 +579,10 @@ class ChuniNew(ChuniBase):
|
|||||||
# no idea, maybe to differentiate between CPUs and real players?
|
# no idea, maybe to differentiate between CPUs and real players?
|
||||||
"matchingMemberRoleList": role_list,
|
"matchingMemberRoleList": role_list,
|
||||||
# TCP/UDP connection?
|
# TCP/UDP connection?
|
||||||
"reflectorUri": f"{self.core_cfg.title.hostname}",
|
"reflectorUri": f"{self.core_cfg.server.hostname}",
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_remove_matching_member_api_request(self, data: Dict) -> Dict:
|
async def handle_remove_matching_member_api_request(self, data: Dict) -> Dict:
|
||||||
# get all matching rooms, because Chuni only returns the userId
|
# get all matching rooms, because Chuni only returns the userId
|
||||||
# not the actual roomId
|
# not the actual roomId
|
||||||
matching_rooms = self.data.item.get_all_matchings(self.version)
|
matching_rooms = self.data.item.get_all_matchings(self.version)
|
||||||
@ -612,7 +612,7 @@ class ChuniNew(ChuniBase):
|
|||||||
|
|
||||||
return {"returnCode": "1"}
|
return {"returnCode": "1"}
|
||||||
|
|
||||||
def handle_get_matching_state_api_request(self, data: Dict) -> Dict:
|
async def handle_get_matching_state_api_request(self, data: Dict) -> Dict:
|
||||||
polling_interval = 1
|
polling_interval = 1
|
||||||
# get the current active room
|
# get the current active room
|
||||||
matching_room = self.data.item.get_matching(self.version, data["roomId"])
|
matching_room = self.data.item.get_matching(self.version, data["roomId"])
|
||||||
|
@ -11,8 +11,8 @@ class ChuniNewPlus(ChuniNew):
|
|||||||
super().__init__(core_cfg, game_cfg)
|
super().__init__(core_cfg, game_cfg)
|
||||||
self.version = ChuniConstants.VER_CHUNITHM_NEW_PLUS
|
self.version = ChuniConstants.VER_CHUNITHM_NEW_PLUS
|
||||||
|
|
||||||
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||||
user_data = super().handle_cm_get_user_preview_api_request(data)
|
user_data = await super().handle_cm_get_user_preview_api_request(data)
|
||||||
|
|
||||||
# hardcode lastDataVersion for CardMaker 1.35 A028
|
# hardcode lastDataVersion for CardMaker 1.35 A028
|
||||||
user_data["lastDataVersion"] = "2.05.00"
|
user_data["lastDataVersion"] = "2.05.00"
|
||||||
|
@ -13,7 +13,7 @@ class ChuniParadise(ChuniBase):
|
|||||||
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:
|
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||||
ret = super().handle_get_game_setting_api_request(data)
|
ret = await super().handle_get_game_setting_api_request(data)
|
||||||
ret["gameSetting"]["dataVersion"] = "1.50.00"
|
ret["gameSetting"]["dataVersion"] = "1.50.00"
|
||||||
return ret
|
return ret
|
||||||
|
@ -11,7 +11,7 @@ class ChuniPlus(ChuniBase):
|
|||||||
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:
|
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||||
ret = super().handle_get_game_setting_api_request(data)
|
ret = await super().handle_get_game_setting_api_request(data)
|
||||||
ret["gameSetting"]["dataVersion"] = "1.05.00"
|
ret["gameSetting"]["dataVersion"] = "1.05.00"
|
||||||
return ret
|
return ret
|
||||||
|
@ -11,7 +11,7 @@ class ChuniStar(ChuniBase):
|
|||||||
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:
|
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||||
ret = super().handle_get_game_setting_api_request(data)
|
ret = await super().handle_get_game_setting_api_request(data)
|
||||||
ret["gameSetting"]["dataVersion"] = "1.20.00"
|
ret["gameSetting"]["dataVersion"] = "1.20.00"
|
||||||
return ret
|
return ret
|
||||||
|
@ -11,7 +11,7 @@ class ChuniStarPlus(ChuniBase):
|
|||||||
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:
|
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||||
ret = super().handle_get_game_setting_api_request(data)
|
ret = await super().handle_get_game_setting_api_request(data)
|
||||||
ret["gameSetting"]["dataVersion"] = "1.25.00"
|
ret["gameSetting"]["dataVersion"] = "1.25.00"
|
||||||
return ret
|
return ret
|
||||||
|
@ -11,8 +11,8 @@ class ChuniSun(ChuniNewPlus):
|
|||||||
super().__init__(core_cfg, game_cfg)
|
super().__init__(core_cfg, game_cfg)
|
||||||
self.version = ChuniConstants.VER_CHUNITHM_SUN
|
self.version = ChuniConstants.VER_CHUNITHM_SUN
|
||||||
|
|
||||||
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||||
user_data = super().handle_cm_get_user_preview_api_request(data)
|
user_data = await super().handle_cm_get_user_preview_api_request(data)
|
||||||
|
|
||||||
# hardcode lastDataVersion for CardMaker 1.35 A032
|
# hardcode lastDataVersion for CardMaker 1.35 A032
|
||||||
user_data["lastDataVersion"] = "2.10.00"
|
user_data["lastDataVersion"] = "2.10.00"
|
||||||
|
@ -11,8 +11,8 @@ class ChuniSunPlus(ChuniSun):
|
|||||||
super().__init__(core_cfg, game_cfg)
|
super().__init__(core_cfg, game_cfg)
|
||||||
self.version = ChuniConstants.VER_CHUNITHM_SUN_PLUS
|
self.version = ChuniConstants.VER_CHUNITHM_SUN_PLUS
|
||||||
|
|
||||||
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||||
user_data = super().handle_cm_get_user_preview_api_request(data)
|
user_data = await super().handle_cm_get_user_preview_api_request(data)
|
||||||
|
|
||||||
# I don't know if lastDataVersion is going to matter, I don't think CardMaker 1.35 works this far up
|
# I don't know if lastDataVersion is going to matter, I don't think CardMaker 1.35 works this far up
|
||||||
user_data["lastDataVersion"] = "2.15.00"
|
user_data["lastDataVersion"] = "2.15.00"
|
||||||
|
@ -29,11 +29,11 @@ class CardMakerBase:
|
|||||||
def _parse_int_ver(version: str) -> str:
|
def _parse_int_ver(version: str) -> str:
|
||||||
return version.replace(".", "")[:3]
|
return version.replace(".", "")[:3]
|
||||||
|
|
||||||
def handle_get_game_connect_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_connect_api_request(self, data: Dict) -> Dict:
|
||||||
if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80:
|
if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80:
|
||||||
uri = f"http://{self.core_cfg.title.hostname}:{Utils.get_title_port(self.core_cfg)}"
|
uri = f"http://{self.core_cfg.server.hostname}:{Utils.get_title_port(self.core_cfg)}"
|
||||||
else:
|
else:
|
||||||
uri = f"http://{self.core_cfg.title.hostname}"
|
uri = f"http://{self.core_cfg.server.hostname}"
|
||||||
|
|
||||||
# grab the dict with all games version numbers from user config
|
# grab the dict with all games version numbers from user config
|
||||||
games_ver = self.game_cfg.version.version(self.version)
|
games_ver = self.game_cfg.version.version(self.version)
|
||||||
@ -62,7 +62,7 @@ class CardMakerBase:
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||||
# if reboot start/end time is not defined use the default behavior of being a few hours ago
|
# if reboot start/end time is not defined use the default behavior of being a few hours ago
|
||||||
if self.core_cfg.title.reboot_start_time == "" or self.core_cfg.title.reboot_end_time == "":
|
if self.core_cfg.title.reboot_start_time == "" or self.core_cfg.title.reboot_end_time == "":
|
||||||
reboot_start = datetime.strftime(
|
reboot_start = datetime.strftime(
|
||||||
@ -110,11 +110,11 @@ class CardMakerBase:
|
|||||||
"isAou": False,
|
"isAou": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_client_bookkeeping_api_request(self, data: Dict) -> Dict:
|
async def handle_get_client_bookkeeping_api_request(self, data: Dict) -> Dict:
|
||||||
return {"placeId": data["placeId"], "length": 0, "clientBookkeepingList": []}
|
return {"placeId": data["placeId"], "length": 0, "clientBookkeepingList": []}
|
||||||
|
|
||||||
def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict:
|
async def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict:
|
||||||
return {"returnCode": 1, "apiName": "UpsertClientSettingApi"}
|
return {"returnCode": 1, "apiName": "UpsertClientSettingApi"}
|
||||||
|
|
||||||
def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict:
|
async def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict:
|
||||||
return {"returnCode": 1, "apiName": "UpsertClientBookkeepingApi"}
|
return {"returnCode": 1, "apiName": "UpsertClientBookkeepingApi"}
|
||||||
|
@ -12,7 +12,7 @@ class CardMaker135(CardMakerBase):
|
|||||||
super().__init__(core_cfg, game_cfg)
|
super().__init__(core_cfg, game_cfg)
|
||||||
self.version = CardMakerConstants.VER_CARD_MAKER_135
|
self.version = CardMakerConstants.VER_CARD_MAKER_135
|
||||||
|
|
||||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||||
ret = super().handle_get_game_setting_api_request(data)
|
ret = await super().handle_get_game_setting_api_request(data)
|
||||||
ret["gameSetting"]["dataVersion"] = "1.35.00"
|
ret["gameSetting"]["dataVersion"] = "1.35.00"
|
||||||
return ret
|
return ret
|
||||||
|
@ -5,10 +5,11 @@ import string
|
|||||||
import logging
|
import logging
|
||||||
import coloredlogs
|
import coloredlogs
|
||||||
import zlib
|
import zlib
|
||||||
|
from starlette.routing import Route
|
||||||
|
from starlette.responses import Response
|
||||||
|
from starlette.requests import Request
|
||||||
from os import path
|
from os import path
|
||||||
from typing import Tuple, List, Dict
|
from typing import List
|
||||||
from twisted.web.http import Request
|
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
|
|
||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
@ -19,7 +20,6 @@ from .const import CardMakerConstants
|
|||||||
from .base import CardMakerBase
|
from .base import CardMakerBase
|
||||||
from .cm135 import CardMaker135
|
from .cm135 import CardMaker135
|
||||||
|
|
||||||
|
|
||||||
class CardMakerServlet(BaseServlet):
|
class CardMakerServlet(BaseServlet):
|
||||||
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
|
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
|
||||||
super().__init__(core_cfg, cfg_dir)
|
super().__init__(core_cfg, cfg_dir)
|
||||||
@ -72,16 +72,15 @@ class CardMakerServlet(BaseServlet):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
|
def get_routes(self) -> List[Route]:
|
||||||
return (
|
return [
|
||||||
[],
|
Route("/SDED/{version:int}/{endpoint:str}", self.render_POST)
|
||||||
[("render_POST", "/SDED/{version}/{endpoint}", {})]
|
]
|
||||||
)
|
|
||||||
|
async def render_POST(self, request: Request) -> bytes:
|
||||||
def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
version: int = request.path_params.get('version')
|
||||||
version = int(matchers['version'])
|
endpoint: str = request.path_params.get('endpoint')
|
||||||
endpoint = matchers['endpoint']
|
req_raw = await request.body()
|
||||||
req_raw = request.content.getvalue()
|
|
||||||
internal_ver = 0
|
internal_ver = 0
|
||||||
client_ip = Utils.get_ip_addr(request)
|
client_ip = Utils.get_ip_addr(request)
|
||||||
|
|
||||||
@ -103,7 +102,7 @@ class CardMakerServlet(BaseServlet):
|
|||||||
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 Response(zlib.compress(b'{"stat": "0"}'))
|
||||||
|
|
||||||
req_data = json.loads(unzip)
|
req_data = json.loads(unzip)
|
||||||
|
|
||||||
@ -114,7 +113,7 @@ class CardMakerServlet(BaseServlet):
|
|||||||
|
|
||||||
if not hasattr(self.versions[internal_ver], func_to_find):
|
if not hasattr(self.versions[internal_ver], func_to_find):
|
||||||
self.logger.warning(f"Unhandled v{version} request {endpoint}")
|
self.logger.warning(f"Unhandled v{version} request {endpoint}")
|
||||||
return zlib.compress(b'{"returnCode": 1}')
|
return Response(zlib.compress(b'{"returnCode": 1}'))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
handler = getattr(self.versions[internal_ver], func_to_find)
|
handler = getattr(self.versions[internal_ver], func_to_find)
|
||||||
@ -123,11 +122,11 @@ class CardMakerServlet(BaseServlet):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
|
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
|
||||||
raise
|
raise
|
||||||
return zlib.compress(b'{"stat": "0"}')
|
return Response(zlib.compress(b'{"stat": "0"}'))
|
||||||
|
|
||||||
if resp is None:
|
if resp is None:
|
||||||
resp = {"returnCode": 1}
|
resp = {"returnCode": 1}
|
||||||
|
|
||||||
self.logger.debug(f"Response {resp}")
|
self.logger.debug(f"Response {resp}")
|
||||||
|
|
||||||
return zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
|
return Response(zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8")))
|
||||||
|
@ -28,13 +28,13 @@ class CxbBase:
|
|||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def handle_action_rpreq_request(self, data: Dict) -> Dict:
|
async def handle_action_rpreq_request(self, data: Dict) -> Dict:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def handle_action_hitreq_request(self, data: Dict) -> Dict:
|
async def handle_action_hitreq_request(self, data: Dict) -> Dict:
|
||||||
return {"data": []}
|
return {"data": []}
|
||||||
|
|
||||||
def handle_auth_usercheck_request(self, data: Dict) -> Dict:
|
async def handle_auth_usercheck_request(self, data: Dict) -> Dict:
|
||||||
profile = self.data.profile.get_profile_index(
|
profile = self.data.profile.get_profile_index(
|
||||||
0, data["usercheck"]["authid"], self.version
|
0, data["usercheck"]["authid"], self.version
|
||||||
)
|
)
|
||||||
@ -45,11 +45,11 @@ class CxbBase:
|
|||||||
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:
|
async 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:
|
async def handle_auth_login_request(self, data: Dict) -> Dict:
|
||||||
profile = self.data.profile.get_profile_index(
|
profile = self.data.profile.get_profile_index(
|
||||||
0, data["login"]["authid"], self.version
|
0, data["login"]["authid"], self.version
|
||||||
)
|
)
|
||||||
@ -198,7 +198,7 @@ class CxbBase:
|
|||||||
).decode("utf-8")
|
).decode("utf-8")
|
||||||
)
|
)
|
||||||
|
|
||||||
def handle_action_loadrange_request(self, data: Dict) -> Dict:
|
async 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"]
|
||||||
@ -282,7 +282,7 @@ class CxbBase:
|
|||||||
|
|
||||||
return {"index": index, "data": data1, "version": versionindex}
|
return {"index": index, "data": data1, "version": versionindex}
|
||||||
|
|
||||||
def handle_action_saveindex_request(self, data: Dict) -> Dict:
|
async def handle_action_saveindex_request(self, data: Dict) -> Dict:
|
||||||
save_data = data["saveindex"]
|
save_data = data["saveindex"]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -443,7 +443,7 @@ class CxbBase:
|
|||||||
i += 1
|
i += 1
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def handle_action_sprankreq_request(self, data: Dict) -> Dict:
|
async 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)
|
||||||
@ -475,16 +475,16 @@ class CxbBase:
|
|||||||
"rankx": [1, 1, 1],
|
"rankx": [1, 1, 1],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_action_getadv_request(self, data: Dict) -> Dict:
|
async 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:
|
async def handle_action_getmsg_request(self, data: Dict) -> Dict:
|
||||||
return {"msgs": []}
|
return {"msgs": []}
|
||||||
|
|
||||||
def handle_auth_logout_request(self, data: Dict) -> Dict:
|
async def handle_auth_logout_request(self, data: Dict) -> Dict:
|
||||||
return {"auth": True}
|
return {"auth": True}
|
||||||
|
|
||||||
def handle_action_rankreg_request(self, data: Dict) -> Dict:
|
async 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}")
|
||||||
|
|
||||||
@ -527,7 +527,7 @@ class CxbBase:
|
|||||||
)
|
)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def handle_action_addenergy_request(self, data: Dict) -> Dict:
|
async 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)
|
||||||
@ -570,10 +570,10 @@ class CxbBase:
|
|||||||
)
|
)
|
||||||
return array[0]
|
return array[0]
|
||||||
|
|
||||||
def handle_action_eventreq_request(self, data: Dict) -> Dict:
|
async def handle_action_eventreq_request(self, data: Dict) -> Dict:
|
||||||
self.logger.info(data)
|
self.logger.info(data)
|
||||||
return {"eventreq": ""}
|
return {"eventreq": ""}
|
||||||
|
|
||||||
def handle_action_stampreq_request(self, data: Dict) -> Dict:
|
async def handle_action_stampreq_request(self, data: Dict) -> Dict:
|
||||||
self.logger.info(data)
|
self.logger.info(data)
|
||||||
return {"stampreq": ""}
|
return {"stampreq": ""}
|
@ -1,4 +1,6 @@
|
|||||||
from twisted.web.http import Request
|
from starlette.requests import Request
|
||||||
|
from starlette.routing import Route
|
||||||
|
from starlette.responses import Response, PlainTextResponse, JSONResponse
|
||||||
import traceback
|
import traceback
|
||||||
import sys
|
import sys
|
||||||
import yaml
|
import yaml
|
||||||
@ -62,6 +64,14 @@ class CxbServlet(BaseServlet):
|
|||||||
CxbRevSunriseS2(core_cfg, self.game_cfg),
|
CxbRevSunriseS2(core_cfg, self.game_cfg),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def get_routes(self) -> List[Route]:
|
||||||
|
return [
|
||||||
|
Route("/data", self.handle_data, methods=['POST']),
|
||||||
|
Route("/action", self.handle_action, methods=['POST']),
|
||||||
|
Route("/v2/action", self.handle_action, methods=['POST']),
|
||||||
|
Route("/auth", self.handle_auth, methods=['POST']),
|
||||||
|
]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_game_enabled(cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str) -> bool:
|
def is_game_enabled(cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str) -> bool:
|
||||||
game_cfg = CxbConfig()
|
game_cfg = CxbConfig()
|
||||||
@ -88,26 +98,12 @@ class CxbServlet(BaseServlet):
|
|||||||
t_port = f":{title_port_int}" if title_port_int and not self.core_cfg.server.is_using_proxy else ""
|
t_port = f":{title_port_int}" if title_port_int and not self.core_cfg.server.is_using_proxy else ""
|
||||||
|
|
||||||
return (
|
return (
|
||||||
f"{proto}://{self.core_cfg.title.hostname}{t_port}",
|
f"{proto}://{self.core_cfg.server.hostname}{t_port}",
|
||||||
"",
|
"",
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
|
async def preprocess(self, req: Request) -> Dict:
|
||||||
return (
|
req_bytes = await req.body()
|
||||||
[],
|
|
||||||
[
|
|
||||||
("handle_data", "/data", {}),
|
|
||||||
("handle_action", "/action", {}),
|
|
||||||
("handle_action", "/v2/action", {}),
|
|
||||||
("handle_auth", "/auth", {}),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
def preprocess(self, req: Request) -> Dict:
|
|
||||||
try:
|
|
||||||
req_bytes = req.content.getvalue()
|
|
||||||
except:
|
|
||||||
req_bytes = req.content.read() # Can we just use this one?
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
req_json: Dict = json.loads(req_bytes)
|
req_json: Dict = json.loads(req_bytes)
|
||||||
@ -126,8 +122,8 @@ class CxbServlet(BaseServlet):
|
|||||||
|
|
||||||
return req_json
|
return req_json
|
||||||
|
|
||||||
def handle_data(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
async def handle_data(self, request: Request) -> bytes:
|
||||||
req_json = self.preprocess(request)
|
req_json = await self.preprocess(request)
|
||||||
func_to_find = "handle_data_"
|
func_to_find = "handle_data_"
|
||||||
version_string = "Base"
|
version_string = "Base"
|
||||||
internal_ver = 0
|
internal_ver = 0
|
||||||
@ -135,7 +131,7 @@ class CxbServlet(BaseServlet):
|
|||||||
|
|
||||||
if req_json == {}:
|
if req_json == {}:
|
||||||
self.logger.warning(f"Empty json request to /data")
|
self.logger.warning(f"Empty json request to /data")
|
||||||
return b""
|
return Response()
|
||||||
|
|
||||||
subcmd = list(req_json.keys())[0]
|
subcmd = list(req_json.keys())[0]
|
||||||
if subcmd == "dldate":
|
if subcmd == "dldate":
|
||||||
@ -145,14 +141,14 @@ class CxbServlet(BaseServlet):
|
|||||||
or "filetype" not in req_json["dldate"]
|
or "filetype" not in req_json["dldate"]
|
||||||
):
|
):
|
||||||
self.logger.warning(f"Malformed dldate request: {req_json}")
|
self.logger.warning(f"Malformed dldate request: {req_json}")
|
||||||
return b""
|
return Response()
|
||||||
|
|
||||||
filetype = req_json["dldate"]["filetype"]
|
filetype = req_json["dldate"]["filetype"]
|
||||||
filetype_split = filetype.split("/")
|
filetype_split = filetype.split("/")
|
||||||
|
|
||||||
if len(filetype_split) < 2 or not filetype_split[0].isnumeric():
|
if len(filetype_split) < 2 or not filetype_split[0].isnumeric():
|
||||||
self.logger.warning(f"Malformed dldate request: {req_json}")
|
self.logger.warning(f"Malformed dldate request: {req_json}")
|
||||||
return b""
|
return Response()
|
||||||
|
|
||||||
version = int(filetype_split[0])
|
version = int(filetype_split[0])
|
||||||
filename = filetype_split[len(filetype_split) - 1]
|
filename = filetype_split[len(filetype_split) - 1]
|
||||||
@ -184,7 +180,7 @@ class CxbServlet(BaseServlet):
|
|||||||
|
|
||||||
if not hasattr(self.versions[internal_ver], func_to_find):
|
if not hasattr(self.versions[internal_ver], func_to_find):
|
||||||
self.logger.warn(f"{version_string} has no handler for filetype {filetype} / {func_to_find}")
|
self.logger.warn(f"{version_string} has no handler for filetype {filetype} / {func_to_find}")
|
||||||
return({"data":""})
|
return JSONResponse({"data":""})
|
||||||
|
|
||||||
self.logger.info(f"{version_string} request for filetype {filetype}")
|
self.logger.info(f"{version_string} request for filetype {filetype}")
|
||||||
self.logger.debug(req_json)
|
self.logger.debug(req_json)
|
||||||
@ -192,7 +188,7 @@ class CxbServlet(BaseServlet):
|
|||||||
handler = getattr(self.versions[internal_ver], func_to_find)
|
handler = getattr(self.versions[internal_ver], func_to_find)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
resp = handler(req_json)
|
resp = await handler(req_json)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error handling request for file {filetype} - {e}")
|
self.logger.error(f"Error handling request for file {filetype} - {e}")
|
||||||
@ -201,19 +197,19 @@ class CxbServlet(BaseServlet):
|
|||||||
traceback.print_exception(tp, val, tb, limit=1)
|
traceback.print_exception(tp, val, tb, limit=1)
|
||||||
with open("{0}/{1}.log".format(self.core_cfg.server.log_dir, "cxb"), "a") as f:
|
with open("{0}/{1}.log".format(self.core_cfg.server.log_dir, "cxb"), "a") as f:
|
||||||
traceback.print_exception(tp, val, tb, limit=1, file=f)
|
traceback.print_exception(tp, val, tb, limit=1, file=f)
|
||||||
return ""
|
return Response()
|
||||||
|
|
||||||
self.logger.debug(f"{version_string} Response {resp}")
|
self.logger.debug(f"{version_string} Response {resp}")
|
||||||
return json.dumps(resp, ensure_ascii=False).encode("utf-8")
|
return JSONResponse(resp, ensure_ascii=False)
|
||||||
|
|
||||||
def handle_action(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
async def handle_action(self, request: Request) -> bytes:
|
||||||
req_json = self.preprocess(request)
|
req_json = await self.preprocess(request)
|
||||||
subcmd = list(req_json.keys())[0]
|
subcmd = list(req_json.keys())[0]
|
||||||
func_to_find = f"handle_action_{subcmd}_request"
|
func_to_find = f"handle_action_{subcmd}_request"
|
||||||
|
|
||||||
if not hasattr(self.versions[0], func_to_find):
|
if not hasattr(self.versions[0], func_to_find):
|
||||||
self.logger.warn(f"No handler for action {subcmd} request")
|
self.logger.warn(f"No handler for action {subcmd} request")
|
||||||
return ""
|
return Response()
|
||||||
|
|
||||||
self.logger.info(f"Action {subcmd} Request")
|
self.logger.info(f"Action {subcmd} Request")
|
||||||
self.logger.debug(req_json)
|
self.logger.debug(req_json)
|
||||||
@ -221,7 +217,7 @@ class CxbServlet(BaseServlet):
|
|||||||
handler = getattr(self.versions[0], func_to_find)
|
handler = getattr(self.versions[0], func_to_find)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
resp = handler(req_json)
|
resp = await handler(req_json)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error handling action {subcmd} request - {e}")
|
self.logger.error(f"Error handling action {subcmd} request - {e}")
|
||||||
@ -230,19 +226,19 @@ class CxbServlet(BaseServlet):
|
|||||||
traceback.print_exception(tp, val, tb, limit=1)
|
traceback.print_exception(tp, val, tb, limit=1)
|
||||||
with open("{0}/{1}.log".format(self.core_cfg.server.log_dir, "cxb"), "a") as f:
|
with open("{0}/{1}.log".format(self.core_cfg.server.log_dir, "cxb"), "a") as f:
|
||||||
traceback.print_exception(tp, val, tb, limit=1, file=f)
|
traceback.print_exception(tp, val, tb, limit=1, file=f)
|
||||||
return ""
|
return Response()
|
||||||
|
|
||||||
self.logger.debug(f"Response {resp}")
|
self.logger.debug(f"Response {resp}")
|
||||||
return json.dumps(resp, ensure_ascii=False).encode("utf-8")
|
return JSONResponse(resp)
|
||||||
|
|
||||||
def handle_auth(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
async def handle_auth(self, request: Request) -> bytes:
|
||||||
req_json = self.preprocess(request)
|
req_json = await self.preprocess(request)
|
||||||
subcmd = list(req_json.keys())[0]
|
subcmd = list(req_json.keys())[0]
|
||||||
func_to_find = f"handle_auth_{subcmd}_request"
|
func_to_find = f"handle_auth_{subcmd}_request"
|
||||||
|
|
||||||
if not hasattr(self.versions[0], func_to_find):
|
if not hasattr(self.versions[0], func_to_find):
|
||||||
self.logger.warn(f"No handler for auth {subcmd} request")
|
self.logger.warn(f"No handler for auth {subcmd} request")
|
||||||
return ""
|
return Response()
|
||||||
|
|
||||||
self.logger.info(f"Action {subcmd} Request")
|
self.logger.info(f"Action {subcmd} Request")
|
||||||
self.logger.debug(req_json)
|
self.logger.debug(req_json)
|
||||||
@ -250,7 +246,7 @@ class CxbServlet(BaseServlet):
|
|||||||
handler = getattr(self.versions[0], func_to_find)
|
handler = getattr(self.versions[0], func_to_find)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
resp = handler(req_json)
|
resp = await handler(req_json)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error handling auth {subcmd} request - {e}")
|
self.logger.error(f"Error handling auth {subcmd} request - {e}")
|
||||||
@ -259,7 +255,7 @@ class CxbServlet(BaseServlet):
|
|||||||
traceback.print_exception(tp, val, tb, limit=1)
|
traceback.print_exception(tp, val, tb, limit=1)
|
||||||
with open("{0}/{1}.log".format(self.core_cfg.server.log_dir, "cxb"), "a") as f:
|
with open("{0}/{1}.log".format(self.core_cfg.server.log_dir, "cxb"), "a") as f:
|
||||||
traceback.print_exception(tp, val, tb, limit=1, file=f)
|
traceback.print_exception(tp, val, tb, limit=1, file=f)
|
||||||
return ""
|
return Response()
|
||||||
|
|
||||||
self.logger.debug(f"Response {resp}")
|
self.logger.debug(f"Response {resp}")
|
||||||
return json.dumps(resp, ensure_ascii=False).encode("utf-8")
|
return JSONResponse(resp)
|
||||||
|
@ -17,10 +17,10 @@ class CxbRev(CxbBase):
|
|||||||
super().__init__(cfg, game_cfg)
|
super().__init__(cfg, game_cfg)
|
||||||
self.version = CxbConstants.VER_CROSSBEATS_REV
|
self.version = CxbConstants.VER_CROSSBEATS_REV
|
||||||
|
|
||||||
def handle_data_path_list_request(self, data: Dict) -> Dict:
|
async def handle_data_path_list_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_putlog_request(self, data: Dict) -> Dict:
|
async def handle_data_putlog_request(self, data: Dict) -> Dict:
|
||||||
if data["putlog"]["type"] == "ResultLog":
|
if data["putlog"]["type"] == "ResultLog":
|
||||||
score_data = json.loads(data["putlog"]["data"])
|
score_data = json.loads(data["putlog"]["data"])
|
||||||
userid = score_data["usid"]
|
userid = score_data["usid"]
|
||||||
@ -45,7 +45,7 @@ class CxbRev(CxbBase):
|
|||||||
return {"data": True}
|
return {"data": True}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_music_list_request(self, data: Dict) -> Dict:
|
async def handle_data_music_list_request(self, data: Dict) -> Dict:
|
||||||
ret_str = ""
|
ret_str = ""
|
||||||
with open(r"titles/cxb/data/rss/MusicArchiveList.csv") as music:
|
with open(r"titles/cxb/data/rss/MusicArchiveList.csv") as music:
|
||||||
lines = music.readlines()
|
lines = music.readlines()
|
||||||
@ -56,7 +56,7 @@ class CxbRev(CxbBase):
|
|||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_item_list_icon_request(self, data: Dict) -> Dict:
|
async def handle_data_item_list_icon_request(self, data: Dict) -> Dict:
|
||||||
ret_str = "\r\n#ItemListIcon\r\n"
|
ret_str = "\r\n#ItemListIcon\r\n"
|
||||||
with open(
|
with open(
|
||||||
r"titles/cxb/data/rss/Item/ItemArchiveList_Icon.csv", encoding="utf-8"
|
r"titles/cxb/data/rss/Item/ItemArchiveList_Icon.csv", encoding="utf-8"
|
||||||
@ -67,7 +67,7 @@ class CxbRev(CxbBase):
|
|||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_item_list_skin_notes_request(self, data: Dict) -> Dict:
|
async def handle_data_item_list_skin_notes_request(self, data: Dict) -> Dict:
|
||||||
ret_str = "\r\n#ItemListSkinNotes\r\n"
|
ret_str = "\r\n#ItemListSkinNotes\r\n"
|
||||||
with open(
|
with open(
|
||||||
r"titles/cxb/data/rss/Item/ItemArchiveList_SkinNotes.csv", encoding="utf-8"
|
r"titles/cxb/data/rss/Item/ItemArchiveList_SkinNotes.csv", encoding="utf-8"
|
||||||
@ -78,7 +78,7 @@ class CxbRev(CxbBase):
|
|||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_item_list_skin_effect_request(self, data: Dict) -> Dict:
|
async def handle_data_item_list_skin_effect_request(self, data: Dict) -> Dict:
|
||||||
ret_str = "\r\n#ItemListSkinEffect\r\n"
|
ret_str = "\r\n#ItemListSkinEffect\r\n"
|
||||||
with open(
|
with open(
|
||||||
r"titles/cxb/data/rss/Item/ItemArchiveList_SkinEffect.csv", encoding="utf-8"
|
r"titles/cxb/data/rss/Item/ItemArchiveList_SkinEffect.csv", encoding="utf-8"
|
||||||
@ -89,7 +89,7 @@ class CxbRev(CxbBase):
|
|||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_item_list_skin_bg_request(self, data: Dict) -> Dict:
|
async def handle_data_item_list_skin_bg_request(self, data: Dict) -> Dict:
|
||||||
ret_str = "\r\n#ItemListSkinBg\r\n"
|
ret_str = "\r\n#ItemListSkinBg\r\n"
|
||||||
with open(
|
with open(
|
||||||
r"titles/cxb/data/rss/Item/ItemArchiveList_SkinBg.csv", encoding="utf-8"
|
r"titles/cxb/data/rss/Item/ItemArchiveList_SkinBg.csv", encoding="utf-8"
|
||||||
@ -100,7 +100,7 @@ class CxbRev(CxbBase):
|
|||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_item_list_title_request(self, data: Dict) -> Dict:
|
async def handle_data_item_list_title_request(self, data: Dict) -> Dict:
|
||||||
ret_str = "\r\n#ItemListTitle\r\n"
|
ret_str = "\r\n#ItemListTitle\r\n"
|
||||||
with open(
|
with open(
|
||||||
r"titles/cxb/data/rss/Item/ItemList_Title.csv", encoding="shift-jis"
|
r"titles/cxb/data/rss/Item/ItemList_Title.csv", encoding="shift-jis"
|
||||||
@ -111,7 +111,7 @@ class CxbRev(CxbBase):
|
|||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_shop_list_music_request(self, data: Dict) -> Dict:
|
async def handle_data_shop_list_music_request(self, data: Dict) -> Dict:
|
||||||
ret_str = "\r\n#ShopListMusic\r\n"
|
ret_str = "\r\n#ShopListMusic\r\n"
|
||||||
with open(
|
with open(
|
||||||
r"titles/cxb/data/rss/Shop/ShopList_Music.csv", encoding="shift-jis"
|
r"titles/cxb/data/rss/Shop/ShopList_Music.csv", encoding="shift-jis"
|
||||||
@ -122,7 +122,7 @@ class CxbRev(CxbBase):
|
|||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_shop_list_icon_request(self, data: Dict) -> Dict:
|
async def handle_data_shop_list_icon_request(self, data: Dict) -> Dict:
|
||||||
ret_str = "\r\n#ShopListIcon\r\n"
|
ret_str = "\r\n#ShopListIcon\r\n"
|
||||||
with open(
|
with open(
|
||||||
r"titles/cxb/data/rss/Shop/ShopList_Icon.csv", encoding="shift-jis"
|
r"titles/cxb/data/rss/Shop/ShopList_Icon.csv", encoding="shift-jis"
|
||||||
@ -133,7 +133,7 @@ class CxbRev(CxbBase):
|
|||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_shop_list_title_request(self, data: Dict) -> Dict:
|
async def handle_data_shop_list_title_request(self, data: Dict) -> Dict:
|
||||||
ret_str = "\r\n#ShopListTitle\r\n"
|
ret_str = "\r\n#ShopListTitle\r\n"
|
||||||
with open(
|
with open(
|
||||||
r"titles/cxb/data/rss/Shop/ShopList_Title.csv", encoding="shift-jis"
|
r"titles/cxb/data/rss/Shop/ShopList_Title.csv", encoding="shift-jis"
|
||||||
@ -143,17 +143,17 @@ class CxbRev(CxbBase):
|
|||||||
ret_str += f"{line[:-1]}\r\n"
|
ret_str += f"{line[:-1]}\r\n"
|
||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
def handle_data_shop_list_skin_hud_request(self, data: Dict) -> Dict:
|
async def handle_data_shop_list_skin_hud_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_shop_list_skin_arrow_request(self, data: Dict) -> Dict:
|
async def handle_data_shop_list_skin_arrow_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_shop_list_skin_hit_request(self, data: Dict) -> Dict:
|
async def handle_data_shop_list_skin_hit_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_shop_list_sale_request(self, data: Dict) -> Dict:
|
async def handle_data_shop_list_sale_request(self, data: Dict) -> Dict:
|
||||||
ret_str = "\r\n#ShopListSale\r\n"
|
ret_str = "\r\n#ShopListSale\r\n"
|
||||||
with open(
|
with open(
|
||||||
r"titles/cxb/data/rss/Shop/ShopList_Sale.csv", encoding="shift-jis"
|
r"titles/cxb/data/rss/Shop/ShopList_Sale.csv", encoding="shift-jis"
|
||||||
@ -163,11 +163,11 @@ class CxbRev(CxbBase):
|
|||||||
ret_str += f"{line[:-1]}\r\n"
|
ret_str += f"{line[:-1]}\r\n"
|
||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
def handle_data_extra_stage_list_request(self, data: Dict) -> Dict:
|
async def handle_data_extra_stage_list_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_exxxxx_request(self, data: Dict) -> Dict:
|
async def handle_data_exxxxx_request(self, data: Dict) -> Dict:
|
||||||
extra_num = int(data["dldate"]["filetype"][-4:])
|
extra_num = int(data["dldate"]["filetype"][-4:])
|
||||||
ret_str = ""
|
ret_str = ""
|
||||||
with open(
|
with open(
|
||||||
@ -178,14 +178,14 @@ class CxbRev(CxbBase):
|
|||||||
ret_str += f"{line[:-1]}\r\n"
|
ret_str += f"{line[:-1]}\r\n"
|
||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
def handle_data_bonus_list10100_request(self, data: Dict) -> Dict:
|
async def handle_data_bonus_list10100_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_free_coupon_request(self, data: Dict) -> Dict:
|
async def handle_data_free_coupon_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_news_list_request(self, data: Dict) -> Dict:
|
async def handle_data_news_list_request(self, data: Dict) -> Dict:
|
||||||
ret_str = ""
|
ret_str = ""
|
||||||
with open(r"titles/cxb/data/rss/NewsList.csv", encoding="UTF-8") as news:
|
with open(r"titles/cxb/data/rss/NewsList.csv", encoding="UTF-8") as news:
|
||||||
lines = news.readlines()
|
lines = news.readlines()
|
||||||
@ -193,11 +193,11 @@ class CxbRev(CxbBase):
|
|||||||
ret_str += f"{line[:-1]}\r\n"
|
ret_str += f"{line[:-1]}\r\n"
|
||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
def handle_data_tips_request(self, data: Dict) -> Dict:
|
async def handle_data_tips_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_license_request(self, data: Dict) -> Dict:
|
async def handle_data_license_request(self, data: Dict) -> Dict:
|
||||||
ret_str = ""
|
ret_str = ""
|
||||||
with open(r"titles/cxb/data/rss/License_Offline.csv", encoding="UTF-8") as lic:
|
with open(r"titles/cxb/data/rss/License_Offline.csv", encoding="UTF-8") as lic:
|
||||||
lines = lic.readlines()
|
lines = lic.readlines()
|
||||||
@ -206,7 +206,7 @@ class CxbRev(CxbBase):
|
|||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_course_list_request(self, data: Dict) -> Dict:
|
async def handle_data_course_list_request(self, data: Dict) -> Dict:
|
||||||
ret_str = ""
|
ret_str = ""
|
||||||
with open(
|
with open(
|
||||||
r"titles/cxb/data/rss/Course/CourseList.csv", encoding="UTF-8"
|
r"titles/cxb/data/rss/Course/CourseList.csv", encoding="UTF-8"
|
||||||
@ -217,7 +217,7 @@ class CxbRev(CxbBase):
|
|||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_csxxxx_request(self, data: Dict) -> Dict:
|
async def handle_data_csxxxx_request(self, data: Dict) -> Dict:
|
||||||
# Removed the CSVs since the format isnt quite right
|
# Removed the CSVs since the format isnt quite right
|
||||||
extra_num = int(data["dldate"]["filetype"][-4:])
|
extra_num = int(data["dldate"]["filetype"][-4:])
|
||||||
ret_str = ""
|
ret_str = ""
|
||||||
@ -230,7 +230,7 @@ class CxbRev(CxbBase):
|
|||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_mission_list_request(self, data: Dict) -> Dict:
|
async def handle_data_mission_list_request(self, data: Dict) -> Dict:
|
||||||
ret_str = ""
|
ret_str = ""
|
||||||
with open(
|
with open(
|
||||||
r"titles/cxb/data/rss/MissionList.csv", encoding="shift-jis"
|
r"titles/cxb/data/rss/MissionList.csv", encoding="shift-jis"
|
||||||
@ -240,14 +240,14 @@ class CxbRev(CxbBase):
|
|||||||
ret_str += f"{line[:-1]}\r\n"
|
ret_str += f"{line[:-1]}\r\n"
|
||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
def handle_data_mission_bonus_request(self, data: Dict) -> Dict:
|
async def handle_data_mission_bonus_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_unlimited_mission_request(self, data: Dict) -> Dict:
|
async def handle_data_unlimited_mission_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_event_list_request(self, data: Dict) -> Dict:
|
async def handle_data_event_list_request(self, data: Dict) -> Dict:
|
||||||
ret_str = ""
|
ret_str = ""
|
||||||
with open(
|
with open(
|
||||||
r"titles/cxb/data/rss/Event/EventArchiveList.csv", encoding="shift-jis"
|
r"titles/cxb/data/rss/Event/EventArchiveList.csv", encoding="shift-jis"
|
||||||
@ -257,39 +257,39 @@ class CxbRev(CxbBase):
|
|||||||
ret_str += f"{line[:-1]}\r\n"
|
ret_str += f"{line[:-1]}\r\n"
|
||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
def handle_data_event_music_list_request(self, data: Dict) -> Dict:
|
async def handle_data_event_music_list_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_event_mission_list_request(self, data: Dict) -> Dict:
|
async def handle_data_event_mission_list_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_event_achievement_single_high_score_list_request(
|
async def handle_data_event_achievement_single_high_score_list_request(
|
||||||
self, data: Dict
|
self, data: Dict
|
||||||
) -> Dict:
|
) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_event_achievement_single_accumulation_request(
|
async def handle_data_event_achievement_single_accumulation_request(
|
||||||
self, data: Dict
|
self, data: Dict
|
||||||
) -> Dict:
|
) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_event_ranking_high_score_list_request(self, data: Dict) -> Dict:
|
async def handle_data_event_ranking_high_score_list_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_event_ranking_accumulation_list_request(self, data: Dict) -> Dict:
|
async def handle_data_event_ranking_accumulation_list_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_event_ranking_stamp_list_request(self, data: Dict) -> Dict:
|
async def handle_data_event_ranking_stamp_list_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_event_ranking_store_list_request(self, data: Dict) -> Dict:
|
async def handle_data_event_ranking_store_list_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_event_ranking_area_list_request(self, data: Dict) -> Dict:
|
async def handle_data_event_ranking_area_list_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_event_stamp_list_request(self, data: Dict) -> Dict:
|
async def handle_data_event_stamp_list_request(self, data: Dict) -> Dict:
|
||||||
ret_str = ""
|
ret_str = ""
|
||||||
with open(
|
with open(
|
||||||
r"titles/cxb/data/rss/Event/EventStampList.csv", encoding="shift-jis"
|
r"titles/cxb/data/rss/Event/EventStampList.csv", encoding="shift-jis"
|
||||||
@ -299,8 +299,8 @@ class CxbRev(CxbBase):
|
|||||||
ret_str += f"{line[:-1]}\r\n"
|
ret_str += f"{line[:-1]}\r\n"
|
||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
def handle_data_event_stamp_map_list_csxxxx_request(self, data: Dict) -> Dict:
|
async def handle_data_event_stamp_map_list_csxxxx_request(self, data: Dict) -> Dict:
|
||||||
return {"data": "1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"}
|
return {"data": "1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"}
|
||||||
|
|
||||||
def handle_data_server_state_request(self, data: Dict) -> Dict:
|
async def handle_data_server_state_request(self, data: Dict) -> Dict:
|
||||||
return {"data": True}
|
return {"data": True}
|
||||||
|
@ -17,11 +17,11 @@ class CxbRevSunriseS1(CxbBase):
|
|||||||
super().__init__(cfg, game_cfg)
|
super().__init__(cfg, game_cfg)
|
||||||
self.version = CxbConstants.VER_CROSSBEATS_REV_SUNRISE_S1
|
self.version = CxbConstants.VER_CROSSBEATS_REV_SUNRISE_S1
|
||||||
|
|
||||||
def handle_data_path_list_request(self, data: Dict) -> Dict:
|
async def handle_data_path_list_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_music_list_request(self, data: Dict) -> Dict:
|
async def handle_data_music_list_request(self, data: Dict) -> Dict:
|
||||||
ret_str = ""
|
ret_str = ""
|
||||||
with open(r"titles/cxb/data/rss1/MusicArchiveList.csv") as music:
|
with open(r"titles/cxb/data/rss1/MusicArchiveList.csv") as music:
|
||||||
lines = music.readlines()
|
lines = music.readlines()
|
||||||
@ -32,7 +32,7 @@ class CxbRevSunriseS1(CxbBase):
|
|||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_item_list_detail_request(self, data: Dict) -> Dict:
|
async def handle_data_item_list_detail_request(self, data: Dict) -> Dict:
|
||||||
# ItemListIcon load
|
# ItemListIcon load
|
||||||
ret_str = "#ItemListIcon\r\n"
|
ret_str = "#ItemListIcon\r\n"
|
||||||
with open(
|
with open(
|
||||||
@ -54,7 +54,7 @@ class CxbRevSunriseS1(CxbBase):
|
|||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_shop_list_detail_request(self, data: Dict) -> Dict:
|
async def handle_data_shop_list_detail_request(self, data: Dict) -> Dict:
|
||||||
# ShopListIcon load
|
# ShopListIcon load
|
||||||
ret_str = "#ShopListIcon\r\n"
|
ret_str = "#ShopListIcon\r\n"
|
||||||
with open(
|
with open(
|
||||||
@ -119,26 +119,26 @@ class CxbRevSunriseS1(CxbBase):
|
|||||||
ret_str += f"{line[:-1]}\r\n"
|
ret_str += f"{line[:-1]}\r\n"
|
||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
def handle_data_extra_stage_list_request(self, data: Dict) -> Dict:
|
async def handle_data_extra_stage_list_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_exxxxx_request(self, data: Dict) -> Dict:
|
async def handle_data_exxxxx_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_one_more_extra_list_request(self, data: Dict) -> Dict:
|
async def handle_data_one_more_extra_list_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_bonus_list10100_request(self, data: Dict) -> Dict:
|
async def handle_data_bonus_list10100_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_oexxxx_request(self, data: Dict) -> Dict:
|
async def handle_data_oexxxx_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_free_coupon_request(self, data: Dict) -> Dict:
|
async def handle_data_free_coupon_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_news_list_request(self, data: Dict) -> Dict:
|
async def handle_data_news_list_request(self, data: Dict) -> Dict:
|
||||||
ret_str = ""
|
ret_str = ""
|
||||||
with open(r"titles/cxb/data/rss1/NewsList.csv", encoding="UTF-8") as news:
|
with open(r"titles/cxb/data/rss1/NewsList.csv", encoding="UTF-8") as news:
|
||||||
lines = news.readlines()
|
lines = news.readlines()
|
||||||
@ -146,14 +146,14 @@ class CxbRevSunriseS1(CxbBase):
|
|||||||
ret_str += f"{line[:-1]}\r\n"
|
ret_str += f"{line[:-1]}\r\n"
|
||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
def handle_data_tips_request(self, data: Dict) -> Dict:
|
async def handle_data_tips_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_release_info_list_request(self, data: Dict) -> Dict:
|
async def handle_data_release_info_list_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_random_music_list_request(self, data: Dict) -> Dict:
|
async def handle_data_random_music_list_request(self, data: Dict) -> Dict:
|
||||||
ret_str = ""
|
ret_str = ""
|
||||||
with open(r"titles/cxb/data/rss1/MusicArchiveList.csv") as music:
|
with open(r"titles/cxb/data/rss1/MusicArchiveList.csv") as music:
|
||||||
lines = music.readlines()
|
lines = music.readlines()
|
||||||
@ -167,7 +167,7 @@ class CxbRevSunriseS1(CxbBase):
|
|||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_license_request(self, data: Dict) -> Dict:
|
async def handle_data_license_request(self, data: Dict) -> Dict:
|
||||||
ret_str = ""
|
ret_str = ""
|
||||||
with open(r"titles/cxb/data/rss1/License.csv", encoding="UTF-8") as licenses:
|
with open(r"titles/cxb/data/rss1/License.csv", encoding="UTF-8") as licenses:
|
||||||
lines = licenses.readlines()
|
lines = licenses.readlines()
|
||||||
@ -176,7 +176,7 @@ class CxbRevSunriseS1(CxbBase):
|
|||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_course_list_request(self, data: Dict) -> Dict:
|
async def handle_data_course_list_request(self, data: Dict) -> Dict:
|
||||||
ret_str = ""
|
ret_str = ""
|
||||||
with open(
|
with open(
|
||||||
r"titles/cxb/data/rss1/Course/CourseList.csv", encoding="UTF-8"
|
r"titles/cxb/data/rss1/Course/CourseList.csv", encoding="UTF-8"
|
||||||
@ -187,7 +187,7 @@ class CxbRevSunriseS1(CxbBase):
|
|||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_csxxxx_request(self, data: Dict) -> Dict:
|
async def handle_data_csxxxx_request(self, data: Dict) -> Dict:
|
||||||
extra_num = int(data["dldate"]["filetype"][-4:])
|
extra_num = int(data["dldate"]["filetype"][-4:])
|
||||||
ret_str = ""
|
ret_str = ""
|
||||||
with open(
|
with open(
|
||||||
@ -198,16 +198,16 @@ class CxbRevSunriseS1(CxbBase):
|
|||||||
ret_str += f"{line[:-1]}\r\n"
|
ret_str += f"{line[:-1]}\r\n"
|
||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
def handle_data_mission_list_request(self, data: Dict) -> Dict:
|
async def handle_data_mission_list_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_mission_bonus_request(self, data: Dict) -> Dict:
|
async def handle_data_mission_bonus_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_unlimited_mission_request(self, data: Dict) -> Dict:
|
async def handle_data_unlimited_mission_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_partner_list_request(self, data: Dict) -> Dict:
|
async def handle_data_partner_list_request(self, data: Dict) -> Dict:
|
||||||
ret_str = ""
|
ret_str = ""
|
||||||
# Lord forgive me for the sins I am about to commit
|
# Lord forgive me for the sins I am about to commit
|
||||||
for i in range(0, 10):
|
for i in range(0, 10):
|
||||||
@ -226,7 +226,7 @@ class CxbRevSunriseS1(CxbBase):
|
|||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_partnerxxxx_request(self, data: Dict) -> Dict:
|
async def handle_data_partnerxxxx_request(self, data: Dict) -> Dict:
|
||||||
partner_num = int(data["dldate"]["filetype"][-4:])
|
partner_num = int(data["dldate"]["filetype"][-4:])
|
||||||
ret_str = f"{partner_num},,{partner_num},1,10000,\r\n"
|
ret_str = f"{partner_num},,{partner_num},1,10000,\r\n"
|
||||||
with open(r"titles/cxb/data/rss1/Partner0000.csv") as partner:
|
with open(r"titles/cxb/data/rss1/Partner0000.csv") as partner:
|
||||||
@ -235,13 +235,13 @@ class CxbRevSunriseS1(CxbBase):
|
|||||||
ret_str += f"{line[:-1]}\r\n"
|
ret_str += f"{line[:-1]}\r\n"
|
||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
def handle_data_server_state_request(self, data: Dict) -> Dict:
|
async def handle_data_server_state_request(self, data: Dict) -> Dict:
|
||||||
return {"data": True}
|
return {"data": True}
|
||||||
|
|
||||||
def handle_data_settings_request(self, data: Dict) -> Dict:
|
async def handle_data_settings_request(self, data: Dict) -> Dict:
|
||||||
return {"data": "2,\r\n"}
|
return {"data": "2,\r\n"}
|
||||||
|
|
||||||
def handle_data_story_list_request(self, data: Dict) -> Dict:
|
async def handle_data_story_list_request(self, data: Dict) -> Dict:
|
||||||
# story id, story name, game version, start time, end time, course arc, unlock flag, song mcode for menu
|
# story id, story name, game version, start time, end time, course arc, unlock flag, song mcode for menu
|
||||||
ret_str = "\r\n"
|
ret_str = "\r\n"
|
||||||
ret_str += (
|
ret_str += (
|
||||||
@ -253,23 +253,23 @@ class CxbRevSunriseS1(CxbBase):
|
|||||||
ret_str += f"st0002,REMNANT,10104,1502127790,4096483201,Cs1000,-1,overcl,\r\n"
|
ret_str += f"st0002,REMNANT,10104,1502127790,4096483201,Cs1000,-1,overcl,\r\n"
|
||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
def handle_data_stxxxx_request(self, data: Dict) -> Dict:
|
async def handle_data_stxxxx_request(self, data: Dict) -> Dict:
|
||||||
story_num = int(data["dldate"]["filetype"][-4:])
|
story_num = int(data["dldate"]["filetype"][-4:])
|
||||||
ret_str = ""
|
ret_str = ""
|
||||||
for i in range(1, 11):
|
for i in range(1, 11):
|
||||||
ret_str += f"{i},st000{story_num}_{i-1},,,,,,,,,,,,,,,,1,,-1,1,\r\n"
|
ret_str += f"{i},st000{story_num}_{i-1},,,,,,,,,,,,,,,,1,,-1,1,\r\n"
|
||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
def handle_data_event_stamp_list_request(self, data: Dict) -> Dict:
|
async def handle_data_event_stamp_list_request(self, data: Dict) -> Dict:
|
||||||
return {"data": "Cs1032,1,1,1,1,1,1,1,1,1,1,\r\n"}
|
return {"data": "Cs1032,1,1,1,1,1,1,1,1,1,1,\r\n"}
|
||||||
|
|
||||||
def handle_data_premium_list_request(self, data: Dict) -> Dict:
|
async def handle_data_premium_list_request(self, data: Dict) -> Dict:
|
||||||
return {"data": "1,,,,10,,,,,99,,,,,,,,,100,,\r\n"}
|
return {"data": "1,,,,10,,,,,99,,,,,,,,,100,,\r\n"}
|
||||||
|
|
||||||
def handle_data_event_list_request(self, data: Dict) -> Dict:
|
async def handle_data_event_list_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_event_detail_list_request(self, data: Dict) -> Dict:
|
async def handle_data_event_detail_list_request(self, data: Dict) -> Dict:
|
||||||
event_id = data["dldate"]["filetype"].split("/")[2]
|
event_id = data["dldate"]["filetype"].split("/")[2]
|
||||||
if "EventStampMapListCs1002" in event_id:
|
if "EventStampMapListCs1002" in event_id:
|
||||||
return {"data": "1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"}
|
return {"data": "1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"}
|
||||||
@ -278,7 +278,7 @@ class CxbRevSunriseS1(CxbBase):
|
|||||||
else:
|
else:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_event_stamp_map_list_csxxxx_request(self, data: Dict) -> Dict:
|
async def handle_data_event_stamp_map_list_csxxxx_request(self, data: Dict) -> Dict:
|
||||||
event_id = data["dldate"]["filetype"].split("/")[2]
|
event_id = data["dldate"]["filetype"].split("/")[2]
|
||||||
if "EventStampMapListCs1002" in event_id:
|
if "EventStampMapListCs1002" in event_id:
|
||||||
return {"data": "1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"}
|
return {"data": "1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"}
|
||||||
|
@ -17,11 +17,11 @@ class CxbRevSunriseS2(CxbBase):
|
|||||||
super().__init__(cfg, game_cfg)
|
super().__init__(cfg, game_cfg)
|
||||||
self.version = CxbConstants.VER_CROSSBEATS_REV_SUNRISE_S2_OMNI
|
self.version = CxbConstants.VER_CROSSBEATS_REV_SUNRISE_S2_OMNI
|
||||||
|
|
||||||
def handle_data_path_list_request(self, data: Dict) -> Dict:
|
async def handle_data_path_list_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_music_list_request(self, data: Dict) -> Dict:
|
async def handle_data_music_list_request(self, data: Dict) -> Dict:
|
||||||
version = data["dldate"]["filetype"].split("/")[0]
|
version = data["dldate"]["filetype"].split("/")[0]
|
||||||
ret_str = ""
|
ret_str = ""
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ class CxbRevSunriseS2(CxbBase):
|
|||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_item_list_detail_request(self, data: Dict) -> Dict:
|
async def handle_data_item_list_detail_request(self, data: Dict) -> Dict:
|
||||||
# ItemListIcon load
|
# ItemListIcon load
|
||||||
ret_str = "#ItemListIcon\r\n"
|
ret_str = "#ItemListIcon\r\n"
|
||||||
with open(
|
with open(
|
||||||
@ -63,7 +63,7 @@ class CxbRevSunriseS2(CxbBase):
|
|||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_shop_list_detail_request(self, data: Dict) -> Dict:
|
async def handle_data_shop_list_detail_request(self, data: Dict) -> Dict:
|
||||||
# ShopListIcon load
|
# ShopListIcon load
|
||||||
ret_str = "#ShopListIcon\r\n"
|
ret_str = "#ShopListIcon\r\n"
|
||||||
with open(
|
with open(
|
||||||
@ -128,7 +128,7 @@ class CxbRevSunriseS2(CxbBase):
|
|||||||
ret_str += f"{line[:-1]}\r\n"
|
ret_str += f"{line[:-1]}\r\n"
|
||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
def handle_data_extra_stage_list_request(self, data: Dict) -> Dict:
|
async def handle_data_extra_stage_list_request(self, data: Dict) -> Dict:
|
||||||
ret_str=""
|
ret_str=""
|
||||||
with open(r"titles/cxb/data/rss2/ExtraStageList.csv") as extra:
|
with open(r"titles/cxb/data/rss2/ExtraStageList.csv") as extra:
|
||||||
lines = extra.readlines()
|
lines = extra.readlines()
|
||||||
@ -136,19 +136,19 @@ class CxbRevSunriseS2(CxbBase):
|
|||||||
ret_str += f"{line[:-1]}\r\n"
|
ret_str += f"{line[:-1]}\r\n"
|
||||||
return({"data":ret_str})
|
return({"data":ret_str})
|
||||||
|
|
||||||
def handle_data_exxxxx_request(self, data: Dict) -> Dict:
|
async def handle_data_exxxxx_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_one_more_extra_list_request(self, data: Dict) -> Dict:
|
async def handle_data_one_more_extra_list_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_bonus_list10100_request(self, data: Dict) -> Dict:
|
async def handle_data_bonus_list10100_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_oexxxx_request(self, data: Dict) -> Dict:
|
async def handle_data_oexxxx_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_free_coupon_request(self, data: Dict) -> Dict:
|
async def handle_data_free_coupon_request(self, data: Dict) -> Dict:
|
||||||
ret_str=""
|
ret_str=""
|
||||||
with open(r"titles/cxb/data/rss2/FreeCoupon.csv") as coupon:
|
with open(r"titles/cxb/data/rss2/FreeCoupon.csv") as coupon:
|
||||||
lines = coupon.readlines()
|
lines = coupon.readlines()
|
||||||
@ -157,7 +157,7 @@ class CxbRevSunriseS2(CxbBase):
|
|||||||
return({"data":ret_str})
|
return({"data":ret_str})
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_news_list_request(self, data: Dict) -> Dict:
|
async def handle_data_news_list_request(self, data: Dict) -> Dict:
|
||||||
ret_str = ""
|
ret_str = ""
|
||||||
with open(r"titles/cxb/data/rss2/NewsList.csv", encoding="UTF-8") as news:
|
with open(r"titles/cxb/data/rss2/NewsList.csv", encoding="UTF-8") as news:
|
||||||
lines = news.readlines()
|
lines = news.readlines()
|
||||||
@ -165,14 +165,14 @@ class CxbRevSunriseS2(CxbBase):
|
|||||||
ret_str += f"{line[:-1]}\r\n"
|
ret_str += f"{line[:-1]}\r\n"
|
||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
def handle_data_tips_request(self, data: Dict) -> Dict:
|
async def handle_data_tips_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_release_info_list_request(self, data: Dict) -> Dict:
|
async def handle_data_release_info_list_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_random_music_list_request(self, data: Dict) -> Dict:
|
async def handle_data_random_music_list_request(self, data: Dict) -> Dict:
|
||||||
ret_str = ""
|
ret_str = ""
|
||||||
with open(r"titles/cxb/data/rss2/MusicArchiveList.csv") as music:
|
with open(r"titles/cxb/data/rss2/MusicArchiveList.csv") as music:
|
||||||
lines = music.readlines()
|
lines = music.readlines()
|
||||||
@ -186,7 +186,7 @@ class CxbRevSunriseS2(CxbBase):
|
|||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_license_request(self, data: Dict) -> Dict:
|
async def handle_data_license_request(self, data: Dict) -> Dict:
|
||||||
ret_str = ""
|
ret_str = ""
|
||||||
with open(r"titles/cxb/data/rss2/License.csv", encoding="UTF-8") as licenses:
|
with open(r"titles/cxb/data/rss2/License.csv", encoding="UTF-8") as licenses:
|
||||||
lines = licenses.readlines()
|
lines = licenses.readlines()
|
||||||
@ -195,7 +195,7 @@ class CxbRevSunriseS2(CxbBase):
|
|||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_course_list_request(self, data: Dict) -> Dict:
|
async def handle_data_course_list_request(self, data: Dict) -> Dict:
|
||||||
ret_str = ""
|
ret_str = ""
|
||||||
with open(
|
with open(
|
||||||
r"titles/cxb/data/rss2/Course/CourseList.csv", encoding="UTF-8"
|
r"titles/cxb/data/rss2/Course/CourseList.csv", encoding="UTF-8"
|
||||||
@ -206,7 +206,7 @@ class CxbRevSunriseS2(CxbBase):
|
|||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_csxxxx_request(self, data: Dict) -> Dict:
|
async def handle_data_csxxxx_request(self, data: Dict) -> Dict:
|
||||||
extra_num = int(data["dldate"]["filetype"][-4:])
|
extra_num = int(data["dldate"]["filetype"][-4:])
|
||||||
ret_str = ""
|
ret_str = ""
|
||||||
with open(
|
with open(
|
||||||
@ -217,16 +217,16 @@ class CxbRevSunriseS2(CxbBase):
|
|||||||
ret_str += f"{line[:-1]}\r\n"
|
ret_str += f"{line[:-1]}\r\n"
|
||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
def handle_data_mission_list_request(self, data: Dict) -> Dict:
|
async def handle_data_mission_list_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_mission_bonus_request(self, data: Dict) -> Dict:
|
async def handle_data_mission_bonus_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_unlimited_mission_request(self, data: Dict) -> Dict:
|
async def handle_data_unlimited_mission_request(self, data: Dict) -> Dict:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_partner_list_request(self, data: Dict) -> Dict:
|
async def handle_data_partner_list_request(self, data: Dict) -> Dict:
|
||||||
ret_str = ""
|
ret_str = ""
|
||||||
# Lord forgive me for the sins I am about to commit
|
# Lord forgive me for the sins I am about to commit
|
||||||
for i in range(0, 10):
|
for i in range(0, 10):
|
||||||
@ -245,7 +245,7 @@ class CxbRevSunriseS2(CxbBase):
|
|||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
@cached(lifetime=86400)
|
@cached(lifetime=86400)
|
||||||
def handle_data_partnerxxxx_request(self, data: Dict) -> Dict:
|
async def handle_data_partnerxxxx_request(self, data: Dict) -> Dict:
|
||||||
partner_num = int(data["dldate"]["filetype"][-4:])
|
partner_num = int(data["dldate"]["filetype"][-4:])
|
||||||
ret_str = f"{partner_num},,{partner_num},1,10000,\r\n"
|
ret_str = f"{partner_num},,{partner_num},1,10000,\r\n"
|
||||||
with open(r"titles/cxb/data/rss2/Partner0000.csv") as partner:
|
with open(r"titles/cxb/data/rss2/Partner0000.csv") as partner:
|
||||||
@ -254,13 +254,13 @@ class CxbRevSunriseS2(CxbBase):
|
|||||||
ret_str += f"{line[:-1]}\r\n"
|
ret_str += f"{line[:-1]}\r\n"
|
||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
def handle_data_server_state_request(self, data: Dict) -> Dict:
|
async def handle_data_server_state_request(self, data: Dict) -> Dict:
|
||||||
return {"data": True}
|
return {"data": True}
|
||||||
|
|
||||||
def handle_data_settings_request(self, data: Dict) -> Dict:
|
async def handle_data_settings_request(self, data: Dict) -> Dict:
|
||||||
return {"data": "2,\r\n"}
|
return {"data": "2,\r\n"}
|
||||||
|
|
||||||
def handle_data_story_list_request(self, data: Dict) -> Dict:
|
async def handle_data_story_list_request(self, data: Dict) -> Dict:
|
||||||
# story id, story name, game version, start time, end time, course arc, unlock flag, song mcode for menu
|
# story id, story name, game version, start time, end time, course arc, unlock flag, song mcode for menu
|
||||||
ret_str = "\r\n"
|
ret_str = "\r\n"
|
||||||
ret_str += (
|
ret_str += (
|
||||||
@ -272,7 +272,7 @@ class CxbRevSunriseS2(CxbBase):
|
|||||||
ret_str += f"st0002,REMNANT,10104,1502127790,4096483201,Cs1000,-1,overcl,\r\n"
|
ret_str += f"st0002,REMNANT,10104,1502127790,4096483201,Cs1000,-1,overcl,\r\n"
|
||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
def handle_data_stxxxx_request(self, data: Dict) -> Dict:
|
async def handle_data_stxxxx_request(self, data: Dict) -> Dict:
|
||||||
story_num = int(data["dldate"]["filetype"][-4:])
|
story_num = int(data["dldate"]["filetype"][-4:])
|
||||||
ret_str = ""
|
ret_str = ""
|
||||||
# Each stories appears to have 10 pieces based on the wiki but as on how they are set.... no clue
|
# Each stories appears to have 10 pieces based on the wiki but as on how they are set.... no clue
|
||||||
@ -280,18 +280,18 @@ class CxbRevSunriseS2(CxbBase):
|
|||||||
ret_str += f"{i},st000{story_num}_{i-1},,,,,,,,,,,,,,,,1,,-1,1,\r\n"
|
ret_str += f"{i},st000{story_num}_{i-1},,,,,,,,,,,,,,,,1,,-1,1,\r\n"
|
||||||
return {"data": ret_str}
|
return {"data": ret_str}
|
||||||
|
|
||||||
def handle_data_event_stamp_list_request(self, data: Dict) -> Dict:
|
async def handle_data_event_stamp_list_request(self, data: Dict) -> Dict:
|
||||||
return {"data": "Cs1002,1,1,1,1,1,1,1,1,1,1,\r\n"}
|
return {"data": "Cs1002,1,1,1,1,1,1,1,1,1,1,\r\n"}
|
||||||
|
|
||||||
def handle_data_premium_list_request(self, data: Dict) -> Dict:
|
async def handle_data_premium_list_request(self, data: Dict) -> Dict:
|
||||||
return {"data": "1,,,,10,,,,,99,,,,,,,,,100,,\r\n"}
|
return {"data": "1,,,,10,,,,,99,,,,,,,,,100,,\r\n"}
|
||||||
|
|
||||||
def handle_data_event_list_request(self, data: Dict) -> Dict:
|
async def handle_data_event_list_request(self, data: Dict) -> Dict:
|
||||||
return {
|
return {
|
||||||
"data": "Cs4001,0,10000,1601510400,1604188799,1,nv2006,1,\r\nCs4005,0,10000,1609459200,1615766399,1,nv2006,1,\r\n"
|
"data": "Cs4001,0,10000,1601510400,1604188799,1,nv2006,1,\r\nCs4005,0,10000,1609459200,1615766399,1,nv2006,1,\r\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_data_event_detail_list_request(self, data: Dict) -> Dict:
|
async def handle_data_event_detail_list_request(self, data: Dict) -> Dict:
|
||||||
event_id = data["dldate"]["filetype"].split("/")[2]
|
event_id = data["dldate"]["filetype"].split("/")[2]
|
||||||
if "Cs4001" in event_id:
|
if "Cs4001" in event_id:
|
||||||
return {
|
return {
|
||||||
@ -308,7 +308,7 @@ class CxbRevSunriseS2(CxbBase):
|
|||||||
else:
|
else:
|
||||||
return {"data": ""}
|
return {"data": ""}
|
||||||
|
|
||||||
def handle_data_event_stamp_map_list_csxxxx_request(self, data: Dict) -> Dict:
|
async def handle_data_event_stamp_map_list_csxxxx_request(self, data: Dict) -> Dict:
|
||||||
event_id = data["dldate"]["filetype"].split("/")[2]
|
event_id = data["dldate"]["filetype"].split("/")[2]
|
||||||
if "EventStampMapListCs1002" in event_id:
|
if "EventStampMapListCs1002" in event_id:
|
||||||
return {"data": "1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"}
|
return {"data": "1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import datetime
|
import datetime
|
||||||
from typing import Any, List, Dict
|
from typing import Dict
|
||||||
import logging
|
import logging
|
||||||
import json
|
import urllib.parse
|
||||||
import urllib
|
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
@ -24,13 +23,13 @@ class DivaBase:
|
|||||||
dt = datetime.datetime.now()
|
dt = datetime.datetime.now()
|
||||||
self.time_lut = urllib.parse.quote(dt.strftime("%Y-%m-%d %H:%M:%S:16.0"))
|
self.time_lut = urllib.parse.quote(dt.strftime("%Y-%m-%d %H:%M:%S:16.0"))
|
||||||
|
|
||||||
def handle_test_request(self, data: Dict) -> Dict:
|
async def handle_test_request(self, data: Dict) -> Dict:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def handle_game_init_request(self, data: Dict) -> Dict:
|
async def handle_game_init_request(self, data: Dict) -> Dict:
|
||||||
return f""
|
return f""
|
||||||
|
|
||||||
def handle_attend_request(self, data: Dict) -> Dict:
|
async def handle_attend_request(self, data: Dict) -> Dict:
|
||||||
encoded = "&"
|
encoded = "&"
|
||||||
params = {
|
params = {
|
||||||
"atnd_prm1": "0,1,1,0,0,0,1,0,100,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1",
|
"atnd_prm1": "0,1,1,0,0,0,1,0,100,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1",
|
||||||
@ -44,7 +43,7 @@ class DivaBase:
|
|||||||
|
|
||||||
return encoded
|
return encoded
|
||||||
|
|
||||||
def handle_ping_request(self, data: Dict) -> Dict:
|
async def handle_ping_request(self, data: Dict) -> Dict:
|
||||||
encoded = "&"
|
encoded = "&"
|
||||||
params = {
|
params = {
|
||||||
"ping_b_msg": f"Welcome to {self.core_cfg.server.name} network!",
|
"ping_b_msg": f"Welcome to {self.core_cfg.server.name} network!",
|
||||||
@ -89,7 +88,7 @@ class DivaBase:
|
|||||||
|
|
||||||
return encoded
|
return encoded
|
||||||
|
|
||||||
def handle_pv_list_request(self, data: Dict) -> Dict:
|
async def handle_pv_list_request(self, data: Dict) -> Dict:
|
||||||
pvlist = ""
|
pvlist = ""
|
||||||
with open(r"titles/diva/data/PvList0.dat", encoding="utf-8") as shop:
|
with open(r"titles/diva/data/PvList0.dat", encoding="utf-8") as shop:
|
||||||
lines = shop.readlines()
|
lines = shop.readlines()
|
||||||
@ -126,7 +125,7 @@ class DivaBase:
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def handle_shop_catalog_request(self, data: Dict) -> Dict:
|
async def handle_shop_catalog_request(self, data: Dict) -> Dict:
|
||||||
catalog = ""
|
catalog = ""
|
||||||
|
|
||||||
shopList = self.data.static.get_enabled_shops(self.version)
|
shopList = self.data.static.get_enabled_shops(self.version)
|
||||||
@ -164,7 +163,7 @@ class DivaBase:
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def handle_buy_module_request(self, data: Dict) -> Dict:
|
async def handle_buy_module_request(self, data: Dict) -> Dict:
|
||||||
profile = self.data.profile.get_profile(data["pd_id"], self.version)
|
profile = self.data.profile.get_profile(data["pd_id"], self.version)
|
||||||
module = self.data.static.get_enabled_shop(self.version, int(data["mdl_id"]))
|
module = self.data.static.get_enabled_shop(self.version, int(data["mdl_id"]))
|
||||||
|
|
||||||
@ -191,7 +190,7 @@ class DivaBase:
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def handle_cstmz_itm_ctlg_request(self, data: Dict) -> Dict:
|
async def handle_cstmz_itm_ctlg_request(self, data: Dict) -> Dict:
|
||||||
catalog = ""
|
catalog = ""
|
||||||
|
|
||||||
itemList = self.data.static.get_enabled_items(self.version)
|
itemList = self.data.static.get_enabled_items(self.version)
|
||||||
@ -229,7 +228,7 @@ class DivaBase:
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def handle_buy_cstmz_itm_request(self, data: Dict) -> Dict:
|
async def handle_buy_cstmz_itm_request(self, data: Dict) -> Dict:
|
||||||
profile = self.data.profile.get_profile(data["pd_id"], self.version)
|
profile = self.data.profile.get_profile(data["pd_id"], self.version)
|
||||||
item = self.data.static.get_enabled_item(
|
item = self.data.static.get_enabled_item(
|
||||||
self.version, int(data["cstmz_itm_id"])
|
self.version, int(data["cstmz_itm_id"])
|
||||||
@ -264,7 +263,7 @@ class DivaBase:
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def handle_festa_info_request(self, data: Dict) -> Dict:
|
async def handle_festa_info_request(self, data: Dict) -> Dict:
|
||||||
encoded = "&"
|
encoded = "&"
|
||||||
params = {
|
params = {
|
||||||
"fi_id": "1,2",
|
"fi_id": "1,2",
|
||||||
@ -287,7 +286,7 @@ class DivaBase:
|
|||||||
|
|
||||||
return encoded
|
return encoded
|
||||||
|
|
||||||
def handle_contest_info_request(self, data: Dict) -> Dict:
|
async def handle_contest_info_request(self, data: Dict) -> Dict:
|
||||||
response = ""
|
response = ""
|
||||||
|
|
||||||
response += f"&ci_lut={self.time_lut}"
|
response += f"&ci_lut={self.time_lut}"
|
||||||
@ -295,7 +294,7 @@ class DivaBase:
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def handle_qst_inf_request(self, data: Dict) -> Dict:
|
async def handle_qst_inf_request(self, data: Dict) -> Dict:
|
||||||
quest = ""
|
quest = ""
|
||||||
|
|
||||||
questList = self.data.static.get_enabled_quests(self.version)
|
questList = self.data.static.get_enabled_quests(self.version)
|
||||||
@ -345,43 +344,43 @@ class DivaBase:
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def handle_nv_ranking_request(self, data: Dict) -> Dict:
|
async def handle_nv_ranking_request(self, data: Dict) -> Dict:
|
||||||
return f""
|
return f""
|
||||||
|
|
||||||
def handle_ps_ranking_request(self, data: Dict) -> Dict:
|
async def handle_ps_ranking_request(self, data: Dict) -> Dict:
|
||||||
return f""
|
return f""
|
||||||
|
|
||||||
def handle_ng_word_request(self, data: Dict) -> Dict:
|
async def handle_ng_word_request(self, data: Dict) -> Dict:
|
||||||
return f""
|
return f""
|
||||||
|
|
||||||
def handle_rmt_wp_list_request(self, data: Dict) -> Dict:
|
async def handle_rmt_wp_list_request(self, data: Dict) -> Dict:
|
||||||
return f""
|
return f""
|
||||||
|
|
||||||
def handle_pv_def_chr_list_request(self, data: Dict) -> Dict:
|
async def handle_pv_def_chr_list_request(self, data: Dict) -> Dict:
|
||||||
return f""
|
return f""
|
||||||
|
|
||||||
def handle_pv_ng_mdl_list_request(self, data: Dict) -> Dict:
|
async def handle_pv_ng_mdl_list_request(self, data: Dict) -> Dict:
|
||||||
return f""
|
return f""
|
||||||
|
|
||||||
def handle_cstmz_itm_ng_mdl_lst_request(self, data: Dict) -> Dict:
|
async def handle_cstmz_itm_ng_mdl_lst_request(self, data: Dict) -> Dict:
|
||||||
return f""
|
return f""
|
||||||
|
|
||||||
def handle_banner_info_request(self, data: Dict) -> Dict:
|
async def handle_banner_info_request(self, data: Dict) -> Dict:
|
||||||
return f""
|
return f""
|
||||||
|
|
||||||
def handle_banner_data_request(self, data: Dict) -> Dict:
|
async def handle_banner_data_request(self, data: Dict) -> Dict:
|
||||||
return f""
|
return f""
|
||||||
|
|
||||||
def handle_cm_ply_info_request(self, data: Dict) -> Dict:
|
async def handle_cm_ply_info_request(self, data: Dict) -> Dict:
|
||||||
return f""
|
return f""
|
||||||
|
|
||||||
def handle_pstd_h_ctrl_request(self, data: Dict) -> Dict:
|
async def handle_pstd_h_ctrl_request(self, data: Dict) -> Dict:
|
||||||
return f""
|
return f""
|
||||||
|
|
||||||
def handle_pstd_item_ng_lst_request(self, data: Dict) -> Dict:
|
async def handle_pstd_item_ng_lst_request(self, data: Dict) -> Dict:
|
||||||
return f""
|
return f""
|
||||||
|
|
||||||
def handle_pre_start_request(self, data: Dict) -> str:
|
async def handle_pre_start_request(self, data: Dict) -> str:
|
||||||
profile = self.data.profile.get_profile(data["aime_id"], self.version)
|
profile = self.data.profile.get_profile(data["aime_id"], self.version)
|
||||||
profile_shop = self.data.item.get_shop(data["aime_id"], self.version)
|
profile_shop = self.data.item.get_shop(data["aime_id"], self.version)
|
||||||
|
|
||||||
@ -422,13 +421,13 @@ class DivaBase:
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def handle_registration_request(self, data: Dict) -> Dict:
|
async def handle_registration_request(self, data: Dict) -> Dict:
|
||||||
self.data.profile.create_profile(
|
self.data.profile.create_profile(
|
||||||
self.version, data["aime_id"], data["player_name"]
|
self.version, data["aime_id"], data["player_name"]
|
||||||
)
|
)
|
||||||
return f"&cd_adm_result=1&pd_id={data['aime_id']}"
|
return f"&cd_adm_result=1&pd_id={data['aime_id']}"
|
||||||
|
|
||||||
def handle_start_request(self, data: Dict) -> Dict:
|
async def handle_start_request(self, data: Dict) -> Dict:
|
||||||
profile = self.data.profile.get_profile(data["pd_id"], self.version)
|
profile = self.data.profile.get_profile(data["pd_id"], self.version)
|
||||||
profile_shop = self.data.item.get_shop(data["pd_id"], self.version)
|
profile_shop = self.data.item.get_shop(data["pd_id"], self.version)
|
||||||
if profile is None:
|
if profile is None:
|
||||||
@ -583,10 +582,10 @@ class DivaBase:
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def handle_pd_unlock_request(self, data: Dict) -> Dict:
|
async def handle_pd_unlock_request(self, data: Dict) -> Dict:
|
||||||
return f""
|
return f""
|
||||||
|
|
||||||
def handle_spend_credit_request(self, data: Dict) -> Dict:
|
async def handle_spend_credit_request(self, data: Dict) -> Dict:
|
||||||
profile = self.data.profile.get_profile(data["pd_id"], self.version)
|
profile = self.data.profile.get_profile(data["pd_id"], self.version)
|
||||||
if profile is None:
|
if profile is None:
|
||||||
return
|
return
|
||||||
@ -705,7 +704,7 @@ class DivaBase:
|
|||||||
pd_by_pv_id.append(urllib.parse.quote(f"{song}***"))
|
pd_by_pv_id.append(urllib.parse.quote(f"{song}***"))
|
||||||
pd_by_pv_id.append(",")
|
pd_by_pv_id.append(",")
|
||||||
|
|
||||||
def handle_get_pv_pd_request(self, data: Dict) -> Dict:
|
async def handle_get_pv_pd_request(self, data: Dict) -> Dict:
|
||||||
song_id = data["pd_pv_id_lst"].split(",")
|
song_id = data["pd_pv_id_lst"].split(",")
|
||||||
pv = ""
|
pv = ""
|
||||||
|
|
||||||
@ -732,10 +731,10 @@ class DivaBase:
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def handle_stage_start_request(self, data: Dict) -> Dict:
|
async def handle_stage_start_request(self, data: Dict) -> Dict:
|
||||||
return f""
|
return f""
|
||||||
|
|
||||||
def handle_stage_result_request(self, data: Dict) -> Dict:
|
async def handle_stage_result_request(self, data: Dict) -> Dict:
|
||||||
profile = self.data.profile.get_profile(data["pd_id"], self.version)
|
profile = self.data.profile.get_profile(data["pd_id"], self.version)
|
||||||
|
|
||||||
pd_song_list = data["stg_ply_pv_id"].split(",")
|
pd_song_list = data["stg_ply_pv_id"].split(",")
|
||||||
@ -914,7 +913,7 @@ class DivaBase:
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def handle_end_request(self, data: Dict) -> Dict:
|
async def handle_end_request(self, data: Dict) -> Dict:
|
||||||
profile = self.data.profile.get_profile(data["pd_id"], self.version)
|
profile = self.data.profile.get_profile(data["pd_id"], self.version)
|
||||||
|
|
||||||
self.data.profile.update_profile(
|
self.data.profile.update_profile(
|
||||||
@ -922,7 +921,7 @@ class DivaBase:
|
|||||||
)
|
)
|
||||||
return f""
|
return f""
|
||||||
|
|
||||||
def handle_shop_exit_request(self, data: Dict) -> Dict:
|
async def handle_shop_exit_request(self, data: Dict) -> Dict:
|
||||||
self.data.item.put_shop(
|
self.data.item.put_shop(
|
||||||
data["pd_id"],
|
data["pd_id"],
|
||||||
self.version,
|
self.version,
|
||||||
@ -952,7 +951,7 @@ class DivaBase:
|
|||||||
response = "&shp_rslt=1"
|
response = "&shp_rslt=1"
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def handle_card_procedure_request(self, data: Dict) -> str:
|
async def handle_card_procedure_request(self, data: Dict) -> str:
|
||||||
profile = self.data.profile.get_profile(data["aime_id"], self.version)
|
profile = self.data.profile.get_profile(data["aime_id"], self.version)
|
||||||
if profile is None:
|
if profile is None:
|
||||||
return "&cd_adm_result=0"
|
return "&cd_adm_result=0"
|
||||||
@ -972,7 +971,7 @@ class DivaBase:
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def handle_change_name_request(self, data: Dict) -> str:
|
async def handle_change_name_request(self, data: Dict) -> str:
|
||||||
profile = self.data.profile.get_profile(data["pd_id"], self.version)
|
profile = self.data.profile.get_profile(data["pd_id"], self.version)
|
||||||
|
|
||||||
# make sure user has enough Vocaloid Points
|
# make sure user has enough Vocaloid Points
|
||||||
@ -992,7 +991,7 @@ class DivaBase:
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def handle_change_passwd_request(self, data: Dict) -> str:
|
async def handle_change_passwd_request(self, data: Dict) -> str:
|
||||||
profile = self.data.profile.get_profile(data["pd_id"], self.version)
|
profile = self.data.profile.get_profile(data["pd_id"], self.version)
|
||||||
|
|
||||||
# TODO: return correct error number instead of 0
|
# TODO: return correct error number instead of 0
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
from twisted.web.http import Request
|
from starlette.requests import Request
|
||||||
|
from starlette.responses import PlainTextResponse
|
||||||
|
from starlette.routing import Route
|
||||||
import yaml
|
import yaml
|
||||||
import logging, coloredlogs
|
import logging, coloredlogs
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
@ -51,17 +53,16 @@ class DivaServlet(BaseServlet):
|
|||||||
level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str
|
level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
|
def get_routes(self) -> List[Route]:
|
||||||
return (
|
return [
|
||||||
[],
|
Route("/DivaServlet/", self.render_POST, methods=['POST'])
|
||||||
[("render_POST", "/DivaServlet/", {})]
|
]
|
||||||
)
|
|
||||||
|
|
||||||
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
|
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
|
||||||
if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80:
|
if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80:
|
||||||
return (f"http://{self.core_cfg.title.hostname}:{Utils.get_title_port(self.core_cfg)}/DivaServlet/", self.core_cfg.title.hostname)
|
return (f"http://{self.core_cfg.server.hostname}:{Utils.get_title_port(self.core_cfg)}/DivaServlet/", self.core_cfg.server.hostname)
|
||||||
|
|
||||||
return (f"http://{self.core_cfg.title.hostname}/DivaServlet/", self.core_cfg.title.hostname)
|
return (f"http://{self.core_cfg.server.hostname}/DivaServlet/", self.core_cfg.server.hostname)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_game_enabled(
|
def is_game_enabled(
|
||||||
@ -78,9 +79,9 @@ class DivaServlet(BaseServlet):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
async def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
||||||
req_raw = request.content.getvalue()
|
req_raw = await request.body()
|
||||||
url_header = request.getAllHeaders()
|
url_header = request.headers
|
||||||
|
|
||||||
# Ping Dispatch
|
# Ping Dispatch
|
||||||
if "THIS_STRING_SEPARATES" in str(url_header):
|
if "THIS_STRING_SEPARATES" in str(url_header):
|
||||||
@ -103,9 +104,7 @@ class DivaServlet(BaseServlet):
|
|||||||
self.logger.debug(
|
self.logger.debug(
|
||||||
f"Response cmd={bin_req_data['cmd']}&req_id={bin_req_data['req_id']}&stat=ok{resp}"
|
f"Response cmd={bin_req_data['cmd']}&req_id={bin_req_data['req_id']}&stat=ok{resp}"
|
||||||
)
|
)
|
||||||
return f"cmd={bin_req_data['cmd']}&req_id={bin_req_data['req_id']}&stat=ok{resp}".encode(
|
return PlainTextResponse(f"cmd={bin_req_data['cmd']}&req_id={bin_req_data['req_id']}&stat=ok{resp}")
|
||||||
"utf-8"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Main Dispatch
|
# Main Dispatch
|
||||||
json_string = json.dumps(
|
json_string = json.dumps(
|
||||||
@ -122,7 +121,7 @@ class DivaServlet(BaseServlet):
|
|||||||
) # Decompressing the gzip
|
) # Decompressing the gzip
|
||||||
except zlib.error as e:
|
except zlib.error as e:
|
||||||
self.logger.error(f"Failed to defalte! {e} -> {gz_string}")
|
self.logger.error(f"Failed to defalte! {e} -> {gz_string}")
|
||||||
return "stat=0"
|
return PlainTextResponse("stat=0")
|
||||||
|
|
||||||
req_kvp = urllib.parse.unquote(url_data)
|
req_kvp = urllib.parse.unquote(url_data)
|
||||||
req_data = {}
|
req_data = {}
|
||||||
@ -141,27 +140,18 @@ class DivaServlet(BaseServlet):
|
|||||||
# Load the requests
|
# Load the requests
|
||||||
try:
|
try:
|
||||||
handler = getattr(self.base, func_to_find)
|
handler = getattr(self.base, func_to_find)
|
||||||
resp = handler(req_data)
|
resp = await handler(req_data)
|
||||||
|
|
||||||
except AttributeError as e:
|
except AttributeError as e:
|
||||||
self.logger.warning(f"Unhandled {req_data['cmd']} request {e}")
|
self.logger.warning(f"Unhandled {req_data['cmd']} request {e}")
|
||||||
return f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok".encode(
|
return PlainTextResponse(f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok")
|
||||||
"utf-8"
|
|
||||||
)
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error handling method {func_to_find} {e}")
|
self.logger.error(f"Error handling method {func_to_find} {e}")
|
||||||
return f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok".encode(
|
return PlainTextResponse(f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok")
|
||||||
"utf-8"
|
|
||||||
)
|
|
||||||
|
|
||||||
request.responseHeaders.addRawHeader(b"content-type", b"text/plain")
|
|
||||||
self.logger.debug(
|
self.logger.debug(
|
||||||
f"Response cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok{resp}"
|
f"Response cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok{resp}"
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return PlainTextResponse(f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok{resp}")
|
||||||
f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok{resp}".encode(
|
|
||||||
"utf-8"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
@ -3,7 +3,7 @@ import yaml
|
|||||||
import jinja2
|
import jinja2
|
||||||
from os import path
|
from os import path
|
||||||
from twisted.web.util import redirectTo
|
from twisted.web.util import redirectTo
|
||||||
from twisted.web.http import Request
|
from starlette.requests import Request
|
||||||
from twisted.web.server import Session
|
from twisted.web.server import Session
|
||||||
|
|
||||||
from core.frontend import FE_Base, IUserSession
|
from core.frontend import FE_Base, IUserSession
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import json
|
import json
|
||||||
import traceback
|
import traceback
|
||||||
import inflection
|
from starlette.routing import Route
|
||||||
|
from starlette.requests import Request
|
||||||
|
from starlette.responses import JSONResponse
|
||||||
import yaml
|
import yaml
|
||||||
import logging
|
import logging
|
||||||
import coloredlogs
|
import coloredlogs
|
||||||
|
|
||||||
from os import path
|
from os import path
|
||||||
from typing import Dict, List, Tuple
|
from typing import Dict, List, Tuple
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
from twisted.web import server
|
|
||||||
from twisted.web.http import Request
|
|
||||||
from twisted.internet import reactor, endpoints
|
|
||||||
|
|
||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
|
from core.title import BaseServlet
|
||||||
from core.utils import Utils
|
from core.utils import Utils
|
||||||
from titles.idac.base import IDACBase
|
from titles.idac.base import IDACBase
|
||||||
from titles.idac.season2 import IDACSeason2
|
from titles.idac.season2 import IDACSeason2
|
||||||
@ -22,7 +22,7 @@ from titles.idac.echo import IDACEchoUDP
|
|||||||
from titles.idac.matching import IDACMatching
|
from titles.idac.matching import IDACMatching
|
||||||
|
|
||||||
|
|
||||||
class IDACServlet:
|
class IDACServlet(BaseServlet):
|
||||||
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 = IDACConfig()
|
self.game_cfg = IDACConfig()
|
||||||
@ -72,12 +72,11 @@ class IDACServlet:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
|
def get_routes(self) -> List[Route]:
|
||||||
return (
|
return [
|
||||||
[],
|
Route("/{version:int}/initiald/{category:str}/{endpoint:str}", self.render_POST, methods=["POST"])
|
||||||
[("render_POST", "/SDGT/{version}/initiald/{category}/{endpoint}", {})]
|
]
|
||||||
)
|
|
||||||
|
|
||||||
def get_allnet_info(
|
def get_allnet_info(
|
||||||
self, game_code: str, game_ver: int, keychip: str
|
self, game_code: str, game_ver: int, keychip: str
|
||||||
@ -88,15 +87,15 @@ class IDACServlet:
|
|||||||
return (
|
return (
|
||||||
f"",
|
f"",
|
||||||
# requires http or else it defaults to https
|
# requires http or else it defaults to https
|
||||||
f"http://{self.core_cfg.title.hostname}{t_port}/{game_code}/{game_ver}/",
|
f"http://{self.core_cfg.server.hostname}{t_port}/{game_code}/{game_ver}/",
|
||||||
)
|
)
|
||||||
|
|
||||||
def render_POST(self, request: Request, game_code: int, matchers: Dict) -> bytes:
|
async def render_POST(self, request: Request) -> bytes:
|
||||||
req_raw = request.content.getvalue()
|
req_raw = await request.body()
|
||||||
internal_ver = 0
|
internal_ver = 0
|
||||||
version = int(matchers['version'])
|
version: int = request.path_params.get('version')
|
||||||
category = matchers['category']
|
category: str = request.path_params.get('category')
|
||||||
endpoint = matchers['endpoint']
|
endpoint: str = request.path_params.get('endpoint')
|
||||||
client_ip = Utils.get_ip_addr(request)
|
client_ip = Utils.get_ip_addr(request)
|
||||||
|
|
||||||
if version >= 100 and version < 140: # IDAC Season 1
|
if version >= 100 and version < 140: # IDAC Season 1
|
||||||
@ -104,7 +103,7 @@ class IDACServlet:
|
|||||||
elif version >= 140 and version < 171: # IDAC Season 2
|
elif version >= 140 and version < 171: # IDAC Season 2
|
||||||
internal_ver = IDACConstants.VER_IDAC_SEASON_2
|
internal_ver = IDACConstants.VER_IDAC_SEASON_2
|
||||||
|
|
||||||
header_application = self.decode_header(request.getAllHeaders())
|
header_application = self.decode_header(request.headers.get("application", ""))
|
||||||
|
|
||||||
req_data = json.loads(req_raw)
|
req_data = json.loads(req_raw)
|
||||||
|
|
||||||
@ -119,7 +118,7 @@ class IDACServlet:
|
|||||||
|
|
||||||
if not hasattr(self.versions[internal_ver], func_to_find):
|
if not hasattr(self.versions[internal_ver], func_to_find):
|
||||||
self.logger.warning(f"Unhandled v{version} request {endpoint}")
|
self.logger.warning(f"Unhandled v{version} request {endpoint}")
|
||||||
return '{"status_code": "0"}'.encode("utf-8")
|
return JSONResponse('{"status_code": "0"}')
|
||||||
|
|
||||||
resp = None
|
resp = None
|
||||||
try:
|
try:
|
||||||
@ -129,17 +128,16 @@ class IDACServlet:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
|
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
|
||||||
return '{"status_code": "0"}'.encode("utf-8")
|
return JSONResponse('{"status_code": "0"}')
|
||||||
|
|
||||||
if resp is None:
|
if resp is None:
|
||||||
resp = {"status_code": "0"}
|
resp = {"status_code": "0"}
|
||||||
|
|
||||||
self.logger.debug(f"Response {resp}")
|
self.logger.debug(f"Response {resp}")
|
||||||
return json.dumps(resp, ensure_ascii=False).encode("utf-8")
|
return JSONResponse(json.dumps(resp, ensure_ascii=False))
|
||||||
|
|
||||||
|
|
||||||
def decode_header(self, data: Dict) -> Dict:
|
def decode_header(self, app: str) -> Dict:
|
||||||
app: str = data[b"application"].decode()
|
|
||||||
ret = {}
|
ret = {}
|
||||||
|
|
||||||
for x in app.split(", "):
|
for x in app.split(", "):
|
||||||
@ -149,6 +147,8 @@ class IDACServlet:
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
|
return
|
||||||
|
"""
|
||||||
if self.game_cfg.server.enable:
|
if self.game_cfg.server.enable:
|
||||||
endpoints.serverFromString(
|
endpoints.serverFromString(
|
||||||
reactor,
|
reactor,
|
||||||
@ -165,3 +165,4 @@ class IDACServlet:
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.logger.info(f"Matching listening on {self.game_cfg.server.matching} with echos on {self.game_cfg.server.echo1} and {self.game_cfg.server.echo2}")
|
self.logger.info(f"Matching listening on {self.game_cfg.server.matching} with echos on {self.game_cfg.server.echo1} and {self.game_cfg.server.echo2}")
|
||||||
|
"""
|
||||||
|
@ -109,9 +109,9 @@ class IDACSeason2(IDACBase):
|
|||||||
ver_str = version.replace(".", "")[:3]
|
ver_str = version.replace(".", "")[:3]
|
||||||
|
|
||||||
if self.core_cfg.server.is_develop:
|
if self.core_cfg.server.is_develop:
|
||||||
domain_api_game = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDGT/{ver_str}/"
|
domain_api_game = f"http://{self.core_cfg.server.hostname}:{self.core_cfg.server.port}/SDGT/{ver_str}/"
|
||||||
else:
|
else:
|
||||||
domain_api_game = f"http://{self.core_cfg.title.hostname}/SDGT/{ver_str}/"
|
domain_api_game = f"http://{self.core_cfg.server.hostname}/SDGT/{ver_str}/"
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"status_code": "0",
|
"status_code": "0",
|
||||||
@ -136,10 +136,10 @@ class IDACSeason2(IDACBase):
|
|||||||
"server_maintenance_end_hour": 0,
|
"server_maintenance_end_hour": 0,
|
||||||
"server_maintenance_end_minutes": 0,
|
"server_maintenance_end_minutes": 0,
|
||||||
"domain_api_game": domain_api_game,
|
"domain_api_game": domain_api_game,
|
||||||
"domain_matching": f"http://{self.core_cfg.title.hostname}:{self.game_config.server.matching}",
|
"domain_matching": f"http://{self.core_cfg.server.hostname}:{self.game_config.server.matching}",
|
||||||
"domain_echo1": f"{self.core_cfg.title.hostname}:{self.game_config.server.echo1}",
|
"domain_echo1": f"{self.core_cfg.server.hostname}:{self.game_config.server.echo1}",
|
||||||
"domain_echo2": f"{self.core_cfg.title.hostname}:{self.game_config.server.echo2}",
|
"domain_echo2": f"{self.core_cfg.server.hostname}:{self.game_config.server.echo2}",
|
||||||
"domain_ping": f"{self.core_cfg.title.hostname}",
|
"domain_ping": f"{self.core_cfg.server.hostname}",
|
||||||
"battle_gift_event_master": [],
|
"battle_gift_event_master": [],
|
||||||
"round_event": [
|
"round_event": [
|
||||||
{
|
{
|
||||||
|
@ -24,10 +24,10 @@ class IDZHandlerLoadServerInfo(IDZHandlerBase):
|
|||||||
|
|
||||||
t_port = Utils.get_title_port(self.core_config)
|
t_port = Utils.get_title_port(self.core_config)
|
||||||
|
|
||||||
news_str = f"http://{self.core_config.title.hostname}:{t_port}/idz/news/news80**.txt"
|
news_str = f"http://{self.core_config.server.hostname}:{t_port}/idz/news/news80**.txt"
|
||||||
err_str = f"http://{self.core_config.title.hostname}:{t_port}/idz/error"
|
err_str = f"http://{self.core_config.server.hostname}:{t_port}/idz/error"
|
||||||
|
|
||||||
len_hostname = len(self.core_config.title.hostname)
|
len_hostname = len(self.core_config.server.hostname)
|
||||||
len_news = len(news_str)
|
len_news = len(news_str)
|
||||||
len_error = len(err_str)
|
len_error = len(err_str)
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ class IDZHandlerLoadServerInfo(IDZHandlerBase):
|
|||||||
f"{len_hostname}s",
|
f"{len_hostname}s",
|
||||||
ret,
|
ret,
|
||||||
0x4 + offset,
|
0x4 + offset,
|
||||||
self.core_config.title.hostname.encode(),
|
self.core_config.server.hostname.encode(),
|
||||||
)
|
)
|
||||||
struct.pack_into("<I", ret, 0x84 + offset, self.game_cfg.ports.userdb)
|
struct.pack_into("<I", ret, 0x84 + offset, self.game_cfg.ports.userdb)
|
||||||
struct.pack_into("<I", ret, 0x86 + offset, self.game_cfg.ports.userdb + 1)
|
struct.pack_into("<I", ret, 0x86 + offset, self.game_cfg.ports.userdb + 1)
|
||||||
@ -45,7 +45,7 @@ class IDZHandlerLoadServerInfo(IDZHandlerBase):
|
|||||||
f"{len_hostname}s",
|
f"{len_hostname}s",
|
||||||
ret,
|
ret,
|
||||||
0x88 + offset,
|
0x88 + offset,
|
||||||
self.core_config.title.hostname.encode(),
|
self.core_config.server.hostname.encode(),
|
||||||
)
|
)
|
||||||
struct.pack_into("<I", ret, 0x108 + offset, self.game_cfg.ports.match - 1)
|
struct.pack_into("<I", ret, 0x108 + offset, self.game_cfg.ports.match - 1)
|
||||||
struct.pack_into("<I", ret, 0x10A + offset, self.game_cfg.ports.match - 3)
|
struct.pack_into("<I", ret, 0x10A + offset, self.game_cfg.ports.match - 3)
|
||||||
@ -59,7 +59,7 @@ class IDZHandlerLoadServerInfo(IDZHandlerBase):
|
|||||||
f"{len_hostname}s",
|
f"{len_hostname}s",
|
||||||
ret,
|
ret,
|
||||||
0x114 + offset,
|
0x114 + offset,
|
||||||
self.core_config.title.hostname.encode(),
|
self.core_config.server.hostname.encode(),
|
||||||
)
|
)
|
||||||
struct.pack_into("<I", ret, 0x194 + offset, self.game_cfg.ports.echo + 2)
|
struct.pack_into("<I", ret, 0x194 + offset, self.game_cfg.ports.echo + 2)
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ class IDZHandlerLoadServerInfo(IDZHandlerBase):
|
|||||||
f"{len_hostname}s",
|
f"{len_hostname}s",
|
||||||
ret,
|
ret,
|
||||||
0x0199 + offset,
|
0x0199 + offset,
|
||||||
self.core_config.title.hostname.encode(),
|
self.core_config.server.hostname.encode(),
|
||||||
)
|
)
|
||||||
struct.pack_into("<I", ret, 0x0219 + offset, self.game_cfg.ports.echo + 3)
|
struct.pack_into("<I", ret, 0x0219 + offset, self.game_cfg.ports.echo + 3)
|
||||||
|
|
||||||
@ -75,19 +75,19 @@ class IDZHandlerLoadServerInfo(IDZHandlerBase):
|
|||||||
f"{len_hostname}s",
|
f"{len_hostname}s",
|
||||||
ret,
|
ret,
|
||||||
0x021C + offset,
|
0x021C + offset,
|
||||||
self.core_config.title.hostname.encode(),
|
self.core_config.server.hostname.encode(),
|
||||||
)
|
)
|
||||||
struct.pack_into(
|
struct.pack_into(
|
||||||
f"{len_hostname}s",
|
f"{len_hostname}s",
|
||||||
ret,
|
ret,
|
||||||
0x029C + offset,
|
0x029C + offset,
|
||||||
self.core_config.title.hostname.encode(),
|
self.core_config.server.hostname.encode(),
|
||||||
)
|
)
|
||||||
struct.pack_into(
|
struct.pack_into(
|
||||||
f"{len_hostname}s",
|
f"{len_hostname}s",
|
||||||
ret,
|
ret,
|
||||||
0x031C + offset,
|
0x031C + offset,
|
||||||
self.core_config.title.hostname.encode(),
|
self.core_config.server.hostname.encode(),
|
||||||
)
|
)
|
||||||
|
|
||||||
struct.pack_into("<I", ret, 0x39C + offset, self.game_cfg.ports.echo)
|
struct.pack_into("<I", ret, 0x39C + offset, self.game_cfg.ports.echo)
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
from twisted.web.http import Request
|
from starlette.requests import Request
|
||||||
|
from starlette.responses import Response, PlainTextResponse
|
||||||
|
from starlette.routing import Route
|
||||||
import yaml
|
import yaml
|
||||||
import logging
|
import logging
|
||||||
import coloredlogs
|
import coloredlogs
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
from os import path
|
from os import path
|
||||||
from typing import Tuple, List, Dict
|
from typing import Tuple, List, Dict
|
||||||
from twisted.internet import reactor, endpoints
|
|
||||||
from twisted.web import server, resource
|
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
@ -83,16 +83,15 @@ class IDZServlet(BaseServlet):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
|
def get_routes(self) -> List[Route]:
|
||||||
return[
|
return [
|
||||||
[("render_GET", "/idz/news/{endpoint:.*?}", {}),
|
Route("/idz/news/{endpoint:str}", self.render_GET),
|
||||||
("render_GET", "/idz/error", {})],
|
Route("/idz/error", self.render_GET)
|
||||||
[]
|
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
|
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
|
||||||
hostname = (
|
hostname = (
|
||||||
self.core_cfg.title.hostname
|
self.core_cfg.server.hostname
|
||||||
if not self.game_cfg.server.hostname
|
if not self.game_cfg.server.hostname
|
||||||
else self.game_cfg.server.hostname
|
else self.game_cfg.server.hostname
|
||||||
)
|
)
|
||||||
@ -135,7 +134,8 @@ class IDZServlet(BaseServlet):
|
|||||||
|
|
||||||
except AttributeError as e:
|
except AttributeError as e:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
"""
|
||||||
endpoints.serverFromString(
|
endpoints.serverFromString(
|
||||||
reactor,
|
reactor,
|
||||||
f"tcp:{self.game_cfg.ports.userdb}:interface={self.core_cfg.server.listen_address}",
|
f"tcp:{self.game_cfg.ports.userdb}:interface={self.core_cfg.server.listen_address}",
|
||||||
@ -155,18 +155,15 @@ class IDZServlet(BaseServlet):
|
|||||||
reactor.listenUDP(
|
reactor.listenUDP(
|
||||||
self.game_cfg.ports.userdb + 1, IDZEcho(self.core_cfg, self.game_cfg)
|
self.game_cfg.ports.userdb + 1, IDZEcho(self.core_cfg, self.game_cfg)
|
||||||
)
|
)
|
||||||
|
"""
|
||||||
self.logger.info(f"UserDB Listening on port {self.game_cfg.ports.userdb}")
|
self.logger.info(f"UserDB Listening on port {self.game_cfg.ports.userdb}")
|
||||||
|
|
||||||
def render_GET(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
async def render_GET(self, request: Request) -> bytes:
|
||||||
url_path = matchers['endpoint']
|
url_path = request.path_params.get('endpoint', '')
|
||||||
|
if not url_path:
|
||||||
|
return Response()
|
||||||
|
|
||||||
self.logger.info(f"IDZ GET request: {url_path}")
|
self.logger.info(f"IDZ GET request: {url_path}")
|
||||||
request.responseHeaders.setRawHeaders(
|
|
||||||
"Content-Type", [b"text/plain; charset=utf-8"]
|
|
||||||
)
|
|
||||||
request.responseHeaders.setRawHeaders(
|
|
||||||
"Last-Modified", [b"Sun, 23 Apr 2023 05:33:20 GMT"]
|
|
||||||
)
|
|
||||||
|
|
||||||
news = (
|
news = (
|
||||||
self.game_cfg.server.news
|
self.game_cfg.server.news
|
||||||
@ -176,4 +173,4 @@ class IDZServlet(BaseServlet):
|
|||||||
news += "\r\n"
|
news += "\r\n"
|
||||||
news = "1979/01/01 00:00:00 2099/12/31 23:59:59 " + news
|
news = "1979/01/01 00:00:00 2099/12/31 23:59:59 " + news
|
||||||
|
|
||||||
return news.encode()
|
return PlainTextResponse(news, media_type="text/plain; charset=utf-8", headers={"Last-Modified": "Sun, 23 Apr 2023 05:33:20 GMT"})
|
||||||
|
@ -5,7 +5,7 @@ import struct
|
|||||||
from typing import Dict, Optional, List, Type
|
from typing import Dict, Optional, List, Type
|
||||||
from twisted.web import server, resource
|
from twisted.web import server, resource
|
||||||
from twisted.internet import reactor, endpoints
|
from twisted.internet import reactor, endpoints
|
||||||
from twisted.web.http import Request
|
from starlette.requests import Request
|
||||||
from routes import Mapper
|
from routes import Mapper
|
||||||
import random
|
import random
|
||||||
from os import walk
|
from os import walk
|
||||||
|
@ -26,12 +26,12 @@ class Mai2Base:
|
|||||||
self.date_time_format = "%Y-%m-%d %H:%M:%S"
|
self.date_time_format = "%Y-%m-%d %H:%M:%S"
|
||||||
|
|
||||||
if not self.core_config.server.is_using_proxy and Utils.get_title_port(self.core_config) != 80:
|
if not self.core_config.server.is_using_proxy and Utils.get_title_port(self.core_config) != 80:
|
||||||
self.old_server = f"http://{self.core_config.title.hostname}:{Utils.get_title_port(cfg)}/197/MaimaiServlet/"
|
self.old_server = f"http://{self.core_config.server.hostname}:{Utils.get_title_port(cfg)}/197/MaimaiServlet/"
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.old_server = f"http://{self.core_config.title.hostname}/197/MaimaiServlet/"
|
self.old_server = f"http://{self.core_config.server.hostname}/197/MaimaiServlet/"
|
||||||
|
|
||||||
def handle_get_game_setting_api_request(self, data: Dict):
|
async def handle_get_game_setting_api_request(self, data: Dict):
|
||||||
# if reboot start/end time is not defined use the default behavior of being a few hours ago
|
# if reboot start/end time is not defined use the default behavior of being a few hours ago
|
||||||
if self.core_config.title.reboot_start_time == "" or self.core_config.title.reboot_end_time == "":
|
if self.core_config.title.reboot_start_time == "" or self.core_config.title.reboot_end_time == "":
|
||||||
reboot_start = datetime.strftime(
|
reboot_start = datetime.strftime(
|
||||||
@ -74,14 +74,14 @@ class Mai2Base:
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_game_ranking_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_ranking_api_request(self, data: Dict) -> Dict:
|
||||||
return {"length": 0, "gameRankingList": []}
|
return {"length": 0, "gameRankingList": []}
|
||||||
|
|
||||||
def handle_get_game_tournament_info_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_tournament_info_api_request(self, data: Dict) -> Dict:
|
||||||
# TODO: Tournament support
|
# TODO: Tournament support
|
||||||
return {"length": 0, "gameTournamentInfoList": []}
|
return {"length": 0, "gameTournamentInfoList": []}
|
||||||
|
|
||||||
def handle_get_game_event_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_event_api_request(self, data: Dict) -> Dict:
|
||||||
events = self.data.static.get_enabled_events(self.version)
|
events = self.data.static.get_enabled_events(self.version)
|
||||||
events_lst = []
|
events_lst = []
|
||||||
if events is None or not events:
|
if events is None or not events:
|
||||||
@ -108,10 +108,10 @@ class Mai2Base:
|
|||||||
"gameEventList": events_lst,
|
"gameEventList": events_lst,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_game_ng_music_id_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_ng_music_id_api_request(self, data: Dict) -> Dict:
|
||||||
return {"length": 0, "musicIdList": []}
|
return {"length": 0, "musicIdList": []}
|
||||||
|
|
||||||
def handle_get_game_charge_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_charge_api_request(self, data: Dict) -> Dict:
|
||||||
game_charge_list = self.data.static.get_enabled_tickets(self.version, 1)
|
game_charge_list = self.data.static.get_enabled_tickets(self.version, 1)
|
||||||
if game_charge_list is None:
|
if game_charge_list is None:
|
||||||
return {"length": 0, "gameChargeList": []}
|
return {"length": 0, "gameChargeList": []}
|
||||||
@ -130,19 +130,19 @@ class Mai2Base:
|
|||||||
|
|
||||||
return {"length": len(charge_list), "gameChargeList": charge_list}
|
return {"length": len(charge_list), "gameChargeList": charge_list}
|
||||||
|
|
||||||
def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict:
|
async def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict:
|
||||||
return {"returnCode": 1, "apiName": "UpsertClientSettingApi"}
|
return {"returnCode": 1, "apiName": "UpsertClientSettingApi"}
|
||||||
|
|
||||||
def handle_upsert_client_upload_api_request(self, data: Dict) -> Dict:
|
async def handle_upsert_client_upload_api_request(self, data: Dict) -> Dict:
|
||||||
return {"returnCode": 1, "apiName": "UpsertClientUploadApi"}
|
return {"returnCode": 1, "apiName": "UpsertClientUploadApi"}
|
||||||
|
|
||||||
def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict:
|
async def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict:
|
||||||
return {"returnCode": 1, "apiName": "UpsertClientBookkeepingApi"}
|
return {"returnCode": 1, "apiName": "UpsertClientBookkeepingApi"}
|
||||||
|
|
||||||
def handle_upsert_client_testmode_api_request(self, data: Dict) -> Dict:
|
async def handle_upsert_client_testmode_api_request(self, data: Dict) -> Dict:
|
||||||
return {"returnCode": 1, "apiName": "UpsertClientTestmodeApi"}
|
return {"returnCode": 1, "apiName": "UpsertClientTestmodeApi"}
|
||||||
|
|
||||||
def handle_get_user_preview_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||||
p = self.data.profile.get_profile_detail(data["userId"], self.version, False)
|
p = self.data.profile.get_profile_detail(data["userId"], self.version, False)
|
||||||
w = self.data.profile.get_web_option(data["userId"], self.version)
|
w = self.data.profile.get_web_option(data["userId"], self.version)
|
||||||
if p is None or w is None:
|
if p is None or w is None:
|
||||||
@ -169,7 +169,7 @@ class Mai2Base:
|
|||||||
"totalLv": profile["totalLv"],
|
"totalLv": profile["totalLv"],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_user_login_api_request(self, data: Dict) -> Dict:
|
async def handle_user_login_api_request(self, data: Dict) -> Dict:
|
||||||
profile = self.data.profile.get_profile_detail(data["userId"], self.version)
|
profile = self.data.profile.get_profile_detail(data["userId"], self.version)
|
||||||
consec = self.data.profile.get_consec_login(data["userId"], self.version)
|
consec = self.data.profile.get_consec_login(data["userId"], self.version)
|
||||||
|
|
||||||
@ -210,7 +210,7 @@ class Mai2Base:
|
|||||||
"consecutiveLoginCount": consec_ct, # Number of consecutive days we've logged in.
|
"consecutiveLoginCount": consec_ct, # Number of consecutive days we've logged in.
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_upload_user_playlog_api_request(self, data: Dict) -> Dict:
|
async def handle_upload_user_playlog_api_request(self, data: Dict) -> Dict:
|
||||||
user_id = data["userId"]
|
user_id = data["userId"]
|
||||||
playlog = data["userPlaylog"]
|
playlog = data["userPlaylog"]
|
||||||
|
|
||||||
@ -218,7 +218,7 @@ class Mai2Base:
|
|||||||
|
|
||||||
return {"returnCode": 1, "apiName": "UploadUserPlaylogApi"}
|
return {"returnCode": 1, "apiName": "UploadUserPlaylogApi"}
|
||||||
|
|
||||||
def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict:
|
async def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict:
|
||||||
user_id = data["userId"]
|
user_id = data["userId"]
|
||||||
charge = data["userCharge"]
|
charge = data["userCharge"]
|
||||||
|
|
||||||
@ -234,7 +234,7 @@ class Mai2Base:
|
|||||||
|
|
||||||
return {"returnCode": 1, "apiName": "UpsertUserChargelogApi"}
|
return {"returnCode": 1, "apiName": "UpsertUserChargelogApi"}
|
||||||
|
|
||||||
def handle_upsert_user_all_api_request(self, data: Dict) -> Dict:
|
async def handle_upsert_user_all_api_request(self, data: Dict) -> Dict:
|
||||||
user_id = data["userId"]
|
user_id = data["userId"]
|
||||||
upsert = data["upsertUserAll"]
|
upsert = data["upsertUserAll"]
|
||||||
|
|
||||||
@ -375,10 +375,10 @@ class Mai2Base:
|
|||||||
|
|
||||||
return {"returnCode": 1, "apiName": "UpsertUserAllApi"}
|
return {"returnCode": 1, "apiName": "UpsertUserAllApi"}
|
||||||
|
|
||||||
def handle_user_logout_api_request(self, data: Dict) -> Dict:
|
async def handle_user_logout_api_request(self, data: Dict) -> Dict:
|
||||||
return {"returnCode": 1}
|
return {"returnCode": 1}
|
||||||
|
|
||||||
def handle_get_user_data_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_data_api_request(self, data: Dict) -> Dict:
|
||||||
profile = self.data.profile.get_profile_detail(data["userId"], self.version, False)
|
profile = self.data.profile.get_profile_detail(data["userId"], self.version, False)
|
||||||
if profile is None:
|
if profile is None:
|
||||||
return
|
return
|
||||||
@ -390,7 +390,7 @@ class Mai2Base:
|
|||||||
|
|
||||||
return {"userId": data["userId"], "userData": profile_dict}
|
return {"userId": data["userId"], "userData": profile_dict}
|
||||||
|
|
||||||
def handle_get_user_extend_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_extend_api_request(self, data: Dict) -> Dict:
|
||||||
extend = self.data.profile.get_profile_extend(data["userId"], self.version)
|
extend = self.data.profile.get_profile_extend(data["userId"], self.version)
|
||||||
if extend is None:
|
if extend is None:
|
||||||
return
|
return
|
||||||
@ -402,7 +402,7 @@ class Mai2Base:
|
|||||||
|
|
||||||
return {"userId": data["userId"], "userExtend": extend_dict}
|
return {"userId": data["userId"], "userExtend": extend_dict}
|
||||||
|
|
||||||
def handle_get_user_option_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_option_api_request(self, data: Dict) -> Dict:
|
||||||
options = self.data.profile.get_profile_option(data["userId"], self.version, False)
|
options = self.data.profile.get_profile_option(data["userId"], self.version, False)
|
||||||
if options is None:
|
if options is None:
|
||||||
return
|
return
|
||||||
@ -414,7 +414,7 @@ class Mai2Base:
|
|||||||
|
|
||||||
return {"userId": data["userId"], "userOption": options_dict}
|
return {"userId": data["userId"], "userOption": options_dict}
|
||||||
|
|
||||||
def handle_get_user_card_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_card_api_request(self, data: Dict) -> Dict:
|
||||||
user_cards = self.data.item.get_cards(data["userId"])
|
user_cards = self.data.item.get_cards(data["userId"])
|
||||||
if user_cards is None:
|
if user_cards is None:
|
||||||
return {"userId": data["userId"], "nextIndex": 0, "userCardList": []}
|
return {"userId": data["userId"], "nextIndex": 0, "userCardList": []}
|
||||||
@ -448,7 +448,7 @@ class Mai2Base:
|
|||||||
"userCardList": card_list[start_idx:end_idx],
|
"userCardList": card_list[start_idx:end_idx],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_charge_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_charge_api_request(self, data: Dict) -> Dict:
|
||||||
user_charges = self.data.item.get_charges(data["userId"])
|
user_charges = self.data.item.get_charges(data["userId"])
|
||||||
if user_charges is None:
|
if user_charges is None:
|
||||||
return {"userId": data["userId"], "length": 0, "userChargeList": []}
|
return {"userId": data["userId"], "length": 0, "userChargeList": []}
|
||||||
@ -467,16 +467,16 @@ class Mai2Base:
|
|||||||
"userChargeList": user_charge_list,
|
"userChargeList": user_charge_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_present_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_present_api_request(self, data: Dict) -> Dict:
|
||||||
return { "userId": data.get("userId", 0), "length": 0, "userPresentList": []}
|
return { "userId": data.get("userId", 0), "length": 0, "userPresentList": []}
|
||||||
|
|
||||||
def handle_get_transfer_friend_api_request(self, data: Dict) -> Dict:
|
async def handle_get_transfer_friend_api_request(self, data: Dict) -> Dict:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def handle_get_user_present_event_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_present_event_api_request(self, data: Dict) -> Dict:
|
||||||
return { "userId": data.get("userId", 0), "length": 0, "userPresentEventList": []}
|
return { "userId": data.get("userId", 0), "length": 0, "userPresentEventList": []}
|
||||||
|
|
||||||
def handle_get_user_boss_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_boss_api_request(self, data: Dict) -> Dict:
|
||||||
b = self.data.profile.get_boss_list(data["userId"])
|
b = self.data.profile.get_boss_list(data["userId"])
|
||||||
if b is None:
|
if b is None:
|
||||||
return { "userId": data.get("userId", 0), "userBossData": {}}
|
return { "userId": data.get("userId", 0), "userBossData": {}}
|
||||||
@ -486,7 +486,7 @@ class Mai2Base:
|
|||||||
|
|
||||||
return { "userId": data.get("userId", 0), "userBossData": boss_lst}
|
return { "userId": data.get("userId", 0), "userBossData": boss_lst}
|
||||||
|
|
||||||
def handle_get_user_item_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_item_api_request(self, data: Dict) -> Dict:
|
||||||
kind = int(data["nextIndex"] / 10000000000)
|
kind = int(data["nextIndex"] / 10000000000)
|
||||||
next_idx = int(data["nextIndex"] % 10000000000)
|
next_idx = int(data["nextIndex"] % 10000000000)
|
||||||
user_item_list = self.data.item.get_items(data["userId"], kind)
|
user_item_list = self.data.item.get_items(data["userId"], kind)
|
||||||
@ -514,7 +514,7 @@ class Mai2Base:
|
|||||||
"userItemList": items,
|
"userItemList": items,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_character_api_request(self, data: Dict) -> Dict:
|
async 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"])
|
||||||
|
|
||||||
chara_list = []
|
chara_list = []
|
||||||
@ -528,7 +528,7 @@ class Mai2Base:
|
|||||||
|
|
||||||
return {"userId": data["userId"], "userCharacterList": chara_list}
|
return {"userId": data["userId"], "userCharacterList": chara_list}
|
||||||
|
|
||||||
def handle_get_user_favorite_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_favorite_api_request(self, data: Dict) -> Dict:
|
||||||
favorites = self.data.item.get_favorites(data["userId"], data["itemKind"])
|
favorites = self.data.item.get_favorites(data["userId"], data["itemKind"])
|
||||||
if favorites is None:
|
if favorites is None:
|
||||||
return
|
return
|
||||||
@ -545,7 +545,7 @@ class Mai2Base:
|
|||||||
|
|
||||||
return {"userId": data["userId"], "userFavoriteData": userFavs}
|
return {"userId": data["userId"], "userFavoriteData": userFavs}
|
||||||
|
|
||||||
def handle_get_user_ghost_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_ghost_api_request(self, data: Dict) -> Dict:
|
||||||
ghost = self.data.profile.get_profile_ghost(data["userId"], self.version)
|
ghost = self.data.profile.get_profile_ghost(data["userId"], self.version)
|
||||||
if ghost is None:
|
if ghost is None:
|
||||||
return
|
return
|
||||||
@ -557,7 +557,7 @@ class Mai2Base:
|
|||||||
|
|
||||||
return {"userId": data["userId"], "userGhost": ghost_dict}
|
return {"userId": data["userId"], "userGhost": ghost_dict}
|
||||||
|
|
||||||
def handle_get_user_recent_rating_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_recent_rating_api_request(self, data: Dict) -> Dict:
|
||||||
rating = self.data.profile.get_recent_rating(data["userId"])
|
rating = self.data.profile.get_recent_rating(data["userId"])
|
||||||
if rating is None:
|
if rating is None:
|
||||||
return
|
return
|
||||||
@ -567,7 +567,7 @@ class Mai2Base:
|
|||||||
|
|
||||||
return {"userId": data["userId"], "length": len(lst), "userRecentRatingList": lst}
|
return {"userId": data["userId"], "length": len(lst), "userRecentRatingList": lst}
|
||||||
|
|
||||||
def handle_get_user_rating_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_rating_api_request(self, data: Dict) -> Dict:
|
||||||
rating = self.data.profile.get_profile_rating(data["userId"], self.version)
|
rating = self.data.profile.get_profile_rating(data["userId"], self.version)
|
||||||
if rating is None:
|
if rating is None:
|
||||||
return
|
return
|
||||||
@ -579,7 +579,7 @@ class Mai2Base:
|
|||||||
|
|
||||||
return {"userId": data["userId"], "userRating": rating_dict}
|
return {"userId": data["userId"], "userRating": rating_dict}
|
||||||
|
|
||||||
def handle_get_user_activity_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_activity_api_request(self, data: Dict) -> Dict:
|
||||||
"""
|
"""
|
||||||
kind 1 is playlist, kind 2 is music list
|
kind 1 is playlist, kind 2 is music list
|
||||||
"""
|
"""
|
||||||
@ -607,7 +607,7 @@ class Mai2Base:
|
|||||||
|
|
||||||
return {"userActivity": {"playList": plst, "musicList": mlst}}
|
return {"userActivity": {"playList": plst, "musicList": mlst}}
|
||||||
|
|
||||||
def handle_get_user_course_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_course_api_request(self, data: Dict) -> Dict:
|
||||||
user_courses = self.data.score.get_courses(data["userId"])
|
user_courses = self.data.score.get_courses(data["userId"])
|
||||||
if user_courses is None:
|
if user_courses is None:
|
||||||
return {"userId": data["userId"], "nextIndex": 0, "userCourseList": []}
|
return {"userId": data["userId"], "nextIndex": 0, "userCourseList": []}
|
||||||
@ -621,11 +621,11 @@ class Mai2Base:
|
|||||||
|
|
||||||
return {"userId": data["userId"], "nextIndex": 0, "userCourseList": course_list}
|
return {"userId": data["userId"], "nextIndex": 0, "userCourseList": course_list}
|
||||||
|
|
||||||
def handle_get_user_portrait_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_portrait_api_request(self, data: Dict) -> Dict:
|
||||||
# No support for custom pfps
|
# No support for custom pfps
|
||||||
return {"length": 0, "userPortraitList": []}
|
return {"length": 0, "userPortraitList": []}
|
||||||
|
|
||||||
def handle_get_user_friend_season_ranking_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_friend_season_ranking_api_request(self, data: Dict) -> Dict:
|
||||||
friend_season_ranking = self.data.item.get_friend_season_ranking(data["userId"])
|
friend_season_ranking = self.data.item.get_friend_season_ranking(data["userId"])
|
||||||
if friend_season_ranking is None:
|
if friend_season_ranking is None:
|
||||||
return {
|
return {
|
||||||
@ -661,7 +661,7 @@ class Mai2Base:
|
|||||||
"userFriendSeasonRankingList": friend_season_ranking_list,
|
"userFriendSeasonRankingList": friend_season_ranking_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_map_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_map_api_request(self, data: Dict) -> Dict:
|
||||||
maps = self.data.item.get_maps(data["userId"])
|
maps = self.data.item.get_maps(data["userId"])
|
||||||
if maps is None:
|
if maps is None:
|
||||||
return {
|
return {
|
||||||
@ -694,7 +694,7 @@ class Mai2Base:
|
|||||||
"userMapList": map_list,
|
"userMapList": map_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict:
|
||||||
login_bonuses = self.data.item.get_login_bonuses(data["userId"])
|
login_bonuses = self.data.item.get_login_bonuses(data["userId"])
|
||||||
if login_bonuses is None:
|
if login_bonuses is None:
|
||||||
return {
|
return {
|
||||||
@ -727,10 +727,10 @@ class Mai2Base:
|
|||||||
"userLoginBonusList": login_bonus_list,
|
"userLoginBonusList": login_bonus_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_region_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_region_api_request(self, data: Dict) -> Dict:
|
||||||
return {"userId": data["userId"], "length": 0, "userRegionList": []}
|
return {"userId": data["userId"], "length": 0, "userRegionList": []}
|
||||||
|
|
||||||
def handle_get_user_web_option_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_web_option_api_request(self, data: Dict) -> Dict:
|
||||||
w = self.data.profile.get_web_option(data["userId"], self.version)
|
w = self.data.profile.get_web_option(data["userId"], self.version)
|
||||||
if w is None:
|
if w is None:
|
||||||
return {"userId": data["userId"], "userWebOption": {}}
|
return {"userId": data["userId"], "userWebOption": {}}
|
||||||
@ -742,10 +742,10 @@ class Mai2Base:
|
|||||||
|
|
||||||
return {"userId": data["userId"], "userWebOption": web_opt}
|
return {"userId": data["userId"], "userWebOption": web_opt}
|
||||||
|
|
||||||
def handle_get_user_survival_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_survival_api_request(self, data: Dict) -> Dict:
|
||||||
return {"userId": data["userId"], "length": 0, "userSurvivalList": []}
|
return {"userId": data["userId"], "length": 0, "userSurvivalList": []}
|
||||||
|
|
||||||
def handle_get_user_grade_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_grade_api_request(self, data: Dict) -> Dict:
|
||||||
g = self.data.profile.get_grade_status(data["userId"])
|
g = self.data.profile.get_grade_status(data["userId"])
|
||||||
if g is None:
|
if g is None:
|
||||||
return {"userId": data["userId"], "userGradeStatus": {}, "length": 0, "userGradeList": []}
|
return {"userId": data["userId"], "userGradeStatus": {}, "length": 0, "userGradeList": []}
|
||||||
@ -755,7 +755,7 @@ class Mai2Base:
|
|||||||
|
|
||||||
return {"userId": data["userId"], "userGradeStatus": grade_stat, "length": 0, "userGradeList": []}
|
return {"userId": data["userId"], "userGradeStatus": grade_stat, "length": 0, "userGradeList": []}
|
||||||
|
|
||||||
def handle_get_user_music_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_music_api_request(self, data: Dict) -> Dict:
|
||||||
user_id = data.get("userId", 0)
|
user_id = data.get("userId", 0)
|
||||||
next_index = data.get("nextIndex", 0)
|
next_index = data.get("nextIndex", 0)
|
||||||
max_ct = data.get("maxCount", 50)
|
max_ct = data.get("maxCount", 50)
|
||||||
@ -794,10 +794,10 @@ class Mai2Base:
|
|||||||
"userMusicList": [{"userMusicDetailList": music_detail_list}],
|
"userMusicList": [{"userMusicDetailList": music_detail_list}],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_upload_user_portrait_api_request(self, data: Dict) -> Dict:
|
async def handle_upload_user_portrait_api_request(self, data: Dict) -> Dict:
|
||||||
self.logger.debug(data)
|
self.logger.debug(data)
|
||||||
|
|
||||||
def handle_upload_user_photo_api_request(self, data: Dict) -> Dict:
|
async def handle_upload_user_photo_api_request(self, data: Dict) -> Dict:
|
||||||
if not self.game_config.uploads.photos or not self.game_config.uploads.photos_dir:
|
if not self.game_config.uploads.photos or not self.game_config.uploads.photos_dir:
|
||||||
return {'returnCode': 0, 'apiName': 'UploadUserPhotoApi'}
|
return {'returnCode': 0, 'apiName': 'UploadUserPhotoApi'}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ class Mai2DX(Mai2Base):
|
|||||||
super().__init__(cfg, game_cfg)
|
super().__init__(cfg, game_cfg)
|
||||||
self.version = Mai2Constants.VER_MAIMAI_DX
|
self.version = Mai2Constants.VER_MAIMAI_DX
|
||||||
|
|
||||||
def handle_get_game_setting_api_request(self, data: Dict):
|
async def handle_get_game_setting_api_request(self, data: Dict):
|
||||||
return {
|
return {
|
||||||
"gameSetting": {
|
"gameSetting": {
|
||||||
"isMaintenance": False,
|
"isMaintenance": False,
|
||||||
@ -33,7 +33,7 @@ class Mai2DX(Mai2Base):
|
|||||||
"isAouAccession": False,
|
"isAouAccession": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_preview_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||||
p = self.data.profile.get_profile_detail(data["userId"], self.version)
|
p = self.data.profile.get_profile_detail(data["userId"], self.version)
|
||||||
o = self.data.profile.get_profile_option(data["userId"], self.version)
|
o = self.data.profile.get_profile_option(data["userId"], self.version)
|
||||||
if p is None or o is None:
|
if p is None or o is None:
|
||||||
@ -69,7 +69,7 @@ class Mai2DX(Mai2Base):
|
|||||||
else 0, # New with uni+
|
else 0, # New with uni+
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_upload_user_playlog_api_request(self, data: Dict) -> Dict:
|
async def handle_upload_user_playlog_api_request(self, data: Dict) -> Dict:
|
||||||
user_id = data["userId"]
|
user_id = data["userId"]
|
||||||
playlog = data["userPlaylog"]
|
playlog = data["userPlaylog"]
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ class Mai2DX(Mai2Base):
|
|||||||
|
|
||||||
return {"returnCode": 1, "apiName": "UploadUserPlaylogApi"}
|
return {"returnCode": 1, "apiName": "UploadUserPlaylogApi"}
|
||||||
|
|
||||||
def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict:
|
async def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict:
|
||||||
user_id = data["userId"]
|
user_id = data["userId"]
|
||||||
charge = data["userCharge"]
|
charge = data["userCharge"]
|
||||||
|
|
||||||
@ -93,7 +93,7 @@ class Mai2DX(Mai2Base):
|
|||||||
|
|
||||||
return {"returnCode": 1, "apiName": "UpsertUserChargelogApi"}
|
return {"returnCode": 1, "apiName": "UpsertUserChargelogApi"}
|
||||||
|
|
||||||
def handle_upsert_user_all_api_request(self, data: Dict) -> Dict:
|
async def handle_upsert_user_all_api_request(self, data: Dict) -> Dict:
|
||||||
user_id = data["userId"]
|
user_id = data["userId"]
|
||||||
upsert = data["upsertUserAll"]
|
upsert = data["upsertUserAll"]
|
||||||
|
|
||||||
@ -215,7 +215,7 @@ class Mai2DX(Mai2Base):
|
|||||||
|
|
||||||
return {"returnCode": 1, "apiName": "UpsertUserAllApi"}
|
return {"returnCode": 1, "apiName": "UpsertUserAllApi"}
|
||||||
|
|
||||||
def handle_get_user_data_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_data_api_request(self, data: Dict) -> Dict:
|
||||||
profile = self.data.profile.get_profile_detail(data["userId"], self.version)
|
profile = self.data.profile.get_profile_detail(data["userId"], self.version)
|
||||||
if profile is None:
|
if profile is None:
|
||||||
return
|
return
|
||||||
@ -227,7 +227,7 @@ class Mai2DX(Mai2Base):
|
|||||||
|
|
||||||
return {"userId": data["userId"], "userData": profile_dict}
|
return {"userId": data["userId"], "userData": profile_dict}
|
||||||
|
|
||||||
def handle_get_user_extend_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_extend_api_request(self, data: Dict) -> Dict:
|
||||||
extend = self.data.profile.get_profile_extend(data["userId"], self.version)
|
extend = self.data.profile.get_profile_extend(data["userId"], self.version)
|
||||||
if extend is None:
|
if extend is None:
|
||||||
return
|
return
|
||||||
@ -239,7 +239,7 @@ class Mai2DX(Mai2Base):
|
|||||||
|
|
||||||
return {"userId": data["userId"], "userExtend": extend_dict}
|
return {"userId": data["userId"], "userExtend": extend_dict}
|
||||||
|
|
||||||
def handle_get_user_option_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_option_api_request(self, data: Dict) -> Dict:
|
||||||
options = self.data.profile.get_profile_option(data["userId"], self.version)
|
options = self.data.profile.get_profile_option(data["userId"], self.version)
|
||||||
if options is None:
|
if options is None:
|
||||||
return
|
return
|
||||||
@ -251,7 +251,7 @@ class Mai2DX(Mai2Base):
|
|||||||
|
|
||||||
return {"userId": data["userId"], "userOption": options_dict}
|
return {"userId": data["userId"], "userOption": options_dict}
|
||||||
|
|
||||||
def handle_get_user_card_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_card_api_request(self, data: Dict) -> Dict:
|
||||||
user_cards = self.data.item.get_cards(data["userId"])
|
user_cards = self.data.item.get_cards(data["userId"])
|
||||||
if user_cards is None:
|
if user_cards is None:
|
||||||
return {"userId": data["userId"], "nextIndex": 0, "userCardList": []}
|
return {"userId": data["userId"], "nextIndex": 0, "userCardList": []}
|
||||||
@ -285,7 +285,7 @@ class Mai2DX(Mai2Base):
|
|||||||
"userCardList": card_list[start_idx:end_idx],
|
"userCardList": card_list[start_idx:end_idx],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_charge_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_charge_api_request(self, data: Dict) -> Dict:
|
||||||
user_charges = self.data.item.get_charges(data["userId"])
|
user_charges = self.data.item.get_charges(data["userId"])
|
||||||
if user_charges is None:
|
if user_charges is None:
|
||||||
return {"userId": data["userId"], "length": 0, "userChargeList": []}
|
return {"userId": data["userId"], "length": 0, "userChargeList": []}
|
||||||
@ -310,7 +310,7 @@ class Mai2DX(Mai2Base):
|
|||||||
"userChargeList": user_charge_list,
|
"userChargeList": user_charge_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_item_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_item_api_request(self, data: Dict) -> Dict:
|
||||||
kind = int(data["nextIndex"] / 10000000000)
|
kind = int(data["nextIndex"] / 10000000000)
|
||||||
next_idx = int(data["nextIndex"] % 10000000000)
|
next_idx = int(data["nextIndex"] % 10000000000)
|
||||||
user_item_list = self.data.item.get_items(data["userId"], kind)
|
user_item_list = self.data.item.get_items(data["userId"], kind)
|
||||||
@ -338,7 +338,7 @@ class Mai2DX(Mai2Base):
|
|||||||
"userItemList": items,
|
"userItemList": items,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_character_api_request(self, data: Dict) -> Dict:
|
async 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"])
|
||||||
|
|
||||||
chara_list = []
|
chara_list = []
|
||||||
@ -350,7 +350,7 @@ class Mai2DX(Mai2Base):
|
|||||||
|
|
||||||
return {"userId": data["userId"], "userCharacterList": chara_list}
|
return {"userId": data["userId"], "userCharacterList": chara_list}
|
||||||
|
|
||||||
def handle_get_user_favorite_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_favorite_api_request(self, data: Dict) -> Dict:
|
||||||
favorites = self.data.item.get_favorites(data["userId"], data["itemKind"])
|
favorites = self.data.item.get_favorites(data["userId"], data["itemKind"])
|
||||||
if favorites is None:
|
if favorites is None:
|
||||||
return
|
return
|
||||||
@ -367,7 +367,7 @@ class Mai2DX(Mai2Base):
|
|||||||
|
|
||||||
return {"userId": data["userId"], "userFavoriteData": userFavs}
|
return {"userId": data["userId"], "userFavoriteData": userFavs}
|
||||||
|
|
||||||
def handle_get_user_ghost_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_ghost_api_request(self, data: Dict) -> Dict:
|
||||||
ghost = self.data.profile.get_profile_ghost(data["userId"], self.version)
|
ghost = self.data.profile.get_profile_ghost(data["userId"], self.version)
|
||||||
if ghost is None:
|
if ghost is None:
|
||||||
return
|
return
|
||||||
@ -379,7 +379,7 @@ class Mai2DX(Mai2Base):
|
|||||||
|
|
||||||
return {"userId": data["userId"], "userGhost": ghost_dict}
|
return {"userId": data["userId"], "userGhost": ghost_dict}
|
||||||
|
|
||||||
def handle_get_user_rating_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_rating_api_request(self, data: Dict) -> Dict:
|
||||||
rating = self.data.profile.get_profile_rating(data["userId"], self.version)
|
rating = self.data.profile.get_profile_rating(data["userId"], self.version)
|
||||||
if rating is None:
|
if rating is None:
|
||||||
return
|
return
|
||||||
@ -391,7 +391,7 @@ class Mai2DX(Mai2Base):
|
|||||||
|
|
||||||
return {"userId": data["userId"], "userRating": rating_dict}
|
return {"userId": data["userId"], "userRating": rating_dict}
|
||||||
|
|
||||||
def handle_get_user_activity_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_activity_api_request(self, data: Dict) -> Dict:
|
||||||
"""
|
"""
|
||||||
kind 1 is playlist, kind 2 is music list
|
kind 1 is playlist, kind 2 is music list
|
||||||
"""
|
"""
|
||||||
@ -419,7 +419,7 @@ class Mai2DX(Mai2Base):
|
|||||||
|
|
||||||
return {"userActivity": {"playList": plst, "musicList": mlst}}
|
return {"userActivity": {"playList": plst, "musicList": mlst}}
|
||||||
|
|
||||||
def handle_get_user_course_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_course_api_request(self, data: Dict) -> Dict:
|
||||||
user_courses = self.data.score.get_courses(data["userId"])
|
user_courses = self.data.score.get_courses(data["userId"])
|
||||||
if user_courses is None:
|
if user_courses is None:
|
||||||
return {"userId": data["userId"], "nextIndex": 0, "userCourseList": []}
|
return {"userId": data["userId"], "nextIndex": 0, "userCourseList": []}
|
||||||
@ -433,11 +433,11 @@ class Mai2DX(Mai2Base):
|
|||||||
|
|
||||||
return {"userId": data["userId"], "nextIndex": 0, "userCourseList": course_list}
|
return {"userId": data["userId"], "nextIndex": 0, "userCourseList": course_list}
|
||||||
|
|
||||||
def handle_get_user_portrait_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_portrait_api_request(self, data: Dict) -> Dict:
|
||||||
# No support for custom pfps
|
# No support for custom pfps
|
||||||
return {"length": 0, "userPortraitList": []}
|
return {"length": 0, "userPortraitList": []}
|
||||||
|
|
||||||
def handle_get_user_friend_season_ranking_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_friend_season_ranking_api_request(self, data: Dict) -> Dict:
|
||||||
friend_season_ranking = self.data.item.get_friend_season_ranking(data["userId"])
|
friend_season_ranking = self.data.item.get_friend_season_ranking(data["userId"])
|
||||||
if friend_season_ranking is None:
|
if friend_season_ranking is None:
|
||||||
return {
|
return {
|
||||||
@ -473,7 +473,7 @@ class Mai2DX(Mai2Base):
|
|||||||
"userFriendSeasonRankingList": friend_season_ranking_list,
|
"userFriendSeasonRankingList": friend_season_ranking_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_map_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_map_api_request(self, data: Dict) -> Dict:
|
||||||
maps = self.data.item.get_maps(data["userId"])
|
maps = self.data.item.get_maps(data["userId"])
|
||||||
if maps is None:
|
if maps is None:
|
||||||
return {
|
return {
|
||||||
@ -506,7 +506,7 @@ class Mai2DX(Mai2Base):
|
|||||||
"userMapList": map_list,
|
"userMapList": map_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict:
|
||||||
login_bonuses = self.data.item.get_login_bonuses(data["userId"])
|
login_bonuses = self.data.item.get_login_bonuses(data["userId"])
|
||||||
if login_bonuses is None:
|
if login_bonuses is None:
|
||||||
return {
|
return {
|
||||||
@ -539,7 +539,7 @@ class Mai2DX(Mai2Base):
|
|||||||
"userLoginBonusList": login_bonus_list,
|
"userLoginBonusList": login_bonus_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_region_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_region_api_request(self, data: Dict) -> Dict:
|
||||||
"""
|
"""
|
||||||
class UserRegionList:
|
class UserRegionList:
|
||||||
regionId: int
|
regionId: int
|
||||||
@ -548,7 +548,7 @@ class Mai2DX(Mai2Base):
|
|||||||
"""
|
"""
|
||||||
return {"userId": data["userId"], "length": 0, "userRegionList": []}
|
return {"userId": data["userId"], "length": 0, "userRegionList": []}
|
||||||
|
|
||||||
def handle_get_user_rival_data_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_rival_data_api_request(self, data: Dict) -> Dict:
|
||||||
user_id = data["userId"]
|
user_id = data["userId"]
|
||||||
rival_id = data["rivalId"]
|
rival_id = data["rivalId"]
|
||||||
|
|
||||||
@ -559,7 +559,7 @@ class Mai2DX(Mai2Base):
|
|||||||
"""
|
"""
|
||||||
return {"userId": user_id, "userRivalData": {}}
|
return {"userId": user_id, "userRivalData": {}}
|
||||||
|
|
||||||
def handle_get_user_rival_music_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_rival_music_api_request(self, data: Dict) -> Dict:
|
||||||
user_id = data["userId"]
|
user_id = data["userId"]
|
||||||
rival_id = data["rivalId"]
|
rival_id = data["rivalId"]
|
||||||
next_idx = data["nextIndex"]
|
next_idx = data["nextIndex"]
|
||||||
@ -577,7 +577,7 @@ class Mai2DX(Mai2Base):
|
|||||||
"""
|
"""
|
||||||
return {"userId": user_id, "nextIndex": 0, "userRivalMusicList": []}
|
return {"userId": user_id, "nextIndex": 0, "userRivalMusicList": []}
|
||||||
|
|
||||||
def handle_get_user_music_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_music_api_request(self, data: Dict) -> Dict:
|
||||||
user_id = data.get("userId", 0)
|
user_id = data.get("userId", 0)
|
||||||
next_index = data.get("nextIndex", 0)
|
next_index = data.get("nextIndex", 0)
|
||||||
max_ct = data.get("maxCount", 50)
|
max_ct = data.get("maxCount", 50)
|
||||||
@ -616,8 +616,8 @@ class Mai2DX(Mai2Base):
|
|||||||
"userMusicList": [{"userMusicDetailList": music_detail_list}],
|
"userMusicList": [{"userMusicDetailList": music_detail_list}],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_user_login_api_request(self, data: Dict) -> Dict:
|
async def handle_user_login_api_request(self, data: Dict) -> Dict:
|
||||||
ret = super().handle_user_login_api_request(data)
|
ret = await super().handle_user_login_api_request(data)
|
||||||
if ret is None or not ret:
|
if ret is None or not ret:
|
||||||
return ret
|
return ret
|
||||||
ret['loginId'] = ret.get('loginCount', 0)
|
ret['loginId'] = ret.get('loginCount', 0)
|
||||||
|
@ -11,26 +11,26 @@ class Mai2Festival(Mai2UniversePlus):
|
|||||||
super().__init__(cfg, game_cfg)
|
super().__init__(cfg, game_cfg)
|
||||||
self.version = Mai2Constants.VER_MAIMAI_DX_FESTIVAL
|
self.version = Mai2Constants.VER_MAIMAI_DX_FESTIVAL
|
||||||
|
|
||||||
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||||
user_data = super().handle_cm_get_user_preview_api_request(data)
|
user_data = await super().handle_cm_get_user_preview_api_request(data)
|
||||||
|
|
||||||
# hardcode lastDataVersion for CardMaker
|
# hardcode lastDataVersion for CardMaker
|
||||||
user_data["lastDataVersion"] = "1.30.00"
|
user_data["lastDataVersion"] = "1.30.00"
|
||||||
return user_data
|
return user_data
|
||||||
|
|
||||||
def handle_user_login_api_request(self, data: Dict) -> Dict:
|
async def handle_user_login_api_request(self, data: Dict) -> Dict:
|
||||||
user_login = super().handle_user_login_api_request(data)
|
user_login = await super().handle_user_login_api_request(data)
|
||||||
# useless?
|
# useless?
|
||||||
user_login["Bearer"] = "ARTEMiSTOKEN"
|
user_login["Bearer"] = "ARTEMiSTOKEN"
|
||||||
return user_login
|
return user_login
|
||||||
|
|
||||||
def handle_get_user_recommend_rate_music_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_recommend_rate_music_api_request(self, data: Dict) -> Dict:
|
||||||
"""
|
"""
|
||||||
userRecommendRateMusicIdList: list[int]
|
userRecommendRateMusicIdList: list[int]
|
||||||
"""
|
"""
|
||||||
return {"userId": data["userId"], "userRecommendRateMusicIdList": []}
|
return {"userId": data["userId"], "userRecommendRateMusicIdList": []}
|
||||||
|
|
||||||
def handle_get_user_recommend_select_music_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_recommend_select_music_api_request(self, data: Dict) -> Dict:
|
||||||
"""
|
"""
|
||||||
userRecommendSelectionMusicIdList: list[int]
|
userRecommendSelectionMusicIdList: list[int]
|
||||||
"""
|
"""
|
||||||
|
@ -11,14 +11,14 @@ class Mai2FestivalPlus(Mai2Festival):
|
|||||||
super().__init__(cfg, game_cfg)
|
super().__init__(cfg, game_cfg)
|
||||||
self.version = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS
|
self.version = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS
|
||||||
|
|
||||||
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||||
user_data = super().handle_cm_get_user_preview_api_request(data)
|
user_data = await super().handle_cm_get_user_preview_api_request(data)
|
||||||
|
|
||||||
# hardcode lastDataVersion for CardMaker
|
# hardcode lastDataVersion for CardMaker
|
||||||
user_data["lastDataVersion"] = "1.35.00"
|
user_data["lastDataVersion"] = "1.35.00"
|
||||||
return user_data
|
return user_data
|
||||||
|
|
||||||
def handle_get_user_favorite_item_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_favorite_item_api_request(self, data: Dict) -> Dict:
|
||||||
user_id = data.get("userId", 0)
|
user_id = data.get("userId", 0)
|
||||||
kind = data.get("kind", 2)
|
kind = data.get("kind", 2)
|
||||||
next_index = data.get("nextIndex", 0)
|
next_index = data.get("nextIndex", 0)
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
from twisted.web.http import Request
|
from starlette.requests import Request
|
||||||
from twisted.web.server import NOT_DONE_YET
|
from starlette.responses import Response, JSONResponse
|
||||||
|
from starlette.routing import Route
|
||||||
import json
|
import json
|
||||||
import inflection
|
import inflection
|
||||||
import yaml
|
import yaml
|
||||||
import string
|
|
||||||
import logging, coloredlogs
|
import logging, coloredlogs
|
||||||
import zlib
|
import zlib
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
@ -101,33 +101,29 @@ class Mai2Servlet(BaseServlet):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
|
def get_routes(self) -> List[Route]:
|
||||||
return (
|
return [
|
||||||
[
|
Route("/{version:int}/MaimaiServlet/api/movie/{endpoint:str}", self.handle_movie, methods=['GET', 'POST']),
|
||||||
("handle_movie", "/{version}/MaimaiServlet/api/movie/{endpoint}", {}),
|
Route("/{version:int}/MaimaiServlet/old/{endpoint:str}", self.handle_old_srv),
|
||||||
("handle_old_srv", "/{version}/MaimaiServlet/old/{endpoint}", {}),
|
Route("/{version:int}/MaimaiServlet/old/{endpoint:str}/{placeid:str}/{keychip:str}/{userid:int}", self.handle_old_srv_userdata),
|
||||||
("handle_old_srv_userdata", "/{version}/MaimaiServlet/old/{endpoint}/{placeid}/{keychip}/{userid}", {}),
|
Route("/{version:int}/MaimaiServlet/old/{endpoint:str}/{userid:int}", self.handle_old_srv_userdata),
|
||||||
("handle_old_srv_userdata", "/{version}/MaimaiServlet/old/{endpoint}/{userid}", {}),
|
Route("/{version:int}/MaimaiServlet/old/{endpoint:str}/{userid:int}", self.handle_old_srv_userdata),
|
||||||
("handle_usbdl", "/{version}/MaimaiServlet/usbdl/{endpoint}", {}),
|
Route("/{version:int}/MaimaiServlet/usbdl/{endpoint:str}", self.handle_usbdl),
|
||||||
("handle_deliver", "/{version}/MaimaiServlet/deliver/{endpoint}", {}),
|
Route("/{version:int}/MaimaiServlet/deliver/{endpoint:str}", self.handle_deliver),
|
||||||
],
|
Route("/{version:int}/MaimaiServlet/{endpoint:str}", self.handle_mai, methods=['POST']),
|
||||||
[
|
Route("/{version:int}/Maimai2Servlet/{endpoint:str}", self.handle_mai2, methods=['POST']),
|
||||||
("handle_movie", "/{version}/MaimaiServlet/api/movie/{endpoint}", {}),
|
]
|
||||||
("handle_mai", "/{version}/MaimaiServlet/{endpoint}", {}),
|
|
||||||
("handle_mai2", "/{version}/Maimai2Servlet/{endpoint}", {}),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
|
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
|
||||||
if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80:
|
if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80:
|
||||||
return (
|
return (
|
||||||
f"http://{self.core_cfg.title.hostname}:{Utils.get_title_port(self.core_cfg)}/{game_ver}/",
|
f"http://{self.core_cfg.server.hostname}:{Utils.get_title_port(self.core_cfg)}/{game_ver}/",
|
||||||
f"{self.core_cfg.title.hostname}",
|
f"{self.core_cfg.server.hostname}",
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
f"http://{self.core_cfg.title.hostname}/{game_ver}/",
|
f"http://{self.core_cfg.server.hostname}/{game_ver}/",
|
||||||
f"{self.core_cfg.title.hostname}",
|
f"{self.core_cfg.server.hostname}",
|
||||||
)
|
)
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
@ -155,13 +151,22 @@ class Mai2Servlet(BaseServlet):
|
|||||||
f"Failed to make movie upload directory at {self.game_cfg.uploads.movies_dir}"
|
f"Failed to make movie upload directory at {self.game_cfg.uploads.movies_dir}"
|
||||||
)
|
)
|
||||||
|
|
||||||
def handle_mai(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
async def handle_movie(self, request: Request):
|
||||||
endpoint = matchers['endpoint']
|
return JSONResponse()
|
||||||
version = int(matchers['version'])
|
|
||||||
|
async def handle_usbdl(self, request: Request):
|
||||||
|
return Response("OK")
|
||||||
|
|
||||||
|
async def handle_deliver(self, request: Request):
|
||||||
|
return Response(status_code=404)
|
||||||
|
|
||||||
|
async def handle_mai(self, request: Request) -> bytes:
|
||||||
|
endpoint: str = request.path_params.get('endpoint')
|
||||||
|
version: int = request.path_params.get('version')
|
||||||
if endpoint.lower() == "ping":
|
if endpoint.lower() == "ping":
|
||||||
return zlib.compress(b'{"returnCode": "1"}')
|
return Response(zlib.compress(b'{"returnCode": "1"}'))
|
||||||
|
|
||||||
req_raw = request.content.getvalue()
|
req_raw = await request.body()
|
||||||
internal_ver = 0
|
internal_ver = 0
|
||||||
client_ip = Utils.get_ip_addr(request)
|
client_ip = Utils.get_ip_addr(request)
|
||||||
|
|
||||||
@ -199,7 +204,7 @@ class Mai2Servlet(BaseServlet):
|
|||||||
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 Response(zlib.compress(b'{"stat": "0"}'))
|
||||||
|
|
||||||
req_data = json.loads(unzip)
|
req_data = json.loads(unzip)
|
||||||
|
|
||||||
@ -216,26 +221,26 @@ class Mai2Servlet(BaseServlet):
|
|||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
handler = getattr(handler_cls, func_to_find)
|
handler = getattr(handler_cls, func_to_find)
|
||||||
resp = handler(req_data)
|
resp = await handler(req_data)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
|
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
|
||||||
return zlib.compress(b'{"returnCode": "0"}')
|
return Response(zlib.compress(b'{"returnCode": "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}")
|
||||||
|
|
||||||
return zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
|
return Response(zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8")))
|
||||||
|
|
||||||
def handle_mai2(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
async def handle_mai2(self, request: Request) -> bytes:
|
||||||
endpoint = matchers['endpoint']
|
endpoint: str = request.path_params.get('endpoint')
|
||||||
version = int(matchers['version'])
|
version: int = request.path_params.get('version')
|
||||||
if endpoint.lower() == "ping":
|
if endpoint.lower() == "ping":
|
||||||
return zlib.compress(b'{"returnCode": "1"}')
|
return Response(zlib.compress(b'{"returnCode": "1"}'))
|
||||||
|
|
||||||
req_raw = request.content.getvalue()
|
req_raw = await request.body()
|
||||||
internal_ver = 0
|
internal_ver = 0
|
||||||
client_ip = Utils.get_ip_addr(request)
|
client_ip = Utils.get_ip_addr(request)
|
||||||
if version < 105: # 1.0
|
if version < 105: # 1.0
|
||||||
@ -256,17 +261,17 @@ class Mai2Servlet(BaseServlet):
|
|||||||
internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS
|
internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS
|
||||||
|
|
||||||
if (
|
if (
|
||||||
request.getHeader("Mai-Encoding") is not None
|
request.headers.get("Mai-Encoding") is not None
|
||||||
or request.getHeader("X-Mai-Encoding") is not None
|
or request.headers.get("X-Mai-Encoding") is not None
|
||||||
):
|
):
|
||||||
# The has is some flavor of MD5 of the endpoint with a constant bolted onto the end of it.
|
# The has is some flavor of MD5 of the endpoint with a constant bolted onto the end of it.
|
||||||
# See cake.dll's Obfuscator function for details. Hopefully most DLL edits will remove
|
# See cake.dll's Obfuscator function for details. Hopefully most DLL edits will remove
|
||||||
# these two(?) headers to not cause issues, but given the general quality of SEGA data...
|
# these two(?) headers to not cause issues, but given the general quality of SEGA data...
|
||||||
enc_ver = request.getHeader("Mai-Encoding")
|
enc_ver = request.headers.get("Mai-Encoding")
|
||||||
if enc_ver is None:
|
if enc_ver is None:
|
||||||
enc_ver = request.getHeader("X-Mai-Encoding")
|
enc_ver = request.headers.get("X-Mai-Encoding")
|
||||||
self.logger.debug(
|
self.logger.debug(
|
||||||
f"Encryption v{enc_ver} - User-Agent: {request.getHeader('User-Agent')}"
|
f"Encryption v{enc_ver} - User-Agent: {request.headers.get('User-Agent')}"
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -276,7 +281,7 @@ class Mai2Servlet(BaseServlet):
|
|||||||
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 Response(zlib.compress(b'{"stat": "0"}'))
|
||||||
|
|
||||||
req_data = json.loads(unzip)
|
req_data = json.loads(unzip)
|
||||||
|
|
||||||
@ -293,80 +298,27 @@ class Mai2Servlet(BaseServlet):
|
|||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
handler = getattr(handler_cls, func_to_find)
|
handler = getattr(handler_cls, func_to_find)
|
||||||
resp = handler(req_data)
|
resp = await handler(req_data)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
|
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
|
||||||
return zlib.compress(b'{"stat": "0"}')
|
return Response(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}")
|
||||||
|
|
||||||
return zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
|
return Response(zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8")))
|
||||||
|
|
||||||
def handle_old_srv(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
async def handle_old_srv(self, request: Request) -> bytes:
|
||||||
endpoint = matchers['endpoint']
|
endpoint = request.path_params.get('endpoint')
|
||||||
version = matchers['version']
|
version = request.path_params.get('version')
|
||||||
self.logger.info(f"v{version} old server {endpoint}")
|
self.logger.info(f"v{version} old server {endpoint}")
|
||||||
return zlib.compress(b"ok")
|
return Response(zlib.compress(b"ok"))
|
||||||
|
|
||||||
def handle_old_srv_userdata(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
async def handle_old_srv_userdata(self, request: Request) -> bytes:
|
||||||
endpoint = matchers['endpoint']
|
endpoint = request.path_params.get('endpoint')
|
||||||
version = matchers['version']
|
version = request.path_params.get('version')
|
||||||
self.logger.info(f"v{version} old server {endpoint}")
|
self.logger.info(f"v{version} old server {endpoint}")
|
||||||
return zlib.compress(b"{}")
|
return Response(zlib.compress(b"{}"))
|
||||||
|
|
||||||
def render_GET(self, request: Request, version: int, url_path: str) -> bytes:
|
|
||||||
self.logger.debug(f"v{version} GET {url_path}")
|
|
||||||
url_split = url_path.split("/")
|
|
||||||
|
|
||||||
if (url_split[0] == "api" and url_split[1] == "movie") or url_split[
|
|
||||||
0
|
|
||||||
] == "movie":
|
|
||||||
if url_split[2] == "moviestart":
|
|
||||||
return json.dumps({"moviestart": {"status": "OK"}}).encode()
|
|
||||||
|
|
||||||
else:
|
|
||||||
request.setResponseCode(404)
|
|
||||||
return b""
|
|
||||||
|
|
||||||
elif url_split[0] == "usbdl":
|
|
||||||
if url_split[1] == "CONNECTIONTEST":
|
|
||||||
self.logger.info(f"v{version} usbdl server test")
|
|
||||||
return b""
|
|
||||||
|
|
||||||
elif self.game_cfg.deliver.udbdl_enable and path.exists(
|
|
||||||
f"{self.game_cfg.deliver.content_folder}/usb/{url_split[-1]}"
|
|
||||||
):
|
|
||||||
with open(
|
|
||||||
f"{self.game_cfg.deliver.content_folder}/usb/{url_split[-1]}", "rb"
|
|
||||||
) as f:
|
|
||||||
return f.read()
|
|
||||||
|
|
||||||
else:
|
|
||||||
request.setResponseCode(404)
|
|
||||||
return b""
|
|
||||||
|
|
||||||
elif url_split[0] == "deliver":
|
|
||||||
file = url_split[len(url_split) - 1]
|
|
||||||
self.logger.info(f"v{version} {file} deliver inquire")
|
|
||||||
self.logger.debug(
|
|
||||||
f"{self.game_cfg.deliver.content_folder}/net_deliver/{file}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.game_cfg.deliver.enable and path.exists(
|
|
||||||
f"{self.game_cfg.deliver.content_folder}/net_deliver/{file}"
|
|
||||||
):
|
|
||||||
with open(
|
|
||||||
f"{self.game_cfg.deliver.content_folder}/net_deliver/{file}", "rb"
|
|
||||||
) as f:
|
|
||||||
return f.read()
|
|
||||||
|
|
||||||
else:
|
|
||||||
request.setResponseCode(404)
|
|
||||||
return b""
|
|
||||||
|
|
||||||
else:
|
|
||||||
return zlib.compress(b"{}")
|
|
||||||
|
@ -15,7 +15,7 @@ class Mai2Universe(Mai2SplashPlus):
|
|||||||
super().__init__(cfg, game_cfg)
|
super().__init__(cfg, game_cfg)
|
||||||
self.version = Mai2Constants.VER_MAIMAI_DX_UNIVERSE
|
self.version = Mai2Constants.VER_MAIMAI_DX_UNIVERSE
|
||||||
|
|
||||||
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||||
p = self.data.profile.get_profile_detail(data["userId"], self.version)
|
p = self.data.profile.get_profile_detail(data["userId"], self.version)
|
||||||
if p is None:
|
if p is None:
|
||||||
return {}
|
return {}
|
||||||
@ -30,7 +30,7 @@ class Mai2Universe(Mai2SplashPlus):
|
|||||||
"isExistSellingCard": True,
|
"isExistSellingCard": True,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict:
|
||||||
# user already exists, because the preview checks that already
|
# user already exists, because the preview checks that already
|
||||||
p = self.data.profile.get_profile_detail(data["userId"], self.version)
|
p = self.data.profile.get_profile_detail(data["userId"], self.version)
|
||||||
|
|
||||||
@ -52,13 +52,13 @@ class Mai2Universe(Mai2SplashPlus):
|
|||||||
|
|
||||||
return {"userId": data["userId"], "userData": user_data}
|
return {"userId": data["userId"], "userData": user_data}
|
||||||
|
|
||||||
def handle_cm_login_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_login_api_request(self, data: Dict) -> Dict:
|
||||||
return {"returnCode": 1}
|
return {"returnCode": 1}
|
||||||
|
|
||||||
def handle_cm_logout_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_logout_api_request(self, data: Dict) -> Dict:
|
||||||
return {"returnCode": 1}
|
return {"returnCode": 1}
|
||||||
|
|
||||||
def handle_cm_get_selling_card_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_get_selling_card_api_request(self, data: Dict) -> Dict:
|
||||||
selling_cards = self.data.static.get_enabled_cards(self.version)
|
selling_cards = self.data.static.get_enabled_cards(self.version)
|
||||||
if selling_cards is None:
|
if selling_cards is None:
|
||||||
return {"length": 0, "sellingCardList": []}
|
return {"length": 0, "sellingCardList": []}
|
||||||
@ -88,7 +88,7 @@ class Mai2Universe(Mai2SplashPlus):
|
|||||||
|
|
||||||
return {"length": len(selling_card_list), "sellingCardList": selling_card_list}
|
return {"length": len(selling_card_list), "sellingCardList": selling_card_list}
|
||||||
|
|
||||||
def handle_cm_get_user_card_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_get_user_card_api_request(self, data: Dict) -> Dict:
|
||||||
user_cards = self.data.item.get_cards(data["userId"])
|
user_cards = self.data.item.get_cards(data["userId"])
|
||||||
if user_cards is None:
|
if user_cards is None:
|
||||||
return {"returnCode": 1, "length": 0, "nextIndex": 0, "userCardList": []}
|
return {"returnCode": 1, "length": 0, "nextIndex": 0, "userCardList": []}
|
||||||
@ -124,10 +124,10 @@ class Mai2Universe(Mai2SplashPlus):
|
|||||||
"userCardList": card_list[start_idx:end_idx],
|
"userCardList": card_list[start_idx:end_idx],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_cm_get_user_item_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_get_user_item_api_request(self, data: Dict) -> Dict:
|
||||||
super().handle_get_user_item_api_request(data)
|
super().handle_get_user_item_api_request(data)
|
||||||
|
|
||||||
def handle_cm_get_user_character_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_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"])
|
||||||
|
|
||||||
chara_list = []
|
chara_list = []
|
||||||
@ -153,10 +153,10 @@ class Mai2Universe(Mai2SplashPlus):
|
|||||||
"userCharacterList": chara_list,
|
"userCharacterList": chara_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_cm_get_user_card_print_error_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_get_user_card_print_error_api_request(self, data: Dict) -> Dict:
|
||||||
return {"length": 0, "userPrintDetailList": []}
|
return {"length": 0, "userPrintDetailList": []}
|
||||||
|
|
||||||
def handle_cm_upsert_user_print_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_upsert_user_print_api_request(self, data: Dict) -> Dict:
|
||||||
user_id = data["userId"]
|
user_id = data["userId"]
|
||||||
upsert = data["userPrintDetail"]
|
upsert = data["userPrintDetail"]
|
||||||
|
|
||||||
@ -209,12 +209,12 @@ class Mai2Universe(Mai2SplashPlus):
|
|||||||
"endDate": datetime.strftime(end_date, Mai2Constants.DATE_TIME_FORMAT),
|
"endDate": datetime.strftime(end_date, Mai2Constants.DATE_TIME_FORMAT),
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_cm_upsert_user_printlog_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_upsert_user_printlog_api_request(self, data: Dict) -> Dict:
|
||||||
return {
|
return {
|
||||||
"returnCode": 1,
|
"returnCode": 1,
|
||||||
"orderId": 0,
|
"orderId": 0,
|
||||||
"serialId": data["userPrintlog"]["serialId"],
|
"serialId": data["userPrintlog"]["serialId"],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_cm_upsert_buy_card_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_upsert_buy_card_api_request(self, data: Dict) -> Dict:
|
||||||
return {"returnCode": 1}
|
return {"returnCode": 1}
|
||||||
|
@ -11,8 +11,8 @@ class Mai2UniversePlus(Mai2Universe):
|
|||||||
super().__init__(cfg, game_cfg)
|
super().__init__(cfg, game_cfg)
|
||||||
self.version = Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS
|
self.version = Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS
|
||||||
|
|
||||||
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||||
user_data = super().handle_cm_get_user_preview_api_request(data)
|
user_data = await super().handle_cm_get_user_preview_api_request(data)
|
||||||
|
|
||||||
# hardcode lastDataVersion for CardMaker 1.35
|
# hardcode lastDataVersion for CardMaker 1.35
|
||||||
user_data["lastDataVersion"] = "1.25.00"
|
user_data["lastDataVersion"] = "1.25.00"
|
||||||
|
@ -103,7 +103,7 @@ class OngekiBase:
|
|||||||
self.game = OngekiConstants.GAME_CODE
|
self.game = OngekiConstants.GAME_CODE
|
||||||
self.version = OngekiConstants.VER_ONGEKI
|
self.version = OngekiConstants.VER_ONGEKI
|
||||||
|
|
||||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||||
# if reboot start/end time is not defined use the default behavior of being a few hours ago
|
# if reboot start/end time is not defined use the default behavior of being a few hours ago
|
||||||
if self.core_cfg.title.reboot_start_time == "" or self.core_cfg.title.reboot_end_time == "":
|
if self.core_cfg.title.reboot_start_time == "" or self.core_cfg.title.reboot_end_time == "":
|
||||||
reboot_start = datetime.strftime(
|
reboot_start = datetime.strftime(
|
||||||
@ -148,7 +148,7 @@ class OngekiBase:
|
|||||||
"isAou": "true",
|
"isAou": "true",
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_game_idlist_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_idlist_api_request(self, data: Dict) -> Dict:
|
||||||
"""
|
"""
|
||||||
Gets lists of song IDs, either disabled songs or recomended songs depending on type?
|
Gets lists of song IDs, either disabled songs or recomended songs depending on type?
|
||||||
"""
|
"""
|
||||||
@ -156,7 +156,7 @@ class OngekiBase:
|
|||||||
# id - int
|
# id - int
|
||||||
return {"type": data["type"], "length": 0, "gameIdlistList": []}
|
return {"type": data["type"], "length": 0, "gameIdlistList": []}
|
||||||
|
|
||||||
def handle_get_game_ranking_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_ranking_api_request(self, data: Dict) -> Dict:
|
||||||
game_ranking_list = self.data.static.get_ranking_list(self.version)
|
game_ranking_list = self.data.static.get_ranking_list(self.version)
|
||||||
|
|
||||||
ranking_list = []
|
ranking_list = []
|
||||||
@ -171,7 +171,7 @@ class OngekiBase:
|
|||||||
"gameRankingList": ranking_list,
|
"gameRankingList": ranking_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_game_point_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_point_api_request(self, data: Dict) -> Dict:
|
||||||
get_game_point = self.data.static.get_static_game_point()
|
get_game_point = self.data.static.get_static_game_point()
|
||||||
game_point = []
|
game_point = []
|
||||||
|
|
||||||
@ -194,16 +194,16 @@ class OngekiBase:
|
|||||||
"gamePointList": game_point,
|
"gamePointList": game_point,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_game_login_api_request(self, data: Dict) -> Dict:
|
async def handle_game_login_api_request(self, data: Dict) -> Dict:
|
||||||
return {"returnCode": 1, "apiName": "gameLogin"}
|
return {"returnCode": 1, "apiName": "gameLogin"}
|
||||||
|
|
||||||
def handle_game_logout_api_request(self, data: Dict) -> Dict:
|
async def handle_game_logout_api_request(self, data: Dict) -> Dict:
|
||||||
return {"returnCode": 1, "apiName": "gameLogout"}
|
return {"returnCode": 1, "apiName": "gameLogout"}
|
||||||
|
|
||||||
def handle_extend_lock_time_api_request(self, data: Dict) -> Dict:
|
async def handle_extend_lock_time_api_request(self, data: Dict) -> Dict:
|
||||||
return {"returnCode": 1, "apiName": "ExtendLockTimeApi"}
|
return {"returnCode": 1, "apiName": "ExtendLockTimeApi"}
|
||||||
|
|
||||||
def handle_get_game_reward_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_reward_api_request(self, data: Dict) -> Dict:
|
||||||
get_game_rewards = self.data.static.get_reward_list(self.version)
|
get_game_rewards = self.data.static.get_reward_list(self.version)
|
||||||
|
|
||||||
reward_list = []
|
reward_list = []
|
||||||
@ -221,7 +221,7 @@ class OngekiBase:
|
|||||||
"gameRewardList": reward_list,
|
"gameRewardList": reward_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_game_present_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_present_api_request(self, data: Dict) -> Dict:
|
||||||
get_present = self.data.static.get_present_list(self.version)
|
get_present = self.data.static.get_present_list(self.version)
|
||||||
|
|
||||||
present_list = []
|
present_list = []
|
||||||
@ -238,13 +238,13 @@ class OngekiBase:
|
|||||||
"gamePresentList": present_list,
|
"gamePresentList": present_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_game_message_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_message_api_request(self, data: Dict) -> Dict:
|
||||||
return {"length": 0, "gameMessageList": []}
|
return {"length": 0, "gameMessageList": []}
|
||||||
|
|
||||||
def handle_get_game_sale_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_sale_api_request(self, data: Dict) -> Dict:
|
||||||
return {"length": 0, "gameSaleList": []}
|
return {"length": 0, "gameSaleList": []}
|
||||||
|
|
||||||
def handle_get_game_tech_music_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_tech_music_api_request(self, data: Dict) -> Dict:
|
||||||
music_list = self.data.static.get_tech_music(self.version)
|
music_list = self.data.static.get_tech_music(self.version)
|
||||||
|
|
||||||
prep_music_list = []
|
prep_music_list = []
|
||||||
@ -262,7 +262,7 @@ class OngekiBase:
|
|||||||
"gameTechMusicList": prep_music_list,
|
"gameTechMusicList": prep_music_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict:
|
async def handle_upsert_client_setting_api_request(self, data: Dict) -> Dict:
|
||||||
if self.core_cfg.server.is_develop:
|
if self.core_cfg.server.is_develop:
|
||||||
return {"returnCode": 1, "apiName": "UpsertClientSettingApi"}
|
return {"returnCode": 1, "apiName": "UpsertClientSettingApi"}
|
||||||
|
|
||||||
@ -273,7 +273,7 @@ class OngekiBase:
|
|||||||
self.data.static.put_client_setting_data(cab['id'], client_setting_data)
|
self.data.static.put_client_setting_data(cab['id'], client_setting_data)
|
||||||
return {"returnCode": 1, "apiName": "UpsertClientSettingApi"}
|
return {"returnCode": 1, "apiName": "UpsertClientSettingApi"}
|
||||||
|
|
||||||
def handle_upsert_client_testmode_api_request(self, data: Dict) -> Dict:
|
async def handle_upsert_client_testmode_api_request(self, data: Dict) -> Dict:
|
||||||
if self.core_cfg.server.is_develop:
|
if self.core_cfg.server.is_develop:
|
||||||
return {"returnCode": 1, "apiName": "UpsertClientTestmodeApi"}
|
return {"returnCode": 1, "apiName": "UpsertClientTestmodeApi"}
|
||||||
|
|
||||||
@ -282,16 +282,16 @@ class OngekiBase:
|
|||||||
self.data.static.put_client_testmode_data(region_id, client_testmode_data)
|
self.data.static.put_client_testmode_data(region_id, client_testmode_data)
|
||||||
return {"returnCode": 1, "apiName": "UpsertClientTestmodeApi"}
|
return {"returnCode": 1, "apiName": "UpsertClientTestmodeApi"}
|
||||||
|
|
||||||
def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict:
|
async def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict:
|
||||||
return {"returnCode": 1, "apiName": "upsertClientBookkeeping"}
|
return {"returnCode": 1, "apiName": "upsertClientBookkeeping"}
|
||||||
|
|
||||||
def handle_upsert_client_develop_api_request(self, data: Dict) -> Dict:
|
async def handle_upsert_client_develop_api_request(self, data: Dict) -> Dict:
|
||||||
return {"returnCode": 1, "apiName": "upsertClientDevelop"}
|
return {"returnCode": 1, "apiName": "upsertClientDevelop"}
|
||||||
|
|
||||||
def handle_upsert_client_error_api_request(self, data: Dict) -> Dict:
|
async def handle_upsert_client_error_api_request(self, data: Dict) -> Dict:
|
||||||
return {"returnCode": 1, "apiName": "upsertClientError"}
|
return {"returnCode": 1, "apiName": "upsertClientError"}
|
||||||
|
|
||||||
def handle_upsert_user_gplog_api_request(self, data: Dict) -> Dict:
|
async def handle_upsert_user_gplog_api_request(self, data: Dict) -> Dict:
|
||||||
user = data["userId"]
|
user = data["userId"]
|
||||||
if user >= 200000000000000: # Account for guest play
|
if user >= 200000000000000: # Account for guest play
|
||||||
user = None
|
user = None
|
||||||
@ -309,10 +309,10 @@ class OngekiBase:
|
|||||||
|
|
||||||
return {"returnCode": 1, "apiName": "UpsertUserGplogApi"}
|
return {"returnCode": 1, "apiName": "UpsertUserGplogApi"}
|
||||||
|
|
||||||
def handle_extend_lock_time_api_request(self, data: Dict) -> Dict:
|
async def handle_extend_lock_time_api_request(self, data: Dict) -> Dict:
|
||||||
return {"returnCode": 1, "apiName": "ExtendLockTimeApi"}
|
return {"returnCode": 1, "apiName": "ExtendLockTimeApi"}
|
||||||
|
|
||||||
def handle_get_game_event_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_event_api_request(self, data: Dict) -> Dict:
|
||||||
evts = self.data.static.get_enabled_events(self.version)
|
evts = self.data.static.get_enabled_events(self.version)
|
||||||
|
|
||||||
if evts is None:
|
if evts is None:
|
||||||
@ -342,7 +342,7 @@ class OngekiBase:
|
|||||||
"gameEventList": evt_list,
|
"gameEventList": evt_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_game_id_list_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_id_list_api_request(self, data: Dict) -> Dict:
|
||||||
game_idlist: List[str, Any] = [] # 1 to 230 & 8000 to 8050
|
game_idlist: List[str, Any] = [] # 1 to 230 & 8000 to 8050
|
||||||
|
|
||||||
if data["type"] == 1:
|
if data["type"] == 1:
|
||||||
@ -362,10 +362,10 @@ class OngekiBase:
|
|||||||
"gameIdlistList": game_idlist,
|
"gameIdlistList": game_idlist,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_region_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_region_api_request(self, data: Dict) -> Dict:
|
||||||
return {"userId": data["userId"], "length": 0, "userRegionList": []}
|
return {"userId": data["userId"], "length": 0, "userRegionList": []}
|
||||||
|
|
||||||
def handle_get_user_preview_api_request(self, data: Dict) -> Dict:
|
async 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:
|
if profile is None:
|
||||||
@ -417,7 +417,7 @@ class OngekiBase:
|
|||||||
"isWarningConfirmed": True,
|
"isWarningConfirmed": True,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_tech_count_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_tech_count_api_request(self, data: Dict) -> Dict:
|
||||||
"""
|
"""
|
||||||
Gets the number of AB and ABPs a player has per-difficulty (7, 7+, 8, etc)
|
Gets the number of AB and ABPs a player has per-difficulty (7, 7+, 8, etc)
|
||||||
The game sends this in upsert so we don't have to calculate it all out thankfully
|
The game sends this in upsert so we don't have to calculate it all out thankfully
|
||||||
@ -436,7 +436,7 @@ class OngekiBase:
|
|||||||
"userTechCountList": userTechCountList,
|
"userTechCountList": userTechCountList,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_tech_event_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_tech_event_api_request(self, data: Dict) -> Dict:
|
||||||
user_tech_event_list = self.data.item.get_tech_event(self.version, data["userId"])
|
user_tech_event_list = self.data.item.get_tech_event(self.version, data["userId"])
|
||||||
if user_tech_event_list is None:
|
if user_tech_event_list is None:
|
||||||
return {}
|
return {}
|
||||||
@ -455,7 +455,7 @@ class OngekiBase:
|
|||||||
"userTechEventList": tech_evt,
|
"userTechEventList": tech_evt,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_tech_event_ranking_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_tech_event_ranking_api_request(self, data: Dict) -> Dict:
|
||||||
user_tech_event_ranks = self.data.item.get_tech_event_ranking(self.version, data["userId"])
|
user_tech_event_ranks = self.data.item.get_tech_event_ranking(self.version, data["userId"])
|
||||||
if user_tech_event_ranks is None:
|
if user_tech_event_ranks is None:
|
||||||
return {
|
return {
|
||||||
@ -481,7 +481,7 @@ class OngekiBase:
|
|||||||
"userTechEventRankingList": evt_ranking,
|
"userTechEventRankingList": evt_ranking,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_kop_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_kop_api_request(self, data: Dict) -> Dict:
|
||||||
kop_list = self.data.profile.get_kop(data["userId"])
|
kop_list = self.data.profile.get_kop(data["userId"])
|
||||||
if kop_list is None:
|
if kop_list is None:
|
||||||
return {}
|
return {}
|
||||||
@ -496,7 +496,7 @@ class OngekiBase:
|
|||||||
"userKopList": kop_list,
|
"userKopList": kop_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_music_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_music_api_request(self, data: Dict) -> Dict:
|
||||||
song_list = self.util_generate_music_list(data["userId"])
|
song_list = self.util_generate_music_list(data["userId"])
|
||||||
max_ct = data["maxCount"]
|
max_ct = data["maxCount"]
|
||||||
next_idx = data["nextIndex"]
|
next_idx = data["nextIndex"]
|
||||||
@ -516,7 +516,7 @@ class OngekiBase:
|
|||||||
"userMusicList": song_list[start_idx:end_idx],
|
"userMusicList": song_list[start_idx:end_idx],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_item_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_item_api_request(self, data: Dict) -> Dict:
|
||||||
kind = data["nextIndex"] / 10000000000
|
kind = data["nextIndex"] / 10000000000
|
||||||
p = self.data.item.get_items(data["userId"], kind)
|
p = self.data.item.get_items(data["userId"], kind)
|
||||||
|
|
||||||
@ -552,7 +552,7 @@ class OngekiBase:
|
|||||||
"userItemList": items,
|
"userItemList": items,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_option_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_option_api_request(self, data: Dict) -> Dict:
|
||||||
o = self.data.profile.get_profile_options(data["userId"])
|
o = self.data.profile.get_profile_options(data["userId"])
|
||||||
if o is None:
|
if o is None:
|
||||||
return {}
|
return {}
|
||||||
@ -566,7 +566,7 @@ class OngekiBase:
|
|||||||
|
|
||||||
return {"userId": data["userId"], "userOption": user_opts}
|
return {"userId": data["userId"], "userOption": user_opts}
|
||||||
|
|
||||||
def handle_get_user_data_api_request(self, data: Dict) -> Dict:
|
async 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:
|
if p is None:
|
||||||
return {}
|
return {}
|
||||||
@ -594,7 +594,7 @@ class OngekiBase:
|
|||||||
|
|
||||||
return {"userId": data["userId"], "userData": user_data}
|
return {"userId": data["userId"], "userData": user_data}
|
||||||
|
|
||||||
def handle_get_user_event_ranking_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_event_ranking_api_request(self, data: Dict) -> Dict:
|
||||||
user_event_ranking_list = self.data.item.get_ranking_event_ranks(self.version, data["userId"])
|
user_event_ranking_list = self.data.item.get_ranking_event_ranks(self.version, data["userId"])
|
||||||
if user_event_ranking_list is None:
|
if user_event_ranking_list is None:
|
||||||
return {}
|
return {}
|
||||||
@ -617,7 +617,7 @@ class OngekiBase:
|
|||||||
"userEventRankingList": prep_event_ranking,
|
"userEventRankingList": prep_event_ranking,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_login_bonus_api_request(self, data: Dict) -> Dict:
|
||||||
user_login_bonus_list = self.data.item.get_login_bonuses(data["userId"])
|
user_login_bonus_list = self.data.item.get_login_bonuses(data["userId"])
|
||||||
if user_login_bonus_list is None:
|
if user_login_bonus_list is None:
|
||||||
return {}
|
return {}
|
||||||
@ -635,7 +635,7 @@ class OngekiBase:
|
|||||||
"userLoginBonusList": login_bonuses,
|
"userLoginBonusList": login_bonuses,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_bp_base_request(self, data: Dict) -> Dict:
|
async def handle_get_user_bp_base_request(self, data: Dict) -> Dict:
|
||||||
p = self.data.profile.get_profile(
|
p = self.data.profile.get_profile(
|
||||||
self.game, self.version, user_id=data["userId"]
|
self.game, self.version, user_id=data["userId"]
|
||||||
)
|
)
|
||||||
@ -648,7 +648,7 @@ class OngekiBase:
|
|||||||
"userBpBaseList": profile["userBpBaseList"],
|
"userBpBaseList": profile["userBpBaseList"],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_recent_rating_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_recent_rating_api_request(self, data: Dict) -> Dict:
|
||||||
recent_rating = self.data.profile.get_profile_recent_rating(data["userId"])
|
recent_rating = self.data.profile.get_profile_recent_rating(data["userId"])
|
||||||
if recent_rating is None:
|
if recent_rating is None:
|
||||||
return {
|
return {
|
||||||
@ -665,7 +665,7 @@ class OngekiBase:
|
|||||||
"userRecentRatingList": userRecentRatingList,
|
"userRecentRatingList": userRecentRatingList,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_activity_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_activity_api_request(self, data: Dict) -> Dict:
|
||||||
activity = self.data.profile.get_profile_activity(data["userId"], data["kind"])
|
activity = self.data.profile.get_profile_activity(data["userId"], data["kind"])
|
||||||
if activity is None:
|
if activity is None:
|
||||||
return {}
|
return {}
|
||||||
@ -692,7 +692,7 @@ class OngekiBase:
|
|||||||
"userActivityList": user_activity,
|
"userActivityList": user_activity,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_story_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_story_api_request(self, data: Dict) -> Dict:
|
||||||
user_stories = self.data.item.get_stories(data["userId"])
|
user_stories = self.data.item.get_stories(data["userId"])
|
||||||
if user_stories is None:
|
if user_stories is None:
|
||||||
return {}
|
return {}
|
||||||
@ -710,7 +710,7 @@ class OngekiBase:
|
|||||||
"userStoryList": story_list,
|
"userStoryList": story_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_chapter_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_chapter_api_request(self, data: Dict) -> Dict:
|
||||||
user_chapters = self.data.item.get_chapters(data["userId"])
|
user_chapters = self.data.item.get_chapters(data["userId"])
|
||||||
if user_chapters is None:
|
if user_chapters is None:
|
||||||
return {}
|
return {}
|
||||||
@ -728,14 +728,14 @@ class OngekiBase:
|
|||||||
"userChapterList": chapter_list,
|
"userChapterList": chapter_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_training_room_by_key_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_training_room_by_key_api_request(self, data: Dict) -> Dict:
|
||||||
return {
|
return {
|
||||||
"userId": data["userId"],
|
"userId": data["userId"],
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"userTrainingRoomList": [],
|
"userTrainingRoomList": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_character_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_character_api_request(self, data: Dict) -> Dict:
|
||||||
user_characters = self.data.item.get_characters(data["userId"])
|
user_characters = self.data.item.get_characters(data["userId"])
|
||||||
if user_characters is None:
|
if user_characters is None:
|
||||||
return {}
|
return {}
|
||||||
@ -753,7 +753,7 @@ class OngekiBase:
|
|||||||
"userCharacterList": character_list,
|
"userCharacterList": character_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_card_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_card_api_request(self, data: Dict) -> Dict:
|
||||||
user_cards = self.data.item.get_cards(data["userId"])
|
user_cards = self.data.item.get_cards(data["userId"])
|
||||||
if user_cards is None:
|
if user_cards is None:
|
||||||
return {}
|
return {}
|
||||||
@ -771,7 +771,7 @@ class OngekiBase:
|
|||||||
"userCardList": card_list,
|
"userCardList": card_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_deck_by_key_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_deck_by_key_api_request(self, data: Dict) -> Dict:
|
||||||
# Auth key doesn't matter, it just wants all the decks
|
# Auth key doesn't matter, it just wants all the decks
|
||||||
decks = self.data.item.get_decks(data["userId"])
|
decks = self.data.item.get_decks(data["userId"])
|
||||||
if decks is None:
|
if decks is None:
|
||||||
@ -790,7 +790,7 @@ class OngekiBase:
|
|||||||
"userDeckList": deck_list,
|
"userDeckList": deck_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_trade_item_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_trade_item_api_request(self, data: Dict) -> Dict:
|
||||||
user_trade_items = self.data.item.get_trade_items(data["userId"])
|
user_trade_items = self.data.item.get_trade_items(data["userId"])
|
||||||
if user_trade_items is None:
|
if user_trade_items is None:
|
||||||
return {}
|
return {}
|
||||||
@ -808,7 +808,7 @@ class OngekiBase:
|
|||||||
"userTradeItemList": trade_item_list,
|
"userTradeItemList": trade_item_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_scenario_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_scenario_api_request(self, data: Dict) -> Dict:
|
||||||
user_scenerio = self.data.item.get_scenerios(data["userId"])
|
user_scenerio = self.data.item.get_scenerios(data["userId"])
|
||||||
if user_scenerio is None:
|
if user_scenerio is None:
|
||||||
return {}
|
return {}
|
||||||
@ -826,7 +826,7 @@ class OngekiBase:
|
|||||||
"userScenarioList": scenerio_list,
|
"userScenarioList": scenerio_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_ratinglog_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_ratinglog_api_request(self, data: Dict) -> Dict:
|
||||||
rating_log = self.data.profile.get_profile_rating_log(data["userId"])
|
rating_log = self.data.profile.get_profile_rating_log(data["userId"])
|
||||||
if rating_log is None:
|
if rating_log is None:
|
||||||
return {}
|
return {}
|
||||||
@ -844,7 +844,7 @@ class OngekiBase:
|
|||||||
"userRatinglogList": userRatinglogList,
|
"userRatinglogList": userRatinglogList,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_mission_point_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_mission_point_api_request(self, data: Dict) -> Dict:
|
||||||
user_mission_point_list = self.data.item.get_mission_points(self.version, data["userId"])
|
user_mission_point_list = self.data.item.get_mission_points(self.version, data["userId"])
|
||||||
if user_mission_point_list is None:
|
if user_mission_point_list is None:
|
||||||
return {}
|
return {}
|
||||||
@ -864,7 +864,7 @@ class OngekiBase:
|
|||||||
"userMissionPointList": mission_point_list,
|
"userMissionPointList": mission_point_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_event_point_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_event_point_api_request(self, data: Dict) -> Dict:
|
||||||
user_event_point_list = self.data.item.get_event_points(data["userId"])
|
user_event_point_list = self.data.item.get_event_points(data["userId"])
|
||||||
if user_event_point_list is None:
|
if user_event_point_list is None:
|
||||||
return {}
|
return {}
|
||||||
@ -886,7 +886,7 @@ class OngekiBase:
|
|||||||
"userEventPointList": event_point_list,
|
"userEventPointList": event_point_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_music_item_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_music_item_api_request(self, data: Dict) -> Dict:
|
||||||
user_music_item_list = self.data.item.get_music_items(data["userId"])
|
user_music_item_list = self.data.item.get_music_items(data["userId"])
|
||||||
if user_music_item_list is None:
|
if user_music_item_list is None:
|
||||||
return {}
|
return {}
|
||||||
@ -904,7 +904,7 @@ class OngekiBase:
|
|||||||
"userMusicItemList": music_item_list,
|
"userMusicItemList": music_item_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_event_music_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_event_music_api_request(self, data: Dict) -> Dict:
|
||||||
user_evt_music_list = self.data.item.get_event_music(data["userId"])
|
user_evt_music_list = self.data.item.get_event_music(data["userId"])
|
||||||
if user_evt_music_list is None:
|
if user_evt_music_list is None:
|
||||||
return {}
|
return {}
|
||||||
@ -922,7 +922,7 @@ class OngekiBase:
|
|||||||
"userEventMusicList": evt_music_list,
|
"userEventMusicList": evt_music_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_boss_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_boss_api_request(self, data: Dict) -> Dict:
|
||||||
p = self.data.item.get_bosses(data["userId"])
|
p = self.data.item.get_bosses(data["userId"])
|
||||||
if p is None:
|
if p is None:
|
||||||
return {}
|
return {}
|
||||||
@ -940,7 +940,7 @@ class OngekiBase:
|
|||||||
"userBossList": boss_list,
|
"userBossList": boss_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_upsert_user_all_api_request(self, data: Dict) -> Dict:
|
async def handle_upsert_user_all_api_request(self, data: Dict) -> Dict:
|
||||||
upsert = data["upsertUserAll"]
|
upsert = data["upsertUserAll"]
|
||||||
user_id = data["userId"]
|
user_id = data["userId"]
|
||||||
|
|
||||||
@ -1070,7 +1070,7 @@ class OngekiBase:
|
|||||||
|
|
||||||
return {"returnCode": 1, "apiName": "upsertUserAll"}
|
return {"returnCode": 1, "apiName": "upsertUserAll"}
|
||||||
|
|
||||||
def handle_get_user_rival_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_rival_api_request(self, data: Dict) -> Dict:
|
||||||
"""
|
"""
|
||||||
Added in Bright
|
Added in Bright
|
||||||
"""
|
"""
|
||||||
@ -1094,7 +1094,7 @@ class OngekiBase:
|
|||||||
"userRivalList": rival_list,
|
"userRivalList": rival_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_rival_data_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_rival_data_api_request(self, data: Dict) -> Dict:
|
||||||
"""
|
"""
|
||||||
Added in Bright
|
Added in Bright
|
||||||
"""
|
"""
|
||||||
@ -1112,7 +1112,7 @@ class OngekiBase:
|
|||||||
"userRivalDataList": rivals,
|
"userRivalDataList": rivals,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_rival_music_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_rival_music_api_request(self, data: Dict) -> Dict:
|
||||||
"""
|
"""
|
||||||
Added in Bright
|
Added in Bright
|
||||||
"""
|
"""
|
||||||
|
@ -15,13 +15,13 @@ class OngekiBright(OngekiBase):
|
|||||||
super().__init__(core_cfg, game_cfg)
|
super().__init__(core_cfg, game_cfg)
|
||||||
self.version = OngekiConstants.VER_ONGEKI_BRIGHT
|
self.version = OngekiConstants.VER_ONGEKI_BRIGHT
|
||||||
|
|
||||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||||
ret = super().handle_get_game_setting_api_request(data)
|
ret = await super().handle_get_game_setting_api_request(data)
|
||||||
ret["gameSetting"]["dataVersion"] = "1.30.00"
|
ret["gameSetting"]["dataVersion"] = "1.30.00"
|
||||||
ret["gameSetting"]["onlineDataVersion"] = "1.30.00"
|
ret["gameSetting"]["onlineDataVersion"] = "1.30.00"
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict:
|
||||||
# check for a bright profile
|
# check for a bright profile
|
||||||
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:
|
if p is None:
|
||||||
@ -55,13 +55,13 @@ class OngekiBright(OngekiBase):
|
|||||||
|
|
||||||
return {"userId": data["userId"], "userData": user_data}
|
return {"userId": data["userId"], "userData": user_data}
|
||||||
|
|
||||||
def handle_printer_login_api_request(self, data: Dict):
|
async def handle_printer_login_api_request(self, data: Dict):
|
||||||
return {"returnCode": 1}
|
return {"returnCode": 1}
|
||||||
|
|
||||||
def handle_printer_logout_api_request(self, data: Dict):
|
async def handle_printer_logout_api_request(self, data: Dict):
|
||||||
return {"returnCode": 1}
|
return {"returnCode": 1}
|
||||||
|
|
||||||
def handle_cm_get_user_card_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_get_user_card_api_request(self, data: Dict) -> Dict:
|
||||||
user_cards = self.data.item.get_cards(data["userId"])
|
user_cards = self.data.item.get_cards(data["userId"])
|
||||||
if user_cards is None:
|
if user_cards is None:
|
||||||
return {}
|
return {}
|
||||||
@ -90,7 +90,7 @@ class OngekiBright(OngekiBase):
|
|||||||
"userCardList": card_list[start_idx:end_idx],
|
"userCardList": card_list[start_idx:end_idx],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_cm_get_user_character_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_get_user_character_api_request(self, data: Dict) -> Dict:
|
||||||
user_characters = self.data.item.get_characters(data["userId"])
|
user_characters = self.data.item.get_characters(data["userId"])
|
||||||
if user_characters is None:
|
if user_characters is None:
|
||||||
return {
|
return {
|
||||||
@ -124,7 +124,7 @@ class OngekiBright(OngekiBase):
|
|||||||
"userCharacterList": character_list[start_idx:end_idx],
|
"userCharacterList": character_list[start_idx:end_idx],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_user_gacha_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_gacha_api_request(self, data: Dict) -> Dict:
|
||||||
user_gachas = self.data.item.get_user_gachas(data["userId"])
|
user_gachas = self.data.item.get_user_gachas(data["userId"])
|
||||||
if user_gachas is None:
|
if user_gachas is None:
|
||||||
return {"userId": data["userId"], "length": 0, "userGachaList": []}
|
return {"userId": data["userId"], "length": 0, "userGachaList": []}
|
||||||
@ -143,10 +143,10 @@ class OngekiBright(OngekiBase):
|
|||||||
"userGachaList": user_gacha_list,
|
"userGachaList": user_gacha_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_cm_get_user_item_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_get_user_item_api_request(self, data: Dict) -> Dict:
|
||||||
return self.handle_get_user_item_api_request(data)
|
return self.handle_get_user_item_api_request(data)
|
||||||
|
|
||||||
def handle_cm_get_user_gacha_supply_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_get_user_gacha_supply_api_request(self, data: Dict) -> Dict:
|
||||||
# not used for now? not sure what it even does
|
# not used for now? not sure what it even does
|
||||||
user_gacha_supplies = self.data.item.get_user_gacha_supplies(data["userId"])
|
user_gacha_supplies = self.data.item.get_user_gacha_supplies(data["userId"])
|
||||||
if user_gacha_supplies is None:
|
if user_gacha_supplies is None:
|
||||||
@ -160,7 +160,7 @@ class OngekiBright(OngekiBase):
|
|||||||
"supplyCardList": supply_list,
|
"supplyCardList": supply_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_game_gacha_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_gacha_api_request(self, data: Dict) -> Dict:
|
||||||
"""
|
"""
|
||||||
returns all current active banners (gachas)
|
returns all current active banners (gachas)
|
||||||
"Select Gacha" requires maxSelectPoint set and isCeiling set to 1
|
"Select Gacha" requires maxSelectPoint set and isCeiling set to 1
|
||||||
@ -207,7 +207,7 @@ class OngekiBright(OngekiBase):
|
|||||||
"registIdList": [],
|
"registIdList": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_roll_gacha_api_request(self, data: Dict) -> Dict:
|
async def handle_roll_gacha_api_request(self, data: Dict) -> Dict:
|
||||||
"""
|
"""
|
||||||
Handle a gacha roll API request
|
Handle a gacha roll API request
|
||||||
"""
|
"""
|
||||||
@ -323,7 +323,7 @@ class OngekiBright(OngekiBase):
|
|||||||
"gameGachaCardList": game_gacha_card_list,
|
"gameGachaCardList": game_gacha_card_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_cm_upsert_user_gacha_api_request(self, data: Dict):
|
async def handle_cm_upsert_user_gacha_api_request(self, data: Dict):
|
||||||
upsert = data["cmUpsertUserGacha"]
|
upsert = data["cmUpsertUserGacha"]
|
||||||
user_id = data["userId"]
|
user_id = data["userId"]
|
||||||
|
|
||||||
@ -405,7 +405,7 @@ class OngekiBright(OngekiBase):
|
|||||||
|
|
||||||
return {"returnCode": 1, "apiName": "CMUpsertUserGachaApi"}
|
return {"returnCode": 1, "apiName": "CMUpsertUserGachaApi"}
|
||||||
|
|
||||||
def handle_cm_upsert_user_select_gacha_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_upsert_user_select_gacha_api_request(self, data: Dict) -> Dict:
|
||||||
upsert = data["cmUpsertUserSelectGacha"]
|
upsert = data["cmUpsertUserSelectGacha"]
|
||||||
user_id = data["userId"]
|
user_id = data["userId"]
|
||||||
|
|
||||||
@ -442,7 +442,7 @@ class OngekiBright(OngekiBase):
|
|||||||
|
|
||||||
return {"returnCode": 1, "apiName": "cmUpsertUserSelectGacha"}
|
return {"returnCode": 1, "apiName": "cmUpsertUserSelectGacha"}
|
||||||
|
|
||||||
def handle_get_game_gacha_card_by_id_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_gacha_card_by_id_api_request(self, data: Dict) -> Dict:
|
||||||
game_gacha_cards = self.data.static.get_gacha_cards(data["gachaId"])
|
game_gacha_cards = self.data.static.get_gacha_cards(data["gachaId"])
|
||||||
if game_gacha_cards == []:
|
if game_gacha_cards == []:
|
||||||
# fallback to be at least able to select that gacha
|
# fallback to be at least able to select that gacha
|
||||||
@ -522,7 +522,7 @@ class OngekiBright(OngekiBase):
|
|||||||
"ssrBookCalcList": [],
|
"ssrBookCalcList": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_game_theater_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_theater_api_request(self, data: Dict) -> Dict:
|
||||||
"""
|
"""
|
||||||
shows a banner after every print, not sure what its used for
|
shows a banner after every print, not sure what its used for
|
||||||
"""
|
"""
|
||||||
@ -548,7 +548,7 @@ class OngekiBright(OngekiBase):
|
|||||||
|
|
||||||
return {"length": 0, "gameTheaterList": [], "registIdList": []}
|
return {"length": 0, "gameTheaterList": [], "registIdList": []}
|
||||||
|
|
||||||
def handle_cm_upsert_user_print_playlog_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_upsert_user_print_playlog_api_request(self, data: Dict) -> Dict:
|
||||||
return {
|
return {
|
||||||
"returnCode": 1,
|
"returnCode": 1,
|
||||||
"orderId": 0,
|
"orderId": 0,
|
||||||
@ -556,7 +556,7 @@ class OngekiBright(OngekiBase):
|
|||||||
"apiName": "CMUpsertUserPrintPlaylogApi",
|
"apiName": "CMUpsertUserPrintPlaylogApi",
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_cm_upsert_user_printlog_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_upsert_user_printlog_api_request(self, data: Dict) -> Dict:
|
||||||
return {
|
return {
|
||||||
"returnCode": 1,
|
"returnCode": 1,
|
||||||
"orderId": 0,
|
"orderId": 0,
|
||||||
@ -564,7 +564,7 @@ class OngekiBright(OngekiBase):
|
|||||||
"apiName": "CMUpsertUserPrintlogApi",
|
"apiName": "CMUpsertUserPrintlogApi",
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_cm_upsert_user_print_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_upsert_user_print_api_request(self, data: Dict) -> Dict:
|
||||||
user_print_detail = data["userPrintDetail"]
|
user_print_detail = data["userPrintDetail"]
|
||||||
|
|
||||||
# generate random serial id
|
# generate random serial id
|
||||||
@ -589,7 +589,7 @@ class OngekiBright(OngekiBase):
|
|||||||
"apiName": "CMUpsertUserPrintApi",
|
"apiName": "CMUpsertUserPrintApi",
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_cm_upsert_user_all_api_request(self, data: Dict) -> Dict:
|
async def handle_cm_upsert_user_all_api_request(self, data: Dict) -> Dict:
|
||||||
upsert = data["cmUpsertUserAll"]
|
upsert = data["cmUpsertUserAll"]
|
||||||
user_id = data["userId"]
|
user_id = data["userId"]
|
||||||
|
|
||||||
|
@ -15,8 +15,8 @@ class OngekiBrightMemory(OngekiBright):
|
|||||||
super().__init__(core_cfg, game_cfg)
|
super().__init__(core_cfg, game_cfg)
|
||||||
self.version = OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY
|
self.version = OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY
|
||||||
|
|
||||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||||
ret = super().handle_get_game_setting_api_request(data)
|
ret = await super().handle_get_game_setting_api_request(data)
|
||||||
ret["gameSetting"]["dataVersion"] = "1.35.00"
|
ret["gameSetting"]["dataVersion"] = "1.35.00"
|
||||||
ret["gameSetting"]["onlineDataVersion"] = "1.35.00"
|
ret["gameSetting"]["onlineDataVersion"] = "1.35.00"
|
||||||
ret["gameSetting"]["maxCountCharacter"] = 50
|
ret["gameSetting"]["maxCountCharacter"] = 50
|
||||||
@ -27,7 +27,7 @@ class OngekiBrightMemory(OngekiBright):
|
|||||||
ret["gameSetting"]["maxCountRivalMusic"] = 300
|
ret["gameSetting"]["maxCountRivalMusic"] = 300
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def handle_get_user_memory_chapter_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_memory_chapter_api_request(self, data: Dict) -> Dict:
|
||||||
memories = self.data.item.get_memorychapters(data["userId"])
|
memories = self.data.item.get_memorychapters(data["userId"])
|
||||||
if not memories:
|
if not memories:
|
||||||
return {
|
return {
|
||||||
@ -134,5 +134,5 @@ class OngekiBrightMemory(OngekiBright):
|
|||||||
"userMemoryChapterList": memory_chp,
|
"userMemoryChapterList": memory_chp,
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_get_game_music_release_state_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_music_release_state_api_request(self, data: Dict) -> Dict:
|
||||||
return {"techScore": 0, "cardNum": 0}
|
return {"techScore": 0, "cardNum": 0}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import yaml
|
import yaml
|
||||||
import jinja2
|
import jinja2
|
||||||
from twisted.web.http import Request
|
from starlette.requests import Request
|
||||||
from os import path
|
from os import path
|
||||||
from twisted.web.util import redirectTo
|
from twisted.web.util import redirectTo
|
||||||
from twisted.web.server import Session
|
from twisted.web.server import Session
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
from twisted.web.http import Request
|
from starlette.requests import Request
|
||||||
|
from starlette.routing import Route
|
||||||
|
from starlette.responses import Response
|
||||||
import json
|
import json
|
||||||
import inflection
|
import inflection
|
||||||
import yaml
|
import yaml
|
||||||
@ -120,35 +122,28 @@ class OngekiServlet(BaseServlet):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
|
def get_routes(self) -> List[Route]:
|
||||||
return (
|
return [
|
||||||
[],
|
Route("/SDDT/{version:int}/{endpoint:str}", self.render_POST, methods=['POST'])
|
||||||
[("render_POST", "/SDDT/{version}/{endpoint}", {})]
|
]
|
||||||
)
|
|
||||||
|
|
||||||
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
|
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
|
||||||
title_port_int = Utils.get_title_port(self.core_cfg)
|
title_port_int = Utils.get_title_port(self.core_cfg)
|
||||||
title_port_ssl_int = Utils.get_title_port_ssl(self.core_cfg)
|
|
||||||
proto = "https" if self.game_cfg.server.use_https and game_ver >= 120 else "http"
|
proto = "https" if self.game_cfg.server.use_https and game_ver >= 120 else "http"
|
||||||
|
t_port = f":{title_port_int}" if title_port_int and not self.core_cfg.server.is_using_proxy else ""
|
||||||
if proto == "https":
|
|
||||||
t_port = f":{title_port_ssl_int}" if title_port_ssl_int and not self.core_cfg.server.is_using_proxy else ""
|
|
||||||
|
|
||||||
else:
|
|
||||||
t_port = f":{title_port_int}" if title_port_int and not self.core_cfg.server.is_using_proxy else ""
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
f"{proto}://{self.core_cfg.title.hostname}{t_port}/{game_code}/{game_ver}/",
|
f"{proto}://{self.core_cfg.server.hostname}{t_port}/{game_code}/{game_ver}/",
|
||||||
f"{self.core_cfg.title.hostname}{t_port}/",
|
f"{self.core_cfg.server.hostname}{t_port}/",
|
||||||
)
|
)
|
||||||
|
|
||||||
def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
async def render_POST(self, request: Request) -> bytes:
|
||||||
endpoint = matchers['endpoint']
|
endpoint: str = request.path_params.get('endpoint', '')
|
||||||
version = int(matchers['version'])
|
version: int = request.path_params.get('version', 0)
|
||||||
if endpoint.lower() == "ping":
|
if endpoint.lower() == "ping":
|
||||||
return zlib.compress(b'{"returnCode": 1}')
|
return Response(zlib.compress(b'{"returnCode": 1}'))
|
||||||
|
|
||||||
req_raw = request.content.getvalue()
|
req_raw = await request.body()
|
||||||
encrtped = False
|
encrtped = False
|
||||||
internal_ver = 0
|
internal_ver = 0
|
||||||
client_ip = Utils.get_ip_addr(request)
|
client_ip = Utils.get_ip_addr(request)
|
||||||
@ -178,13 +173,13 @@ class OngekiServlet(BaseServlet):
|
|||||||
self.logger.error(
|
self.logger.error(
|
||||||
f"v{version} does not support encryption or no keys entered"
|
f"v{version} does not support encryption or no keys entered"
|
||||||
)
|
)
|
||||||
return zlib.compress(b'{"stat": "0"}')
|
return Response(zlib.compress(b'{"stat": "0"}'))
|
||||||
|
|
||||||
elif endpoint.lower() not in self.hash_table[internal_ver]:
|
elif endpoint.lower() not in self.hash_table[internal_ver]:
|
||||||
self.logger.error(
|
self.logger.error(
|
||||||
f"No hash found for v{version} endpoint {endpoint}"
|
f"No hash found for v{version} endpoint {endpoint}"
|
||||||
)
|
)
|
||||||
return zlib.compress(b'{"stat": "0"}')
|
return Response(zlib.compress(b'{"stat": "0"}'))
|
||||||
|
|
||||||
endpoint = self.hash_table[internal_ver][endpoint.lower()]
|
endpoint = self.hash_table[internal_ver][endpoint.lower()]
|
||||||
|
|
||||||
@ -201,7 +196,7 @@ class OngekiServlet(BaseServlet):
|
|||||||
self.logger.error(
|
self.logger.error(
|
||||||
f"Failed to decrypt v{version} request to {endpoint} -> {e}"
|
f"Failed to decrypt v{version} request to {endpoint} -> {e}"
|
||||||
)
|
)
|
||||||
return zlib.compress(b'{"stat": "0"}')
|
return Response(zlib.compress(b'{"stat": "0"}'))
|
||||||
|
|
||||||
encrtped = True
|
encrtped = True
|
||||||
|
|
||||||
@ -213,7 +208,7 @@ class OngekiServlet(BaseServlet):
|
|||||||
self.logger.error(
|
self.logger.error(
|
||||||
f"Unencrypted v{version} {endpoint} request, but config is set to encrypted only: {req_raw}"
|
f"Unencrypted v{version} {endpoint} request, but config is set to encrypted only: {req_raw}"
|
||||||
)
|
)
|
||||||
return zlib.compress(b'{"stat": "0"}')
|
return Response(zlib.compress(b'{"stat": "0"}'))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
unzip = zlib.decompress(req_raw)
|
unzip = zlib.decompress(req_raw)
|
||||||
@ -222,7 +217,7 @@ class OngekiServlet(BaseServlet):
|
|||||||
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 Response(zlib.compress(b'{"stat": "0"}'))
|
||||||
|
|
||||||
req_data = json.loads(unzip)
|
req_data = json.loads(unzip)
|
||||||
|
|
||||||
@ -235,15 +230,15 @@ class OngekiServlet(BaseServlet):
|
|||||||
|
|
||||||
if not hasattr(self.versions[internal_ver], func_to_find):
|
if not hasattr(self.versions[internal_ver], func_to_find):
|
||||||
self.logger.warning(f"Unhandled v{version} request {endpoint}")
|
self.logger.warning(f"Unhandled v{version} request {endpoint}")
|
||||||
return zlib.compress(b'{"returnCode": 1}')
|
return Response(zlib.compress(b'{"returnCode": 1}'))
|
||||||
|
|
||||||
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 = await handler(req_data)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
|
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
|
||||||
return zlib.compress(b'{"stat": "0"}')
|
return Response(zlib.compress(b'{"stat": "0"}'))
|
||||||
|
|
||||||
if resp == None:
|
if resp == None:
|
||||||
resp = {"returnCode": 1}
|
resp = {"returnCode": 1}
|
||||||
@ -253,7 +248,7 @@ class OngekiServlet(BaseServlet):
|
|||||||
zipped = zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
|
zipped = zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
|
||||||
|
|
||||||
if not encrtped or version < 120:
|
if not encrtped or version < 120:
|
||||||
return zipped
|
return Response(zipped)
|
||||||
|
|
||||||
padded = pad(zipped, 16)
|
padded = pad(zipped, 16)
|
||||||
|
|
||||||
@ -263,4 +258,4 @@ class OngekiServlet(BaseServlet):
|
|||||||
bytes.fromhex(self.game_cfg.crypto.keys[internal_ver][1]),
|
bytes.fromhex(self.game_cfg.crypto.keys[internal_ver][1]),
|
||||||
)
|
)
|
||||||
|
|
||||||
return crypt.encrypt(padded)
|
return Response(crypt.encrypt(padded))
|
@ -11,8 +11,8 @@ class OngekiPlus(OngekiBase):
|
|||||||
super().__init__(core_cfg, game_cfg)
|
super().__init__(core_cfg, game_cfg)
|
||||||
self.version = OngekiConstants.VER_ONGEKI_PLUS
|
self.version = OngekiConstants.VER_ONGEKI_PLUS
|
||||||
|
|
||||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||||
ret = super().handle_get_game_setting_api_request(data)
|
ret = await super().handle_get_game_setting_api_request(data)
|
||||||
ret["gameSetting"]["dataVersion"] = "1.05.00"
|
ret["gameSetting"]["dataVersion"] = "1.05.00"
|
||||||
ret["gameSetting"]["onlineDataVersion"] = "1.05.00"
|
ret["gameSetting"]["onlineDataVersion"] = "1.05.00"
|
||||||
return ret
|
return ret
|
||||||
|
@ -11,8 +11,8 @@ class OngekiRed(OngekiBase):
|
|||||||
super().__init__(core_cfg, game_cfg)
|
super().__init__(core_cfg, game_cfg)
|
||||||
self.version = OngekiConstants.VER_ONGEKI_RED
|
self.version = OngekiConstants.VER_ONGEKI_RED
|
||||||
|
|
||||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||||
ret = super().handle_get_game_setting_api_request(data)
|
ret = await super().handle_get_game_setting_api_request(data)
|
||||||
ret["gameSetting"]["dataVersion"] = "1.20.00"
|
ret["gameSetting"]["dataVersion"] = "1.20.00"
|
||||||
ret["gameSetting"]["onlineDataVersion"] = "1.20.00"
|
ret["gameSetting"]["onlineDataVersion"] = "1.20.00"
|
||||||
return ret
|
return ret
|
||||||
|
@ -11,8 +11,8 @@ class OngekiRedPlus(OngekiBase):
|
|||||||
super().__init__(core_cfg, game_cfg)
|
super().__init__(core_cfg, game_cfg)
|
||||||
self.version = OngekiConstants.VER_ONGEKI_RED_PLUS
|
self.version = OngekiConstants.VER_ONGEKI_RED_PLUS
|
||||||
|
|
||||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||||
ret = super().handle_get_game_setting_api_request(data)
|
ret = await super().handle_get_game_setting_api_request(data)
|
||||||
ret["gameSetting"]["dataVersion"] = "1.25.00"
|
ret["gameSetting"]["dataVersion"] = "1.25.00"
|
||||||
ret["gameSetting"]["onlineDataVersion"] = "1.25.00"
|
ret["gameSetting"]["onlineDataVersion"] = "1.25.00"
|
||||||
ret["gameSetting"]["maxCountCharacter"] = 50
|
ret["gameSetting"]["maxCountCharacter"] = 50
|
||||||
|
@ -11,8 +11,8 @@ class OngekiSummer(OngekiBase):
|
|||||||
super().__init__(core_cfg, game_cfg)
|
super().__init__(core_cfg, game_cfg)
|
||||||
self.version = OngekiConstants.VER_ONGEKI_SUMMER
|
self.version = OngekiConstants.VER_ONGEKI_SUMMER
|
||||||
|
|
||||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||||
ret = super().handle_get_game_setting_api_request(data)
|
ret = await super().handle_get_game_setting_api_request(data)
|
||||||
ret["gameSetting"]["dataVersion"] = "1.10.00"
|
ret["gameSetting"]["dataVersion"] = "1.10.00"
|
||||||
ret["gameSetting"]["onlineDataVersion"] = "1.10.00"
|
ret["gameSetting"]["onlineDataVersion"] = "1.10.00"
|
||||||
return ret
|
return ret
|
||||||
|
@ -11,8 +11,8 @@ class OngekiSummerPlus(OngekiBase):
|
|||||||
super().__init__(core_cfg, game_cfg)
|
super().__init__(core_cfg, game_cfg)
|
||||||
self.version = OngekiConstants.VER_ONGEKI_SUMMER_PLUS
|
self.version = OngekiConstants.VER_ONGEKI_SUMMER_PLUS
|
||||||
|
|
||||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||||
ret = super().handle_get_game_setting_api_request(data)
|
ret = await super().handle_get_game_setting_api_request(data)
|
||||||
ret["gameSetting"]["dataVersion"] = "1.15.00"
|
ret["gameSetting"]["dataVersion"] = "1.15.00"
|
||||||
ret["gameSetting"]["onlineDataVersion"] = "1.15.00"
|
ret["gameSetting"]["onlineDataVersion"] = "1.15.00"
|
||||||
return ret
|
return ret
|
||||||
|
@ -20,21 +20,21 @@ class PokkenBase:
|
|||||||
self.data = PokkenData(core_cfg)
|
self.data = PokkenData(core_cfg)
|
||||||
self.SUPPORT_SET_NONE = 4294967295
|
self.SUPPORT_SET_NONE = 4294967295
|
||||||
|
|
||||||
def handle_noop(self, request: Any) -> bytes:
|
async def handle_noop(self, request: Any) -> bytes:
|
||||||
res = jackal_pb2.Response()
|
res = jackal_pb2.Response()
|
||||||
res.result = 1
|
res.result = 1
|
||||||
res.type = request.type
|
res.type = request.type
|
||||||
|
|
||||||
return res.SerializeToString()
|
return res.SerializeToString()
|
||||||
|
|
||||||
def handle_ping(self, request: jackal_pb2.Request) -> bytes:
|
async def handle_ping(self, request: jackal_pb2.Request) -> bytes:
|
||||||
res = jackal_pb2.Response()
|
res = jackal_pb2.Response()
|
||||||
res.result = 1
|
res.result = 1
|
||||||
res.type = jackal_pb2.MessageType.PING
|
res.type = jackal_pb2.MessageType.PING
|
||||||
|
|
||||||
return res.SerializeToString()
|
return res.SerializeToString()
|
||||||
|
|
||||||
def handle_register_pcb(self, request: jackal_pb2.Request) -> bytes:
|
async def handle_register_pcb(self, request: jackal_pb2.Request) -> bytes:
|
||||||
res = jackal_pb2.Response()
|
res = jackal_pb2.Response()
|
||||||
res.result = 1
|
res.result = 1
|
||||||
res.type = jackal_pb2.MessageType.REGISTER_PCB
|
res.type = jackal_pb2.MessageType.REGISTER_PCB
|
||||||
@ -61,35 +61,35 @@ class PokkenBase:
|
|||||||
"logfilename": "JackalMatchingLibrary.log",
|
"logfilename": "JackalMatchingLibrary.log",
|
||||||
"biwalogfilename": "./biwa.log",
|
"biwalogfilename": "./biwa.log",
|
||||||
}
|
}
|
||||||
regist_pcb.bnp_baseuri = f"{self.core_cfg.title.hostname}/bna"
|
regist_pcb.bnp_baseuri = f"{self.core_cfg.server.hostname}/bna"
|
||||||
regist_pcb.biwa_setting = json.dumps(biwa_setting)
|
regist_pcb.biwa_setting = json.dumps(biwa_setting)
|
||||||
|
|
||||||
res.register_pcb.CopyFrom(regist_pcb)
|
res.register_pcb.CopyFrom(regist_pcb)
|
||||||
|
|
||||||
return res.SerializeToString()
|
return res.SerializeToString()
|
||||||
|
|
||||||
def handle_save_ads(self, request: jackal_pb2.Request) -> bytes:
|
async def handle_save_ads(self, request: jackal_pb2.Request) -> bytes:
|
||||||
res = jackal_pb2.Response()
|
res = jackal_pb2.Response()
|
||||||
res.result = 1
|
res.result = 1
|
||||||
res.type = jackal_pb2.MessageType.SAVE_ADS
|
res.type = jackal_pb2.MessageType.SAVE_ADS
|
||||||
|
|
||||||
return res.SerializeToString()
|
return res.SerializeToString()
|
||||||
|
|
||||||
def handle_save_client_log(self, request: jackal_pb2.Request) -> bytes:
|
async def handle_save_client_log(self, request: jackal_pb2.Request) -> bytes:
|
||||||
res = jackal_pb2.Response()
|
res = jackal_pb2.Response()
|
||||||
res.result = 1
|
res.result = 1
|
||||||
res.type = jackal_pb2.MessageType.SAVE_CLIENT_LOG
|
res.type = jackal_pb2.MessageType.SAVE_CLIENT_LOG
|
||||||
|
|
||||||
return res.SerializeToString()
|
return res.SerializeToString()
|
||||||
|
|
||||||
def handle_check_diagnosis(self, request: jackal_pb2.Request) -> bytes:
|
async def handle_check_diagnosis(self, request: jackal_pb2.Request) -> bytes:
|
||||||
res = jackal_pb2.Response()
|
res = jackal_pb2.Response()
|
||||||
res.result = 1
|
res.result = 1
|
||||||
res.type = jackal_pb2.MessageType.CHECK_DIAGNOSIS
|
res.type = jackal_pb2.MessageType.CHECK_DIAGNOSIS
|
||||||
|
|
||||||
return res.SerializeToString()
|
return res.SerializeToString()
|
||||||
|
|
||||||
def handle_load_client_settings(self, request: jackal_pb2.Request) -> bytes:
|
async def handle_load_client_settings(self, request: jackal_pb2.Request) -> bytes:
|
||||||
res = jackal_pb2.Response()
|
res = jackal_pb2.Response()
|
||||||
res.result = 1
|
res.result = 1
|
||||||
res.type = jackal_pb2.MessageType.LOAD_CLIENT_SETTINGS
|
res.type = jackal_pb2.MessageType.LOAD_CLIENT_SETTINGS
|
||||||
@ -112,7 +112,7 @@ class PokkenBase:
|
|||||||
|
|
||||||
return res.SerializeToString()
|
return res.SerializeToString()
|
||||||
|
|
||||||
def handle_load_ranking(self, request: jackal_pb2.Request) -> bytes:
|
async def handle_load_ranking(self, request: jackal_pb2.Request) -> bytes:
|
||||||
res = jackal_pb2.Response()
|
res = jackal_pb2.Response()
|
||||||
res.result = 1
|
res.result = 1
|
||||||
res.type = jackal_pb2.MessageType.LOAD_RANKING
|
res.type = jackal_pb2.MessageType.LOAD_RANKING
|
||||||
@ -126,7 +126,7 @@ class PokkenBase:
|
|||||||
res.load_ranking.CopyFrom(ranking)
|
res.load_ranking.CopyFrom(ranking)
|
||||||
return res.SerializeToString()
|
return res.SerializeToString()
|
||||||
|
|
||||||
def handle_load_user(self, request: jackal_pb2.Request) -> bytes:
|
async def handle_load_user(self, request: jackal_pb2.Request) -> bytes:
|
||||||
res = jackal_pb2.Response()
|
res = jackal_pb2.Response()
|
||||||
res.result = 1
|
res.result = 1
|
||||||
res.type = jackal_pb2.MessageType.LOAD_USER
|
res.type = jackal_pb2.MessageType.LOAD_USER
|
||||||
@ -287,13 +287,13 @@ class PokkenBase:
|
|||||||
res.load_user.CopyFrom(load_usr)
|
res.load_user.CopyFrom(load_usr)
|
||||||
return res.SerializeToString()
|
return res.SerializeToString()
|
||||||
|
|
||||||
def handle_set_bnpassid_lock(self, data: jackal_pb2.Request) -> bytes:
|
async def handle_set_bnpassid_lock(self, data: jackal_pb2.Request) -> bytes:
|
||||||
res = jackal_pb2.Response()
|
res = jackal_pb2.Response()
|
||||||
res.result = 1
|
res.result = 1
|
||||||
res.type = jackal_pb2.MessageType.SET_BNPASSID_LOCK
|
res.type = jackal_pb2.MessageType.SET_BNPASSID_LOCK
|
||||||
return res.SerializeToString()
|
return res.SerializeToString()
|
||||||
|
|
||||||
def handle_save_user(self, request: jackal_pb2.Request) -> bytes:
|
async def handle_save_user(self, request: jackal_pb2.Request) -> bytes:
|
||||||
res = jackal_pb2.Response()
|
res = jackal_pb2.Response()
|
||||||
res.result = 1
|
res.result = 1
|
||||||
res.type = jackal_pb2.MessageType.SAVE_USER
|
res.type = jackal_pb2.MessageType.SAVE_USER
|
||||||
@ -394,38 +394,31 @@ class PokkenBase:
|
|||||||
|
|
||||||
return res.SerializeToString()
|
return res.SerializeToString()
|
||||||
|
|
||||||
def handle_save_ingame_log(self, data: jackal_pb2.Request) -> bytes:
|
async def handle_save_ingame_log(self, data: jackal_pb2.Request) -> bytes:
|
||||||
res = jackal_pb2.Response()
|
res = jackal_pb2.Response()
|
||||||
res.result = 1
|
res.result = 1
|
||||||
res.type = jackal_pb2.MessageType.SAVE_INGAME_LOG
|
res.type = jackal_pb2.MessageType.SAVE_INGAME_LOG
|
||||||
return res.SerializeToString()
|
return res.SerializeToString()
|
||||||
|
|
||||||
def handle_save_charge(self, data: jackal_pb2.Request) -> bytes:
|
async def handle_save_charge(self, data: jackal_pb2.Request) -> bytes:
|
||||||
res = jackal_pb2.Response()
|
res = jackal_pb2.Response()
|
||||||
res.result = 1
|
res.result = 1
|
||||||
res.type = jackal_pb2.MessageType.SAVE_CHARGE
|
res.type = jackal_pb2.MessageType.SAVE_CHARGE
|
||||||
return res.SerializeToString()
|
return res.SerializeToString()
|
||||||
|
|
||||||
def handle_matching_noop(
|
async def handle_matching_noop(
|
||||||
self, data: Dict = {}, client_ip: str = "127.0.0.1"
|
self, data: Dict = {}, client_ip: str = "127.0.0.1"
|
||||||
) -> Dict:
|
) -> Dict:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def handle_matching_start_matching(
|
async def handle_matching_start_matching(
|
||||||
self, data: Dict = {}, client_ip: str = "127.0.0.1"
|
self, data: Dict = {}, client_ip: str = "127.0.0.1"
|
||||||
) -> Dict:
|
) -> Dict:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def handle_matching_is_matching(
|
async def handle_matching_is_matching(
|
||||||
self, data: Dict = {}, client_ip: str = "127.0.0.1"
|
self, data: Dict = {}, client_ip: str = "127.0.0.1"
|
||||||
) -> Dict:
|
) -> Dict:
|
||||||
"""
|
|
||||||
"sessionId":"12345678",
|
|
||||||
"A":{
|
|
||||||
"pcb_id": data["data"]["must"]["pcb_id"],
|
|
||||||
"gip": client_ip
|
|
||||||
},
|
|
||||||
"""
|
|
||||||
return {
|
return {
|
||||||
"data": {
|
"data": {
|
||||||
"sessionId":"12345678",
|
"sessionId":"12345678",
|
||||||
@ -437,15 +430,15 @@ class PokkenBase:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_matching_stop_matching(
|
async def handle_matching_stop_matching(
|
||||||
self, data: Dict = {}, client_ip: str = "127.0.0.1"
|
self, data: Dict = {}, client_ip: str = "127.0.0.1"
|
||||||
) -> Dict:
|
) -> Dict:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def handle_admission_noop(self, data: Dict, req_ip: str = "127.0.0.1") -> Dict:
|
async def handle_admission_noop(self, data: Dict, req_ip: str = "127.0.0.1") -> Dict:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def handle_admission_joinsession(self, data: Dict, req_ip: str = "127.0.0.1") -> Dict:
|
async def handle_admission_joinsession(self, data: Dict, req_ip: str = "127.0.0.1") -> Dict:
|
||||||
self.logger.info(f"Admission: JoinSession from {req_ip}")
|
self.logger.info(f"Admission: JoinSession from {req_ip}")
|
||||||
return {
|
return {
|
||||||
'data': {
|
'data': {
|
||||||
|
@ -3,6 +3,7 @@ from enum import Enum
|
|||||||
|
|
||||||
class PokkenConstants:
|
class PokkenConstants:
|
||||||
GAME_CODE = "SDAK"
|
GAME_CODE = "SDAK"
|
||||||
|
GAME_CDS = ["PKF1"]
|
||||||
|
|
||||||
CONFIG_NAME = "pokken.yaml"
|
CONFIG_NAME = "pokken.yaml"
|
||||||
|
|
||||||
@ -10,6 +11,12 @@ class PokkenConstants:
|
|||||||
|
|
||||||
VERSION_NAMES = "Pokken Tournament"
|
VERSION_NAMES = "Pokken Tournament"
|
||||||
|
|
||||||
|
SERIAL_IDENT = [2747]
|
||||||
|
NETID_PREFIX = ["ABGN"]
|
||||||
|
SERIAL_REGIONS = [1]
|
||||||
|
SERIAL_ROLES = [3]
|
||||||
|
SERIAL_CAB_IDENTS = [19]
|
||||||
|
|
||||||
class BATTLE_TYPE(Enum):
|
class BATTLE_TYPE(Enum):
|
||||||
TUTORIAL = 1
|
TUTORIAL = 1
|
||||||
AI = 2
|
AI = 2
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import yaml
|
import yaml
|
||||||
import jinja2
|
import jinja2
|
||||||
from twisted.web.http import Request
|
from starlette.requests import Request
|
||||||
from os import path
|
from os import path
|
||||||
from twisted.web.server import Session
|
from twisted.web.server import Session
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
from typing import Tuple, List, Dict
|
from typing import Tuple, List, Dict
|
||||||
from twisted.web.http import Request
|
from starlette.requests import Request
|
||||||
from twisted.web import resource
|
from starlette.requests import Request
|
||||||
from twisted.internet import reactor
|
from starlette.responses import Response, JSONResponse
|
||||||
import json, ast
|
from starlette.routing import Route, WebSocketRoute
|
||||||
|
from starlette.websockets import WebSocket, WebSocketState, WebSocketDisconnect
|
||||||
|
import ast
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import yaml
|
import yaml
|
||||||
import logging, coloredlogs
|
import logging, coloredlogs
|
||||||
@ -17,8 +19,6 @@ from .config import PokkenConfig
|
|||||||
from .base import PokkenBase
|
from .base import PokkenBase
|
||||||
from .const import PokkenConstants
|
from .const import PokkenConstants
|
||||||
from .proto import jackal_pb2
|
from .proto import jackal_pb2
|
||||||
from .services import PokkenAdmissionFactory
|
|
||||||
|
|
||||||
|
|
||||||
class PokkenServlet(BaseServlet):
|
class PokkenServlet(BaseServlet):
|
||||||
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
|
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
|
||||||
@ -69,47 +69,73 @@ class PokkenServlet(BaseServlet):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
|
def get_routes(self) -> List[Route]:
|
||||||
return (
|
return [
|
||||||
[],
|
Route("/pokken/", self.render_POST, methods=['POST']),
|
||||||
[
|
Route("/pokken/matching", self.handle_matching, methods=['POST']),
|
||||||
("render_POST", "/pokken/", {}),
|
WebSocketRoute("/pokken/admission", self.handle_admission)
|
||||||
("handle_matching", "/pokken/matching", {}),
|
]
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
|
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
|
||||||
if self.game_cfg.ports.game != 443:
|
|
||||||
return (
|
|
||||||
f"https://{self.game_cfg.server.hostname}:{self.game_cfg.ports.game}/pokken/",
|
|
||||||
f"{self.game_cfg.server.hostname}/pokken/",
|
|
||||||
)
|
|
||||||
return (
|
return (
|
||||||
f"https://{self.game_cfg.server.hostname}/pokken/",
|
f"https://{self.game_cfg.server.hostname}:{self.game_cfg.ports.game}/pokken/",
|
||||||
f"{self.game_cfg.server.hostname}/pokken/",
|
f"{self.game_cfg.server.hostname}:{self.game_cfg.ports.game}/pokken/",
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_mucha_info(self, core_cfg: CoreConfig, cfg_dir: str) -> Tuple[bool, str]:
|
def get_mucha_info(self, core_cfg: CoreConfig, cfg_dir: str) -> Tuple[bool, str]:
|
||||||
game_cfg = PokkenConfig()
|
if not self.game_cfg.server.enable:
|
||||||
|
return (False, [], [])
|
||||||
|
|
||||||
if path.exists(f"{cfg_dir}/{PokkenConstants.CONFIG_NAME}"):
|
return (True, PokkenConstants.GAME_CDS, PokkenConstants.NETID_PREFIX)
|
||||||
game_cfg.update(
|
|
||||||
yaml.safe_load(open(f"{cfg_dir}/{PokkenConstants.CONFIG_NAME}"))
|
async def handle_admission(self, ws: WebSocket) -> None:
|
||||||
)
|
client_ip = Utils.get_ip_addr(ws)
|
||||||
|
await ws.accept()
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
msg: Dict = await ws.receive_json()
|
||||||
|
except WebSocketDisconnect as e:
|
||||||
|
self.logger.debug(f"Client {client_ip} disconnected - {e}")
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Could not load JSON from message from {client_ip} - {e}")
|
||||||
|
if ws.client_state != WebSocketState.DISCONNECTED:
|
||||||
|
await ws.close()
|
||||||
|
break
|
||||||
|
|
||||||
|
self.logger.debug(f"Admission: Message from {client_ip}:{ws.client.port} - {msg}")
|
||||||
|
|
||||||
|
api = msg.get("api", "noop")
|
||||||
|
handler = getattr(self.base, f"handle_admission_{api.lower()}")
|
||||||
|
resp = await handler(msg, client_ip)
|
||||||
|
|
||||||
|
if resp is None:
|
||||||
|
resp = {}
|
||||||
|
|
||||||
if not game_cfg.server.enable:
|
if "type" not in resp:
|
||||||
return (False, "")
|
resp['type'] = "res"
|
||||||
|
if "data" not in resp:
|
||||||
|
resp['data'] = {}
|
||||||
|
if "api" not in resp:
|
||||||
|
resp['api'] = api
|
||||||
|
if "result" not in resp:
|
||||||
|
resp['result'] = 'true'
|
||||||
|
|
||||||
|
self.logger.debug(f"Websocket response: {resp}")
|
||||||
|
try:
|
||||||
|
await ws.send_json(resp)
|
||||||
|
except WebSocketDisconnect as e:
|
||||||
|
self.logger.debug(f"Client {client_ip} disconnected - {e}")
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Could not send JSON message to {client_ip} - {e}")
|
||||||
|
break
|
||||||
|
|
||||||
|
if ws.client_state != WebSocketState.DISCONNECTED:
|
||||||
|
await ws.close()
|
||||||
|
|
||||||
return (True, "PKF1")
|
async def render_POST(self, request: Request) -> bytes:
|
||||||
|
content = await request.body()
|
||||||
def setup(self) -> None:
|
|
||||||
if self.game_cfg.server.enable_matching:
|
|
||||||
reactor.listenTCP(
|
|
||||||
self.game_cfg.ports.admission, PokkenAdmissionFactory(self.core_cfg, self.game_cfg)
|
|
||||||
)
|
|
||||||
|
|
||||||
def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
|
||||||
content = request.content.getvalue()
|
|
||||||
if content == b"":
|
if content == b"":
|
||||||
self.logger.info("Empty request")
|
self.logger.info("Empty request")
|
||||||
return b""
|
return b""
|
||||||
@ -134,19 +160,19 @@ class PokkenServlet(BaseServlet):
|
|||||||
|
|
||||||
self.logger.info(f"{endpoint} request from {Utils.get_ip_addr(request)}")
|
self.logger.info(f"{endpoint} request from {Utils.get_ip_addr(request)}")
|
||||||
|
|
||||||
ret = handler(pokken_request)
|
ret = await handler(pokken_request)
|
||||||
return ret
|
return Response(ret)
|
||||||
|
|
||||||
def handle_matching(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
async def handle_matching(self, request: Request) -> bytes:
|
||||||
if not self.game_cfg.server.enable_matching:
|
if not self.game_cfg.server.enable_matching:
|
||||||
return b""
|
return Response()
|
||||||
|
|
||||||
content = request.content.getvalue()
|
content = await request.body()
|
||||||
client_ip = Utils.get_ip_addr(request)
|
client_ip = Utils.get_ip_addr(request)
|
||||||
|
|
||||||
if content is None or content == b"":
|
if content is None or content == b"":
|
||||||
self.logger.info("Empty matching request")
|
self.logger.info("Empty matching request")
|
||||||
return json.dumps(self.base.handle_matching_noop()).encode()
|
return JSONResponse(self.base.handle_matching_noop())
|
||||||
|
|
||||||
json_content = ast.literal_eval(
|
json_content = ast.literal_eval(
|
||||||
content.decode()
|
content.decode()
|
||||||
@ -166,7 +192,7 @@ class PokkenServlet(BaseServlet):
|
|||||||
self.logger.warning(
|
self.logger.warning(
|
||||||
f"No handler found for message type {json_content['call']}"
|
f"No handler found for message type {json_content['call']}"
|
||||||
)
|
)
|
||||||
return json.dumps(self.base.handle_matching_noop()).encode()
|
return JSONResponse(self.base.handle_matching_noop())
|
||||||
|
|
||||||
ret = handler(json_content, client_ip)
|
ret = handler(json_content, client_ip)
|
||||||
|
|
||||||
@ -181,4 +207,4 @@ class PokkenServlet(BaseServlet):
|
|||||||
|
|
||||||
self.logger.debug(f"Response {ret}")
|
self.logger.debug(f"Response {ret}")
|
||||||
|
|
||||||
return json.dumps(ret).encode()
|
return JSONResponse(ret)
|
||||||
|
@ -32,42 +32,42 @@ class SaoBase:
|
|||||||
self.logger.warning(f"Failed to find csv file {file}.csv")
|
self.logger.warning(f"Failed to find csv file {file}.csv")
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def handle_noop(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_noop(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
self.logger.info(f"Using Generic handler")
|
self.logger.info(f"Using Generic handler")
|
||||||
resp_thing = SaoNoopResponse(header.cmd + 1)
|
resp_thing = SaoNoopResponse(header.cmd + 1)
|
||||||
return resp_thing.make()
|
return resp_thing.make()
|
||||||
|
|
||||||
def handle_c122(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c122(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#common/get_maintenance_info
|
#common/get_maintenance_info
|
||||||
resp = SaoGetMaintResponse(header.cmd +1)
|
resp = SaoGetMaintResponse(header.cmd +1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c12e(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c12e(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#common/ac_cabinet_boot_notification
|
#common/ac_cabinet_boot_notification
|
||||||
resp = SaoCommonAcCabinetBootNotificationResponse(header.cmd +1)
|
resp = SaoCommonAcCabinetBootNotificationResponse(header.cmd +1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c100(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c100(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#common/get_app_versions
|
#common/get_app_versions
|
||||||
resp = SaoCommonGetAppVersionsRequest(header.cmd +1)
|
resp = SaoCommonGetAppVersionsRequest(header.cmd +1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c102(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c102(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#common/master_data_version_check
|
#common/master_data_version_check
|
||||||
resp = SaoMasterDataVersionCheckResponse(header.cmd +1)
|
resp = SaoMasterDataVersionCheckResponse(header.cmd +1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c10a(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c10a(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#common/paying_play_start
|
#common/paying_play_start
|
||||||
resp = SaoCommonPayingPlayStartRequest(header.cmd +1)
|
resp = SaoCommonPayingPlayStartRequest(header.cmd +1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_ca02(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_ca02(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#quest_multi_play_room/get_quest_scene_multi_play_photon_server
|
#quest_multi_play_room/get_quest_scene_multi_play_photon_server
|
||||||
resp = SaoGetQuestSceneMultiPlayPhotonServerResponse(header.cmd +1)
|
resp = SaoGetQuestSceneMultiPlayPhotonServerResponse(header.cmd +1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c11e(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c11e(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#common/get_auth_card_data
|
#common/get_auth_card_data
|
||||||
req = SaoGetAuthCardDataRequest(header, request)
|
req = SaoGetAuthCardDataRequest(header, request)
|
||||||
|
|
||||||
@ -120,12 +120,12 @@ class SaoBase:
|
|||||||
resp = SaoGetAuthCardDataResponse(header.cmd +1, profile_data)
|
resp = SaoGetAuthCardDataResponse(header.cmd +1, profile_data)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c40c(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c40c(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#home/check_ac_login_bonus
|
#home/check_ac_login_bonus
|
||||||
resp = SaoHomeCheckAcLoginBonusResponse(header.cmd +1)
|
resp = SaoHomeCheckAcLoginBonusResponse(header.cmd +1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c104(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c104(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#common/login
|
#common/login
|
||||||
req = SaoCommonLoginRequest(header, request)
|
req = SaoCommonLoginRequest(header, request)
|
||||||
|
|
||||||
@ -135,17 +135,17 @@ class SaoBase:
|
|||||||
resp = SaoCommonLoginResponse(header.cmd +1, profile_data)
|
resp = SaoCommonLoginResponse(header.cmd +1, profile_data)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c404(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c404(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#home/check_comeback_event
|
#home/check_comeback_event
|
||||||
resp = SaoCheckComebackEventRequest(header.cmd +1)
|
resp = SaoCheckComebackEventRequest(header.cmd +1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c000(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c000(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#ticket/ticket
|
#ticket/ticket
|
||||||
resp = SaoTicketResponse(header.cmd +1)
|
resp = SaoTicketResponse(header.cmd +1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c500(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c500(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#user_info/get_user_basic_data
|
#user_info/get_user_basic_data
|
||||||
req = SaoGetUserBasicDataRequest(header, request)
|
req = SaoGetUserBasicDataRequest(header, request)
|
||||||
|
|
||||||
@ -154,7 +154,7 @@ class SaoBase:
|
|||||||
resp = SaoGetUserBasicDataResponse(header.cmd +1, profile_data)
|
resp = SaoGetUserBasicDataResponse(header.cmd +1, profile_data)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c600(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c600(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#have_object/get_hero_log_user_data_list
|
#have_object/get_hero_log_user_data_list
|
||||||
req = SaoGetHeroLogUserDataListRequest(header, request)
|
req = SaoGetHeroLogUserDataListRequest(header, request)
|
||||||
|
|
||||||
@ -163,7 +163,7 @@ class SaoBase:
|
|||||||
resp = SaoGetHeroLogUserDataListResponse(header.cmd +1, hero_data)
|
resp = SaoGetHeroLogUserDataListResponse(header.cmd +1, hero_data)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c602(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c602(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#have_object/get_equipment_user_data_list
|
#have_object/get_equipment_user_data_list
|
||||||
req = SaoGetEquipmentUserDataListRequest(header, request)
|
req = SaoGetEquipmentUserDataListRequest(header, request)
|
||||||
|
|
||||||
@ -172,7 +172,7 @@ class SaoBase:
|
|||||||
resp = SaoGetEquipmentUserDataListResponse(header.cmd +1, equipment_data)
|
resp = SaoGetEquipmentUserDataListResponse(header.cmd +1, equipment_data)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c604(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c604(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#have_object/get_item_user_data_list
|
#have_object/get_item_user_data_list
|
||||||
req = SaoGetItemUserDataListRequest(header, request)
|
req = SaoGetItemUserDataListRequest(header, request)
|
||||||
|
|
||||||
@ -181,21 +181,21 @@ class SaoBase:
|
|||||||
resp = SaoGetItemUserDataListResponse(header.cmd +1, item_data)
|
resp = SaoGetItemUserDataListResponse(header.cmd +1, item_data)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c606(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c606(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#have_object/get_support_log_user_data_list
|
#have_object/get_support_log_user_data_list
|
||||||
supportIdsData = self.game_data.static.get_support_log_ids(0, True)
|
supportIdsData = self.game_data.static.get_support_log_ids(0, True)
|
||||||
|
|
||||||
resp = SaoGetSupportLogUserDataListResponse(header.cmd +1, supportIdsData)
|
resp = SaoGetSupportLogUserDataListResponse(header.cmd +1, supportIdsData)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c800(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c800(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#custom/get_title_user_data_list
|
#custom/get_title_user_data_list
|
||||||
titleIdsData = self.game_data.static.get_title_ids(0, True)
|
titleIdsData = self.game_data.static.get_title_ids(0, True)
|
||||||
|
|
||||||
resp = SaoGetTitleUserDataListResponse(header.cmd +1, titleIdsData)
|
resp = SaoGetTitleUserDataListResponse(header.cmd +1, titleIdsData)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c608(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c608(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#have_object/get_episode_append_data_list
|
#have_object/get_episode_append_data_list
|
||||||
req = SaoGetEpisodeAppendDataListRequest(header, request)
|
req = SaoGetEpisodeAppendDataListRequest(header, request)
|
||||||
|
|
||||||
@ -204,7 +204,7 @@ class SaoBase:
|
|||||||
resp = SaoGetEpisodeAppendDataListResponse(header.cmd +1, profile_data)
|
resp = SaoGetEpisodeAppendDataListResponse(header.cmd +1, profile_data)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c804(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c804(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#custom/get_party_data_list
|
#custom/get_party_data_list
|
||||||
req = SaoGetPartyDataListRequest(header, request)
|
req = SaoGetPartyDataListRequest(header, request)
|
||||||
|
|
||||||
@ -216,17 +216,17 @@ class SaoBase:
|
|||||||
resp = SaoGetPartyDataListResponse(header.cmd +1, hero1_data, hero2_data, hero3_data)
|
resp = SaoGetPartyDataListResponse(header.cmd +1, hero1_data, hero2_data, hero3_data)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c902(self, header: SaoRequestHeader, request: bytes) -> bytes: # for whatever reason, having all entries empty or filled changes nothing
|
async def handle_c902(self, header: SaoRequestHeader, request: bytes) -> bytes: # for whatever reason, having all entries empty or filled changes nothing
|
||||||
#quest/get_quest_scene_prev_scan_profile_card
|
#quest/get_quest_scene_prev_scan_profile_card
|
||||||
resp = SaoGetQuestScenePrevScanProfileCardResponse(header.cmd +1)
|
resp = SaoGetQuestScenePrevScanProfileCardResponse(header.cmd +1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c124(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c124(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#common/get_resource_path_info
|
#common/get_resource_path_info
|
||||||
resp = SaoGetResourcePathInfoResponse(header.cmd +1)
|
resp = SaoGetResourcePathInfoResponse(header.cmd +1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c900(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c900(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#quest/get_quest_scene_user_data_list // QuestScene.csv
|
#quest/get_quest_scene_user_data_list // QuestScene.csv
|
||||||
req = SaoGetQuestSceneUserDataListRequest(header, request)
|
req = SaoGetQuestSceneUserDataListRequest(header, request)
|
||||||
|
|
||||||
@ -235,22 +235,22 @@ class SaoBase:
|
|||||||
resp = SaoGetQuestSceneUserDataListResponse(header.cmd +1, quest_data)
|
resp = SaoGetQuestSceneUserDataListResponse(header.cmd +1, quest_data)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c400(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c400(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#home/check_yui_medal_get_condition
|
#home/check_yui_medal_get_condition
|
||||||
resp = SaoCheckYuiMedalGetConditionResponse(header.cmd +1)
|
resp = SaoCheckYuiMedalGetConditionResponse(header.cmd +1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c402(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c402(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#home/get_yui_medal_bonus_user_data
|
#home/get_yui_medal_bonus_user_data
|
||||||
resp = SaoGetYuiMedalBonusUserDataResponse(header.cmd +1)
|
resp = SaoGetYuiMedalBonusUserDataResponse(header.cmd +1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c40a(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c40a(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#home/check_profile_card_used_reward
|
#home/check_profile_card_used_reward
|
||||||
resp = SaoCheckProfileCardUsedRewardResponse(header.cmd +1)
|
resp = SaoCheckProfileCardUsedRewardResponse(header.cmd +1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c814(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c814(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#custom/synthesize_enhancement_hero_log
|
#custom/synthesize_enhancement_hero_log
|
||||||
req = SaoSynthesizeEnhancementHeroLogRequest(header, request)
|
req = SaoSynthesizeEnhancementHeroLogRequest(header, request)
|
||||||
|
|
||||||
@ -324,7 +324,7 @@ class SaoBase:
|
|||||||
resp = SaoSynthesizeEnhancementHeroLogResponse(header.cmd +1, synthesize_hero_log_data)
|
resp = SaoSynthesizeEnhancementHeroLogResponse(header.cmd +1, synthesize_hero_log_data)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c816(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c816(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#custom/synthesize_enhancement_equipment
|
#custom/synthesize_enhancement_equipment
|
||||||
req_data = SaoSynthesizeEnhancementEquipmentRequest(header, request)
|
req_data = SaoSynthesizeEnhancementEquipmentRequest(header, request)
|
||||||
synthesize_equipment_data = self.game_data.item.get_user_equipment(req_data.user_id, req_data.origin_user_equipment_id)
|
synthesize_equipment_data = self.game_data.item.get_user_equipment(req_data.user_id, req_data.origin_user_equipment_id)
|
||||||
@ -386,7 +386,7 @@ class SaoBase:
|
|||||||
resp = SaoSynthesizeEnhancementEquipmentResponse(header.cmd +1, synthesize_equipment_data)
|
resp = SaoSynthesizeEnhancementEquipmentResponse(header.cmd +1, synthesize_equipment_data)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c806(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c806(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#custom/change_party
|
#custom/change_party
|
||||||
req_data = SaoChangePartyRequest(header, request)
|
req_data = SaoChangePartyRequest(header, request)
|
||||||
party_hero_list = []
|
party_hero_list = []
|
||||||
@ -421,7 +421,7 @@ class SaoBase:
|
|||||||
resp = SaoNoopResponse(header.cmd +1)
|
resp = SaoNoopResponse(header.cmd +1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c904(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c904(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#quest/episode_play_start
|
#quest/episode_play_start
|
||||||
req_data = SaoEpisodePlayStartRequest(header, request)
|
req_data = SaoEpisodePlayStartRequest(header, request)
|
||||||
|
|
||||||
@ -439,7 +439,7 @@ class SaoBase:
|
|||||||
resp = SaoEpisodePlayStartResponse(header.cmd +1, profile_data)
|
resp = SaoEpisodePlayStartResponse(header.cmd +1, profile_data)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c908(self, header: SaoRequestHeader, request: bytes) -> bytes: # Level calculation missing for the profile and heroes
|
async def handle_c908(self, header: SaoRequestHeader, request: bytes) -> bytes: # Level calculation missing for the profile and heroes
|
||||||
#quest/episode_play_end
|
#quest/episode_play_end
|
||||||
|
|
||||||
req_data = SaoEpisodePlayEndRequest(header, request)
|
req_data = SaoEpisodePlayEndRequest(header, request)
|
||||||
@ -599,7 +599,7 @@ class SaoBase:
|
|||||||
resp = SaoEpisodePlayEndResponse(header.cmd +1)
|
resp = SaoEpisodePlayEndResponse(header.cmd +1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c914(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c914(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#quest/trial_tower_play_start
|
#quest/trial_tower_play_start
|
||||||
req_data = SaoTrialTowerPlayStartRequest(header, request)
|
req_data = SaoTrialTowerPlayStartRequest(header, request)
|
||||||
|
|
||||||
@ -618,7 +618,7 @@ class SaoBase:
|
|||||||
resp = SaoEpisodePlayStartResponse(header.cmd +1, profile_data)
|
resp = SaoEpisodePlayStartResponse(header.cmd +1, profile_data)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c918(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c918(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#quest/trial_tower_play_end
|
#quest/trial_tower_play_end
|
||||||
req_data = SaoTrialTowerPlayEndRequest(header, request)
|
req_data = SaoTrialTowerPlayEndRequest(header, request)
|
||||||
|
|
||||||
@ -799,7 +799,7 @@ class SaoBase:
|
|||||||
resp = SaoTrialTowerPlayEndResponse(header.cmd +1)
|
resp = SaoTrialTowerPlayEndResponse(header.cmd +1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c90a(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c90a(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#quest/episode_play_end_unanalyzed_log_fixed
|
#quest/episode_play_end_unanalyzed_log_fixed
|
||||||
|
|
||||||
req = SaoEpisodePlayEndUnanalyzedLogFixedRequest(header, request)
|
req = SaoEpisodePlayEndUnanalyzedLogFixedRequest(header, request)
|
||||||
@ -809,7 +809,7 @@ class SaoBase:
|
|||||||
resp = SaoEpisodePlayEndUnanalyzedLogFixedResponse(header.cmd +1, end_session_data[4])
|
resp = SaoEpisodePlayEndUnanalyzedLogFixedResponse(header.cmd +1, end_session_data[4])
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c91a(self, header: SaoRequestHeader, request: bytes) -> bytes: # handler is identical to the episode
|
async def handle_c91a(self, header: SaoRequestHeader, request: bytes) -> bytes: # handler is identical to the episode
|
||||||
#quest/trial_tower_play_end_unanalyzed_log_fixed
|
#quest/trial_tower_play_end_unanalyzed_log_fixed
|
||||||
req = TrialTowerPlayEndUnanalyzedLogFixed(header, request)
|
req = TrialTowerPlayEndUnanalyzedLogFixed(header, request)
|
||||||
|
|
||||||
@ -818,58 +818,58 @@ class SaoBase:
|
|||||||
resp = SaoEpisodePlayEndUnanalyzedLogFixedResponse(header.cmd +1, end_session_data[4])
|
resp = SaoEpisodePlayEndUnanalyzedLogFixedResponse(header.cmd +1, end_session_data[4])
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_cd00(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_cd00(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#defrag_match/get_defrag_match_basic_data
|
#defrag_match/get_defrag_match_basic_data
|
||||||
resp = SaoGetDefragMatchBasicDataResponse(header.cmd +1)
|
resp = SaoGetDefragMatchBasicDataResponse(header.cmd +1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_cd02(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_cd02(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#defrag_match/get_defrag_match_ranking_user_data
|
#defrag_match/get_defrag_match_ranking_user_data
|
||||||
resp = SaoGetDefragMatchRankingUserDataResponse(header.cmd +1)
|
resp = SaoGetDefragMatchRankingUserDataResponse(header.cmd +1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_cd04(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_cd04(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#defrag_match/get_defrag_match_league_point_ranking_list
|
#defrag_match/get_defrag_match_league_point_ranking_list
|
||||||
resp = SaoGetDefragMatchLeaguePointRankingListResponse(header.cmd +1)
|
resp = SaoGetDefragMatchLeaguePointRankingListResponse(header.cmd +1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_cd06(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_cd06(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#defrag_match/get_defrag_match_league_score_ranking_list
|
#defrag_match/get_defrag_match_league_score_ranking_list
|
||||||
resp = SaoGetDefragMatchLeagueScoreRankingListResponse(header.cmd +1)
|
resp = SaoGetDefragMatchLeagueScoreRankingListResponse(header.cmd +1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_d404(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_d404(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#other/bnid_serial_code_check
|
#other/bnid_serial_code_check
|
||||||
resp = SaoBnidSerialCodeCheckResponse(header.cmd +1)
|
resp = SaoBnidSerialCodeCheckResponse(header.cmd +1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c306(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c306(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
#card/scan_qr_quest_profile_card
|
#card/scan_qr_quest_profile_card
|
||||||
resp = SaoScanQrQuestProfileCardResponse(header.cmd +1)
|
resp = SaoScanQrQuestProfileCardResponse(header.cmd +1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_c700(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_c700(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
# shop/get_shop_resource_sales_data_list
|
# shop/get_shop_resource_sales_data_list
|
||||||
# TODO: Get user shop data
|
# TODO: Get user shop data
|
||||||
req = GetShopResourceSalesDataListRequest(header, request)
|
req = GetShopResourceSalesDataListRequest(header, request)
|
||||||
resp = GetShopResourceSalesDataListResponse(header.cmd + 1)
|
resp = GetShopResourceSalesDataListResponse(header.cmd + 1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_d100(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_d100(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
# shop/get_yui_medal_shop_user_data_list
|
# shop/get_yui_medal_shop_user_data_list
|
||||||
# TODO: Get user shop data
|
# TODO: Get user shop data
|
||||||
req = GetYuiMedalShopUserDataListRequest(header, request)
|
req = GetYuiMedalShopUserDataListRequest(header, request)
|
||||||
resp = GetYuiMedalShopUserDataListResponse(header.cmd + 1)
|
resp = GetYuiMedalShopUserDataListResponse(header.cmd + 1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_cf0e(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_cf0e(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
# gasha/get_gasha_medal_shop_user_data_list
|
# gasha/get_gasha_medal_shop_user_data_list
|
||||||
# TODO: Get user shop data
|
# TODO: Get user shop data
|
||||||
req = GetGashaMedalShopUserDataListRequest(header, request)
|
req = GetGashaMedalShopUserDataListRequest(header, request)
|
||||||
resp = GetGashaMedalShopUserDataListResponse(header.cmd + 1)
|
resp = GetGashaMedalShopUserDataListResponse(header.cmd + 1)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_d5da(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_d5da(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
# master_data/get_m_yui_medal_shops
|
# master_data/get_m_yui_medal_shops
|
||||||
req = GetMYuiMedalShopDataRequest(header, request)
|
req = GetMYuiMedalShopDataRequest(header, request)
|
||||||
resp = GetMYuiMedalShopDataResponse(header.cmd + 1)
|
resp = GetMYuiMedalShopDataResponse(header.cmd + 1)
|
||||||
@ -897,7 +897,7 @@ class SaoBase:
|
|||||||
self.logger.debug(f"Load {len(resp.data_list)} Yui Medal Shops")
|
self.logger.debug(f"Load {len(resp.data_list)} Yui Medal Shops")
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_d5dc(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_d5dc(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
# master_data/get_m_yui_medal_shop_items
|
# master_data/get_m_yui_medal_shop_items
|
||||||
req = GetMYuiMedalShopItemsRequest(header, request)
|
req = GetMYuiMedalShopItemsRequest(header, request)
|
||||||
resp = GetMYuiMedalShopItemsResponse(header.cmd + 1)
|
resp = GetMYuiMedalShopItemsResponse(header.cmd + 1)
|
||||||
@ -927,7 +927,7 @@ class SaoBase:
|
|||||||
self.logger.debug(f"Load {len(resp.data_list)} Yui Medal Shop Items")
|
self.logger.debug(f"Load {len(resp.data_list)} Yui Medal Shop Items")
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_d5fc(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_d5fc(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
# master_data/get_m_gasha_medal_shops
|
# master_data/get_m_gasha_medal_shops
|
||||||
req = GetMGashaMedalShopsRequest(header, request)
|
req = GetMGashaMedalShopsRequest(header, request)
|
||||||
resp = GetMGashaMedalShopsResponse(header.cmd + 1)
|
resp = GetMGashaMedalShopsResponse(header.cmd + 1)
|
||||||
@ -942,11 +942,11 @@ class SaoBase:
|
|||||||
self.logger.debug(f"Load {len(resp.data_list)} Gasha Medal Shops")
|
self.logger.debug(f"Load {len(resp.data_list)} Gasha Medal Shops")
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_d5fe(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_d5fe(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
# master_data/get_m_gasha_medal_shop_items
|
# master_data/get_m_gasha_medal_shop_items
|
||||||
return SaoNoopResponse(header.cmd + 1).make()
|
return SaoNoopResponse(header.cmd + 1).make()
|
||||||
|
|
||||||
def handle_d604(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_d604(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
# master_data_2/get_m_res_earn_campaign_shops
|
# master_data_2/get_m_res_earn_campaign_shops
|
||||||
req = GetMResEarnCampaignShopsRequest(header, request)
|
req = GetMResEarnCampaignShopsRequest(header, request)
|
||||||
resp = GetMResEarnCampaignShopsResponse(header.cmd + 1)
|
resp = GetMResEarnCampaignShopsResponse(header.cmd + 1)
|
||||||
@ -968,6 +968,6 @@ class SaoBase:
|
|||||||
#self.logger.debug(f"Load {len(resp.data_list)} Res Earn Campaign Shops")
|
#self.logger.debug(f"Load {len(resp.data_list)} Res Earn Campaign Shops")
|
||||||
return SaoNoopResponse(header.cmd + 1).make()
|
return SaoNoopResponse(header.cmd + 1).make()
|
||||||
|
|
||||||
def handle_d606(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
async def handle_d606(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||||
# master_data_2/get_m_res_earn_campaign_shop_items
|
# master_data_2/get_m_res_earn_campaign_shop_items
|
||||||
return SaoNoopResponse(header.cmd + 1).make()
|
return SaoNoopResponse(header.cmd + 1).make()
|
@ -3,12 +3,22 @@ from enum import Enum
|
|||||||
|
|
||||||
class SaoConstants:
|
class SaoConstants:
|
||||||
GAME_CODE = "SDEW"
|
GAME_CODE = "SDEW"
|
||||||
|
GAME_CDS = ["SAO1"]
|
||||||
|
|
||||||
CONFIG_NAME = "sao.yaml"
|
CONFIG_NAME = "sao.yaml"
|
||||||
|
|
||||||
VER_SAO = 0
|
VER_SAO = 0
|
||||||
|
|
||||||
VERSION_NAMES = ("Sword Art Online Arcade")
|
VERSION_NAMES = ("Sword Art Online Arcade")
|
||||||
|
|
||||||
|
SERIAL_IDENT_SATALITE = 4
|
||||||
|
SERIAL_IDENT_TERMINAL = 5
|
||||||
|
|
||||||
|
SERIAL_IDENT = [2825]
|
||||||
|
NETID_PREFIX = ["ABLN"]
|
||||||
|
SERIAL_REGIONS = [1]
|
||||||
|
SERIAL_ROLES = [3]
|
||||||
|
SERIAL_CAB_IDENTS = [SERIAL_IDENT_SATALITE, SERIAL_IDENT_TERMINAL]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def game_ver_to_string(cls, ver: int):
|
def game_ver_to_string(cls, ver: int):
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
from typing import Tuple, Dict, List
|
from typing import Tuple, Dict, List
|
||||||
from twisted.web.http import Request
|
from starlette.requests import Request
|
||||||
|
from starlette.responses import Response
|
||||||
|
from starlette.routing import Route
|
||||||
import yaml
|
import yaml
|
||||||
import logging, coloredlogs
|
import logging, coloredlogs
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
@ -55,11 +57,10 @@ class SaoServlet(BaseServlet):
|
|||||||
if self.game_cfg.hash.verify_hash:
|
if self.game_cfg.hash.verify_hash:
|
||||||
self.static_hash = md5(self.game_cfg.hash.hash_base.encode()).digest() # Greate hashing guys, really validates the data
|
self.static_hash = md5(self.game_cfg.hash.hash_base.encode()).digest() # Greate hashing guys, really validates the data
|
||||||
|
|
||||||
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
|
def get_routes(self) -> List[Route]:
|
||||||
return (
|
return [
|
||||||
[],
|
Route("/{datecode:int}/proto/if/{category:str}/{endpoint:str}", self.render_POST, methods=['POST'])
|
||||||
[("render_POST", "/{datecode}/proto/if/{category}/{endpoint}", {})]
|
]
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_game_enabled(cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str) -> bool:
|
def is_game_enabled(cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str) -> bool:
|
||||||
@ -86,30 +87,29 @@ class SaoServlet(BaseServlet):
|
|||||||
proto = "https"
|
proto = "https"
|
||||||
port = f":{port_ssl}" if not self.core_cfg.server.is_using_proxy and port_ssl != 443 else ""
|
port = f":{port_ssl}" if not self.core_cfg.server.is_using_proxy and port_ssl != 443 else ""
|
||||||
|
|
||||||
return (f"{proto}://{self.core_cfg.title.hostname}{port}/", "")
|
return (f"{proto}://{self.core_cfg.server.hostname}{port}/", "")
|
||||||
|
|
||||||
def get_mucha_info(self, core_cfg: CoreConfig, cfg_dir: str) -> Tuple[bool, str]:
|
def get_mucha_info(self, core_cfg: CoreConfig, cfg_dir: str) -> Tuple[bool, str]:
|
||||||
if not self.game_cfg.server.enable:
|
if not self.game_cfg.server.enable:
|
||||||
return (False, "")
|
return (False, [], [])
|
||||||
|
|
||||||
return (True, "SAO1")
|
return (True, SaoConstants.GAME_CDS, SaoConstants.NETID_PREFIX)
|
||||||
|
|
||||||
def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
async def render_POST(self, request: Request) -> bytes:
|
||||||
endpoint = matchers.get('endpoint', '')
|
endpoint = request.path_params.get('endpoint', '')
|
||||||
request.responseHeaders.addRawHeader(b"content-type", b"text/html; charset=utf-8")
|
|
||||||
iv = b""
|
iv = b""
|
||||||
|
|
||||||
req_raw = request.content.read()
|
req_raw = await request.body()
|
||||||
if len(req_raw) < 40:
|
if len(req_raw) < 40:
|
||||||
self.logger.warn(f"Malformed request to {endpoint} - {req_raw.hex()}")
|
self.logger.warn(f"Malformed request to {endpoint} - {req_raw.hex()}")
|
||||||
return b""
|
return Response()
|
||||||
req_header = SaoRequestHeader(req_raw)
|
req_header = SaoRequestHeader(req_raw)
|
||||||
|
|
||||||
cmd_str = f"{req_header.cmd:04x}"
|
cmd_str = f"{req_header.cmd:04x}"
|
||||||
|
|
||||||
if self.game_cfg.hash.verify_hash and self.static_hash != req_header.hash:
|
if self.game_cfg.hash.verify_hash and self.static_hash != req_header.hash:
|
||||||
self.logger.error(f"Hash mismatch! Expecting {self.static_hash} but recieved {req_header.hash}")
|
self.logger.error(f"Hash mismatch! Expecting {self.static_hash} but recieved {req_header.hash}")
|
||||||
return b""
|
return Response()
|
||||||
|
|
||||||
if self.game_cfg.crypt.enable:
|
if self.game_cfg.crypt.enable:
|
||||||
iv = req_raw[40:48]
|
iv = req_raw[40:48]
|
||||||
@ -124,7 +124,7 @@ class SaoServlet(BaseServlet):
|
|||||||
handler = getattr(self.base, f"handle_{cmd_str}", self.base.handle_noop)
|
handler = getattr(self.base, f"handle_{cmd_str}", self.base.handle_noop)
|
||||||
self.logger.info(f"{endpoint} - {cmd_str} request")
|
self.logger.info(f"{endpoint} - {cmd_str} request")
|
||||||
self.logger.debug(f"Request: {req_raw.hex()}")
|
self.logger.debug(f"Request: {req_raw.hex()}")
|
||||||
resp = handler(req_header, req_data)
|
resp = await handler(req_header, req_data)
|
||||||
|
|
||||||
if resp is None:
|
if resp is None:
|
||||||
resp = SaoNoopResponse(req_header.cmd + 1).make()
|
resp = SaoNoopResponse(req_header.cmd + 1).make()
|
||||||
@ -137,7 +137,7 @@ class SaoServlet(BaseServlet):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
self.logger.error(f"Unknown response type {type(resp)}")
|
self.logger.error(f"Unknown response type {type(resp)}")
|
||||||
return b""
|
return Response()
|
||||||
|
|
||||||
self.logger.debug(f"Response: {resp.hex()}")
|
self.logger.debug(f"Response: {resp.hex()}")
|
||||||
|
|
||||||
@ -153,5 +153,6 @@ class SaoServlet(BaseServlet):
|
|||||||
tmp = struct.pack("!I", crypt_data_len) # does it want the length of the encrypted response??
|
tmp = struct.pack("!I", crypt_data_len) # does it want the length of the encrypted response??
|
||||||
resp = resp[:20] + tmp + iv + data_crypt
|
resp = resp[:20] + tmp + iv + data_crypt
|
||||||
self.logger.debug(f"Encrypted Response: {resp.hex()}")
|
self.logger.debug(f"Encrypted Response: {resp.hex()}")
|
||||||
|
|
||||||
return resp
|
|
||||||
|
return Response(resp, media_type="text/html; charset=utf-8")
|
@ -83,18 +83,18 @@ class WaccaBase:
|
|||||||
else:
|
else:
|
||||||
self.region_id = WaccaConstants.Region[prefecture_name]
|
self.region_id = WaccaConstants.Region[prefecture_name]
|
||||||
|
|
||||||
def handle_housing_get_request(self, data: Dict) -> Dict:
|
async def handle_housing_get_request(self, data: Dict) -> Dict:
|
||||||
req = BaseRequest(data)
|
req = BaseRequest(data)
|
||||||
housing_id = 1337
|
housing_id = 1337
|
||||||
self.logger.info(f"{req.chipId} -> {housing_id}")
|
self.logger.info(f"{req.chipId} -> {housing_id}")
|
||||||
resp = HousingGetResponse(housing_id)
|
resp = HousingGetResponse(housing_id)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_advertise_GetRanking_request(self, data: Dict) -> Dict:
|
async def handle_advertise_GetRanking_request(self, data: Dict) -> Dict:
|
||||||
req = AdvertiseGetRankingRequest(data)
|
req = AdvertiseGetRankingRequest(data)
|
||||||
return AdvertiseGetRankingResponse().make()
|
return AdvertiseGetRankingResponse().make()
|
||||||
|
|
||||||
def handle_housing_start_request(self, data: Dict) -> Dict:
|
async def handle_housing_start_request(self, data: Dict) -> Dict:
|
||||||
req = HousingStartRequestV1(data)
|
req = HousingStartRequestV1(data)
|
||||||
allnet_region_id = None
|
allnet_region_id = None
|
||||||
|
|
||||||
@ -126,16 +126,16 @@ class WaccaBase:
|
|||||||
resp = HousingStartResponseV1(region_id)
|
resp = HousingStartResponseV1(region_id)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_advertise_GetNews_request(self, data: Dict) -> Dict:
|
async def handle_advertise_GetNews_request(self, data: Dict) -> Dict:
|
||||||
resp = GetNewsResponseV1()
|
resp = GetNewsResponseV1()
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_user_status_logout_request(self, data: Dict) -> Dict:
|
async def handle_user_status_logout_request(self, data: Dict) -> Dict:
|
||||||
req = UserStatusLogoutRequest(data)
|
req = UserStatusLogoutRequest(data)
|
||||||
self.logger.info(f"Log out user {req.userId} from {req.chipId}")
|
self.logger.info(f"Log out user {req.userId} from {req.chipId}")
|
||||||
return BaseResponse().make()
|
return BaseResponse().make()
|
||||||
|
|
||||||
def handle_user_status_get_request(self, data: Dict) -> Dict:
|
async def handle_user_status_get_request(self, data: Dict) -> Dict:
|
||||||
req = UserStatusGetRequest(data)
|
req = UserStatusGetRequest(data)
|
||||||
resp = UserStatusGetV1Response()
|
resp = UserStatusGetV1Response()
|
||||||
|
|
||||||
@ -181,7 +181,7 @@ class WaccaBase:
|
|||||||
|
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_user_status_login_request(self, data: Dict) -> Dict:
|
async def handle_user_status_login_request(self, data: Dict) -> Dict:
|
||||||
req = UserStatusLoginRequest(data)
|
req = UserStatusLoginRequest(data)
|
||||||
resp = UserStatusLoginResponseV1()
|
resp = UserStatusLoginResponseV1()
|
||||||
is_consec_day = True
|
is_consec_day = True
|
||||||
@ -227,7 +227,7 @@ class WaccaBase:
|
|||||||
|
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_user_status_create_request(self, data: Dict) -> Dict:
|
async def handle_user_status_create_request(self, data: Dict) -> Dict:
|
||||||
req = UserStatusCreateRequest(data)
|
req = UserStatusCreateRequest(data)
|
||||||
|
|
||||||
profileId = self.data.profile.create_profile(
|
profileId = self.data.profile.create_profile(
|
||||||
@ -268,7 +268,7 @@ class WaccaBase:
|
|||||||
|
|
||||||
return UserStatusCreateResponseV2(profileId, req.username).make()
|
return UserStatusCreateResponseV2(profileId, req.username).make()
|
||||||
|
|
||||||
def handle_user_status_getDetail_request(self, data: Dict) -> Dict:
|
async def handle_user_status_getDetail_request(self, data: Dict) -> Dict:
|
||||||
req = UserStatusGetDetailRequest(data)
|
req = UserStatusGetDetailRequest(data)
|
||||||
resp = UserStatusGetDetailResponseV1()
|
resp = UserStatusGetDetailResponseV1()
|
||||||
|
|
||||||
@ -433,7 +433,7 @@ class WaccaBase:
|
|||||||
|
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_user_trial_get_request(self, data: Dict) -> Dict:
|
async def handle_user_trial_get_request(self, data: Dict) -> Dict:
|
||||||
req = UserTrialGetRequest(data)
|
req = UserTrialGetRequest(data)
|
||||||
resp = UserTrialGetResponse()
|
resp = UserTrialGetResponse()
|
||||||
|
|
||||||
@ -475,7 +475,7 @@ class WaccaBase:
|
|||||||
|
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_user_trial_update_request(self, data: Dict) -> Dict:
|
async def handle_user_trial_update_request(self, data: Dict) -> Dict:
|
||||||
req = UserTrialUpdateRequest(data)
|
req = UserTrialUpdateRequest(data)
|
||||||
|
|
||||||
total_score = 0
|
total_score = 0
|
||||||
@ -571,7 +571,7 @@ class WaccaBase:
|
|||||||
)
|
)
|
||||||
return BaseResponse().make()
|
return BaseResponse().make()
|
||||||
|
|
||||||
def handle_user_sugoroku_update_request(self, data: Dict) -> Dict:
|
async def handle_user_sugoroku_update_request(self, data: Dict) -> Dict:
|
||||||
ver_split = data["appVersion"].split(".")
|
ver_split = data["appVersion"].split(".")
|
||||||
resp = BaseResponse()
|
resp = BaseResponse()
|
||||||
|
|
||||||
@ -603,10 +603,10 @@ class WaccaBase:
|
|||||||
)
|
)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_user_info_getMyroom_request(self, data: Dict) -> Dict:
|
async def handle_user_info_getMyroom_request(self, data: Dict) -> Dict:
|
||||||
return UserInfogetMyroomResponseV1().make()
|
return UserInfogetMyroomResponseV1().make()
|
||||||
|
|
||||||
def handle_user_music_unlock_request(self, data: Dict) -> Dict:
|
async def handle_user_music_unlock_request(self, data: Dict) -> Dict:
|
||||||
req = UserMusicUnlockRequest(data)
|
req = UserMusicUnlockRequest(data)
|
||||||
|
|
||||||
profile = self.data.profile.get_profile(req.profileId)
|
profile = self.data.profile.get_profile(req.profileId)
|
||||||
@ -672,12 +672,12 @@ class WaccaBase:
|
|||||||
|
|
||||||
return UserMusicUnlockResponse(current_wp, new_tickets).make()
|
return UserMusicUnlockResponse(current_wp, new_tickets).make()
|
||||||
|
|
||||||
def handle_user_info_getRanking_request(self, data: Dict) -> Dict:
|
async def handle_user_info_getRanking_request(self, data: Dict) -> Dict:
|
||||||
# total score, high score by song, cumulative socre, stage up score, other score, WP ranking
|
# total score, high score by song, cumulative socre, stage up score, other score, WP ranking
|
||||||
# This likely requies calculating standings at regular intervals and caching the results
|
# This likely requies calculating standings at regular intervals and caching the results
|
||||||
return UserInfogetRankingResponse().make()
|
return UserInfogetRankingResponse().make()
|
||||||
|
|
||||||
def handle_user_music_update_request(self, data: Dict) -> Dict:
|
async def handle_user_music_update_request(self, data: Dict) -> Dict:
|
||||||
ver_split = data["appVersion"].split(".")
|
ver_split = data["appVersion"].split(".")
|
||||||
if int(ver_split[0]) >= 3:
|
if int(ver_split[0]) >= 3:
|
||||||
resp = UserMusicUpdateResponseV3()
|
resp = UserMusicUpdateResponseV3()
|
||||||
@ -831,18 +831,18 @@ class WaccaBase:
|
|||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
# TODO: Coop and vs data
|
# TODO: Coop and vs data
|
||||||
def handle_user_music_updateCoop_request(self, data: Dict) -> Dict:
|
async def handle_user_music_updateCoop_request(self, data: Dict) -> Dict:
|
||||||
coop_info = data["params"][4]
|
coop_info = data["params"][4]
|
||||||
return self.handle_user_music_update_request(data)
|
return self.handle_user_music_update_request(data)
|
||||||
|
|
||||||
def handle_user_music_updateVersus_request(self, data: Dict) -> Dict:
|
async def handle_user_music_updateVersus_request(self, data: Dict) -> Dict:
|
||||||
vs_info = data["params"][4]
|
vs_info = data["params"][4]
|
||||||
return self.handle_user_music_update_request(data)
|
return self.handle_user_music_update_request(data)
|
||||||
|
|
||||||
def handle_user_music_updateTrial_request(self, data: Dict) -> Dict:
|
async def handle_user_music_updateTrial_request(self, data: Dict) -> Dict:
|
||||||
return self.handle_user_music_update_request(data)
|
return self.handle_user_music_update_request(data)
|
||||||
|
|
||||||
def handle_user_mission_update_request(self, data: Dict) -> Dict:
|
async def handle_user_mission_update_request(self, data: Dict) -> Dict:
|
||||||
req = UserMissionUpdateRequest(data)
|
req = UserMissionUpdateRequest(data)
|
||||||
page_status = req.params[1][1]
|
page_status = req.params[1][1]
|
||||||
|
|
||||||
@ -860,7 +860,7 @@ class WaccaBase:
|
|||||||
|
|
||||||
return BaseResponse().make()
|
return BaseResponse().make()
|
||||||
|
|
||||||
def handle_user_goods_purchase_request(self, data: Dict) -> Dict:
|
async def handle_user_goods_purchase_request(self, data: Dict) -> Dict:
|
||||||
req = UserGoodsPurchaseRequest(data)
|
req = UserGoodsPurchaseRequest(data)
|
||||||
resp = UserGoodsPurchaseResponse()
|
resp = UserGoodsPurchaseResponse()
|
||||||
|
|
||||||
@ -905,13 +905,13 @@ class WaccaBase:
|
|||||||
|
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_competition_status_login_request(self, data: Dict) -> Dict:
|
async def handle_competition_status_login_request(self, data: Dict) -> Dict:
|
||||||
return BaseResponse().make()
|
return BaseResponse().make()
|
||||||
|
|
||||||
def handle_competition_status_update_request(self, data: Dict) -> Dict:
|
async def handle_competition_status_update_request(self, data: Dict) -> Dict:
|
||||||
return BaseResponse().make()
|
return BaseResponse().make()
|
||||||
|
|
||||||
def handle_user_rating_update_request(self, data: Dict) -> Dict:
|
async def handle_user_rating_update_request(self, data: Dict) -> Dict:
|
||||||
req = UserRatingUpdateRequest(data)
|
req = UserRatingUpdateRequest(data)
|
||||||
|
|
||||||
user_id = self.data.profile.profile_to_aime_user(req.profileId)
|
user_id = self.data.profile.profile_to_aime_user(req.profileId)
|
||||||
@ -931,7 +931,7 @@ class WaccaBase:
|
|||||||
|
|
||||||
return BaseResponse().make()
|
return BaseResponse().make()
|
||||||
|
|
||||||
def handle_user_status_update_request(self, data: Dict) -> Dict:
|
async def handle_user_status_update_request(self, data: Dict) -> Dict:
|
||||||
req = UserStatusUpdateRequestV1(data)
|
req = UserStatusUpdateRequestV1(data)
|
||||||
|
|
||||||
user_id = self.data.profile.profile_to_aime_user(req.profileId)
|
user_id = self.data.profile.profile_to_aime_user(req.profileId)
|
||||||
@ -970,7 +970,7 @@ class WaccaBase:
|
|||||||
)
|
)
|
||||||
return BaseResponse().make()
|
return BaseResponse().make()
|
||||||
|
|
||||||
def handle_user_info_update_request(self, data: Dict) -> Dict:
|
async def handle_user_info_update_request(self, data: Dict) -> Dict:
|
||||||
req = UserInfoUpdateRequest(data)
|
req = UserInfoUpdateRequest(data)
|
||||||
|
|
||||||
user_id = self.data.profile.profile_to_aime_user(req.profileId)
|
user_id = self.data.profile.profile_to_aime_user(req.profileId)
|
||||||
@ -989,7 +989,7 @@ class WaccaBase:
|
|||||||
|
|
||||||
return BaseResponse().make()
|
return BaseResponse().make()
|
||||||
|
|
||||||
def handle_user_vip_get_request(self, data: Dict) -> Dict:
|
async def handle_user_vip_get_request(self, data: Dict) -> Dict:
|
||||||
req = UserVipGetRequest(data)
|
req = UserVipGetRequest(data)
|
||||||
resp = UserVipGetResponse()
|
resp = UserVipGetResponse()
|
||||||
|
|
||||||
@ -1021,7 +1021,7 @@ class WaccaBase:
|
|||||||
|
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_user_vip_start_request(self, data: Dict) -> Dict:
|
async def handle_user_vip_start_request(self, data: Dict) -> Dict:
|
||||||
req = UserVipStartRequest(data)
|
req = UserVipStartRequest(data)
|
||||||
|
|
||||||
profile = self.data.profile.get_profile(req.profileId)
|
profile = self.data.profile.get_profile(req.profileId)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import yaml
|
import yaml
|
||||||
import jinja2
|
import jinja2
|
||||||
from twisted.web.http import Request
|
from starlette.requests import Request
|
||||||
from os import path
|
from os import path
|
||||||
from twisted.web.server import Session
|
from twisted.web.server import Session
|
||||||
|
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
|
from starlette.routing import Route
|
||||||
import yaml
|
import yaml
|
||||||
import logging, coloredlogs
|
import logging, coloredlogs
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
from twisted.web.http import Request
|
from starlette.requests import Request
|
||||||
|
from starlette.responses import JSONResponse, Response
|
||||||
from typing import Dict, Tuple, List
|
from typing import Dict, Tuple, List
|
||||||
from os import path
|
from os import path
|
||||||
import traceback
|
import traceback
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from core import CoreConfig, Utils
|
from core import CoreConfig, Utils
|
||||||
|
from core.title import BaseServlet
|
||||||
from .config import WaccaConfig
|
from .config import WaccaConfig
|
||||||
from .config import WaccaConfig
|
from .config import WaccaConfig
|
||||||
from .const import WaccaConstants
|
from .const import WaccaConstants
|
||||||
@ -23,7 +26,7 @@ from .handlers.base import BaseResponse, BaseRequest
|
|||||||
from .handlers.helpers import Version
|
from .handlers.helpers import Version
|
||||||
|
|
||||||
|
|
||||||
class WaccaServlet:
|
class WaccaServlet(BaseServlet):
|
||||||
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 = WaccaConfig()
|
self.game_cfg = WaccaConfig()
|
||||||
@ -62,15 +65,12 @@ class WaccaServlet:
|
|||||||
coloredlogs.install(
|
coloredlogs.install(
|
||||||
level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str
|
level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
|
def get_routes(self) -> List[Route]:
|
||||||
return (
|
return [
|
||||||
[],
|
Route("/WaccaServlet/api/{api:str}/{endpoint:str}", self.render_POST, methods=['POST']),
|
||||||
[
|
Route("/WaccaServlet/api/{api:str}/{branch:str}/{endpoint:str}", self.render_POST, methods=['POST']),
|
||||||
("render_POST", "/WaccaServlet/api/{api}/{endpoint}", {}),
|
]
|
||||||
("render_POST", "/WaccaServlet/api/{api}/{branch}/{endpoint}", {})
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_game_enabled(cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str) -> bool:
|
def is_game_enabled(cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str) -> bool:
|
||||||
@ -88,22 +88,22 @@ class WaccaServlet:
|
|||||||
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
|
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
|
||||||
if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80:
|
if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80:
|
||||||
return (
|
return (
|
||||||
f"http://{self.core_cfg.title.hostname}:{Utils.get_title_port(self.core_cfg)}/WaccaServlet",
|
f"http://{self.core_cfg.server.hostname}:{Utils.get_title_port(self.core_cfg)}/WaccaServlet",
|
||||||
self.core_cfg.title.hostname,
|
self.core_cfg.server.hostname,
|
||||||
)
|
)
|
||||||
|
|
||||||
return (f"http://{self.core_cfg.title.hostname}/WaccaServlet", self.core_cfg.title.hostname)
|
return (f"http://{self.core_cfg.server.hostname}/WaccaServlet", self.core_cfg.server.hostname)
|
||||||
|
|
||||||
def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
async def render_POST(self, request: Request) -> bytes:
|
||||||
def end(resp: Dict) -> bytes:
|
def end(resp: Dict) -> bytes:
|
||||||
hash = md5(json.dumps(resp, ensure_ascii=False).encode()).digest()
|
hash = md5(json.dumps(resp, ensure_ascii=False).encode()).digest()
|
||||||
request.responseHeaders.addRawHeader(b"X-Wacca-Hash", hash.hex().encode())
|
return JSONResponse(resp, headers=["X-Wacca-Hash", hash.hex()])
|
||||||
return json.dumps(resp).encode()
|
|
||||||
|
|
||||||
api = matchers['api']
|
api = request.path_params.get('api', '')
|
||||||
branch = matchers.get('branch', '')
|
branch = request.path_params.get('branch', '')
|
||||||
endpoint = matchers['endpoint']
|
endpoint = request.path_params.get('endpoint', '')
|
||||||
client_ip = Utils.get_ip_addr(request)
|
client_ip = Utils.get_ip_addr(request)
|
||||||
|
bod = await request.body()
|
||||||
|
|
||||||
if branch:
|
if branch:
|
||||||
url_path = f"{api}/{branch}/{endpoint}"
|
url_path = f"{api}/{branch}/{endpoint}"
|
||||||
@ -114,13 +114,13 @@ class WaccaServlet:
|
|||||||
func_to_find = f"handle_{api}_{endpoint}_request"
|
func_to_find = f"handle_{api}_{endpoint}_request"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
req_json = json.loads(request.content.getvalue())
|
req_json = json.loads(bod)
|
||||||
version_full = Version(req_json["appVersion"])
|
version_full = Version(req_json["appVersion"])
|
||||||
req = BaseRequest(req_json)
|
req = BaseRequest(req_json)
|
||||||
|
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
self.logger.error(
|
self.logger.error(
|
||||||
f"Failed to parse request to {request.content.getvalue()} -> Missing required value {e}"
|
f"Failed to parse request to {bod} -> Missing required value {e}"
|
||||||
)
|
)
|
||||||
resp = BaseResponse()
|
resp = BaseResponse()
|
||||||
resp.status = 1
|
resp.status = 1
|
||||||
@ -129,7 +129,7 @@ class WaccaServlet:
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(
|
self.logger.error(
|
||||||
f"Failed to parse request to {url_path} -> {request.content.getvalue()} -> {e}"
|
f"Failed to parse request to {url_path} -> {bod} -> {e}"
|
||||||
)
|
)
|
||||||
resp = BaseResponse()
|
resp = BaseResponse()
|
||||||
resp.status = 1
|
resp.status = 1
|
||||||
@ -176,7 +176,7 @@ class WaccaServlet:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
handler = getattr(self.versions[internal_ver], func_to_find)
|
handler = getattr(self.versions[internal_ver], func_to_find)
|
||||||
resp = handler(req_json)
|
resp = await handler(req_json)
|
||||||
|
|
||||||
self.logger.debug(f"{req.appVersion} response {resp}")
|
self.logger.debug(f"{req.appVersion} response {resp}")
|
||||||
return end(resp)
|
return end(resp)
|
||||||
|
@ -37,13 +37,13 @@ class WaccaLily(WaccaS):
|
|||||||
(210003, 0),
|
(210003, 0),
|
||||||
]
|
]
|
||||||
|
|
||||||
def handle_advertise_GetNews_request(self, data: Dict) -> Dict:
|
async def handle_advertise_GetNews_request(self, data: Dict) -> Dict:
|
||||||
resp = GetNewsResponseV3()
|
resp = GetNewsResponseV3()
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_user_status_create_request(self, data: Dict) -> Dict:
|
async def handle_user_status_create_request(self, data: Dict) -> Dict:
|
||||||
req = UserStatusCreateRequest(data)
|
req = UserStatusCreateRequest(data)
|
||||||
ret = super().handle_user_status_create_request(data)
|
ret = await super().handle_user_status_create_request(data)
|
||||||
|
|
||||||
new_user = self.data.profile.get_profile(aime_id=req.aimeId)
|
new_user = self.data.profile.get_profile(aime_id=req.aimeId)
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ class WaccaLily(WaccaS):
|
|||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def handle_user_status_get_request(self, data: Dict) -> Dict:
|
async def handle_user_status_get_request(self, data: Dict) -> Dict:
|
||||||
req = UserStatusGetRequest(data)
|
req = UserStatusGetRequest(data)
|
||||||
resp = UserStatusGetV2Response()
|
resp = UserStatusGetV2Response()
|
||||||
|
|
||||||
@ -145,7 +145,7 @@ class WaccaLily(WaccaS):
|
|||||||
|
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_user_status_login_request(self, data: Dict) -> Dict:
|
async def handle_user_status_login_request(self, data: Dict) -> Dict:
|
||||||
req = UserStatusLoginRequest(data)
|
req = UserStatusLoginRequest(data)
|
||||||
resp = UserStatusLoginResponseV2()
|
resp = UserStatusLoginResponseV2()
|
||||||
is_consec_day = True
|
is_consec_day = True
|
||||||
@ -189,7 +189,7 @@ class WaccaLily(WaccaS):
|
|||||||
|
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_user_status_getDetail_request(self, data: Dict) -> Dict:
|
async def handle_user_status_getDetail_request(self, data: Dict) -> Dict:
|
||||||
req = UserStatusGetDetailRequest(data)
|
req = UserStatusGetDetailRequest(data)
|
||||||
if req.appVersion.minor >= 53:
|
if req.appVersion.minor >= 53:
|
||||||
resp = UserStatusGetDetailResponseV3()
|
resp = UserStatusGetDetailResponseV3()
|
||||||
@ -440,10 +440,10 @@ class WaccaLily(WaccaS):
|
|||||||
|
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_user_info_getMyroom_request(self, data: Dict) -> Dict:
|
async def handle_user_info_getMyroom_request(self, data: Dict) -> Dict:
|
||||||
return UserInfogetMyroomResponseV2().make()
|
return UserInfogetMyroomResponseV2().make()
|
||||||
|
|
||||||
def handle_user_status_update_request(self, data: Dict) -> Dict:
|
async def handle_user_status_update_request(self, data: Dict) -> Dict:
|
||||||
super().handle_user_status_update_request(data)
|
super().handle_user_status_update_request(data)
|
||||||
req = UserStatusUpdateRequestV2(data)
|
req = UserStatusUpdateRequestV2(data)
|
||||||
self.data.profile.update_profile_lastplayed(
|
self.data.profile.update_profile_lastplayed(
|
||||||
|
@ -39,7 +39,7 @@ class WaccaLilyR(WaccaLily):
|
|||||||
(210003, 0),
|
(210003, 0),
|
||||||
]
|
]
|
||||||
|
|
||||||
def handle_housing_start_request(self, data: Dict) -> Dict:
|
async def handle_housing_start_request(self, data: Dict) -> Dict:
|
||||||
req = HousingStartRequestV2(data)
|
req = HousingStartRequestV2(data)
|
||||||
allnet_region_id = None
|
allnet_region_id = None
|
||||||
|
|
||||||
@ -71,9 +71,9 @@ class WaccaLilyR(WaccaLily):
|
|||||||
resp = HousingStartResponseV1(region_id)
|
resp = HousingStartResponseV1(region_id)
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_user_status_create_request(self, data: Dict) -> Dict:
|
async def handle_user_status_create_request(self, data: Dict) -> Dict:
|
||||||
req = UserStatusCreateRequest(data)
|
req = UserStatusCreateRequest(data)
|
||||||
resp = super().handle_user_status_create_request(data)
|
resp = await super().handle_user_status_create_request(data)
|
||||||
|
|
||||||
self.data.item.put_item(
|
self.data.item.put_item(
|
||||||
req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210054
|
req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210054
|
||||||
@ -102,5 +102,5 @@ class WaccaLilyR(WaccaLily):
|
|||||||
|
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
def handle_user_status_logout_request(self, data: Dict) -> Dict:
|
async def handle_user_status_logout_request(self, data: Dict) -> Dict:
|
||||||
return BaseResponse().make()
|
return BaseResponse().make()
|
||||||
|
@ -47,12 +47,12 @@ class WaccaReverse(WaccaLilyR):
|
|||||||
(310006, 0),
|
(310006, 0),
|
||||||
]
|
]
|
||||||
|
|
||||||
def handle_user_status_login_request(self, data: Dict) -> Dict:
|
async def handle_user_status_login_request(self, data: Dict) -> Dict:
|
||||||
resp = super().handle_user_status_login_request(data)
|
resp = await super().handle_user_status_login_request(data)
|
||||||
resp["params"].append([])
|
resp["params"].append([])
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
def handle_user_status_getDetail_request(self, data: Dict) -> Dict:
|
async def handle_user_status_getDetail_request(self, data: Dict) -> Dict:
|
||||||
req = UserStatusGetDetailRequest(data)
|
req = UserStatusGetDetailRequest(data)
|
||||||
resp = UserStatusGetDetailResponseV4()
|
resp = UserStatusGetDetailResponseV4()
|
||||||
|
|
||||||
@ -305,9 +305,9 @@ class WaccaReverse(WaccaLilyR):
|
|||||||
|
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
|
||||||
def handle_user_status_create_request(self, data: Dict) -> Dict:
|
async def handle_user_status_create_request(self, data: Dict) -> Dict:
|
||||||
req = UserStatusCreateRequest(data)
|
req = UserStatusCreateRequest(data)
|
||||||
resp = super().handle_user_status_create_request(data)
|
resp = await super().handle_user_status_create_request(data)
|
||||||
|
|
||||||
self.data.item.put_item(
|
self.data.item.put_item(
|
||||||
req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 310001
|
req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 310001
|
||||||
|
@ -31,6 +31,6 @@ class WaccaS(WaccaBase):
|
|||||||
super().__init__(cfg, game_cfg)
|
super().__init__(cfg, game_cfg)
|
||||||
self.version = WaccaConstants.VER_WACCA_S
|
self.version = WaccaConstants.VER_WACCA_S
|
||||||
|
|
||||||
def handle_advertise_GetNews_request(self, data: Dict) -> Dict:
|
async def handle_advertise_GetNews_request(self, data: Dict) -> Dict:
|
||||||
resp = GetNewsResponseV2()
|
resp = GetNewsResponseV2()
|
||||||
return resp.make()
|
return resp.make()
|
||||||
|
Loading…
Reference in New Issue
Block a user