begin move
This commit is contained in:
parent
b056ff218d
commit
14fa0f5e8e
@ -1,6 +1,6 @@
|
||||
from core.config import CoreConfig
|
||||
from core.allnet import AllnetServlet
|
||||
from core.aimedb import AimedbFactory
|
||||
from core.allnet import AllnetServlet, BillingServlet
|
||||
from core.aimedb import AimedbServlette
|
||||
from core.title import TitleServlet
|
||||
from core.utils import Utils
|
||||
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)
|
||||
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)}")
|
||||
|
||||
return head
|
||||
|
178
core/aimedb.py
178
core/aimedb.py
@ -2,8 +2,8 @@ from twisted.internet.protocol import Factory, Protocol
|
||||
import logging, coloredlogs
|
||||
from Crypto.Cipher import AES
|
||||
import struct
|
||||
from typing import Dict, Tuple, Callable, Union
|
||||
from typing_extensions import Final
|
||||
from typing import Dict, Tuple, Callable, Union, Optional
|
||||
import asyncio
|
||||
from logging.handlers import TimedRotatingFileHandler
|
||||
|
||||
from core.config import CoreConfig
|
||||
@ -11,15 +11,37 @@ from core.utils import create_sega_auth_key
|
||||
from core.data import Data
|
||||
from .adb_handlers import *
|
||||
|
||||
|
||||
class AimedbProtocol(Protocol):
|
||||
class AimedbServlette():
|
||||
request_list: Dict[int, Tuple[Callable[[bytes, int], Union[ADBBaseResponse, bytes]], int, str]] = {}
|
||||
|
||||
def __init__(self, core_cfg: CoreConfig) -> None:
|
||||
self.logger = logging.getLogger("aimedb")
|
||||
self.config = core_cfg
|
||||
def __init__(self, core_cfg: CoreConfig) -> None:
|
||||
self.config = 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!!!")
|
||||
exit(1)
|
||||
|
||||
@ -40,27 +62,30 @@ class AimedbProtocol(Protocol):
|
||||
|
||||
self.register_handler(0x13, 0x14, self.handle_log_ex, 'aime_log_ex')
|
||||
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:
|
||||
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):
|
||||
"""Appends 0s to the end of the data until it's at the correct size"""
|
||||
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:
|
||||
async def process_data(self, data: bytes, reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> Optional[bytes]:
|
||||
addr = writer.get_extra_info('peername')[0]
|
||||
cipher = AES.new(self.config.aimedb.key.encode(), AES.MODE_ECB)
|
||||
|
||||
try:
|
||||
@ -68,9 +93,9 @@ class AimedbProtocol(Protocol):
|
||||
|
||||
except Exception as 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:
|
||||
head = ADBHeader.from_data(decrypted)
|
||||
@ -79,7 +104,9 @@ class AimedbProtocol(Protocol):
|
||||
self.logger.error(f"Error parsing ADB header: {e}")
|
||||
try:
|
||||
encrypted = cipher.encrypt(ADBBaseResponse().make())
|
||||
self.transport.write(encrypted)
|
||||
writer.write(encrypted)
|
||||
await writer.drain()
|
||||
return
|
||||
|
||||
except Exception as 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:
|
||||
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'))
|
||||
|
||||
if resp_code is None:
|
||||
self.logger.warning(f"No handler for cmd {hex(head.cmd)}")
|
||||
|
||||
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):
|
||||
resp_bytes = resp.make()
|
||||
if len(resp_bytes) != resp.head.length:
|
||||
resp_bytes = self.append_padding(resp_bytes)
|
||||
|
||||
elif type(resp) == bytes:
|
||||
resp_bytes = resp
|
||||
|
||||
elif resp is None: # Nothing to send, probably a goodbye
|
||||
self.logger.warn(f"None return by handler for {name}")
|
||||
return
|
||||
|
||||
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)}")
|
||||
|
||||
try:
|
||||
try:
|
||||
encrypted = cipher.encrypt(resp_bytes)
|
||||
self.logger.debug(f"Response {resp_bytes.hex()}")
|
||||
self.transport.write(encrypted)
|
||||
writer.write(encrypted)
|
||||
|
||||
except Exception as 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)
|
||||
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:
|
||||
return self.handle_default(data, resp_code)
|
||||
async def handle_hello(self, data: bytes, resp_code: int) -> ADBBaseResponse:
|
||||
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)
|
||||
if h.protocol_ver >= 0x3030:
|
||||
req = h
|
||||
@ -143,26 +175,26 @@ class AimedbProtocol(Protocol):
|
||||
# We don't currently support campaigns
|
||||
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)
|
||||
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_locked = self.data.card.get_card_locked(req.access_code)
|
||||
|
||||
|
||||
ret = ADBLookupResponse.from_req(req.head, user_id)
|
||||
if is_banned and is_locked:
|
||||
ret.head.status = ADBStatus.BAN_SYS_USER
|
||||
elif is_banned:
|
||||
ret.head.status = ADBStatus.BAN_SYS
|
||||
elif is_locked:
|
||||
ret.head.status = ADBStatus.LOCK_USER
|
||||
ret = ADBLookupResponse.from_req(req.head, user_id)
|
||||
|
||||
self.logger.info(
|
||||
f"access_code {req.access_code} -> user_id {ret.user_id}"
|
||||
)
|
||||
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)
|
||||
user_id = self.data.card.get_user_id_from_card(req.access_code)
|
||||
|
||||
@ -191,7 +223,7 @@ class AimedbProtocol(Protocol):
|
||||
|
||||
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
|
||||
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)
|
||||
|
||||
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.
|
||||
"""
|
||||
@ -239,7 +271,7 @@ class AimedbProtocol(Protocol):
|
||||
|
||||
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)
|
||||
access_code = self.data.card.to_access_code(req.idm)
|
||||
user_id = self.data.card.get_user_id_from_card(access_code=access_code)
|
||||
@ -263,7 +295,7 @@ class AimedbProtocol(Protocol):
|
||||
|
||||
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)
|
||||
|
||||
resp = ADBCampaignClearResponse.from_req(req.head)
|
||||
@ -271,7 +303,7 @@ class AimedbProtocol(Protocol):
|
||||
# We don't support campaign stuff
|
||||
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)
|
||||
user_id = -1
|
||||
|
||||
@ -305,17 +337,17 @@ class AimedbProtocol(Protocol):
|
||||
return resp
|
||||
|
||||
# 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)
|
||||
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)
|
||||
|
||||
def handle_log(self, data: bytes, resp_code: int) -> bytes:
|
||||
async def handle_log(self, data: bytes, resp_code: int) -> bytes:
|
||||
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}")
|
||||
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)
|
||||
strs = []
|
||||
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}")
|
||||
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 base64
|
||||
import zlib
|
||||
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 Crypto.PublicKey import RSA
|
||||
from Crypto.Hash import SHA
|
||||
from Crypto.Signature import PKCS1_v1_5
|
||||
from time import strptime
|
||||
from os import path
|
||||
import urllib.parse
|
||||
import math
|
||||
from os import path, environ, mkdir, access, W_OK
|
||||
|
||||
from .config import CoreConfig
|
||||
from .utils import Utils
|
||||
@ -91,7 +95,6 @@ class DLI_STATUS(Enum):
|
||||
|
||||
class AllnetServlet:
|
||||
def __init__(self, core_cfg: CoreConfig, cfg_folder: str):
|
||||
super().__init__()
|
||||
self.config = core_cfg
|
||||
self.config_folder = cfg_folder
|
||||
self.data = Data(core_cfg)
|
||||
@ -126,19 +129,20 @@ class AllnetServlet:
|
||||
self.logger.error("No games detected!")
|
||||
|
||||
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)
|
||||
pragma_header = request.getHeader('Pragma')
|
||||
pragma_header = request.headers.get('Pragma', "")
|
||||
is_dfi = pragma_header is not None and pragma_header == "DFI"
|
||||
data = await request.body()
|
||||
|
||||
try:
|
||||
if is_dfi:
|
||||
req_urlencode = self.from_dfi(request.content.getvalue())
|
||||
req_urlencode = self.from_dfi(data)
|
||||
else:
|
||||
req_urlencode = request.content.getvalue().decode()
|
||||
req_urlencode = data
|
||||
|
||||
req_dict = self.allnet_req_to_dict(req_urlencode)
|
||||
if req_dict is None:
|
||||
@ -155,7 +159,7 @@ class AllnetServlet:
|
||||
except AllnetRequestException as e:
|
||||
if e.message != "":
|
||||
self.logger.error(e)
|
||||
return b""
|
||||
return PlainTextResponse()
|
||||
|
||||
if req.format_ver == 3:
|
||||
resp = AllnetPowerOnResponse3(req.token)
|
||||
@ -176,7 +180,7 @@ class AllnetServlet:
|
||||
|
||||
resp.stat = ALLNET_STAT.bad_machine.value
|
||||
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:
|
||||
arcade = self.data.arcade.get_arcade(machine["arcade"])
|
||||
@ -190,7 +194,7 @@ class AllnetServlet:
|
||||
|
||||
resp.stat = ALLNET_STAT.bad_shop.value
|
||||
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:
|
||||
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_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 = (
|
||||
@ -245,20 +249,20 @@ class AllnetServlet:
|
||||
|
||||
resp.stat = ALLNET_STAT.bad_game.value
|
||||
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:
|
||||
self.logger.info(
|
||||
f"Allowed unknown game {req.game_id} v{req.ver} to authenticate from {request_ip} due to 'is_develop' being enabled. S/N: {req.serial}"
|
||||
)
|
||||
resp.uri = f"http://{self.config.title.hostname}:{self.config.title.port}/{req.game_id}/{req.ver.replace('.', '')}/"
|
||||
resp.host = f"{self.config.title.hostname}:{self.config.title.port}"
|
||||
resp.uri = f"http://{self.config.server.hostname}:{self.config.server.port}/{req.game_id}/{req.ver.replace('.', '')}/"
|
||||
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_str = urllib.parse.unquote(urllib.parse.urlencode(resp_dict))
|
||||
|
||||
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(".", "")
|
||||
@ -277,18 +281,19 @@ class AllnetServlet:
|
||||
request.responseHeaders.addRawHeader('Pragma', 'DFI')
|
||||
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)
|
||||
pragma_header = request.getHeader('Pragma')
|
||||
pragma_header = request.headers.get('Pragma', "")
|
||||
is_dfi = pragma_header is not None and pragma_header == "DFI"
|
||||
data = await request.body()
|
||||
|
||||
try:
|
||||
if is_dfi:
|
||||
req_urlencode = self.from_dfi(request.content.getvalue())
|
||||
req_urlencode = self.from_dfi(data)
|
||||
else:
|
||||
req_urlencode = request.content.getvalue().decode()
|
||||
req_urlencode = data.decode()
|
||||
|
||||
req_dict = self.allnet_req_to_dict(req_urlencode)
|
||||
if req_dict is None:
|
||||
@ -305,7 +310,7 @@ class AllnetServlet:
|
||||
except AllnetRequestException as e:
|
||||
if e.message != "":
|
||||
self.logger.error(e)
|
||||
return b""
|
||||
return PlainTextResponse()
|
||||
|
||||
self.logger.info(
|
||||
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
|
||||
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
|
||||
if path.exists(
|
||||
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(
|
||||
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.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')
|
||||
return self.to_dfi(res_str)"""
|
||||
|
||||
return res_str
|
||||
return PlainTextResponse(res_str)
|
||||
|
||||
def handle_dlorder_ini(self, request: Request, match: Dict) -> bytes:
|
||||
if "file" not in match:
|
||||
return b""
|
||||
async def handle_dlorder_ini(self, request: Request) -> bytes:
|
||||
req_file = request.path_params.get("file", "").replace("%0A", "")
|
||||
|
||||
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}"):
|
||||
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}")
|
||||
|
||||
return open(
|
||||
f"{self.config.allnet.update_cfg_folder}/{req_file}", "rb"
|
||||
).read()
|
||||
return PlainTextResponse(open(
|
||||
f"{self.config.allnet.update_cfg_folder}/{req_file}", "r"
|
||||
).read())
|
||||
|
||||
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:
|
||||
req_raw = request.content.getvalue()
|
||||
async def handle_dlorder_report(self, request: Request) -> bytes:
|
||||
req_raw = await request.body()
|
||||
client_ip = Utils.get_ip_addr(request)
|
||||
try:
|
||||
req_dict: Dict = json.loads(req_raw)
|
||||
except Exception as e:
|
||||
self.logger.warning(f"Failed to parse DL Report: {e}")
|
||||
return "NG"
|
||||
return PlainTextResponse("NG")
|
||||
|
||||
dl_data_type = DLIMG_TYPE.app
|
||||
dl_data = req_dict.get("appimage", {})
|
||||
@ -374,13 +379,13 @@ class AllnetServlet:
|
||||
|
||||
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")
|
||||
return "NG"
|
||||
return PlainTextResponse("NG")
|
||||
|
||||
rep = DLReport(dl_data, dl_data_type)
|
||||
|
||||
if not rep.validate():
|
||||
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}:"\
|
||||
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.logger.info(msg)
|
||||
|
||||
return "OK"
|
||||
return PlainTextResponse("OK")
|
||||
|
||||
def handle_loaderstaterecorder(self, request: Request, match: Dict) -> bytes:
|
||||
req_data = request.content.getvalue()
|
||||
async def handle_loaderstaterecorder(self, request: Request) -> bytes:
|
||||
req_data = await request.body()
|
||||
sections = req_data.decode("utf-8").split("\r\n")
|
||||
|
||||
req_dict = dict(urllib.parse.parse_qsl(sections[0]))
|
||||
@ -403,18 +408,94 @@ class AllnetServlet:
|
||||
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:
|
||||
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})")
|
||||
return "OK".encode()
|
||||
return PlainTextResponse("OK")
|
||||
|
||||
def handle_alive(self, request: Request, match: Dict) -> bytes:
|
||||
return "OK".encode()
|
||||
async def handle_alive(self, request: Request) -> bytes:
|
||||
return PlainTextResponse("OK")
|
||||
|
||||
def handle_billing_request(self, request: Request, _: Dict):
|
||||
req_raw = request.content.getvalue()
|
||||
async def handle_naomitest(self, request: Request) -> bytes:
|
||||
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)
|
||||
else:
|
||||
req_unzip = req_raw
|
||||
@ -423,8 +504,8 @@ class AllnetServlet:
|
||||
request_ip = Utils.get_ip_addr(request)
|
||||
|
||||
if req_dict is None:
|
||||
self.logger.error(f"Failed to parse request {request.content.getvalue()}")
|
||||
return b""
|
||||
self.logger.error(f"Failed to parse request {req_raw}")
|
||||
return PlainTextResponse()
|
||||
|
||||
self.logger.debug(f"request {req_dict}")
|
||||
|
||||
@ -436,7 +517,7 @@ class AllnetServlet:
|
||||
req = BillingInfo(req_dict[0])
|
||||
except KeyError as 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)):
|
||||
if not req_dict[x]:
|
||||
@ -467,7 +548,7 @@ class AllnetServlet:
|
||||
)
|
||||
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 = (
|
||||
f"Billing checkin from {request_ip}: game {req.gameid} ver {req.gamever} keychip {req.keychipid} playcount "
|
||||
@ -496,7 +577,6 @@ class AllnetServlet:
|
||||
|
||||
# TODO: playhistory
|
||||
|
||||
#resp = BillingResponse(playlimit, playlimit_sig, nearfull, nearfull_sig)
|
||||
resp = BillingResponse(playlimit, playlimit_sig, nearfull, nearfull_sig, req.requestno, req.protocolver)
|
||||
|
||||
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)}")
|
||||
if req.traceleft > 0:
|
||||
self.logger.info(f"Requesting 20 more of {req.traceleft} unsent tracelogs")
|
||||
return f"result=6&waittime=0&linelimit=20\r\n".encode()
|
||||
|
||||
return resp_str.encode("utf-8")
|
||||
|
||||
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)
|
||||
|
||||
return PlainTextResponse("result=6&waittime=0&linelimit=20\r\n")
|
||||
|
||||
return PlainTextResponse(resp_str)
|
||||
|
||||
class AllnetPowerOnRequest:
|
||||
def __init__(self, req: Dict) -> None:
|
||||
@ -613,7 +646,6 @@ class AllnetPowerOnResponse3(AllnetPowerOnResponse):
|
||||
self.minute = None
|
||||
self.second = None
|
||||
|
||||
|
||||
class AllnetPowerOnResponse2(AllnetPowerOnResponse):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
@ -623,7 +655,6 @@ class AllnetPowerOnResponse2(AllnetPowerOnResponse):
|
||||
self.timezone = "+09:00"
|
||||
self.res_class = "PowerOnResponseV2"
|
||||
|
||||
|
||||
class AllnetDownloadOrderRequest:
|
||||
def __init__(self, req: Dict) -> None:
|
||||
self.game_id = req.get("game_id", "")
|
||||
@ -631,7 +662,6 @@ class AllnetDownloadOrderRequest:
|
||||
self.serial = req.get("serial", "")
|
||||
self.encode = req.get("encode", "")
|
||||
|
||||
|
||||
class AllnetDownloadOrderResponse:
|
||||
def __init__(self, stat: int = 1, serial: str = "", uri: str = "") -> None:
|
||||
self.stat = stat
|
||||
@ -781,7 +811,6 @@ class BillingResponse:
|
||||
# playhistory -> YYYYMM/C:...
|
||||
# YYYY -> 4 digit year, MM -> 2 digit month, C -> Playcount during that period
|
||||
|
||||
|
||||
class AllnetRequestException(Exception):
|
||||
def __init__(self, message="") -> None:
|
||||
self.message = message
|
||||
@ -849,3 +878,26 @@ class DLReport:
|
||||
return False
|
||||
|
||||
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
|
||||
from typing import Any
|
||||
|
||||
|
||||
class ServerConfig:
|
||||
def __init__(self, parent_config: "CoreConfig") -> None:
|
||||
self.__config = parent_config
|
||||
|
||||
@property
|
||||
def listen_address(self) -> str:
|
||||
"""
|
||||
Address Artemis will bind to and listen on
|
||||
"""
|
||||
return CoreConfig.get_config_field(
|
||||
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
|
||||
def allow_user_registration(self) -> bool:
|
||||
@ -43,9 +75,13 @@ class ServerConfig:
|
||||
)
|
||||
|
||||
@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(
|
||||
self.__config, "core", "server", "threading", default=False
|
||||
self.__config, "core", "title", "proxy_port", default=0
|
||||
)
|
||||
|
||||
@property
|
||||
@ -66,7 +102,6 @@ class ServerConfig:
|
||||
self.__config, "core", "server", "strict_ip_checking", default=False
|
||||
)
|
||||
|
||||
|
||||
class TitleConfig:
|
||||
def __init__(self, parent_config: "CoreConfig") -> None:
|
||||
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
|
||||
def reboot_start_time(self) -> str:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "core", "title", "reboot_start_time", default=""
|
||||
self.__config, "core", "title", "reboot_start_time", default="04:00"
|
||||
)
|
||||
|
||||
@property
|
||||
def reboot_end_time(self) -> str:
|
||||
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:
|
||||
def __init__(self, parent_config: "CoreConfig") -> None:
|
||||
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
|
||||
def enable_memcached(self) -> bool:
|
||||
return CoreConfig.get_config_field(
|
||||
@ -198,21 +192,14 @@ class DatabaseConfig:
|
||||
self.__config, "core", "database", "memcached_host", default="localhost"
|
||||
)
|
||||
|
||||
|
||||
class FrontendConfig:
|
||||
def __init__(self, parent_config: "CoreConfig") -> None:
|
||||
self.__config = parent_config
|
||||
|
||||
@property
|
||||
def enable(self) -> int:
|
||||
def standalone(self) -> int:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "core", "frontend", "enable", default=False
|
||||
)
|
||||
|
||||
@property
|
||||
def port(self) -> int:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "core", "frontend", "port", default=8090
|
||||
self.__config, "core", "frontend", "standalone", default=True
|
||||
)
|
||||
|
||||
@property
|
||||
@ -222,7 +209,12 @@ class FrontendConfig:
|
||||
self.__config, "core", "frontend", "loglevel", default="info"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@property
|
||||
def secret(self) -> str:
|
||||
CoreConfig.get_config_field(
|
||||
self.__config, "core", "frontend", "secret", default=""
|
||||
)
|
||||
|
||||
class AllnetConfig:
|
||||
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
|
||||
def allow_online_updates(self) -> int:
|
||||
return CoreConfig.get_config_field(
|
||||
@ -260,10 +240,23 @@ class AllnetConfig:
|
||||
self.__config, "core", "allnet", "update_cfg_folder", default=""
|
||||
)
|
||||
|
||||
|
||||
class BillingConfig:
|
||||
def __init__(self, parent_config: "CoreConfig") -> None:
|
||||
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
|
||||
def port(self) -> int:
|
||||
@ -289,7 +282,6 @@ class BillingConfig:
|
||||
self.__config, "core", "billing", "signing_key", default="cert/billing.key"
|
||||
)
|
||||
|
||||
|
||||
class AimedbConfig:
|
||||
def __init__(self, parent_config: "CoreConfig") -> None:
|
||||
self.__config = parent_config
|
||||
@ -326,17 +318,10 @@ class AimedbConfig:
|
||||
self.__config, "core", "aimedb", "id_lifetime_seconds", default=86400
|
||||
)
|
||||
|
||||
|
||||
class MuchaConfig:
|
||||
def __init__(self, parent_config: "CoreConfig") -> None:
|
||||
self.__config = parent_config
|
||||
|
||||
@property
|
||||
def enable(self) -> int:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "core", "mucha", "enable", default=False
|
||||
)
|
||||
|
||||
@property
|
||||
def loglevel(self) -> int:
|
||||
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):
|
||||
def __init__(self) -> None:
|
||||
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 twisted.web import resource
|
||||
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 twisted.web.server import Session
|
||||
from zope.interface import Interface, Attribute, implementer
|
||||
@ -98,8 +100,15 @@ class FrontendServlet(resource.Resource):
|
||||
self.putChild(b"game", fe_game)
|
||||
|
||||
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):
|
||||
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
|
||||
from logging.handlers import TimedRotatingFileHandler
|
||||
from twisted.web import resource
|
||||
from twisted.web.http import Request
|
||||
from starlette.requests import Request
|
||||
from datetime import datetime
|
||||
from Crypto.Cipher import Blowfish
|
||||
import pytz
|
||||
@ -12,7 +12,7 @@ from .utils import Utils
|
||||
from .title import TitleServlet
|
||||
|
||||
class MuchaServlet:
|
||||
mucha_registry: List[str] = []
|
||||
mucha_registry: Dict[str, str] = {}
|
||||
def __init__(self, cfg: CoreConfig, cfg_dir: str) -> None:
|
||||
self.config = cfg
|
||||
self.config_dir = cfg_dir
|
||||
@ -39,11 +39,12 @@ class MuchaServlet:
|
||||
|
||||
for _, mod in TitleServlet.title_registry.items():
|
||||
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
|
||||
)
|
||||
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")
|
||||
|
||||
@ -75,7 +76,7 @@ class MuchaServlet:
|
||||
self.logger.debug(f"Decrypt SN to {sn_decrypt.hex()}")
|
||||
|
||||
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)}")
|
||||
@ -100,7 +101,7 @@ class MuchaServlet:
|
||||
self.logger.warning(f"Unknown gameCd {req.gameCd}")
|
||||
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)}")
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
from typing import Dict, List, Tuple
|
||||
import logging, coloredlogs
|
||||
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.data import Data
|
||||
@ -28,18 +30,16 @@ class BaseServlet:
|
||||
"""
|
||||
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
|
||||
|
||||
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,
|
||||
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.
|
||||
List[Route]: A list of Routes, WebSocketRoutes, or similar classes
|
||||
"""
|
||||
return (
|
||||
[("render_GET", "/{game}/{version}/{endpoint}", {'game': R'S...'})],
|
||||
[("render_POST", "/{game}/{version}/{endpoint}", {'game': R'S...'})]
|
||||
)
|
||||
return [
|
||||
Route("/{game}/{version}/{endpoint}", self.render_POST, methods=["POST"]),
|
||||
Route("/{game}/{version}/{endpoint}", self.render_GET, methods=["GET"]),
|
||||
]
|
||||
|
||||
def setup(self) -> None:
|
||||
"""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
|
||||
"""
|
||||
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
|
||||
|
||||
Args:
|
||||
@ -72,15 +72,15 @@ class BaseServlet:
|
||||
Returns:
|
||||
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:
|
||||
self.logger.warn(f"{game_code} Does not dispatch POST")
|
||||
return None
|
||||
async def render_POST(self, request: Request) -> bytes:
|
||||
self.logger.warn(f"Game Does not dispatch POST")
|
||||
return Response()
|
||||
|
||||
def render_GET(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
||||
self.logger.warn(f"{game_code} Does not dispatch GET")
|
||||
return None
|
||||
async def render_GET(self, request: Request) -> bytes:
|
||||
self.logger.warn(f"Game Does not dispatch GET")
|
||||
return Response()
|
||||
|
||||
class TitleServlet:
|
||||
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.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:
|
||||
|
@ -1,6 +1,6 @@
|
||||
from typing import Dict, Any, Optional
|
||||
from types import ModuleType
|
||||
from twisted.web.http import Request
|
||||
from starlette.requests import Request
|
||||
import logging
|
||||
import importlib
|
||||
from os import walk
|
||||
@ -34,36 +34,16 @@ class Utils:
|
||||
|
||||
@classmethod
|
||||
def get_ip_addr(cls, req: Request) -> str:
|
||||
return (
|
||||
req.getAllHeaders()[b"x-forwarded-for"].decode()
|
||||
if b"x-forwarded-for" in req.getAllHeaders()
|
||||
else req.getClientAddress().host
|
||||
)
|
||||
return req.headers.get("x-forwarded-for", req.client.host)
|
||||
|
||||
@classmethod
|
||||
def get_title_port(cls, cfg: CoreConfig):
|
||||
if cls.real_title_port is not None: return cls.real_title_port
|
||||
|
||||
if cfg.title.port == 0:
|
||||
cls.real_title_port = cfg.allnet.port
|
||||
|
||||
else:
|
||||
cls.real_title_port = cfg.title.port
|
||||
cls.real_title_port = cfg.server.proxy_port if cfg.server.is_using_proxy else cfg.server.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]:
|
||||
logger = logging.getLogger(err_logger)
|
||||
try:
|
||||
|
67
dbutils.py
67
dbutils.py
@ -1,9 +1,11 @@
|
||||
import yaml
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import logging
|
||||
from core.config import CoreConfig
|
||||
from core.data import Data
|
||||
from os import path, mkdir, access, W_OK
|
||||
from os import mkdir, path, access, W_OK
|
||||
import yaml
|
||||
import asyncio
|
||||
|
||||
from core import Data, CoreConfig
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Database utilities")
|
||||
@ -16,19 +18,9 @@ if __name__ == "__main__":
|
||||
type=str,
|
||||
help="Version of the database to upgrade/rollback to",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--game",
|
||||
"-g",
|
||||
type=str,
|
||||
help="Game code of the game who's schema will be updated/rolled back. Ex. SDFE",
|
||||
)
|
||||
parser.add_argument("--email", "-e", type=str, help="Email for the new user")
|
||||
parser.add_argument("--old_ac", "-o", type=str, help="Access code to transfer from")
|
||||
parser.add_argument("--new_ac", "-n", type=str, help="Access code to transfer to")
|
||||
parser.add_argument("--force", "-f", type=bool, help="Force the action to happen")
|
||||
parser.add_argument(
|
||||
"action", type=str, help="DB Action, create, recreate, upgrade, or rollback"
|
||||
)
|
||||
parser.add_argument("--access_code", "-a", type=str, help="Access code for new/transfer user", default="00000000000000000000")
|
||||
parser.add_argument("action", type=str, help="create, upgrade, create-owner")
|
||||
args = parser.parse_args()
|
||||
|
||||
cfg = CoreConfig()
|
||||
@ -50,42 +42,13 @@ if __name__ == "__main__":
|
||||
|
||||
if args.action == "create":
|
||||
data.create_database()
|
||||
|
||||
elif args.action == "recreate":
|
||||
data.recreate_database()
|
||||
|
||||
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 == "upgrade":
|
||||
data.schema_upgrade(args.version)
|
||||
|
||||
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":
|
||||
data.migrate_card(args.old_ac, args.new_ac, args.force)
|
||||
|
||||
elif args.action == "cleanup":
|
||||
data.delete_hanging_users()
|
||||
|
||||
elif args.action == "version":
|
||||
data.show_versions()
|
||||
|
||||
data.logger.info("Done")
|
||||
else:
|
||||
logging.getLogger("database").info(f"Unknown action {args.action}")
|
||||
|
@ -1,26 +1,24 @@
|
||||
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_unregistered_serials: True
|
||||
name: "ARTEMiS"
|
||||
is_develop: True
|
||||
is_using_proxy: False
|
||||
threading: False
|
||||
proxy_port: 0
|
||||
log_dir: "logs"
|
||||
check_arcade_ip: False
|
||||
strict_ip_checking: False
|
||||
|
||||
title:
|
||||
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_end_time: "05:00"
|
||||
|
||||
|
||||
ssl_key: "cert/title.key"
|
||||
database:
|
||||
host: "localhost"
|
||||
username: "aime"
|
||||
@ -30,23 +28,22 @@ database:
|
||||
protocol: "mysql"
|
||||
sha2_password: False
|
||||
loglevel: "warn"
|
||||
user_table_autoincrement_start: 10000
|
||||
enable_memcached: True
|
||||
memcached_host: "localhost"
|
||||
|
||||
frontend:
|
||||
enable: False
|
||||
port: 8090
|
||||
standalone: True
|
||||
loglevel: "info"
|
||||
secret: ""
|
||||
|
||||
allnet:
|
||||
loglevel: "info"
|
||||
port: 80
|
||||
ip_check: False
|
||||
allow_online_updates: False
|
||||
update_cfg_folder: ""
|
||||
|
||||
billing:
|
||||
standalone: True
|
||||
loglevel: "info"
|
||||
port: 8443
|
||||
ssl_key: "cert/server.key"
|
||||
ssl_cert: "cert/server.pem"
|
||||
@ -60,6 +57,4 @@ aimedb:
|
||||
id_lifetime_seconds: 86400
|
||||
|
||||
mucha:
|
||||
enable: False
|
||||
hostname: "localhost"
|
||||
loglevel: "info"
|
||||
|
375
index.py
375
index.py
@ -1,335 +1,92 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import logging, coloredlogs
|
||||
from logging.handlers import TimedRotatingFileHandler
|
||||
from typing import Dict
|
||||
import yaml
|
||||
from os import path, mkdir, access, W_OK
|
||||
from core import *
|
||||
from os import path, mkdir, access, W_OK, environ
|
||||
import uvicorn
|
||||
import logging
|
||||
import asyncio
|
||||
|
||||
from twisted.web import server, resource
|
||||
from twisted.internet import reactor, endpoints
|
||||
from twisted.web.http import Request
|
||||
from routes import Mapper
|
||||
from threading import Thread
|
||||
from core import CoreConfig, AimedbServlette
|
||||
|
||||
class HttpDispatcher(resource.Resource):
|
||||
def __init__(self, cfg: CoreConfig, config_dir: str):
|
||||
super().__init__()
|
||||
self.config = cfg
|
||||
self.isLeaf = True
|
||||
self.map_get = Mapper()
|
||||
self.map_post = Mapper()
|
||||
self.logger = logging.getLogger("core")
|
||||
|
||||
self.title = TitleServlet(cfg, config_dir)
|
||||
self.allnet = AllnetServlet(cfg, config_dir)
|
||||
self.mucha = MuchaServlet(cfg, config_dir)
|
||||
|
||||
self.map_get.connect(
|
||||
"allnet_downloadorder_ini",
|
||||
"/dl/ini/{file}",
|
||||
controller="allnet",
|
||||
action="handle_dlorder_ini",
|
||||
conditions=dict(method=["GET"]),
|
||||
async def launch_main(cfg: CoreConfig, ssl: bool) -> None:
|
||||
if ssl:
|
||||
server_cfg = uvicorn.Config(
|
||||
"core.app:app",
|
||||
host=cfg.server.listen_address,
|
||||
port=cfg.server.port if args.port == 0 else args.port,
|
||||
reload=cfg.server.is_develop,
|
||||
log_level="info" if cfg.server.is_develop else "critical",
|
||||
ssl_version=3,
|
||||
ssl_certfile=cfg.server.ssl_cert,
|
||||
ssl_keyfile=cfg.server.ssl_key
|
||||
)
|
||||
else:
|
||||
server_cfg = uvicorn.Config(
|
||||
"core.app:app",
|
||||
host=cfg.server.listen_address,
|
||||
port=cfg.server.port if args.port == 0 else args.port,
|
||||
reload=cfg.server.is_develop,
|
||||
log_level="info" if cfg.server.is_develop else "critical"
|
||||
)
|
||||
server = uvicorn.Server(server_cfg)
|
||||
await server.serve()
|
||||
|
||||
self.map_post.connect(
|
||||
"allnet_downloadorder_report",
|
||||
"/report-api/Report",
|
||||
controller="allnet",
|
||||
action="handle_dlorder_report",
|
||||
conditions=dict(method=["POST"]),
|
||||
async def launch_billing(cfg: CoreConfig) -> None:
|
||||
server_cfg = uvicorn.Config(
|
||||
"core.allnet:app",
|
||||
host=cfg.server.listen_address,
|
||||
port=cfg.billing.port,
|
||||
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
|
||||
self.map_post.connect(
|
||||
"mucha_boardauth",
|
||||
"/mucha/boardauth.do",
|
||||
controller="mucha",
|
||||
action="handle_boardauth",
|
||||
conditions=dict(method=["POST"]),
|
||||
)
|
||||
self.map_post.connect(
|
||||
"mucha_updatacheck",
|
||||
"/mucha/updatacheck.do",
|
||||
controller="mucha",
|
||||
action="handle_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""
|
||||
async def launcher(cfg: CoreConfig, ssl: bool) -> None:
|
||||
AimedbServlette(cfg).start()
|
||||
done, pending = await asyncio.wait(
|
||||
[
|
||||
asyncio.create_task(launch_main(cfg, ssl)),
|
||||
asyncio.create_task(launch_billing(cfg)),
|
||||
|
||||
],
|
||||
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__":
|
||||
parser = argparse.ArgumentParser(description="ARTEMiS main entry point")
|
||||
parser = argparse.ArgumentParser(description="Artemis main entry point")
|
||||
parser.add_argument(
|
||||
"--config", "-c", type=str, default="config", help="Configuration folder"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--port", "-p", type=int, default=0, help="Port override"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--ssl", "-s", type=bool, help="Launch with SSL"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
if not path.exists(f"{args.config}/core.yaml"):
|
||||
print(
|
||||
f"The config folder you specified ({args.config}) does not exist or does not contain core.yaml.\nDid you copy the example folder?"
|
||||
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()
|
||||
if path.exists(f"{args.config}/core.yaml"):
|
||||
cfg.update(yaml.safe_load(open(f"{args.config}/core.yaml")))
|
||||
|
||||
if not path.exists(cfg.server.log_dir):
|
||||
mkdir(cfg.server.log_dir)
|
||||
environ["ARTEMIS_CFG_DIR"] = args.config
|
||||
|
||||
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)
|
||||
|
||||
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()
|
||||
asyncio.run(launcher(cfg, args.ssl))
|
||||
|
2
read.py
2
read.py
@ -1,4 +1,4 @@
|
||||
# vim: set fileencoding=utf-8
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import re
|
||||
import os
|
||||
|
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
@ -11,7 +11,7 @@ class ChuniAir(ChuniBase):
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_AIR
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = await super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.10.00"
|
||||
return ret
|
||||
|
@ -11,7 +11,7 @@ class ChuniAirPlus(ChuniBase):
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_AIR_PLUS
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = await super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.15.00"
|
||||
return ret
|
||||
|
@ -13,7 +13,7 @@ class ChuniAmazon(ChuniBase):
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_AMAZON
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = await super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.30.00"
|
||||
return ret
|
||||
|
@ -13,7 +13,7 @@ class ChuniAmazonPlus(ChuniBase):
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_AMAZON_PLUS
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = await super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.35.00"
|
||||
return ret
|
||||
|
@ -22,7 +22,7 @@ class ChuniBase:
|
||||
self.game = ChuniConstants.GAME_CODE
|
||||
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
|
||||
getUserLoginBonus gets called after getUserItem and therefore the
|
||||
@ -119,11 +119,11 @@ class ChuniBase:
|
||||
|
||||
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"]})
|
||||
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)
|
||||
|
||||
if game_charge_list is None or len(game_charge_list) == 0:
|
||||
@ -145,7 +145,7 @@ class ChuniBase:
|
||||
)
|
||||
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)
|
||||
|
||||
if game_events is None or len(game_events) == 0:
|
||||
@ -177,10 +177,10 @@ class ChuniBase:
|
||||
"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": []}
|
||||
|
||||
def handle_get_game_message_api_request(self, data: Dict) -> Dict:
|
||||
async def handle_get_game_message_api_request(self, data: Dict) -> Dict:
|
||||
return {
|
||||
"type": data["type"],
|
||||
"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)
|
||||
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": []}
|
||||
|
||||
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 self.core_cfg.title.reboot_start_time == "" or self.core_cfg.title.reboot_end_time == "":
|
||||
reboot_start = datetime.strftime(
|
||||
@ -240,7 +240,7 @@ class ChuniBase:
|
||||
"isDumpUpload": "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(
|
||||
data["userId"], data["kind"]
|
||||
)
|
||||
@ -261,7 +261,7 @@ class ChuniBase:
|
||||
"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"])
|
||||
if characters is None:
|
||||
return {
|
||||
@ -296,7 +296,7 @@ class ChuniBase:
|
||||
"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"])
|
||||
|
||||
charge_list = []
|
||||
@ -312,14 +312,14 @@ class ChuniBase:
|
||||
"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 {
|
||||
"userId": data["userId"],
|
||||
"length": 0,
|
||||
"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"])
|
||||
if user_course_list is None:
|
||||
return {
|
||||
@ -354,7 +354,7 @@ class ChuniBase:
|
||||
"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)
|
||||
if p is None:
|
||||
return {}
|
||||
@ -366,7 +366,7 @@ class ChuniBase:
|
||||
|
||||
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)
|
||||
if p is None:
|
||||
return {}
|
||||
@ -378,7 +378,7 @@ class ChuniBase:
|
||||
|
||||
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"])
|
||||
if user_duel_list is None:
|
||||
return {}
|
||||
@ -396,7 +396,7 @@ class ChuniBase:
|
||||
"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"])
|
||||
if p is None:
|
||||
return {}
|
||||
@ -409,7 +409,7 @@ class ChuniBase:
|
||||
"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"]
|
||||
next_index = int(data["nextIndex"])
|
||||
max_count = int(data["maxCount"])
|
||||
@ -462,7 +462,7 @@ class ChuniBase:
|
||||
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 = []
|
||||
|
||||
# still needs to be implemented on WebUI
|
||||
@ -482,14 +482,14 @@ class ChuniBase:
|
||||
"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
|
||||
"""
|
||||
|
||||
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)
|
||||
next_idx = int(int(data["nextIndex"]) % 10000000000)
|
||||
user_item_list = self.data.item.get_items(data["userId"], kind)
|
||||
@ -526,7 +526,7 @@ class ChuniBase:
|
||||
"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_login_bonus = self.data.item.get_all_login_bonus(user_id, self.version)
|
||||
# ignore the loginBonus request if its disabled in config
|
||||
@ -552,7 +552,7 @@ class ChuniBase:
|
||||
"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"])
|
||||
if user_map_list is None:
|
||||
return {}
|
||||
@ -570,7 +570,7 @@ class ChuniBase:
|
||||
"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"])
|
||||
if music_detail is None:
|
||||
return {
|
||||
@ -629,7 +629,7 @@ class ChuniBase:
|
||||
"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"])
|
||||
|
||||
option = p._asdict()
|
||||
@ -638,7 +638,7 @@ class ChuniBase:
|
||||
|
||||
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"])
|
||||
|
||||
option = p._asdict()
|
||||
@ -650,7 +650,7 @@ class ChuniBase:
|
||||
def read_wtf8(self, src):
|
||||
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)
|
||||
if profile is None:
|
||||
return None
|
||||
@ -692,7 +692,7 @@ class ChuniBase:
|
||||
"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"])
|
||||
if recent_rating_list is None:
|
||||
return {
|
||||
@ -707,7 +707,7 @@ class ChuniBase:
|
||||
"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
|
||||
return {
|
||||
"userId": data["userId"],
|
||||
@ -715,7 +715,7 @@ class ChuniBase:
|
||||
"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
|
||||
team_id = 65535
|
||||
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 {
|
||||
"userId": data["userId"],
|
||||
"length": 0,
|
||||
@ -758,7 +758,7 @@ class ChuniBase:
|
||||
"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 {
|
||||
"userId": data["userId"],
|
||||
"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 {
|
||||
"userId": data["userId"],
|
||||
"length": 0,
|
||||
@ -790,7 +790,7 @@ class ChuniBase:
|
||||
"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 {
|
||||
"userId": data["userId"],
|
||||
"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"]
|
||||
user_id = data["userId"]
|
||||
|
||||
@ -927,28 +927,28 @@ class ChuniBase:
|
||||
|
||||
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
|
||||
# still valid after an unsuccessful logout
|
||||
self.data.profile.put_profile_charge(data["userId"], data["userCharge"])
|
||||
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"}
|
||||
|
||||
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"}
|
||||
|
||||
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"}
|
||||
|
||||
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"}
|
||||
|
||||
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"}
|
||||
|
||||
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 {
|
||||
"userId": data["userId"],
|
||||
"userNetBattleData": {"recentNBSelectMusicList": []},
|
||||
|
@ -13,7 +13,7 @@ class ChuniCrystal(ChuniBase):
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_CRYSTAL
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = await super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.40.00"
|
||||
return ret
|
||||
|
@ -13,7 +13,7 @@ class ChuniCrystalPlus(ChuniBase):
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_CRYSTAL_PLUS
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = await super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.45.00"
|
||||
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
|
||||
from logging.handlers import TimedRotatingFileHandler
|
||||
import zlib
|
||||
@ -33,7 +35,6 @@ from .newplus import ChuniNewPlus
|
||||
from .sun import ChuniSun
|
||||
from .sunplus import ChuniSunPlus
|
||||
|
||||
|
||||
class ChuniServlet(BaseServlet):
|
||||
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
|
||||
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()}"
|
||||
)
|
||||
|
||||
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
|
||||
def is_game_enabled(
|
||||
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]:
|
||||
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:
|
||||
endpoint = matchers['endpoint']
|
||||
version = int(matchers['version'])
|
||||
game_code = matchers['game']
|
||||
def get_routes(self) -> List[Route]:
|
||||
return [
|
||||
Route("/{game:str}/{version:int}/ChuniServlet/{endpoint:str}", self.render_POST, methods=['POST']),
|
||||
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":
|
||||
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
|
||||
internal_ver = 0
|
||||
@ -201,7 +199,7 @@ class ChuniServlet(BaseServlet):
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_SUN_PLUS
|
||||
elif game_code == "SDGS": # Int
|
||||
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
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_NEW
|
||||
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
|
||||
# technically not 0
|
||||
if internal_ver < ChuniConstants.VER_CHUNITHM_NEW:
|
||||
endpoint = request.getHeader("User-Agent").split("#")[0]
|
||||
endpoint = request.headers.get("User-Agent").split("#")[0]
|
||||
|
||||
else:
|
||||
if internal_ver not in self.hash_table:
|
||||
self.logger.error(
|
||||
f"v{version} does not support encryption or no keys entered"
|
||||
)
|
||||
return zlib.compress(b'{"stat": "0"}')
|
||||
return Response(zlib.compress(b'{"stat": "0"}'))
|
||||
|
||||
elif endpoint.lower() not in self.hash_table[internal_ver]:
|
||||
self.logger.error(
|
||||
f"No hash found for v{version} endpoint {endpoint}"
|
||||
)
|
||||
return zlib.compress(b'{"stat": "0"}')
|
||||
return Response(zlib.compress(b'{"stat": "0"}'))
|
||||
|
||||
endpoint = self.hash_table[internal_ver][endpoint.lower()]
|
||||
|
||||
@ -246,7 +244,7 @@ class ChuniServlet(BaseServlet):
|
||||
self.logger.error(
|
||||
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
|
||||
|
||||
@ -258,7 +256,7 @@ class ChuniServlet(BaseServlet):
|
||||
self.logger.error(
|
||||
f"Unencrypted v{version} {endpoint} request, but config is set to encrypted only: {req_raw}"
|
||||
)
|
||||
return zlib.compress(b'{"stat": "0"}')
|
||||
return Response(zlib.compress(b'{"stat": "0"}'))
|
||||
|
||||
try:
|
||||
unzip = zlib.decompress(req_raw)
|
||||
@ -267,7 +265,7 @@ class ChuniServlet(BaseServlet):
|
||||
self.logger.error(
|
||||
f"Failed to decompress v{version} {endpoint} request -> {e}"
|
||||
)
|
||||
return b""
|
||||
return Response(zlib.compress(b'{"stat": "0"}'))
|
||||
|
||||
req_data = json.loads(unzip)
|
||||
|
||||
@ -285,11 +283,11 @@ class ChuniServlet(BaseServlet):
|
||||
else:
|
||||
try:
|
||||
handler = getattr(handler_cls, func_to_find)
|
||||
resp = handler(req_data)
|
||||
resp = await handler(req_data)
|
||||
|
||||
except Exception as 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:
|
||||
resp = {"returnCode": 1}
|
||||
@ -299,7 +297,7 @@ class ChuniServlet(BaseServlet):
|
||||
zipped = zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
|
||||
|
||||
if not encrtped:
|
||||
return zipped
|
||||
return Response(zipped)
|
||||
|
||||
padded = pad(zipped, 16)
|
||||
|
||||
@ -309,4 +307,4 @@ class ChuniServlet(BaseServlet):
|
||||
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:
|
||||
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
|
||||
# matching therefore starts one hour before and lasts for 8 hours
|
||||
match_start = datetime.strftime(
|
||||
@ -82,26 +82,26 @@ class ChuniNew(ChuniBase):
|
||||
"matchErrorLimit": self.game_cfg.matching.match_error_limit,
|
||||
"romVersion": self.game_cfg.version.version(self.version)["rom"],
|
||||
"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/",
|
||||
"matchingUriX": 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.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
|
||||
"udpHolePunchUri": 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.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.server.hostname}:{self.core_cfg.server.port}/SDHD/{self._interal_ver_to_intver()}/ChuniServlet/",
|
||||
},
|
||||
"isDumpUpload": 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"}
|
||||
|
||||
def handle_delete_token_api_request(self, data: Dict) -> Dict:
|
||||
async def handle_delete_token_api_request(self, data: Dict) -> Dict:
|
||||
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"}
|
||||
|
||||
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"])
|
||||
|
||||
map_areas = []
|
||||
@ -113,10 +113,10 @@ class ChuniNew(ChuniBase):
|
||||
|
||||
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": []}
|
||||
|
||||
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)
|
||||
if profile is None:
|
||||
return None
|
||||
@ -164,7 +164,7 @@ class ChuniNew(ChuniBase):
|
||||
}
|
||||
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)
|
||||
if p is None:
|
||||
return {}
|
||||
@ -177,13 +177,13 @@ class ChuniNew(ChuniBase):
|
||||
"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}
|
||||
|
||||
def handle_printer_logout_api_request(self, data: Dict) -> Dict:
|
||||
async def handle_printer_logout_api_request(self, data: Dict) -> Dict:
|
||||
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)
|
||||
"""
|
||||
@ -213,7 +213,7 @@ class ChuniNew(ChuniBase):
|
||||
"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
|
||||
"""
|
||||
@ -237,7 +237,7 @@ class ChuniNew(ChuniBase):
|
||||
"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)
|
||||
if p is None:
|
||||
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"])
|
||||
if user_gachas is None:
|
||||
return {"userId": data["userId"], "length": 0, "userGachaList": []}
|
||||
@ -281,7 +281,7 @@ class ChuniNew(ChuniBase):
|
||||
"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(
|
||||
data["userId"], has_completed=True
|
||||
)
|
||||
@ -316,7 +316,7 @@ class ChuniNew(ChuniBase):
|
||||
"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_print_states = self.data.item.get_user_print_states(
|
||||
@ -338,13 +338,13 @@ class ChuniNew(ChuniBase):
|
||||
"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)
|
||||
|
||||
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)
|
||||
|
||||
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:
|
||||
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}
|
||||
|
||||
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"]
|
||||
user_id = data["userId"]
|
||||
place_id = data["placeId"]
|
||||
@ -441,7 +441,7 @@ class ChuniNew(ChuniBase):
|
||||
"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 {
|
||||
"returnCode": 1,
|
||||
"orderId": 0,
|
||||
@ -449,7 +449,7 @@ class ChuniNew(ChuniBase):
|
||||
"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_id = data["userId"]
|
||||
|
||||
@ -474,7 +474,7 @@ class ChuniNew(ChuniBase):
|
||||
"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"]
|
||||
user_id = data["userId"]
|
||||
place_id = data["placeId"]
|
||||
@ -491,7 +491,7 @@ class ChuniNew(ChuniBase):
|
||||
|
||||
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"]
|
||||
user_id = data["userId"]
|
||||
|
||||
@ -501,11 +501,11 @@ class ChuniNew(ChuniBase):
|
||||
|
||||
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
|
||||
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
|
||||
# check if there is a free matching room
|
||||
matching_room = self.data.item.get_oldest_free_matching(self.version)
|
||||
@ -554,7 +554,7 @@ class ChuniNew(ChuniBase):
|
||||
|
||||
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"])
|
||||
members = matching_room["matchingMemberInfoList"]
|
||||
|
||||
@ -579,10 +579,10 @@ class ChuniNew(ChuniBase):
|
||||
# no idea, maybe to differentiate between CPUs and real players?
|
||||
"matchingMemberRoleList": role_list,
|
||||
# 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
|
||||
# not the actual roomId
|
||||
matching_rooms = self.data.item.get_all_matchings(self.version)
|
||||
@ -612,7 +612,7 @@ class ChuniNew(ChuniBase):
|
||||
|
||||
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
|
||||
# get the current active room
|
||||
matching_room = self.data.item.get_matching(self.version, data["roomId"])
|
||||
|
@ -11,8 +11,8 @@ class ChuniNewPlus(ChuniNew):
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_NEW_PLUS
|
||||
|
||||
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||
user_data = super().handle_cm_get_user_preview_api_request(data)
|
||||
async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||
user_data = await super().handle_cm_get_user_preview_api_request(data)
|
||||
|
||||
# hardcode lastDataVersion for CardMaker 1.35 A028
|
||||
user_data["lastDataVersion"] = "2.05.00"
|
||||
|
@ -13,7 +13,7 @@ class ChuniParadise(ChuniBase):
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_PARADISE
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = await super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.50.00"
|
||||
return ret
|
||||
|
@ -11,7 +11,7 @@ class ChuniPlus(ChuniBase):
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_PLUS
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = await super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.05.00"
|
||||
return ret
|
||||
|
@ -11,7 +11,7 @@ class ChuniStar(ChuniBase):
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_STAR
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = await super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.20.00"
|
||||
return ret
|
||||
|
@ -11,7 +11,7 @@ class ChuniStarPlus(ChuniBase):
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_STAR_PLUS
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = await super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.25.00"
|
||||
return ret
|
||||
|
@ -11,8 +11,8 @@ class ChuniSun(ChuniNewPlus):
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_SUN
|
||||
|
||||
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||
user_data = super().handle_cm_get_user_preview_api_request(data)
|
||||
async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||
user_data = await super().handle_cm_get_user_preview_api_request(data)
|
||||
|
||||
# hardcode lastDataVersion for CardMaker 1.35 A032
|
||||
user_data["lastDataVersion"] = "2.10.00"
|
||||
|
@ -11,8 +11,8 @@ class ChuniSunPlus(ChuniSun):
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_SUN_PLUS
|
||||
|
||||
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||
user_data = super().handle_cm_get_user_preview_api_request(data)
|
||||
async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||
user_data = await super().handle_cm_get_user_preview_api_request(data)
|
||||
|
||||
# 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"
|
||||
|
@ -29,11 +29,11 @@ class CardMakerBase:
|
||||
def _parse_int_ver(version: str) -> str:
|
||||
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:
|
||||
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:
|
||||
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
|
||||
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 self.core_cfg.title.reboot_start_time == "" or self.core_cfg.title.reboot_end_time == "":
|
||||
reboot_start = datetime.strftime(
|
||||
@ -110,11 +110,11 @@ class CardMakerBase:
|
||||
"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": []}
|
||||
|
||||
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"}
|
||||
|
||||
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"}
|
||||
|
@ -12,7 +12,7 @@ class CardMaker135(CardMakerBase):
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = CardMakerConstants.VER_CARD_MAKER_135
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = await super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.35.00"
|
||||
return ret
|
||||
|
@ -5,10 +5,11 @@ import string
|
||||
import logging
|
||||
import coloredlogs
|
||||
import zlib
|
||||
|
||||
from starlette.routing import Route
|
||||
from starlette.responses import Response
|
||||
from starlette.requests import Request
|
||||
from os import path
|
||||
from typing import Tuple, List, Dict
|
||||
from twisted.web.http import Request
|
||||
from typing import List
|
||||
from logging.handlers import TimedRotatingFileHandler
|
||||
|
||||
from core.config import CoreConfig
|
||||
@ -19,7 +20,6 @@ from .const import CardMakerConstants
|
||||
from .base import CardMakerBase
|
||||
from .cm135 import CardMaker135
|
||||
|
||||
|
||||
class CardMakerServlet(BaseServlet):
|
||||
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
|
||||
super().__init__(core_cfg, cfg_dir)
|
||||
@ -72,16 +72,15 @@ class CardMakerServlet(BaseServlet):
|
||||
|
||||
return True
|
||||
|
||||
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
|
||||
return (
|
||||
[],
|
||||
[("render_POST", "/SDED/{version}/{endpoint}", {})]
|
||||
)
|
||||
|
||||
def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
||||
version = int(matchers['version'])
|
||||
endpoint = matchers['endpoint']
|
||||
req_raw = request.content.getvalue()
|
||||
def get_routes(self) -> List[Route]:
|
||||
return [
|
||||
Route("/SDED/{version:int}/{endpoint:str}", self.render_POST)
|
||||
]
|
||||
|
||||
async def render_POST(self, request: Request) -> bytes:
|
||||
version: int = request.path_params.get('version')
|
||||
endpoint: str = request.path_params.get('endpoint')
|
||||
req_raw = await request.body()
|
||||
internal_ver = 0
|
||||
client_ip = Utils.get_ip_addr(request)
|
||||
|
||||
@ -103,7 +102,7 @@ class CardMakerServlet(BaseServlet):
|
||||
self.logger.error(
|
||||
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)
|
||||
|
||||
@ -114,7 +113,7 @@ class CardMakerServlet(BaseServlet):
|
||||
|
||||
if not hasattr(self.versions[internal_ver], func_to_find):
|
||||
self.logger.warning(f"Unhandled v{version} request {endpoint}")
|
||||
return zlib.compress(b'{"returnCode": 1}')
|
||||
return Response(zlib.compress(b'{"returnCode": 1}'))
|
||||
|
||||
try:
|
||||
handler = getattr(self.versions[internal_ver], func_to_find)
|
||||
@ -123,11 +122,11 @@ class CardMakerServlet(BaseServlet):
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
|
||||
raise
|
||||
return zlib.compress(b'{"stat": "0"}')
|
||||
return Response(zlib.compress(b'{"stat": "0"}'))
|
||||
|
||||
if resp is None:
|
||||
resp = {"returnCode": 1}
|
||||
|
||||
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 []
|
||||
|
||||
def handle_action_rpreq_request(self, data: Dict) -> Dict:
|
||||
async def handle_action_rpreq_request(self, data: Dict) -> Dict:
|
||||
return {}
|
||||
|
||||
def handle_action_hitreq_request(self, data: Dict) -> Dict:
|
||||
async def handle_action_hitreq_request(self, data: Dict) -> Dict:
|
||||
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(
|
||||
0, data["usercheck"]["authid"], self.version
|
||||
)
|
||||
@ -45,11 +45,11 @@ class CxbBase:
|
||||
self.logger.info(f"No profile for aime id {data['usercheck']['authid']}")
|
||||
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']}")
|
||||
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(
|
||||
0, data["login"]["authid"], self.version
|
||||
)
|
||||
@ -198,7 +198,7 @@ class CxbBase:
|
||||
).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_end = data["loadrange"]["range"][1]
|
||||
uid = data["loadrange"]["uid"]
|
||||
@ -282,7 +282,7 @@ class CxbBase:
|
||||
|
||||
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"]
|
||||
|
||||
try:
|
||||
@ -443,7 +443,7 @@ class CxbBase:
|
||||
i += 1
|
||||
return {}
|
||||
|
||||
def handle_action_sprankreq_request(self, data: Dict) -> Dict:
|
||||
async def handle_action_sprankreq_request(self, data: Dict) -> Dict:
|
||||
uid = data["sprankreq"]["uid"]
|
||||
self.logger.info(f"Get best rankings for {uid}")
|
||||
p = self.data.score.get_best_rankings(uid)
|
||||
@ -475,16 +475,16 @@ class CxbBase:
|
||||
"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"}]}
|
||||
|
||||
def handle_action_getmsg_request(self, data: Dict) -> Dict:
|
||||
async def handle_action_getmsg_request(self, data: Dict) -> Dict:
|
||||
return {"msgs": []}
|
||||
|
||||
def handle_auth_logout_request(self, data: Dict) -> Dict:
|
||||
async def handle_auth_logout_request(self, data: Dict) -> Dict:
|
||||
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"]
|
||||
self.logger.info(f"Put {len(data['rankreg']['data'])} rankings for {uid}")
|
||||
|
||||
@ -527,7 +527,7 @@ class CxbBase:
|
||||
)
|
||||
return {}
|
||||
|
||||
def handle_action_addenergy_request(self, data: Dict) -> Dict:
|
||||
async def handle_action_addenergy_request(self, data: Dict) -> Dict:
|
||||
uid = data["addenergy"]["uid"]
|
||||
self.logger.info(f"Add energy to user {uid}")
|
||||
profile = self.data.profile.get_profile_index(0, uid, self.version)
|
||||
@ -570,10 +570,10 @@ class CxbBase:
|
||||
)
|
||||
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)
|
||||
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)
|
||||
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 sys
|
||||
import yaml
|
||||
@ -62,6 +64,14 @@ class CxbServlet(BaseServlet):
|
||||
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
|
||||
def is_game_enabled(cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str) -> bool:
|
||||
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 ""
|
||||
|
||||
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]]]:
|
||||
return (
|
||||
[],
|
||||
[
|
||||
("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?
|
||||
async def preprocess(self, req: Request) -> Dict:
|
||||
req_bytes = await req.body()
|
||||
|
||||
try:
|
||||
req_json: Dict = json.loads(req_bytes)
|
||||
@ -126,8 +122,8 @@ class CxbServlet(BaseServlet):
|
||||
|
||||
return req_json
|
||||
|
||||
def handle_data(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
||||
req_json = self.preprocess(request)
|
||||
async def handle_data(self, request: Request) -> bytes:
|
||||
req_json = await self.preprocess(request)
|
||||
func_to_find = "handle_data_"
|
||||
version_string = "Base"
|
||||
internal_ver = 0
|
||||
@ -135,7 +131,7 @@ class CxbServlet(BaseServlet):
|
||||
|
||||
if req_json == {}:
|
||||
self.logger.warning(f"Empty json request to /data")
|
||||
return b""
|
||||
return Response()
|
||||
|
||||
subcmd = list(req_json.keys())[0]
|
||||
if subcmd == "dldate":
|
||||
@ -145,14 +141,14 @@ class CxbServlet(BaseServlet):
|
||||
or "filetype" not in req_json["dldate"]
|
||||
):
|
||||
self.logger.warning(f"Malformed dldate request: {req_json}")
|
||||
return b""
|
||||
return Response()
|
||||
|
||||
filetype = req_json["dldate"]["filetype"]
|
||||
filetype_split = filetype.split("/")
|
||||
|
||||
if len(filetype_split) < 2 or not filetype_split[0].isnumeric():
|
||||
self.logger.warning(f"Malformed dldate request: {req_json}")
|
||||
return b""
|
||||
return Response()
|
||||
|
||||
version = int(filetype_split[0])
|
||||
filename = filetype_split[len(filetype_split) - 1]
|
||||
@ -184,7 +180,7 @@ class CxbServlet(BaseServlet):
|
||||
|
||||
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}")
|
||||
return({"data":""})
|
||||
return JSONResponse({"data":""})
|
||||
|
||||
self.logger.info(f"{version_string} request for filetype {filetype}")
|
||||
self.logger.debug(req_json)
|
||||
@ -192,7 +188,7 @@ class CxbServlet(BaseServlet):
|
||||
handler = getattr(self.versions[internal_ver], func_to_find)
|
||||
|
||||
try:
|
||||
resp = handler(req_json)
|
||||
resp = await handler(req_json)
|
||||
|
||||
except Exception as 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)
|
||||
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)
|
||||
return ""
|
||||
return Response()
|
||||
|
||||
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:
|
||||
req_json = self.preprocess(request)
|
||||
async def handle_action(self, request: Request) -> bytes:
|
||||
req_json = await self.preprocess(request)
|
||||
subcmd = list(req_json.keys())[0]
|
||||
func_to_find = f"handle_action_{subcmd}_request"
|
||||
|
||||
if not hasattr(self.versions[0], func_to_find):
|
||||
self.logger.warn(f"No handler for action {subcmd} request")
|
||||
return ""
|
||||
return Response()
|
||||
|
||||
self.logger.info(f"Action {subcmd} Request")
|
||||
self.logger.debug(req_json)
|
||||
@ -221,7 +217,7 @@ class CxbServlet(BaseServlet):
|
||||
handler = getattr(self.versions[0], func_to_find)
|
||||
|
||||
try:
|
||||
resp = handler(req_json)
|
||||
resp = await handler(req_json)
|
||||
|
||||
except Exception as 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)
|
||||
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)
|
||||
return ""
|
||||
return Response()
|
||||
|
||||
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:
|
||||
req_json = self.preprocess(request)
|
||||
async def handle_auth(self, request: Request) -> bytes:
|
||||
req_json = await self.preprocess(request)
|
||||
subcmd = list(req_json.keys())[0]
|
||||
func_to_find = f"handle_auth_{subcmd}_request"
|
||||
|
||||
if not hasattr(self.versions[0], func_to_find):
|
||||
self.logger.warn(f"No handler for auth {subcmd} request")
|
||||
return ""
|
||||
return Response()
|
||||
|
||||
self.logger.info(f"Action {subcmd} Request")
|
||||
self.logger.debug(req_json)
|
||||
@ -250,7 +246,7 @@ class CxbServlet(BaseServlet):
|
||||
handler = getattr(self.versions[0], func_to_find)
|
||||
|
||||
try:
|
||||
resp = handler(req_json)
|
||||
resp = await handler(req_json)
|
||||
|
||||
except Exception as 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)
|
||||
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)
|
||||
return ""
|
||||
return Response()
|
||||
|
||||
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)
|
||||
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": ""}
|
||||
|
||||
def handle_data_putlog_request(self, data: Dict) -> Dict:
|
||||
async def handle_data_putlog_request(self, data: Dict) -> Dict:
|
||||
if data["putlog"]["type"] == "ResultLog":
|
||||
score_data = json.loads(data["putlog"]["data"])
|
||||
userid = score_data["usid"]
|
||||
@ -45,7 +45,7 @@ class CxbRev(CxbBase):
|
||||
return {"data": True}
|
||||
|
||||
@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 = ""
|
||||
with open(r"titles/cxb/data/rss/MusicArchiveList.csv") as music:
|
||||
lines = music.readlines()
|
||||
@ -56,7 +56,7 @@ class CxbRev(CxbBase):
|
||||
return {"data": ret_str}
|
||||
|
||||
@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"
|
||||
with open(
|
||||
r"titles/cxb/data/rss/Item/ItemArchiveList_Icon.csv", encoding="utf-8"
|
||||
@ -67,7 +67,7 @@ class CxbRev(CxbBase):
|
||||
return {"data": ret_str}
|
||||
|
||||
@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"
|
||||
with open(
|
||||
r"titles/cxb/data/rss/Item/ItemArchiveList_SkinNotes.csv", encoding="utf-8"
|
||||
@ -78,7 +78,7 @@ class CxbRev(CxbBase):
|
||||
return {"data": ret_str}
|
||||
|
||||
@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"
|
||||
with open(
|
||||
r"titles/cxb/data/rss/Item/ItemArchiveList_SkinEffect.csv", encoding="utf-8"
|
||||
@ -89,7 +89,7 @@ class CxbRev(CxbBase):
|
||||
return {"data": ret_str}
|
||||
|
||||
@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"
|
||||
with open(
|
||||
r"titles/cxb/data/rss/Item/ItemArchiveList_SkinBg.csv", encoding="utf-8"
|
||||
@ -100,7 +100,7 @@ class CxbRev(CxbBase):
|
||||
return {"data": ret_str}
|
||||
|
||||
@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"
|
||||
with open(
|
||||
r"titles/cxb/data/rss/Item/ItemList_Title.csv", encoding="shift-jis"
|
||||
@ -111,7 +111,7 @@ class CxbRev(CxbBase):
|
||||
return {"data": ret_str}
|
||||
|
||||
@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"
|
||||
with open(
|
||||
r"titles/cxb/data/rss/Shop/ShopList_Music.csv", encoding="shift-jis"
|
||||
@ -122,7 +122,7 @@ class CxbRev(CxbBase):
|
||||
return {"data": ret_str}
|
||||
|
||||
@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"
|
||||
with open(
|
||||
r"titles/cxb/data/rss/Shop/ShopList_Icon.csv", encoding="shift-jis"
|
||||
@ -133,7 +133,7 @@ class CxbRev(CxbBase):
|
||||
return {"data": ret_str}
|
||||
|
||||
@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"
|
||||
with open(
|
||||
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"
|
||||
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": ""}
|
||||
|
||||
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": ""}
|
||||
|
||||
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": ""}
|
||||
|
||||
@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"
|
||||
with open(
|
||||
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"
|
||||
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": ""}
|
||||
|
||||
@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:])
|
||||
ret_str = ""
|
||||
with open(
|
||||
@ -178,14 +178,14 @@ class CxbRev(CxbBase):
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
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": ""}
|
||||
|
||||
def handle_data_free_coupon_request(self, data: Dict) -> Dict:
|
||||
async def handle_data_free_coupon_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
@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 = ""
|
||||
with open(r"titles/cxb/data/rss/NewsList.csv", encoding="UTF-8") as news:
|
||||
lines = news.readlines()
|
||||
@ -193,11 +193,11 @@ class CxbRev(CxbBase):
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
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": ""}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_license_request(self, data: Dict) -> Dict:
|
||||
async def handle_data_license_request(self, data: Dict) -> Dict:
|
||||
ret_str = ""
|
||||
with open(r"titles/cxb/data/rss/License_Offline.csv", encoding="UTF-8") as lic:
|
||||
lines = lic.readlines()
|
||||
@ -206,7 +206,7 @@ class CxbRev(CxbBase):
|
||||
return {"data": ret_str}
|
||||
|
||||
@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 = ""
|
||||
with open(
|
||||
r"titles/cxb/data/rss/Course/CourseList.csv", encoding="UTF-8"
|
||||
@ -217,7 +217,7 @@ class CxbRev(CxbBase):
|
||||
return {"data": ret_str}
|
||||
|
||||
@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
|
||||
extra_num = int(data["dldate"]["filetype"][-4:])
|
||||
ret_str = ""
|
||||
@ -230,7 +230,7 @@ class CxbRev(CxbBase):
|
||||
return {"data": ret_str}
|
||||
|
||||
@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 = ""
|
||||
with open(
|
||||
r"titles/cxb/data/rss/MissionList.csv", encoding="shift-jis"
|
||||
@ -240,14 +240,14 @@ class CxbRev(CxbBase):
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
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": ""}
|
||||
|
||||
def handle_data_unlimited_mission_request(self, data: Dict) -> Dict:
|
||||
async def handle_data_unlimited_mission_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
@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 = ""
|
||||
with open(
|
||||
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"
|
||||
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": ""}
|
||||
|
||||
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": ""}
|
||||
|
||||
def handle_data_event_achievement_single_high_score_list_request(
|
||||
async def handle_data_event_achievement_single_high_score_list_request(
|
||||
self, data: Dict
|
||||
) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_event_achievement_single_accumulation_request(
|
||||
async def handle_data_event_achievement_single_accumulation_request(
|
||||
self, data: Dict
|
||||
) -> Dict:
|
||||
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": ""}
|
||||
|
||||
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": ""}
|
||||
|
||||
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": ""}
|
||||
|
||||
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": ""}
|
||||
|
||||
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": ""}
|
||||
|
||||
@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 = ""
|
||||
with open(
|
||||
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"
|
||||
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"}
|
||||
|
||||
def handle_data_server_state_request(self, data: Dict) -> Dict:
|
||||
async def handle_data_server_state_request(self, data: Dict) -> Dict:
|
||||
return {"data": True}
|
||||
|
@ -17,11 +17,11 @@ class CxbRevSunriseS1(CxbBase):
|
||||
super().__init__(cfg, game_cfg)
|
||||
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": ""}
|
||||
|
||||
@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 = ""
|
||||
with open(r"titles/cxb/data/rss1/MusicArchiveList.csv") as music:
|
||||
lines = music.readlines()
|
||||
@ -32,7 +32,7 @@ class CxbRevSunriseS1(CxbBase):
|
||||
return {"data": ret_str}
|
||||
|
||||
@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
|
||||
ret_str = "#ItemListIcon\r\n"
|
||||
with open(
|
||||
@ -54,7 +54,7 @@ class CxbRevSunriseS1(CxbBase):
|
||||
return {"data": ret_str}
|
||||
|
||||
@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
|
||||
ret_str = "#ShopListIcon\r\n"
|
||||
with open(
|
||||
@ -119,26 +119,26 @@ class CxbRevSunriseS1(CxbBase):
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
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": ""}
|
||||
|
||||
def handle_data_exxxxx_request(self, data: Dict) -> Dict:
|
||||
async def handle_data_exxxxx_request(self, data: Dict) -> Dict:
|
||||
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": ""}
|
||||
|
||||
def handle_data_bonus_list10100_request(self, data: Dict) -> Dict:
|
||||
async def handle_data_bonus_list10100_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_oexxxx_request(self, data: Dict) -> Dict:
|
||||
async def handle_data_oexxxx_request(self, data: Dict) -> Dict:
|
||||
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": ""}
|
||||
|
||||
@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 = ""
|
||||
with open(r"titles/cxb/data/rss1/NewsList.csv", encoding="UTF-8") as news:
|
||||
lines = news.readlines()
|
||||
@ -146,14 +146,14 @@ class CxbRevSunriseS1(CxbBase):
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
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": ""}
|
||||
|
||||
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": ""}
|
||||
|
||||
@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 = ""
|
||||
with open(r"titles/cxb/data/rss1/MusicArchiveList.csv") as music:
|
||||
lines = music.readlines()
|
||||
@ -167,7 +167,7 @@ class CxbRevSunriseS1(CxbBase):
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_license_request(self, data: Dict) -> Dict:
|
||||
async def handle_data_license_request(self, data: Dict) -> Dict:
|
||||
ret_str = ""
|
||||
with open(r"titles/cxb/data/rss1/License.csv", encoding="UTF-8") as licenses:
|
||||
lines = licenses.readlines()
|
||||
@ -176,7 +176,7 @@ class CxbRevSunriseS1(CxbBase):
|
||||
return {"data": ret_str}
|
||||
|
||||
@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 = ""
|
||||
with open(
|
||||
r"titles/cxb/data/rss1/Course/CourseList.csv", encoding="UTF-8"
|
||||
@ -187,7 +187,7 @@ class CxbRevSunriseS1(CxbBase):
|
||||
return {"data": ret_str}
|
||||
|
||||
@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:])
|
||||
ret_str = ""
|
||||
with open(
|
||||
@ -198,16 +198,16 @@ class CxbRevSunriseS1(CxbBase):
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
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": ""}
|
||||
|
||||
def handle_data_mission_bonus_request(self, data: Dict) -> Dict:
|
||||
async def handle_data_mission_bonus_request(self, data: Dict) -> Dict:
|
||||
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": ""}
|
||||
|
||||
def handle_data_partner_list_request(self, data: Dict) -> Dict:
|
||||
async def handle_data_partner_list_request(self, data: Dict) -> Dict:
|
||||
ret_str = ""
|
||||
# Lord forgive me for the sins I am about to commit
|
||||
for i in range(0, 10):
|
||||
@ -226,7 +226,7 @@ class CxbRevSunriseS1(CxbBase):
|
||||
return {"data": ret_str}
|
||||
|
||||
@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:])
|
||||
ret_str = f"{partner_num},,{partner_num},1,10000,\r\n"
|
||||
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"
|
||||
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}
|
||||
|
||||
def handle_data_settings_request(self, data: Dict) -> Dict:
|
||||
async def handle_data_settings_request(self, data: Dict) -> Dict:
|
||||
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
|
||||
ret_str = "\r\n"
|
||||
ret_str += (
|
||||
@ -253,23 +253,23 @@ class CxbRevSunriseS1(CxbBase):
|
||||
ret_str += f"st0002,REMNANT,10104,1502127790,4096483201,Cs1000,-1,overcl,\r\n"
|
||||
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:])
|
||||
ret_str = ""
|
||||
for i in range(1, 11):
|
||||
ret_str += f"{i},st000{story_num}_{i-1},,,,,,,,,,,,,,,,1,,-1,1,\r\n"
|
||||
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"}
|
||||
|
||||
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"}
|
||||
|
||||
def handle_data_event_list_request(self, data: Dict) -> Dict:
|
||||
async def handle_data_event_list_request(self, data: Dict) -> Dict:
|
||||
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]
|
||||
if "EventStampMapListCs1002" in event_id:
|
||||
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:
|
||||
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]
|
||||
if "EventStampMapListCs1002" in event_id:
|
||||
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)
|
||||
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": ""}
|
||||
|
||||
@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]
|
||||
ret_str = ""
|
||||
|
||||
@ -41,7 +41,7 @@ class CxbRevSunriseS2(CxbBase):
|
||||
return {"data": ret_str}
|
||||
|
||||
@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
|
||||
ret_str = "#ItemListIcon\r\n"
|
||||
with open(
|
||||
@ -63,7 +63,7 @@ class CxbRevSunriseS2(CxbBase):
|
||||
return {"data": ret_str}
|
||||
|
||||
@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
|
||||
ret_str = "#ShopListIcon\r\n"
|
||||
with open(
|
||||
@ -128,7 +128,7 @@ class CxbRevSunriseS2(CxbBase):
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
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=""
|
||||
with open(r"titles/cxb/data/rss2/ExtraStageList.csv") as extra:
|
||||
lines = extra.readlines()
|
||||
@ -136,19 +136,19 @@ class CxbRevSunriseS2(CxbBase):
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
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": ""}
|
||||
|
||||
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": ""}
|
||||
|
||||
def handle_data_bonus_list10100_request(self, data: Dict) -> Dict:
|
||||
async def handle_data_bonus_list10100_request(self, data: Dict) -> Dict:
|
||||
return {"data": ""}
|
||||
|
||||
def handle_data_oexxxx_request(self, data: Dict) -> Dict:
|
||||
async def handle_data_oexxxx_request(self, data: Dict) -> Dict:
|
||||
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=""
|
||||
with open(r"titles/cxb/data/rss2/FreeCoupon.csv") as coupon:
|
||||
lines = coupon.readlines()
|
||||
@ -157,7 +157,7 @@ class CxbRevSunriseS2(CxbBase):
|
||||
return({"data":ret_str})
|
||||
|
||||
@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 = ""
|
||||
with open(r"titles/cxb/data/rss2/NewsList.csv", encoding="UTF-8") as news:
|
||||
lines = news.readlines()
|
||||
@ -165,14 +165,14 @@ class CxbRevSunriseS2(CxbBase):
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
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": ""}
|
||||
|
||||
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": ""}
|
||||
|
||||
@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 = ""
|
||||
with open(r"titles/cxb/data/rss2/MusicArchiveList.csv") as music:
|
||||
lines = music.readlines()
|
||||
@ -186,7 +186,7 @@ class CxbRevSunriseS2(CxbBase):
|
||||
return {"data": ret_str}
|
||||
|
||||
@cached(lifetime=86400)
|
||||
def handle_data_license_request(self, data: Dict) -> Dict:
|
||||
async def handle_data_license_request(self, data: Dict) -> Dict:
|
||||
ret_str = ""
|
||||
with open(r"titles/cxb/data/rss2/License.csv", encoding="UTF-8") as licenses:
|
||||
lines = licenses.readlines()
|
||||
@ -195,7 +195,7 @@ class CxbRevSunriseS2(CxbBase):
|
||||
return {"data": ret_str}
|
||||
|
||||
@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 = ""
|
||||
with open(
|
||||
r"titles/cxb/data/rss2/Course/CourseList.csv", encoding="UTF-8"
|
||||
@ -206,7 +206,7 @@ class CxbRevSunriseS2(CxbBase):
|
||||
return {"data": ret_str}
|
||||
|
||||
@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:])
|
||||
ret_str = ""
|
||||
with open(
|
||||
@ -217,16 +217,16 @@ class CxbRevSunriseS2(CxbBase):
|
||||
ret_str += f"{line[:-1]}\r\n"
|
||||
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": ""}
|
||||
|
||||
def handle_data_mission_bonus_request(self, data: Dict) -> Dict:
|
||||
async def handle_data_mission_bonus_request(self, data: Dict) -> Dict:
|
||||
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": ""}
|
||||
|
||||
def handle_data_partner_list_request(self, data: Dict) -> Dict:
|
||||
async def handle_data_partner_list_request(self, data: Dict) -> Dict:
|
||||
ret_str = ""
|
||||
# Lord forgive me for the sins I am about to commit
|
||||
for i in range(0, 10):
|
||||
@ -245,7 +245,7 @@ class CxbRevSunriseS2(CxbBase):
|
||||
return {"data": ret_str}
|
||||
|
||||
@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:])
|
||||
ret_str = f"{partner_num},,{partner_num},1,10000,\r\n"
|
||||
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"
|
||||
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}
|
||||
|
||||
def handle_data_settings_request(self, data: Dict) -> Dict:
|
||||
async def handle_data_settings_request(self, data: Dict) -> Dict:
|
||||
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
|
||||
ret_str = "\r\n"
|
||||
ret_str += (
|
||||
@ -272,7 +272,7 @@ class CxbRevSunriseS2(CxbBase):
|
||||
ret_str += f"st0002,REMNANT,10104,1502127790,4096483201,Cs1000,-1,overcl,\r\n"
|
||||
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:])
|
||||
ret_str = ""
|
||||
# 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"
|
||||
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"}
|
||||
|
||||
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"}
|
||||
|
||||
def handle_data_event_list_request(self, data: Dict) -> Dict:
|
||||
async def handle_data_event_list_request(self, data: Dict) -> Dict:
|
||||
return {
|
||||
"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]
|
||||
if "Cs4001" in event_id:
|
||||
return {
|
||||
@ -308,7 +308,7 @@ class CxbRevSunriseS2(CxbBase):
|
||||
else:
|
||||
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]
|
||||
if "EventStampMapListCs1002" in event_id:
|
||||
return {"data": "1,2,1,1,2,3,9,5,6,7,8,9,10,\r\n"}
|
||||
|
@ -1,8 +1,7 @@
|
||||
import datetime
|
||||
from typing import Any, List, Dict
|
||||
from typing import Dict
|
||||
import logging
|
||||
import json
|
||||
import urllib
|
||||
import urllib.parse
|
||||
from threading import Thread
|
||||
|
||||
from core.config import CoreConfig
|
||||
@ -24,13 +23,13 @@ class DivaBase:
|
||||
dt = datetime.datetime.now()
|
||||
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 ""
|
||||
|
||||
def handle_game_init_request(self, data: Dict) -> Dict:
|
||||
async def handle_game_init_request(self, data: Dict) -> Dict:
|
||||
return f""
|
||||
|
||||
def handle_attend_request(self, data: Dict) -> Dict:
|
||||
async def handle_attend_request(self, data: Dict) -> Dict:
|
||||
encoded = "&"
|
||||
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",
|
||||
@ -44,7 +43,7 @@ class DivaBase:
|
||||
|
||||
return encoded
|
||||
|
||||
def handle_ping_request(self, data: Dict) -> Dict:
|
||||
async def handle_ping_request(self, data: Dict) -> Dict:
|
||||
encoded = "&"
|
||||
params = {
|
||||
"ping_b_msg": f"Welcome to {self.core_cfg.server.name} network!",
|
||||
@ -89,7 +88,7 @@ class DivaBase:
|
||||
|
||||
return encoded
|
||||
|
||||
def handle_pv_list_request(self, data: Dict) -> Dict:
|
||||
async def handle_pv_list_request(self, data: Dict) -> Dict:
|
||||
pvlist = ""
|
||||
with open(r"titles/diva/data/PvList0.dat", encoding="utf-8") as shop:
|
||||
lines = shop.readlines()
|
||||
@ -126,7 +125,7 @@ class DivaBase:
|
||||
|
||||
return response
|
||||
|
||||
def handle_shop_catalog_request(self, data: Dict) -> Dict:
|
||||
async def handle_shop_catalog_request(self, data: Dict) -> Dict:
|
||||
catalog = ""
|
||||
|
||||
shopList = self.data.static.get_enabled_shops(self.version)
|
||||
@ -164,7 +163,7 @@ class DivaBase:
|
||||
|
||||
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)
|
||||
module = self.data.static.get_enabled_shop(self.version, int(data["mdl_id"]))
|
||||
|
||||
@ -191,7 +190,7 @@ class DivaBase:
|
||||
|
||||
return response
|
||||
|
||||
def handle_cstmz_itm_ctlg_request(self, data: Dict) -> Dict:
|
||||
async def handle_cstmz_itm_ctlg_request(self, data: Dict) -> Dict:
|
||||
catalog = ""
|
||||
|
||||
itemList = self.data.static.get_enabled_items(self.version)
|
||||
@ -229,7 +228,7 @@ class DivaBase:
|
||||
|
||||
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)
|
||||
item = self.data.static.get_enabled_item(
|
||||
self.version, int(data["cstmz_itm_id"])
|
||||
@ -264,7 +263,7 @@ class DivaBase:
|
||||
|
||||
return response
|
||||
|
||||
def handle_festa_info_request(self, data: Dict) -> Dict:
|
||||
async def handle_festa_info_request(self, data: Dict) -> Dict:
|
||||
encoded = "&"
|
||||
params = {
|
||||
"fi_id": "1,2",
|
||||
@ -287,7 +286,7 @@ class DivaBase:
|
||||
|
||||
return encoded
|
||||
|
||||
def handle_contest_info_request(self, data: Dict) -> Dict:
|
||||
async def handle_contest_info_request(self, data: Dict) -> Dict:
|
||||
response = ""
|
||||
|
||||
response += f"&ci_lut={self.time_lut}"
|
||||
@ -295,7 +294,7 @@ class DivaBase:
|
||||
|
||||
return response
|
||||
|
||||
def handle_qst_inf_request(self, data: Dict) -> Dict:
|
||||
async def handle_qst_inf_request(self, data: Dict) -> Dict:
|
||||
quest = ""
|
||||
|
||||
questList = self.data.static.get_enabled_quests(self.version)
|
||||
@ -345,43 +344,43 @@ class DivaBase:
|
||||
|
||||
return response
|
||||
|
||||
def handle_nv_ranking_request(self, data: Dict) -> Dict:
|
||||
async def handle_nv_ranking_request(self, data: Dict) -> Dict:
|
||||
return f""
|
||||
|
||||
def handle_ps_ranking_request(self, data: Dict) -> Dict:
|
||||
async def handle_ps_ranking_request(self, data: Dict) -> Dict:
|
||||
return f""
|
||||
|
||||
def handle_ng_word_request(self, data: Dict) -> Dict:
|
||||
async def handle_ng_word_request(self, data: Dict) -> Dict:
|
||||
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""
|
||||
|
||||
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""
|
||||
|
||||
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""
|
||||
|
||||
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""
|
||||
|
||||
def handle_banner_info_request(self, data: Dict) -> Dict:
|
||||
async def handle_banner_info_request(self, data: Dict) -> Dict:
|
||||
return f""
|
||||
|
||||
def handle_banner_data_request(self, data: Dict) -> Dict:
|
||||
async def handle_banner_data_request(self, data: Dict) -> Dict:
|
||||
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""
|
||||
|
||||
def handle_pstd_h_ctrl_request(self, data: Dict) -> Dict:
|
||||
async def handle_pstd_h_ctrl_request(self, data: Dict) -> Dict:
|
||||
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""
|
||||
|
||||
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_shop = self.data.item.get_shop(data["aime_id"], self.version)
|
||||
|
||||
@ -422,13 +421,13 @@ class DivaBase:
|
||||
|
||||
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.version, data["aime_id"], data["player_name"]
|
||||
)
|
||||
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_shop = self.data.item.get_shop(data["pd_id"], self.version)
|
||||
if profile is None:
|
||||
@ -583,10 +582,10 @@ class DivaBase:
|
||||
|
||||
return response
|
||||
|
||||
def handle_pd_unlock_request(self, data: Dict) -> Dict:
|
||||
async def handle_pd_unlock_request(self, data: Dict) -> Dict:
|
||||
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)
|
||||
if profile is None:
|
||||
return
|
||||
@ -705,7 +704,7 @@ class DivaBase:
|
||||
pd_by_pv_id.append(urllib.parse.quote(f"{song}***"))
|
||||
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(",")
|
||||
pv = ""
|
||||
|
||||
@ -732,10 +731,10 @@ class DivaBase:
|
||||
|
||||
return response
|
||||
|
||||
def handle_stage_start_request(self, data: Dict) -> Dict:
|
||||
async def handle_stage_start_request(self, data: Dict) -> Dict:
|
||||
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)
|
||||
|
||||
pd_song_list = data["stg_ply_pv_id"].split(",")
|
||||
@ -914,7 +913,7 @@ class DivaBase:
|
||||
|
||||
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)
|
||||
|
||||
self.data.profile.update_profile(
|
||||
@ -922,7 +921,7 @@ class DivaBase:
|
||||
)
|
||||
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(
|
||||
data["pd_id"],
|
||||
self.version,
|
||||
@ -952,7 +951,7 @@ class DivaBase:
|
||||
response = "&shp_rslt=1"
|
||||
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)
|
||||
if profile is None:
|
||||
return "&cd_adm_result=0"
|
||||
@ -972,7 +971,7 @@ class DivaBase:
|
||||
|
||||
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)
|
||||
|
||||
# make sure user has enough Vocaloid Points
|
||||
@ -992,7 +991,7 @@ class DivaBase:
|
||||
|
||||
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)
|
||||
|
||||
# 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 logging, coloredlogs
|
||||
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
|
||||
)
|
||||
|
||||
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
|
||||
return (
|
||||
[],
|
||||
[("render_POST", "/DivaServlet/", {})]
|
||||
)
|
||||
def get_routes(self) -> List[Route]:
|
||||
return [
|
||||
Route("/DivaServlet/", self.render_POST, methods=['POST'])
|
||||
]
|
||||
|
||||
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:
|
||||
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
|
||||
def is_game_enabled(
|
||||
@ -78,9 +79,9 @@ class DivaServlet(BaseServlet):
|
||||
|
||||
return True
|
||||
|
||||
def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
||||
req_raw = request.content.getvalue()
|
||||
url_header = request.getAllHeaders()
|
||||
async def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
||||
req_raw = await request.body()
|
||||
url_header = request.headers
|
||||
|
||||
# Ping Dispatch
|
||||
if "THIS_STRING_SEPARATES" in str(url_header):
|
||||
@ -103,9 +104,7 @@ class DivaServlet(BaseServlet):
|
||||
self.logger.debug(
|
||||
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(
|
||||
"utf-8"
|
||||
)
|
||||
return PlainTextResponse(f"cmd={bin_req_data['cmd']}&req_id={bin_req_data['req_id']}&stat=ok{resp}")
|
||||
|
||||
# Main Dispatch
|
||||
json_string = json.dumps(
|
||||
@ -122,7 +121,7 @@ class DivaServlet(BaseServlet):
|
||||
) # Decompressing the gzip
|
||||
except zlib.error as e:
|
||||
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_data = {}
|
||||
@ -141,27 +140,18 @@ class DivaServlet(BaseServlet):
|
||||
# Load the requests
|
||||
try:
|
||||
handler = getattr(self.base, func_to_find)
|
||||
resp = handler(req_data)
|
||||
resp = await handler(req_data)
|
||||
|
||||
except AttributeError as 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(
|
||||
"utf-8"
|
||||
)
|
||||
return PlainTextResponse(f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok")
|
||||
|
||||
except Exception as 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(
|
||||
"utf-8"
|
||||
)
|
||||
return PlainTextResponse(f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok")
|
||||
|
||||
request.responseHeaders.addRawHeader(b"content-type", b"text/plain")
|
||||
self.logger.debug(
|
||||
f"Response cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok{resp}"
|
||||
)
|
||||
|
||||
return (
|
||||
f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok{resp}".encode(
|
||||
"utf-8"
|
||||
)
|
||||
)
|
||||
return PlainTextResponse(f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok{resp}")
|
||||
|
@ -3,7 +3,7 @@ import yaml
|
||||
import jinja2
|
||||
from os import path
|
||||
from twisted.web.util import redirectTo
|
||||
from twisted.web.http import Request
|
||||
from starlette.requests import Request
|
||||
from twisted.web.server import Session
|
||||
|
||||
from core.frontend import FE_Base, IUserSession
|
||||
|
@ -1,18 +1,18 @@
|
||||
import json
|
||||
import traceback
|
||||
import inflection
|
||||
from starlette.routing import Route
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import JSONResponse
|
||||
import yaml
|
||||
import logging
|
||||
import coloredlogs
|
||||
|
||||
from os import path
|
||||
from typing import Dict, List, Tuple
|
||||
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.title import BaseServlet
|
||||
from core.utils import Utils
|
||||
from titles.idac.base import IDACBase
|
||||
from titles.idac.season2 import IDACSeason2
|
||||
@ -22,7 +22,7 @@ from titles.idac.echo import IDACEchoUDP
|
||||
from titles.idac.matching import IDACMatching
|
||||
|
||||
|
||||
class IDACServlet:
|
||||
class IDACServlet(BaseServlet):
|
||||
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
|
||||
self.core_cfg = core_cfg
|
||||
self.game_cfg = IDACConfig()
|
||||
@ -72,12 +72,11 @@ class IDACServlet:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
|
||||
return (
|
||||
[],
|
||||
[("render_POST", "/SDGT/{version}/initiald/{category}/{endpoint}", {})]
|
||||
)
|
||||
|
||||
def get_routes(self) -> List[Route]:
|
||||
return [
|
||||
Route("/{version:int}/initiald/{category:str}/{endpoint:str}", self.render_POST, methods=["POST"])
|
||||
]
|
||||
|
||||
def get_allnet_info(
|
||||
self, game_code: str, game_ver: int, keychip: str
|
||||
@ -88,15 +87,15 @@ class IDACServlet:
|
||||
return (
|
||||
f"",
|
||||
# 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:
|
||||
req_raw = request.content.getvalue()
|
||||
async def render_POST(self, request: Request) -> bytes:
|
||||
req_raw = await request.body()
|
||||
internal_ver = 0
|
||||
version = int(matchers['version'])
|
||||
category = matchers['category']
|
||||
endpoint = matchers['endpoint']
|
||||
version: int = request.path_params.get('version')
|
||||
category: str = request.path_params.get('category')
|
||||
endpoint: str = request.path_params.get('endpoint')
|
||||
client_ip = Utils.get_ip_addr(request)
|
||||
|
||||
if version >= 100 and version < 140: # IDAC Season 1
|
||||
@ -104,7 +103,7 @@ class IDACServlet:
|
||||
elif version >= 140 and version < 171: # 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)
|
||||
|
||||
@ -119,7 +118,7 @@ class IDACServlet:
|
||||
|
||||
if not hasattr(self.versions[internal_ver], func_to_find):
|
||||
self.logger.warning(f"Unhandled v{version} request {endpoint}")
|
||||
return '{"status_code": "0"}'.encode("utf-8")
|
||||
return JSONResponse('{"status_code": "0"}')
|
||||
|
||||
resp = None
|
||||
try:
|
||||
@ -129,17 +128,16 @@ class IDACServlet:
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
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:
|
||||
resp = {"status_code": "0"}
|
||||
|
||||
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:
|
||||
app: str = data[b"application"].decode()
|
||||
def decode_header(self, app: str) -> Dict:
|
||||
ret = {}
|
||||
|
||||
for x in app.split(", "):
|
||||
@ -149,6 +147,8 @@ class IDACServlet:
|
||||
return ret
|
||||
|
||||
def setup(self):
|
||||
return
|
||||
"""
|
||||
if self.game_cfg.server.enable:
|
||||
endpoints.serverFromString(
|
||||
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}")
|
||||
"""
|
||||
|
@ -109,9 +109,9 @@ class IDACSeason2(IDACBase):
|
||||
ver_str = version.replace(".", "")[:3]
|
||||
|
||||
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:
|
||||
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 {
|
||||
"status_code": "0",
|
||||
@ -136,10 +136,10 @@ class IDACSeason2(IDACBase):
|
||||
"server_maintenance_end_hour": 0,
|
||||
"server_maintenance_end_minutes": 0,
|
||||
"domain_api_game": domain_api_game,
|
||||
"domain_matching": f"http://{self.core_cfg.title.hostname}:{self.game_config.server.matching}",
|
||||
"domain_echo1": f"{self.core_cfg.title.hostname}:{self.game_config.server.echo1}",
|
||||
"domain_echo2": f"{self.core_cfg.title.hostname}:{self.game_config.server.echo2}",
|
||||
"domain_ping": f"{self.core_cfg.title.hostname}",
|
||||
"domain_matching": f"http://{self.core_cfg.server.hostname}:{self.game_config.server.matching}",
|
||||
"domain_echo1": f"{self.core_cfg.server.hostname}:{self.game_config.server.echo1}",
|
||||
"domain_echo2": f"{self.core_cfg.server.hostname}:{self.game_config.server.echo2}",
|
||||
"domain_ping": f"{self.core_cfg.server.hostname}",
|
||||
"battle_gift_event_master": [],
|
||||
"round_event": [
|
||||
{
|
||||
|
@ -24,10 +24,10 @@ class IDZHandlerLoadServerInfo(IDZHandlerBase):
|
||||
|
||||
t_port = Utils.get_title_port(self.core_config)
|
||||
|
||||
news_str = f"http://{self.core_config.title.hostname}:{t_port}/idz/news/news80**.txt"
|
||||
err_str = f"http://{self.core_config.title.hostname}:{t_port}/idz/error"
|
||||
news_str = f"http://{self.core_config.server.hostname}:{t_port}/idz/news/news80**.txt"
|
||||
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_error = len(err_str)
|
||||
|
||||
@ -36,7 +36,7 @@ class IDZHandlerLoadServerInfo(IDZHandlerBase):
|
||||
f"{len_hostname}s",
|
||||
ret,
|
||||
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, 0x86 + offset, self.game_cfg.ports.userdb + 1)
|
||||
@ -45,7 +45,7 @@ class IDZHandlerLoadServerInfo(IDZHandlerBase):
|
||||
f"{len_hostname}s",
|
||||
ret,
|
||||
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, 0x10A + offset, self.game_cfg.ports.match - 3)
|
||||
@ -59,7 +59,7 @@ class IDZHandlerLoadServerInfo(IDZHandlerBase):
|
||||
f"{len_hostname}s",
|
||||
ret,
|
||||
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)
|
||||
|
||||
@ -67,7 +67,7 @@ class IDZHandlerLoadServerInfo(IDZHandlerBase):
|
||||
f"{len_hostname}s",
|
||||
ret,
|
||||
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)
|
||||
|
||||
@ -75,19 +75,19 @@ class IDZHandlerLoadServerInfo(IDZHandlerBase):
|
||||
f"{len_hostname}s",
|
||||
ret,
|
||||
0x021C + offset,
|
||||
self.core_config.title.hostname.encode(),
|
||||
self.core_config.server.hostname.encode(),
|
||||
)
|
||||
struct.pack_into(
|
||||
f"{len_hostname}s",
|
||||
ret,
|
||||
0x029C + offset,
|
||||
self.core_config.title.hostname.encode(),
|
||||
self.core_config.server.hostname.encode(),
|
||||
)
|
||||
struct.pack_into(
|
||||
f"{len_hostname}s",
|
||||
ret,
|
||||
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)
|
||||
|
@ -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 logging
|
||||
import coloredlogs
|
||||
from logging.handlers import TimedRotatingFileHandler
|
||||
from os import path
|
||||
from typing import Tuple, List, Dict
|
||||
from twisted.internet import reactor, endpoints
|
||||
from twisted.web import server, resource
|
||||
import importlib
|
||||
|
||||
from core.config import CoreConfig
|
||||
@ -83,16 +83,15 @@ class IDZServlet(BaseServlet):
|
||||
|
||||
return True
|
||||
|
||||
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
|
||||
return[
|
||||
[("render_GET", "/idz/news/{endpoint:.*?}", {}),
|
||||
("render_GET", "/idz/error", {})],
|
||||
[]
|
||||
def get_routes(self) -> List[Route]:
|
||||
return [
|
||||
Route("/idz/news/{endpoint:str}", self.render_GET),
|
||||
Route("/idz/error", self.render_GET)
|
||||
]
|
||||
|
||||
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
|
||||
hostname = (
|
||||
self.core_cfg.title.hostname
|
||||
self.core_cfg.server.hostname
|
||||
if not self.game_cfg.server.hostname
|
||||
else self.game_cfg.server.hostname
|
||||
)
|
||||
@ -135,7 +134,8 @@ class IDZServlet(BaseServlet):
|
||||
|
||||
except AttributeError as e:
|
||||
continue
|
||||
|
||||
|
||||
"""
|
||||
endpoints.serverFromString(
|
||||
reactor,
|
||||
f"tcp:{self.game_cfg.ports.userdb}:interface={self.core_cfg.server.listen_address}",
|
||||
@ -155,18 +155,15 @@ class IDZServlet(BaseServlet):
|
||||
reactor.listenUDP(
|
||||
self.game_cfg.ports.userdb + 1, IDZEcho(self.core_cfg, self.game_cfg)
|
||||
)
|
||||
|
||||
"""
|
||||
self.logger.info(f"UserDB Listening on port {self.game_cfg.ports.userdb}")
|
||||
|
||||
def render_GET(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
||||
url_path = matchers['endpoint']
|
||||
async def render_GET(self, request: Request) -> bytes:
|
||||
url_path = request.path_params.get('endpoint', '')
|
||||
if not url_path:
|
||||
return Response()
|
||||
|
||||
self.logger.info(f"IDZ GET request: {url_path}")
|
||||
request.responseHeaders.setRawHeaders(
|
||||
"Content-Type", [b"text/plain; charset=utf-8"]
|
||||
)
|
||||
request.responseHeaders.setRawHeaders(
|
||||
"Last-Modified", [b"Sun, 23 Apr 2023 05:33:20 GMT"]
|
||||
)
|
||||
|
||||
news = (
|
||||
self.game_cfg.server.news
|
||||
@ -176,4 +173,4 @@ class IDZServlet(BaseServlet):
|
||||
news += "\r\n"
|
||||
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 twisted.web import server, resource
|
||||
from twisted.internet import reactor, endpoints
|
||||
from twisted.web.http import Request
|
||||
from starlette.requests import Request
|
||||
from routes import Mapper
|
||||
import random
|
||||
from os import walk
|
||||
|
@ -26,12 +26,12 @@ class Mai2Base:
|
||||
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:
|
||||
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:
|
||||
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 self.core_config.title.reboot_start_time == "" or self.core_config.title.reboot_end_time == "":
|
||||
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": []}
|
||||
|
||||
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
|
||||
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_lst = []
|
||||
if events is None or not events:
|
||||
@ -108,10 +108,10 @@ class Mai2Base:
|
||||
"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": []}
|
||||
|
||||
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)
|
||||
if game_charge_list is None:
|
||||
return {"length": 0, "gameChargeList": []}
|
||||
@ -130,19 +130,19 @@ class Mai2Base:
|
||||
|
||||
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"}
|
||||
|
||||
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"}
|
||||
|
||||
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"}
|
||||
|
||||
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"}
|
||||
|
||||
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)
|
||||
w = self.data.profile.get_web_option(data["userId"], self.version)
|
||||
if p is None or w is None:
|
||||
@ -169,7 +169,7 @@ class Mai2Base:
|
||||
"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)
|
||||
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.
|
||||
}
|
||||
|
||||
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"]
|
||||
playlog = data["userPlaylog"]
|
||||
|
||||
@ -218,7 +218,7 @@ class Mai2Base:
|
||||
|
||||
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"]
|
||||
charge = data["userCharge"]
|
||||
|
||||
@ -234,7 +234,7 @@ class Mai2Base:
|
||||
|
||||
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"]
|
||||
upsert = data["upsertUserAll"]
|
||||
|
||||
@ -375,10 +375,10 @@ class Mai2Base:
|
||||
|
||||
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}
|
||||
|
||||
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)
|
||||
if profile is None:
|
||||
return
|
||||
@ -390,7 +390,7 @@ class Mai2Base:
|
||||
|
||||
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)
|
||||
if extend is None:
|
||||
return
|
||||
@ -402,7 +402,7 @@ class Mai2Base:
|
||||
|
||||
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)
|
||||
if options is None:
|
||||
return
|
||||
@ -414,7 +414,7 @@ class Mai2Base:
|
||||
|
||||
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"])
|
||||
if user_cards is None:
|
||||
return {"userId": data["userId"], "nextIndex": 0, "userCardList": []}
|
||||
@ -448,7 +448,7 @@ class Mai2Base:
|
||||
"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"])
|
||||
if user_charges is None:
|
||||
return {"userId": data["userId"], "length": 0, "userChargeList": []}
|
||||
@ -467,16 +467,16 @@ class Mai2Base:
|
||||
"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": []}
|
||||
|
||||
def handle_get_transfer_friend_api_request(self, data: Dict) -> Dict:
|
||||
async def handle_get_transfer_friend_api_request(self, data: Dict) -> Dict:
|
||||
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": []}
|
||||
|
||||
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"])
|
||||
if b is None:
|
||||
return { "userId": data.get("userId", 0), "userBossData": {}}
|
||||
@ -486,7 +486,7 @@ class Mai2Base:
|
||||
|
||||
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)
|
||||
next_idx = int(data["nextIndex"] % 10000000000)
|
||||
user_item_list = self.data.item.get_items(data["userId"], kind)
|
||||
@ -514,7 +514,7 @@ class Mai2Base:
|
||||
"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"])
|
||||
|
||||
chara_list = []
|
||||
@ -528,7 +528,7 @@ class Mai2Base:
|
||||
|
||||
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"])
|
||||
if favorites is None:
|
||||
return
|
||||
@ -545,7 +545,7 @@ class Mai2Base:
|
||||
|
||||
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)
|
||||
if ghost is None:
|
||||
return
|
||||
@ -557,7 +557,7 @@ class Mai2Base:
|
||||
|
||||
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"])
|
||||
if rating is None:
|
||||
return
|
||||
@ -567,7 +567,7 @@ class Mai2Base:
|
||||
|
||||
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)
|
||||
if rating is None:
|
||||
return
|
||||
@ -579,7 +579,7 @@ class Mai2Base:
|
||||
|
||||
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
|
||||
"""
|
||||
@ -607,7 +607,7 @@ class Mai2Base:
|
||||
|
||||
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"])
|
||||
if user_courses is None:
|
||||
return {"userId": data["userId"], "nextIndex": 0, "userCourseList": []}
|
||||
@ -621,11 +621,11 @@ class Mai2Base:
|
||||
|
||||
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
|
||||
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"])
|
||||
if friend_season_ranking is None:
|
||||
return {
|
||||
@ -661,7 +661,7 @@ class Mai2Base:
|
||||
"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"])
|
||||
if maps is None:
|
||||
return {
|
||||
@ -694,7 +694,7 @@ class Mai2Base:
|
||||
"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"])
|
||||
if login_bonuses is None:
|
||||
return {
|
||||
@ -727,10 +727,10 @@ class Mai2Base:
|
||||
"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": []}
|
||||
|
||||
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)
|
||||
if w is None:
|
||||
return {"userId": data["userId"], "userWebOption": {}}
|
||||
@ -742,10 +742,10 @@ class Mai2Base:
|
||||
|
||||
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": []}
|
||||
|
||||
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"])
|
||||
if g is None:
|
||||
return {"userId": data["userId"], "userGradeStatus": {}, "length": 0, "userGradeList": []}
|
||||
@ -755,7 +755,7 @@ class Mai2Base:
|
||||
|
||||
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)
|
||||
next_index = data.get("nextIndex", 0)
|
||||
max_ct = data.get("maxCount", 50)
|
||||
@ -794,10 +794,10 @@ class Mai2Base:
|
||||
"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)
|
||||
|
||||
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:
|
||||
return {'returnCode': 0, 'apiName': 'UploadUserPhotoApi'}
|
||||
|
||||
|
@ -15,7 +15,7 @@ class Mai2DX(Mai2Base):
|
||||
super().__init__(cfg, game_cfg)
|
||||
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 {
|
||||
"gameSetting": {
|
||||
"isMaintenance": False,
|
||||
@ -33,7 +33,7 @@ class Mai2DX(Mai2Base):
|
||||
"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)
|
||||
o = self.data.profile.get_profile_option(data["userId"], self.version)
|
||||
if p is None or o is None:
|
||||
@ -69,7 +69,7 @@ class Mai2DX(Mai2Base):
|
||||
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"]
|
||||
playlog = data["userPlaylog"]
|
||||
|
||||
@ -77,7 +77,7 @@ class Mai2DX(Mai2Base):
|
||||
|
||||
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"]
|
||||
charge = data["userCharge"]
|
||||
|
||||
@ -93,7 +93,7 @@ class Mai2DX(Mai2Base):
|
||||
|
||||
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"]
|
||||
upsert = data["upsertUserAll"]
|
||||
|
||||
@ -215,7 +215,7 @@ class Mai2DX(Mai2Base):
|
||||
|
||||
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)
|
||||
if profile is None:
|
||||
return
|
||||
@ -227,7 +227,7 @@ class Mai2DX(Mai2Base):
|
||||
|
||||
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)
|
||||
if extend is None:
|
||||
return
|
||||
@ -239,7 +239,7 @@ class Mai2DX(Mai2Base):
|
||||
|
||||
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)
|
||||
if options is None:
|
||||
return
|
||||
@ -251,7 +251,7 @@ class Mai2DX(Mai2Base):
|
||||
|
||||
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"])
|
||||
if user_cards is None:
|
||||
return {"userId": data["userId"], "nextIndex": 0, "userCardList": []}
|
||||
@ -285,7 +285,7 @@ class Mai2DX(Mai2Base):
|
||||
"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"])
|
||||
if user_charges is None:
|
||||
return {"userId": data["userId"], "length": 0, "userChargeList": []}
|
||||
@ -310,7 +310,7 @@ class Mai2DX(Mai2Base):
|
||||
"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)
|
||||
next_idx = int(data["nextIndex"] % 10000000000)
|
||||
user_item_list = self.data.item.get_items(data["userId"], kind)
|
||||
@ -338,7 +338,7 @@ class Mai2DX(Mai2Base):
|
||||
"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"])
|
||||
|
||||
chara_list = []
|
||||
@ -350,7 +350,7 @@ class Mai2DX(Mai2Base):
|
||||
|
||||
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"])
|
||||
if favorites is None:
|
||||
return
|
||||
@ -367,7 +367,7 @@ class Mai2DX(Mai2Base):
|
||||
|
||||
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)
|
||||
if ghost is None:
|
||||
return
|
||||
@ -379,7 +379,7 @@ class Mai2DX(Mai2Base):
|
||||
|
||||
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)
|
||||
if rating is None:
|
||||
return
|
||||
@ -391,7 +391,7 @@ class Mai2DX(Mai2Base):
|
||||
|
||||
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
|
||||
"""
|
||||
@ -419,7 +419,7 @@ class Mai2DX(Mai2Base):
|
||||
|
||||
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"])
|
||||
if user_courses is None:
|
||||
return {"userId": data["userId"], "nextIndex": 0, "userCourseList": []}
|
||||
@ -433,11 +433,11 @@ class Mai2DX(Mai2Base):
|
||||
|
||||
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
|
||||
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"])
|
||||
if friend_season_ranking is None:
|
||||
return {
|
||||
@ -473,7 +473,7 @@ class Mai2DX(Mai2Base):
|
||||
"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"])
|
||||
if maps is None:
|
||||
return {
|
||||
@ -506,7 +506,7 @@ class Mai2DX(Mai2Base):
|
||||
"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"])
|
||||
if login_bonuses is None:
|
||||
return {
|
||||
@ -539,7 +539,7 @@ class Mai2DX(Mai2Base):
|
||||
"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:
|
||||
regionId: int
|
||||
@ -548,7 +548,7 @@ class Mai2DX(Mai2Base):
|
||||
"""
|
||||
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"]
|
||||
rival_id = data["rivalId"]
|
||||
|
||||
@ -559,7 +559,7 @@ class Mai2DX(Mai2Base):
|
||||
"""
|
||||
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"]
|
||||
rival_id = data["rivalId"]
|
||||
next_idx = data["nextIndex"]
|
||||
@ -577,7 +577,7 @@ class Mai2DX(Mai2Base):
|
||||
"""
|
||||
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)
|
||||
next_index = data.get("nextIndex", 0)
|
||||
max_ct = data.get("maxCount", 50)
|
||||
@ -616,8 +616,8 @@ class Mai2DX(Mai2Base):
|
||||
"userMusicList": [{"userMusicDetailList": music_detail_list}],
|
||||
}
|
||||
|
||||
def handle_user_login_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_user_login_api_request(data)
|
||||
async def handle_user_login_api_request(self, data: Dict) -> Dict:
|
||||
ret = await super().handle_user_login_api_request(data)
|
||||
if ret is None or not ret:
|
||||
return ret
|
||||
ret['loginId'] = ret.get('loginCount', 0)
|
||||
|
@ -11,26 +11,26 @@ class Mai2Festival(Mai2UniversePlus):
|
||||
super().__init__(cfg, game_cfg)
|
||||
self.version = Mai2Constants.VER_MAIMAI_DX_FESTIVAL
|
||||
|
||||
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||
user_data = super().handle_cm_get_user_preview_api_request(data)
|
||||
async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||
user_data = await super().handle_cm_get_user_preview_api_request(data)
|
||||
|
||||
# hardcode lastDataVersion for CardMaker
|
||||
user_data["lastDataVersion"] = "1.30.00"
|
||||
return user_data
|
||||
|
||||
def handle_user_login_api_request(self, data: Dict) -> Dict:
|
||||
user_login = super().handle_user_login_api_request(data)
|
||||
async def handle_user_login_api_request(self, data: Dict) -> Dict:
|
||||
user_login = await super().handle_user_login_api_request(data)
|
||||
# useless?
|
||||
user_login["Bearer"] = "ARTEMiSTOKEN"
|
||||
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]
|
||||
"""
|
||||
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]
|
||||
"""
|
||||
|
@ -11,14 +11,14 @@ class Mai2FestivalPlus(Mai2Festival):
|
||||
super().__init__(cfg, game_cfg)
|
||||
self.version = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS
|
||||
|
||||
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||
user_data = super().handle_cm_get_user_preview_api_request(data)
|
||||
async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||
user_data = await super().handle_cm_get_user_preview_api_request(data)
|
||||
|
||||
# hardcode lastDataVersion for CardMaker
|
||||
user_data["lastDataVersion"] = "1.35.00"
|
||||
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)
|
||||
kind = data.get("kind", 2)
|
||||
next_index = data.get("nextIndex", 0)
|
||||
|
@ -1,9 +1,9 @@
|
||||
from twisted.web.http import Request
|
||||
from twisted.web.server import NOT_DONE_YET
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import Response, JSONResponse
|
||||
from starlette.routing import Route
|
||||
import json
|
||||
import inflection
|
||||
import yaml
|
||||
import string
|
||||
import logging, coloredlogs
|
||||
import zlib
|
||||
from logging.handlers import TimedRotatingFileHandler
|
||||
@ -101,33 +101,29 @@ class Mai2Servlet(BaseServlet):
|
||||
|
||||
return True
|
||||
|
||||
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
|
||||
return (
|
||||
[
|
||||
("handle_movie", "/{version}/MaimaiServlet/api/movie/{endpoint}", {}),
|
||||
("handle_old_srv", "/{version}/MaimaiServlet/old/{endpoint}", {}),
|
||||
("handle_old_srv_userdata", "/{version}/MaimaiServlet/old/{endpoint}/{placeid}/{keychip}/{userid}", {}),
|
||||
("handle_old_srv_userdata", "/{version}/MaimaiServlet/old/{endpoint}/{userid}", {}),
|
||||
("handle_usbdl", "/{version}/MaimaiServlet/usbdl/{endpoint}", {}),
|
||||
("handle_deliver", "/{version}/MaimaiServlet/deliver/{endpoint}", {}),
|
||||
],
|
||||
[
|
||||
("handle_movie", "/{version}/MaimaiServlet/api/movie/{endpoint}", {}),
|
||||
("handle_mai", "/{version}/MaimaiServlet/{endpoint}", {}),
|
||||
("handle_mai2", "/{version}/Maimai2Servlet/{endpoint}", {}),
|
||||
]
|
||||
)
|
||||
|
||||
def get_routes(self) -> List[Route]:
|
||||
return [
|
||||
Route("/{version:int}/MaimaiServlet/api/movie/{endpoint:str}", self.handle_movie, methods=['GET', 'POST']),
|
||||
Route("/{version:int}/MaimaiServlet/old/{endpoint:str}", self.handle_old_srv),
|
||||
Route("/{version:int}/MaimaiServlet/old/{endpoint:str}/{placeid:str}/{keychip:str}/{userid:int}", self.handle_old_srv_userdata),
|
||||
Route("/{version:int}/MaimaiServlet/old/{endpoint:str}/{userid:int}", self.handle_old_srv_userdata),
|
||||
Route("/{version:int}/MaimaiServlet/old/{endpoint:str}/{userid:int}", self.handle_old_srv_userdata),
|
||||
Route("/{version:int}/MaimaiServlet/usbdl/{endpoint:str}", self.handle_usbdl),
|
||||
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']),
|
||||
]
|
||||
|
||||
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:
|
||||
return (
|
||||
f"http://{self.core_cfg.title.hostname}:{Utils.get_title_port(self.core_cfg)}/{game_ver}/",
|
||||
f"{self.core_cfg.title.hostname}",
|
||||
f"http://{self.core_cfg.server.hostname}:{Utils.get_title_port(self.core_cfg)}/{game_ver}/",
|
||||
f"{self.core_cfg.server.hostname}",
|
||||
)
|
||||
|
||||
return (
|
||||
f"http://{self.core_cfg.title.hostname}/{game_ver}/",
|
||||
f"{self.core_cfg.title.hostname}",
|
||||
f"http://{self.core_cfg.server.hostname}/{game_ver}/",
|
||||
f"{self.core_cfg.server.hostname}",
|
||||
)
|
||||
|
||||
def setup(self):
|
||||
@ -155,13 +151,22 @@ class Mai2Servlet(BaseServlet):
|
||||
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:
|
||||
endpoint = matchers['endpoint']
|
||||
version = int(matchers['version'])
|
||||
async def handle_movie(self, request: Request):
|
||||
return JSONResponse()
|
||||
|
||||
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":
|
||||
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
|
||||
client_ip = Utils.get_ip_addr(request)
|
||||
|
||||
@ -199,7 +204,7 @@ class Mai2Servlet(BaseServlet):
|
||||
self.logger.error(
|
||||
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)
|
||||
|
||||
@ -216,26 +221,26 @@ class Mai2Servlet(BaseServlet):
|
||||
else:
|
||||
try:
|
||||
handler = getattr(handler_cls, func_to_find)
|
||||
resp = handler(req_data)
|
||||
resp = await handler(req_data)
|
||||
|
||||
except Exception as 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:
|
||||
resp = {"returnCode": 1}
|
||||
|
||||
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:
|
||||
endpoint = matchers['endpoint']
|
||||
version = int(matchers['version'])
|
||||
async def handle_mai2(self, request: Request) -> bytes:
|
||||
endpoint: str = request.path_params.get('endpoint')
|
||||
version: int = request.path_params.get('version')
|
||||
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
|
||||
client_ip = Utils.get_ip_addr(request)
|
||||
if version < 105: # 1.0
|
||||
@ -256,17 +261,17 @@ class Mai2Servlet(BaseServlet):
|
||||
internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS
|
||||
|
||||
if (
|
||||
request.getHeader("Mai-Encoding") is not None
|
||||
or request.getHeader("X-Mai-Encoding") is not None
|
||||
request.headers.get("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.
|
||||
# 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...
|
||||
enc_ver = request.getHeader("Mai-Encoding")
|
||||
enc_ver = request.headers.get("Mai-Encoding")
|
||||
if enc_ver is None:
|
||||
enc_ver = request.getHeader("X-Mai-Encoding")
|
||||
enc_ver = request.headers.get("X-Mai-Encoding")
|
||||
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:
|
||||
@ -276,7 +281,7 @@ class Mai2Servlet(BaseServlet):
|
||||
self.logger.error(
|
||||
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)
|
||||
|
||||
@ -293,80 +298,27 @@ class Mai2Servlet(BaseServlet):
|
||||
else:
|
||||
try:
|
||||
handler = getattr(handler_cls, func_to_find)
|
||||
resp = handler(req_data)
|
||||
resp = await handler(req_data)
|
||||
|
||||
except Exception as 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:
|
||||
resp = {"returnCode": 1}
|
||||
|
||||
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:
|
||||
endpoint = matchers['endpoint']
|
||||
version = matchers['version']
|
||||
async def handle_old_srv(self, request: Request) -> bytes:
|
||||
endpoint = request.path_params.get('endpoint')
|
||||
version = request.path_params.get('version')
|
||||
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:
|
||||
endpoint = matchers['endpoint']
|
||||
version = matchers['version']
|
||||
async def handle_old_srv_userdata(self, request: Request) -> bytes:
|
||||
endpoint = request.path_params.get('endpoint')
|
||||
version = request.path_params.get('version')
|
||||
self.logger.info(f"v{version} old server {endpoint}")
|
||||
return 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"{}")
|
||||
return Response(zlib.compress(b"{}"))
|
||||
|
@ -15,7 +15,7 @@ class Mai2Universe(Mai2SplashPlus):
|
||||
super().__init__(cfg, game_cfg)
|
||||
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)
|
||||
if p is None:
|
||||
return {}
|
||||
@ -30,7 +30,7 @@ class Mai2Universe(Mai2SplashPlus):
|
||||
"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
|
||||
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}
|
||||
|
||||
def handle_cm_login_api_request(self, data: Dict) -> Dict:
|
||||
async def handle_cm_login_api_request(self, data: Dict) -> Dict:
|
||||
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}
|
||||
|
||||
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)
|
||||
if selling_cards is None:
|
||||
return {"length": 0, "sellingCardList": []}
|
||||
@ -88,7 +88,7 @@ class Mai2Universe(Mai2SplashPlus):
|
||||
|
||||
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"])
|
||||
if user_cards is None:
|
||||
return {"returnCode": 1, "length": 0, "nextIndex": 0, "userCardList": []}
|
||||
@ -124,10 +124,10 @@ class Mai2Universe(Mai2SplashPlus):
|
||||
"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)
|
||||
|
||||
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"])
|
||||
|
||||
chara_list = []
|
||||
@ -153,10 +153,10 @@ class Mai2Universe(Mai2SplashPlus):
|
||||
"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": []}
|
||||
|
||||
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"]
|
||||
upsert = data["userPrintDetail"]
|
||||
|
||||
@ -209,12 +209,12 @@ class Mai2Universe(Mai2SplashPlus):
|
||||
"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 {
|
||||
"returnCode": 1,
|
||||
"orderId": 0,
|
||||
"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}
|
||||
|
@ -11,8 +11,8 @@ class Mai2UniversePlus(Mai2Universe):
|
||||
super().__init__(cfg, game_cfg)
|
||||
self.version = Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS
|
||||
|
||||
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||
user_data = super().handle_cm_get_user_preview_api_request(data)
|
||||
async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||
user_data = await super().handle_cm_get_user_preview_api_request(data)
|
||||
|
||||
# hardcode lastDataVersion for CardMaker 1.35
|
||||
user_data["lastDataVersion"] = "1.25.00"
|
||||
|
@ -103,7 +103,7 @@ class OngekiBase:
|
||||
self.game = OngekiConstants.GAME_CODE
|
||||
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 self.core_cfg.title.reboot_start_time == "" or self.core_cfg.title.reboot_end_time == "":
|
||||
reboot_start = datetime.strftime(
|
||||
@ -148,7 +148,7 @@ class OngekiBase:
|
||||
"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?
|
||||
"""
|
||||
@ -156,7 +156,7 @@ class OngekiBase:
|
||||
# id - int
|
||||
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)
|
||||
|
||||
ranking_list = []
|
||||
@ -171,7 +171,7 @@ class OngekiBase:
|
||||
"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()
|
||||
game_point = []
|
||||
|
||||
@ -194,16 +194,16 @@ class OngekiBase:
|
||||
"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"}
|
||||
|
||||
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"}
|
||||
|
||||
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"}
|
||||
|
||||
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)
|
||||
|
||||
reward_list = []
|
||||
@ -221,7 +221,7 @@ class OngekiBase:
|
||||
"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)
|
||||
|
||||
present_list = []
|
||||
@ -238,13 +238,13 @@ class OngekiBase:
|
||||
"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": []}
|
||||
|
||||
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": []}
|
||||
|
||||
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)
|
||||
|
||||
prep_music_list = []
|
||||
@ -262,7 +262,7 @@ class OngekiBase:
|
||||
"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:
|
||||
return {"returnCode": 1, "apiName": "UpsertClientSettingApi"}
|
||||
|
||||
@ -273,7 +273,7 @@ class OngekiBase:
|
||||
self.data.static.put_client_setting_data(cab['id'], client_setting_data)
|
||||
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:
|
||||
return {"returnCode": 1, "apiName": "UpsertClientTestmodeApi"}
|
||||
|
||||
@ -282,16 +282,16 @@ class OngekiBase:
|
||||
self.data.static.put_client_testmode_data(region_id, client_testmode_data)
|
||||
return {"returnCode": 1, "apiName": "UpsertClientTestmodeApi"}
|
||||
|
||||
def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict:
|
||||
async def handle_upsert_client_bookkeeping_api_request(self, data: Dict) -> Dict:
|
||||
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"}
|
||||
|
||||
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"}
|
||||
|
||||
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"]
|
||||
if user >= 200000000000000: # Account for guest play
|
||||
user = None
|
||||
@ -309,10 +309,10 @@ class OngekiBase:
|
||||
|
||||
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"}
|
||||
|
||||
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)
|
||||
|
||||
if evts is None:
|
||||
@ -342,7 +342,7 @@ class OngekiBase:
|
||||
"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
|
||||
|
||||
if data["type"] == 1:
|
||||
@ -362,10 +362,10 @@ class OngekiBase:
|
||||
"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": []}
|
||||
|
||||
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)
|
||||
|
||||
if profile is None:
|
||||
@ -417,7 +417,7 @@ class OngekiBase:
|
||||
"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)
|
||||
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,
|
||||
}
|
||||
|
||||
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"])
|
||||
if user_tech_event_list is None:
|
||||
return {}
|
||||
@ -455,7 +455,7 @@ class OngekiBase:
|
||||
"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"])
|
||||
if user_tech_event_ranks is None:
|
||||
return {
|
||||
@ -481,7 +481,7 @@ class OngekiBase:
|
||||
"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"])
|
||||
if kop_list is None:
|
||||
return {}
|
||||
@ -496,7 +496,7 @@ class OngekiBase:
|
||||
"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"])
|
||||
max_ct = data["maxCount"]
|
||||
next_idx = data["nextIndex"]
|
||||
@ -516,7 +516,7 @@ class OngekiBase:
|
||||
"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
|
||||
p = self.data.item.get_items(data["userId"], kind)
|
||||
|
||||
@ -552,7 +552,7 @@ class OngekiBase:
|
||||
"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"])
|
||||
if o is None:
|
||||
return {}
|
||||
@ -566,7 +566,7 @@ class OngekiBase:
|
||||
|
||||
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)
|
||||
if p is None:
|
||||
return {}
|
||||
@ -594,7 +594,7 @@ class OngekiBase:
|
||||
|
||||
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"])
|
||||
if user_event_ranking_list is None:
|
||||
return {}
|
||||
@ -617,7 +617,7 @@ class OngekiBase:
|
||||
"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"])
|
||||
if user_login_bonus_list is None:
|
||||
return {}
|
||||
@ -635,7 +635,7 @@ class OngekiBase:
|
||||
"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(
|
||||
self.game, self.version, user_id=data["userId"]
|
||||
)
|
||||
@ -648,7 +648,7 @@ class OngekiBase:
|
||||
"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"])
|
||||
if recent_rating is None:
|
||||
return {
|
||||
@ -665,7 +665,7 @@ class OngekiBase:
|
||||
"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"])
|
||||
if activity is None:
|
||||
return {}
|
||||
@ -692,7 +692,7 @@ class OngekiBase:
|
||||
"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"])
|
||||
if user_stories is None:
|
||||
return {}
|
||||
@ -710,7 +710,7 @@ class OngekiBase:
|
||||
"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"])
|
||||
if user_chapters is None:
|
||||
return {}
|
||||
@ -728,14 +728,14 @@ class OngekiBase:
|
||||
"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 {
|
||||
"userId": data["userId"],
|
||||
"length": 0,
|
||||
"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"])
|
||||
if user_characters is None:
|
||||
return {}
|
||||
@ -753,7 +753,7 @@ class OngekiBase:
|
||||
"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"])
|
||||
if user_cards is None:
|
||||
return {}
|
||||
@ -771,7 +771,7 @@ class OngekiBase:
|
||||
"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
|
||||
decks = self.data.item.get_decks(data["userId"])
|
||||
if decks is None:
|
||||
@ -790,7 +790,7 @@ class OngekiBase:
|
||||
"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"])
|
||||
if user_trade_items is None:
|
||||
return {}
|
||||
@ -808,7 +808,7 @@ class OngekiBase:
|
||||
"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"])
|
||||
if user_scenerio is None:
|
||||
return {}
|
||||
@ -826,7 +826,7 @@ class OngekiBase:
|
||||
"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"])
|
||||
if rating_log is None:
|
||||
return {}
|
||||
@ -844,7 +844,7 @@ class OngekiBase:
|
||||
"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"])
|
||||
if user_mission_point_list is None:
|
||||
return {}
|
||||
@ -864,7 +864,7 @@ class OngekiBase:
|
||||
"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"])
|
||||
if user_event_point_list is None:
|
||||
return {}
|
||||
@ -886,7 +886,7 @@ class OngekiBase:
|
||||
"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"])
|
||||
if user_music_item_list is None:
|
||||
return {}
|
||||
@ -904,7 +904,7 @@ class OngekiBase:
|
||||
"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"])
|
||||
if user_evt_music_list is None:
|
||||
return {}
|
||||
@ -922,7 +922,7 @@ class OngekiBase:
|
||||
"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"])
|
||||
if p is None:
|
||||
return {}
|
||||
@ -940,7 +940,7 @@ class OngekiBase:
|
||||
"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"]
|
||||
user_id = data["userId"]
|
||||
|
||||
@ -1070,7 +1070,7 @@ class OngekiBase:
|
||||
|
||||
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
|
||||
"""
|
||||
@ -1094,7 +1094,7 @@ class OngekiBase:
|
||||
"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
|
||||
"""
|
||||
@ -1112,7 +1112,7 @@ class OngekiBase:
|
||||
"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
|
||||
"""
|
||||
|
@ -15,13 +15,13 @@ class OngekiBright(OngekiBase):
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = OngekiConstants.VER_ONGEKI_BRIGHT
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = await super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.30.00"
|
||||
ret["gameSetting"]["onlineDataVersion"] = "1.30.00"
|
||||
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
|
||||
p = self.data.profile.get_profile_data(data["userId"], self.version)
|
||||
if p is None:
|
||||
@ -55,13 +55,13 @@ class OngekiBright(OngekiBase):
|
||||
|
||||
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}
|
||||
|
||||
def handle_printer_logout_api_request(self, data: Dict):
|
||||
async def handle_printer_logout_api_request(self, data: Dict):
|
||||
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"])
|
||||
if user_cards is None:
|
||||
return {}
|
||||
@ -90,7 +90,7 @@ class OngekiBright(OngekiBase):
|
||||
"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"])
|
||||
if user_characters is None:
|
||||
return {
|
||||
@ -124,7 +124,7 @@ class OngekiBright(OngekiBase):
|
||||
"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"])
|
||||
if user_gachas is None:
|
||||
return {"userId": data["userId"], "length": 0, "userGachaList": []}
|
||||
@ -143,10 +143,10 @@ class OngekiBright(OngekiBase):
|
||||
"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)
|
||||
|
||||
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
|
||||
user_gacha_supplies = self.data.item.get_user_gacha_supplies(data["userId"])
|
||||
if user_gacha_supplies is None:
|
||||
@ -160,7 +160,7 @@ class OngekiBright(OngekiBase):
|
||||
"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)
|
||||
"Select Gacha" requires maxSelectPoint set and isCeiling set to 1
|
||||
@ -207,7 +207,7 @@ class OngekiBright(OngekiBase):
|
||||
"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
|
||||
"""
|
||||
@ -323,7 +323,7 @@ class OngekiBright(OngekiBase):
|
||||
"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"]
|
||||
user_id = data["userId"]
|
||||
|
||||
@ -405,7 +405,7 @@ class OngekiBright(OngekiBase):
|
||||
|
||||
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"]
|
||||
user_id = data["userId"]
|
||||
|
||||
@ -442,7 +442,7 @@ class OngekiBright(OngekiBase):
|
||||
|
||||
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"])
|
||||
if game_gacha_cards == []:
|
||||
# fallback to be at least able to select that gacha
|
||||
@ -522,7 +522,7 @@ class OngekiBright(OngekiBase):
|
||||
"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
|
||||
"""
|
||||
@ -548,7 +548,7 @@ class OngekiBright(OngekiBase):
|
||||
|
||||
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 {
|
||||
"returnCode": 1,
|
||||
"orderId": 0,
|
||||
@ -556,7 +556,7 @@ class OngekiBright(OngekiBase):
|
||||
"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 {
|
||||
"returnCode": 1,
|
||||
"orderId": 0,
|
||||
@ -564,7 +564,7 @@ class OngekiBright(OngekiBase):
|
||||
"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"]
|
||||
|
||||
# generate random serial id
|
||||
@ -589,7 +589,7 @@ class OngekiBright(OngekiBase):
|
||||
"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"]
|
||||
user_id = data["userId"]
|
||||
|
||||
|
@ -15,8 +15,8 @@ class OngekiBrightMemory(OngekiBright):
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = await super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.35.00"
|
||||
ret["gameSetting"]["onlineDataVersion"] = "1.35.00"
|
||||
ret["gameSetting"]["maxCountCharacter"] = 50
|
||||
@ -27,7 +27,7 @@ class OngekiBrightMemory(OngekiBright):
|
||||
ret["gameSetting"]["maxCountRivalMusic"] = 300
|
||||
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"])
|
||||
if not memories:
|
||||
return {
|
||||
@ -134,5 +134,5 @@ class OngekiBrightMemory(OngekiBright):
|
||||
"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}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import yaml
|
||||
import jinja2
|
||||
from twisted.web.http import Request
|
||||
from starlette.requests import Request
|
||||
from os import path
|
||||
from twisted.web.util import redirectTo
|
||||
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 inflection
|
||||
import yaml
|
||||
@ -120,35 +122,28 @@ class OngekiServlet(BaseServlet):
|
||||
|
||||
return True
|
||||
|
||||
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
|
||||
return (
|
||||
[],
|
||||
[("render_POST", "/SDDT/{version}/{endpoint}", {})]
|
||||
)
|
||||
def get_routes(self) -> List[Route]:
|
||||
return [
|
||||
Route("/SDDT/{version:int}/{endpoint:str}", self.render_POST, methods=['POST'])
|
||||
]
|
||||
|
||||
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_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"
|
||||
|
||||
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 ""
|
||||
t_port = f":{title_port_int}" if title_port_int and not self.core_cfg.server.is_using_proxy else ""
|
||||
|
||||
return (
|
||||
f"{proto}://{self.core_cfg.title.hostname}{t_port}/{game_code}/{game_ver}/",
|
||||
f"{self.core_cfg.title.hostname}{t_port}/",
|
||||
f"{proto}://{self.core_cfg.server.hostname}{t_port}/{game_code}/{game_ver}/",
|
||||
f"{self.core_cfg.server.hostname}{t_port}/",
|
||||
)
|
||||
|
||||
def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
||||
endpoint = matchers['endpoint']
|
||||
version = int(matchers['version'])
|
||||
async def render_POST(self, request: Request) -> bytes:
|
||||
endpoint: str = request.path_params.get('endpoint', '')
|
||||
version: int = request.path_params.get('version', 0)
|
||||
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
|
||||
internal_ver = 0
|
||||
client_ip = Utils.get_ip_addr(request)
|
||||
@ -178,13 +173,13 @@ class OngekiServlet(BaseServlet):
|
||||
self.logger.error(
|
||||
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]:
|
||||
self.logger.error(
|
||||
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()]
|
||||
|
||||
@ -201,7 +196,7 @@ class OngekiServlet(BaseServlet):
|
||||
self.logger.error(
|
||||
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
|
||||
|
||||
@ -213,7 +208,7 @@ class OngekiServlet(BaseServlet):
|
||||
self.logger.error(
|
||||
f"Unencrypted v{version} {endpoint} request, but config is set to encrypted only: {req_raw}"
|
||||
)
|
||||
return zlib.compress(b'{"stat": "0"}')
|
||||
return Response(zlib.compress(b'{"stat": "0"}'))
|
||||
|
||||
try:
|
||||
unzip = zlib.decompress(req_raw)
|
||||
@ -222,7 +217,7 @@ class OngekiServlet(BaseServlet):
|
||||
self.logger.error(
|
||||
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)
|
||||
|
||||
@ -235,15 +230,15 @@ class OngekiServlet(BaseServlet):
|
||||
|
||||
if not hasattr(self.versions[internal_ver], func_to_find):
|
||||
self.logger.warning(f"Unhandled v{version} request {endpoint}")
|
||||
return zlib.compress(b'{"returnCode": 1}')
|
||||
return Response(zlib.compress(b'{"returnCode": 1}'))
|
||||
|
||||
try:
|
||||
handler = getattr(self.versions[internal_ver], func_to_find)
|
||||
resp = handler(req_data)
|
||||
resp = await handler(req_data)
|
||||
|
||||
except Exception as 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:
|
||||
resp = {"returnCode": 1}
|
||||
@ -253,7 +248,7 @@ class OngekiServlet(BaseServlet):
|
||||
zipped = zlib.compress(json.dumps(resp, ensure_ascii=False).encode("utf-8"))
|
||||
|
||||
if not encrtped or version < 120:
|
||||
return zipped
|
||||
return Response(zipped)
|
||||
|
||||
padded = pad(zipped, 16)
|
||||
|
||||
@ -263,4 +258,4 @@ class OngekiServlet(BaseServlet):
|
||||
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)
|
||||
self.version = OngekiConstants.VER_ONGEKI_PLUS
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = await super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.05.00"
|
||||
ret["gameSetting"]["onlineDataVersion"] = "1.05.00"
|
||||
return ret
|
||||
|
@ -11,8 +11,8 @@ class OngekiRed(OngekiBase):
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = OngekiConstants.VER_ONGEKI_RED
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = await super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.20.00"
|
||||
ret["gameSetting"]["onlineDataVersion"] = "1.20.00"
|
||||
return ret
|
||||
|
@ -11,8 +11,8 @@ class OngekiRedPlus(OngekiBase):
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = OngekiConstants.VER_ONGEKI_RED_PLUS
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = await super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.25.00"
|
||||
ret["gameSetting"]["onlineDataVersion"] = "1.25.00"
|
||||
ret["gameSetting"]["maxCountCharacter"] = 50
|
||||
|
@ -11,8 +11,8 @@ class OngekiSummer(OngekiBase):
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = OngekiConstants.VER_ONGEKI_SUMMER
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = await super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.10.00"
|
||||
ret["gameSetting"]["onlineDataVersion"] = "1.10.00"
|
||||
return ret
|
||||
|
@ -11,8 +11,8 @@ class OngekiSummerPlus(OngekiBase):
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = OngekiConstants.VER_ONGEKI_SUMMER_PLUS
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
async def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = await super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["dataVersion"] = "1.15.00"
|
||||
ret["gameSetting"]["onlineDataVersion"] = "1.15.00"
|
||||
return ret
|
||||
|
@ -20,21 +20,21 @@ class PokkenBase:
|
||||
self.data = PokkenData(core_cfg)
|
||||
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.result = 1
|
||||
res.type = request.type
|
||||
|
||||
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.result = 1
|
||||
res.type = jackal_pb2.MessageType.PING
|
||||
|
||||
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.result = 1
|
||||
res.type = jackal_pb2.MessageType.REGISTER_PCB
|
||||
@ -61,35 +61,35 @@ class PokkenBase:
|
||||
"logfilename": "JackalMatchingLibrary.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)
|
||||
|
||||
res.register_pcb.CopyFrom(regist_pcb)
|
||||
|
||||
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.result = 1
|
||||
res.type = jackal_pb2.MessageType.SAVE_ADS
|
||||
|
||||
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.result = 1
|
||||
res.type = jackal_pb2.MessageType.SAVE_CLIENT_LOG
|
||||
|
||||
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.result = 1
|
||||
res.type = jackal_pb2.MessageType.CHECK_DIAGNOSIS
|
||||
|
||||
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.result = 1
|
||||
res.type = jackal_pb2.MessageType.LOAD_CLIENT_SETTINGS
|
||||
@ -112,7 +112,7 @@ class PokkenBase:
|
||||
|
||||
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.result = 1
|
||||
res.type = jackal_pb2.MessageType.LOAD_RANKING
|
||||
@ -126,7 +126,7 @@ class PokkenBase:
|
||||
res.load_ranking.CopyFrom(ranking)
|
||||
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.result = 1
|
||||
res.type = jackal_pb2.MessageType.LOAD_USER
|
||||
@ -287,13 +287,13 @@ class PokkenBase:
|
||||
res.load_user.CopyFrom(load_usr)
|
||||
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.result = 1
|
||||
res.type = jackal_pb2.MessageType.SET_BNPASSID_LOCK
|
||||
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.result = 1
|
||||
res.type = jackal_pb2.MessageType.SAVE_USER
|
||||
@ -394,38 +394,31 @@ class PokkenBase:
|
||||
|
||||
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.result = 1
|
||||
res.type = jackal_pb2.MessageType.SAVE_INGAME_LOG
|
||||
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.result = 1
|
||||
res.type = jackal_pb2.MessageType.SAVE_CHARGE
|
||||
return res.SerializeToString()
|
||||
|
||||
def handle_matching_noop(
|
||||
async def handle_matching_noop(
|
||||
self, data: Dict = {}, client_ip: str = "127.0.0.1"
|
||||
) -> Dict:
|
||||
return {}
|
||||
|
||||
def handle_matching_start_matching(
|
||||
async def handle_matching_start_matching(
|
||||
self, data: Dict = {}, client_ip: str = "127.0.0.1"
|
||||
) -> Dict:
|
||||
return {}
|
||||
|
||||
def handle_matching_is_matching(
|
||||
async def handle_matching_is_matching(
|
||||
self, data: Dict = {}, client_ip: str = "127.0.0.1"
|
||||
) -> Dict:
|
||||
"""
|
||||
"sessionId":"12345678",
|
||||
"A":{
|
||||
"pcb_id": data["data"]["must"]["pcb_id"],
|
||||
"gip": client_ip
|
||||
},
|
||||
"""
|
||||
return {
|
||||
"data": {
|
||||
"sessionId":"12345678",
|
||||
@ -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"
|
||||
) -> Dict:
|
||||
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 {}
|
||||
|
||||
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}")
|
||||
return {
|
||||
'data': {
|
||||
|
@ -3,6 +3,7 @@ from enum import Enum
|
||||
|
||||
class PokkenConstants:
|
||||
GAME_CODE = "SDAK"
|
||||
GAME_CDS = ["PKF1"]
|
||||
|
||||
CONFIG_NAME = "pokken.yaml"
|
||||
|
||||
@ -10,6 +11,12 @@ class PokkenConstants:
|
||||
|
||||
VERSION_NAMES = "Pokken Tournament"
|
||||
|
||||
SERIAL_IDENT = [2747]
|
||||
NETID_PREFIX = ["ABGN"]
|
||||
SERIAL_REGIONS = [1]
|
||||
SERIAL_ROLES = [3]
|
||||
SERIAL_CAB_IDENTS = [19]
|
||||
|
||||
class BATTLE_TYPE(Enum):
|
||||
TUTORIAL = 1
|
||||
AI = 2
|
||||
|
@ -1,6 +1,6 @@
|
||||
import yaml
|
||||
import jinja2
|
||||
from twisted.web.http import Request
|
||||
from starlette.requests import Request
|
||||
from os import path
|
||||
from twisted.web.server import Session
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
from typing import Tuple, List, Dict
|
||||
from twisted.web.http import Request
|
||||
from twisted.web import resource
|
||||
from twisted.internet import reactor
|
||||
import json, ast
|
||||
from starlette.requests import Request
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import Response, JSONResponse
|
||||
from starlette.routing import Route, WebSocketRoute
|
||||
from starlette.websockets import WebSocket, WebSocketState, WebSocketDisconnect
|
||||
import ast
|
||||
from datetime import datetime
|
||||
import yaml
|
||||
import logging, coloredlogs
|
||||
@ -17,8 +19,6 @@ from .config import PokkenConfig
|
||||
from .base import PokkenBase
|
||||
from .const import PokkenConstants
|
||||
from .proto import jackal_pb2
|
||||
from .services import PokkenAdmissionFactory
|
||||
|
||||
|
||||
class PokkenServlet(BaseServlet):
|
||||
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
|
||||
@ -69,47 +69,73 @@ class PokkenServlet(BaseServlet):
|
||||
|
||||
return True
|
||||
|
||||
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
|
||||
return (
|
||||
[],
|
||||
[
|
||||
("render_POST", "/pokken/", {}),
|
||||
("handle_matching", "/pokken/matching", {}),
|
||||
]
|
||||
)
|
||||
def get_routes(self) -> List[Route]:
|
||||
return [
|
||||
Route("/pokken/", self.render_POST, methods=['POST']),
|
||||
Route("/pokken/matching", self.handle_matching, methods=['POST']),
|
||||
WebSocketRoute("/pokken/admission", self.handle_admission)
|
||||
]
|
||||
|
||||
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 (
|
||||
f"https://{self.game_cfg.server.hostname}/pokken/",
|
||||
f"{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}:{self.game_cfg.ports.game}/pokken/",
|
||||
)
|
||||
|
||||
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}"):
|
||||
game_cfg.update(
|
||||
yaml.safe_load(open(f"{cfg_dir}/{PokkenConstants.CONFIG_NAME}"))
|
||||
)
|
||||
return (True, PokkenConstants.GAME_CDS, PokkenConstants.NETID_PREFIX)
|
||||
|
||||
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:
|
||||
return (False, "")
|
||||
if "type" not in resp:
|
||||
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")
|
||||
|
||||
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()
|
||||
async def render_POST(self, request: Request) -> bytes:
|
||||
content = await request.body()
|
||||
if content == b"":
|
||||
self.logger.info("Empty request")
|
||||
return b""
|
||||
@ -134,19 +160,19 @@ class PokkenServlet(BaseServlet):
|
||||
|
||||
self.logger.info(f"{endpoint} request from {Utils.get_ip_addr(request)}")
|
||||
|
||||
ret = handler(pokken_request)
|
||||
return ret
|
||||
ret = await handler(pokken_request)
|
||||
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:
|
||||
return b""
|
||||
return Response()
|
||||
|
||||
content = request.content.getvalue()
|
||||
content = await request.body()
|
||||
client_ip = Utils.get_ip_addr(request)
|
||||
|
||||
if content is None or content == b"":
|
||||
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(
|
||||
content.decode()
|
||||
@ -166,7 +192,7 @@ class PokkenServlet(BaseServlet):
|
||||
self.logger.warning(
|
||||
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)
|
||||
|
||||
@ -181,4 +207,4 @@ class PokkenServlet(BaseServlet):
|
||||
|
||||
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")
|
||||
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")
|
||||
resp_thing = SaoNoopResponse(header.cmd + 1)
|
||||
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
|
||||
resp = SaoGetMaintResponse(header.cmd +1)
|
||||
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
|
||||
resp = SaoCommonAcCabinetBootNotificationResponse(header.cmd +1)
|
||||
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
|
||||
resp = SaoCommonGetAppVersionsRequest(header.cmd +1)
|
||||
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
|
||||
resp = SaoMasterDataVersionCheckResponse(header.cmd +1)
|
||||
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
|
||||
resp = SaoCommonPayingPlayStartRequest(header.cmd +1)
|
||||
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
|
||||
resp = SaoGetQuestSceneMultiPlayPhotonServerResponse(header.cmd +1)
|
||||
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
|
||||
req = SaoGetAuthCardDataRequest(header, request)
|
||||
|
||||
@ -120,12 +120,12 @@ class SaoBase:
|
||||
resp = SaoGetAuthCardDataResponse(header.cmd +1, profile_data)
|
||||
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
|
||||
resp = SaoHomeCheckAcLoginBonusResponse(header.cmd +1)
|
||||
return resp.make()
|
||||
|
||||
def handle_c104(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||
async def handle_c104(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||
#common/login
|
||||
req = SaoCommonLoginRequest(header, request)
|
||||
|
||||
@ -135,17 +135,17 @@ class SaoBase:
|
||||
resp = SaoCommonLoginResponse(header.cmd +1, profile_data)
|
||||
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
|
||||
resp = SaoCheckComebackEventRequest(header.cmd +1)
|
||||
return resp.make()
|
||||
|
||||
def handle_c000(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||
async def handle_c000(self, header: SaoRequestHeader, request: bytes) -> bytes:
|
||||
#ticket/ticket
|
||||
resp = SaoTicketResponse(header.cmd +1)
|
||||
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
|
||||
req = SaoGetUserBasicDataRequest(header, request)
|
||||
|
||||
@ -154,7 +154,7 @@ class SaoBase:
|
||||
resp = SaoGetUserBasicDataResponse(header.cmd +1, profile_data)
|
||||
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
|
||||
req = SaoGetHeroLogUserDataListRequest(header, request)
|
||||
|
||||
@ -163,7 +163,7 @@ class SaoBase:
|
||||
resp = SaoGetHeroLogUserDataListResponse(header.cmd +1, hero_data)
|
||||
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
|
||||
req = SaoGetEquipmentUserDataListRequest(header, request)
|
||||
|
||||
@ -172,7 +172,7 @@ class SaoBase:
|
||||
resp = SaoGetEquipmentUserDataListResponse(header.cmd +1, equipment_data)
|
||||
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
|
||||
req = SaoGetItemUserDataListRequest(header, request)
|
||||
|
||||
@ -181,21 +181,21 @@ class SaoBase:
|
||||
resp = SaoGetItemUserDataListResponse(header.cmd +1, item_data)
|
||||
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
|
||||
supportIdsData = self.game_data.static.get_support_log_ids(0, True)
|
||||
|
||||
resp = SaoGetSupportLogUserDataListResponse(header.cmd +1, supportIdsData)
|
||||
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
|
||||
titleIdsData = self.game_data.static.get_title_ids(0, True)
|
||||
|
||||
resp = SaoGetTitleUserDataListResponse(header.cmd +1, titleIdsData)
|
||||
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
|
||||
req = SaoGetEpisodeAppendDataListRequest(header, request)
|
||||
|
||||
@ -204,7 +204,7 @@ class SaoBase:
|
||||
resp = SaoGetEpisodeAppendDataListResponse(header.cmd +1, profile_data)
|
||||
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
|
||||
req = SaoGetPartyDataListRequest(header, request)
|
||||
|
||||
@ -216,17 +216,17 @@ class SaoBase:
|
||||
resp = SaoGetPartyDataListResponse(header.cmd +1, hero1_data, hero2_data, hero3_data)
|
||||
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
|
||||
resp = SaoGetQuestScenePrevScanProfileCardResponse(header.cmd +1)
|
||||
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
|
||||
resp = SaoGetResourcePathInfoResponse(header.cmd +1)
|
||||
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
|
||||
req = SaoGetQuestSceneUserDataListRequest(header, request)
|
||||
|
||||
@ -235,22 +235,22 @@ class SaoBase:
|
||||
resp = SaoGetQuestSceneUserDataListResponse(header.cmd +1, quest_data)
|
||||
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
|
||||
resp = SaoCheckYuiMedalGetConditionResponse(header.cmd +1)
|
||||
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
|
||||
resp = SaoGetYuiMedalBonusUserDataResponse(header.cmd +1)
|
||||
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
|
||||
resp = SaoCheckProfileCardUsedRewardResponse(header.cmd +1)
|
||||
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
|
||||
req = SaoSynthesizeEnhancementHeroLogRequest(header, request)
|
||||
|
||||
@ -324,7 +324,7 @@ class SaoBase:
|
||||
resp = SaoSynthesizeEnhancementHeroLogResponse(header.cmd +1, synthesize_hero_log_data)
|
||||
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
|
||||
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)
|
||||
@ -386,7 +386,7 @@ class SaoBase:
|
||||
resp = SaoSynthesizeEnhancementEquipmentResponse(header.cmd +1, synthesize_equipment_data)
|
||||
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
|
||||
req_data = SaoChangePartyRequest(header, request)
|
||||
party_hero_list = []
|
||||
@ -421,7 +421,7 @@ class SaoBase:
|
||||
resp = SaoNoopResponse(header.cmd +1)
|
||||
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
|
||||
req_data = SaoEpisodePlayStartRequest(header, request)
|
||||
|
||||
@ -439,7 +439,7 @@ class SaoBase:
|
||||
resp = SaoEpisodePlayStartResponse(header.cmd +1, profile_data)
|
||||
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
|
||||
|
||||
req_data = SaoEpisodePlayEndRequest(header, request)
|
||||
@ -599,7 +599,7 @@ class SaoBase:
|
||||
resp = SaoEpisodePlayEndResponse(header.cmd +1)
|
||||
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
|
||||
req_data = SaoTrialTowerPlayStartRequest(header, request)
|
||||
|
||||
@ -618,7 +618,7 @@ class SaoBase:
|
||||
resp = SaoEpisodePlayStartResponse(header.cmd +1, profile_data)
|
||||
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
|
||||
req_data = SaoTrialTowerPlayEndRequest(header, request)
|
||||
|
||||
@ -799,7 +799,7 @@ class SaoBase:
|
||||
resp = SaoTrialTowerPlayEndResponse(header.cmd +1)
|
||||
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
|
||||
|
||||
req = SaoEpisodePlayEndUnanalyzedLogFixedRequest(header, request)
|
||||
@ -809,7 +809,7 @@ class SaoBase:
|
||||
resp = SaoEpisodePlayEndUnanalyzedLogFixedResponse(header.cmd +1, end_session_data[4])
|
||||
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
|
||||
req = TrialTowerPlayEndUnanalyzedLogFixed(header, request)
|
||||
|
||||
@ -818,58 +818,58 @@ class SaoBase:
|
||||
resp = SaoEpisodePlayEndUnanalyzedLogFixedResponse(header.cmd +1, end_session_data[4])
|
||||
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
|
||||
resp = SaoGetDefragMatchBasicDataResponse(header.cmd +1)
|
||||
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
|
||||
resp = SaoGetDefragMatchRankingUserDataResponse(header.cmd +1)
|
||||
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
|
||||
resp = SaoGetDefragMatchLeaguePointRankingListResponse(header.cmd +1)
|
||||
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
|
||||
resp = SaoGetDefragMatchLeagueScoreRankingListResponse(header.cmd +1)
|
||||
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
|
||||
resp = SaoBnidSerialCodeCheckResponse(header.cmd +1)
|
||||
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
|
||||
resp = SaoScanQrQuestProfileCardResponse(header.cmd +1)
|
||||
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
|
||||
# TODO: Get user shop data
|
||||
req = GetShopResourceSalesDataListRequest(header, request)
|
||||
resp = GetShopResourceSalesDataListResponse(header.cmd + 1)
|
||||
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
|
||||
# TODO: Get user shop data
|
||||
req = GetYuiMedalShopUserDataListRequest(header, request)
|
||||
resp = GetYuiMedalShopUserDataListResponse(header.cmd + 1)
|
||||
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
|
||||
# TODO: Get user shop data
|
||||
req = GetGashaMedalShopUserDataListRequest(header, request)
|
||||
resp = GetGashaMedalShopUserDataListResponse(header.cmd + 1)
|
||||
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
|
||||
req = GetMYuiMedalShopDataRequest(header, request)
|
||||
resp = GetMYuiMedalShopDataResponse(header.cmd + 1)
|
||||
@ -897,7 +897,7 @@ class SaoBase:
|
||||
self.logger.debug(f"Load {len(resp.data_list)} Yui Medal Shops")
|
||||
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
|
||||
req = GetMYuiMedalShopItemsRequest(header, request)
|
||||
resp = GetMYuiMedalShopItemsResponse(header.cmd + 1)
|
||||
@ -927,7 +927,7 @@ class SaoBase:
|
||||
self.logger.debug(f"Load {len(resp.data_list)} Yui Medal Shop Items")
|
||||
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
|
||||
req = GetMGashaMedalShopsRequest(header, request)
|
||||
resp = GetMGashaMedalShopsResponse(header.cmd + 1)
|
||||
@ -942,11 +942,11 @@ class SaoBase:
|
||||
self.logger.debug(f"Load {len(resp.data_list)} Gasha Medal Shops")
|
||||
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
|
||||
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
|
||||
req = GetMResEarnCampaignShopsRequest(header, request)
|
||||
resp = GetMResEarnCampaignShopsResponse(header.cmd + 1)
|
||||
@ -968,6 +968,6 @@ class SaoBase:
|
||||
#self.logger.debug(f"Load {len(resp.data_list)} Res Earn Campaign Shops")
|
||||
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
|
||||
return SaoNoopResponse(header.cmd + 1).make()
|
@ -3,12 +3,22 @@ from enum import Enum
|
||||
|
||||
class SaoConstants:
|
||||
GAME_CODE = "SDEW"
|
||||
GAME_CDS = ["SAO1"]
|
||||
|
||||
CONFIG_NAME = "sao.yaml"
|
||||
|
||||
VER_SAO = 0
|
||||
|
||||
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
|
||||
def game_ver_to_string(cls, ver: int):
|
||||
|
@ -1,5 +1,7 @@
|
||||
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 logging, coloredlogs
|
||||
from logging.handlers import TimedRotatingFileHandler
|
||||
@ -55,11 +57,10 @@ class SaoServlet(BaseServlet):
|
||||
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
|
||||
|
||||
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
|
||||
return (
|
||||
[],
|
||||
[("render_POST", "/{datecode}/proto/if/{category}/{endpoint}", {})]
|
||||
)
|
||||
def get_routes(self) -> List[Route]:
|
||||
return [
|
||||
Route("/{datecode:int}/proto/if/{category:str}/{endpoint:str}", self.render_POST, methods=['POST'])
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def is_game_enabled(cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str) -> bool:
|
||||
@ -86,30 +87,29 @@ class SaoServlet(BaseServlet):
|
||||
proto = "https"
|
||||
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]:
|
||||
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:
|
||||
endpoint = matchers.get('endpoint', '')
|
||||
request.responseHeaders.addRawHeader(b"content-type", b"text/html; charset=utf-8")
|
||||
async def render_POST(self, request: Request) -> bytes:
|
||||
endpoint = request.path_params.get('endpoint', '')
|
||||
iv = b""
|
||||
|
||||
req_raw = request.content.read()
|
||||
req_raw = await request.body()
|
||||
if len(req_raw) < 40:
|
||||
self.logger.warn(f"Malformed request to {endpoint} - {req_raw.hex()}")
|
||||
return b""
|
||||
return Response()
|
||||
req_header = SaoRequestHeader(req_raw)
|
||||
|
||||
cmd_str = f"{req_header.cmd:04x}"
|
||||
|
||||
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}")
|
||||
return b""
|
||||
return Response()
|
||||
|
||||
if self.game_cfg.crypt.enable:
|
||||
iv = req_raw[40:48]
|
||||
@ -124,7 +124,7 @@ class SaoServlet(BaseServlet):
|
||||
handler = getattr(self.base, f"handle_{cmd_str}", self.base.handle_noop)
|
||||
self.logger.info(f"{endpoint} - {cmd_str} request")
|
||||
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:
|
||||
resp = SaoNoopResponse(req_header.cmd + 1).make()
|
||||
@ -137,7 +137,7 @@ class SaoServlet(BaseServlet):
|
||||
|
||||
else:
|
||||
self.logger.error(f"Unknown response type {type(resp)}")
|
||||
return b""
|
||||
return Response()
|
||||
|
||||
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??
|
||||
resp = resp[:20] + tmp + iv + data_crypt
|
||||
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:
|
||||
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)
|
||||
housing_id = 1337
|
||||
self.logger.info(f"{req.chipId} -> {housing_id}")
|
||||
resp = HousingGetResponse(housing_id)
|
||||
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)
|
||||
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)
|
||||
allnet_region_id = None
|
||||
|
||||
@ -126,16 +126,16 @@ class WaccaBase:
|
||||
resp = HousingStartResponseV1(region_id)
|
||||
return resp.make()
|
||||
|
||||
def handle_advertise_GetNews_request(self, data: Dict) -> Dict:
|
||||
async def handle_advertise_GetNews_request(self, data: Dict) -> Dict:
|
||||
resp = GetNewsResponseV1()
|
||||
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)
|
||||
self.logger.info(f"Log out user {req.userId} from {req.chipId}")
|
||||
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)
|
||||
resp = UserStatusGetV1Response()
|
||||
|
||||
@ -181,7 +181,7 @@ class WaccaBase:
|
||||
|
||||
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)
|
||||
resp = UserStatusLoginResponseV1()
|
||||
is_consec_day = True
|
||||
@ -227,7 +227,7 @@ class WaccaBase:
|
||||
|
||||
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)
|
||||
|
||||
profileId = self.data.profile.create_profile(
|
||||
@ -268,7 +268,7 @@ class WaccaBase:
|
||||
|
||||
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)
|
||||
resp = UserStatusGetDetailResponseV1()
|
||||
|
||||
@ -433,7 +433,7 @@ class WaccaBase:
|
||||
|
||||
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)
|
||||
resp = UserTrialGetResponse()
|
||||
|
||||
@ -475,7 +475,7 @@ class WaccaBase:
|
||||
|
||||
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)
|
||||
|
||||
total_score = 0
|
||||
@ -571,7 +571,7 @@ class WaccaBase:
|
||||
)
|
||||
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(".")
|
||||
resp = BaseResponse()
|
||||
|
||||
@ -603,10 +603,10 @@ class WaccaBase:
|
||||
)
|
||||
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()
|
||||
|
||||
def handle_user_music_unlock_request(self, data: Dict) -> Dict:
|
||||
async def handle_user_music_unlock_request(self, data: Dict) -> Dict:
|
||||
req = UserMusicUnlockRequest(data)
|
||||
|
||||
profile = self.data.profile.get_profile(req.profileId)
|
||||
@ -672,12 +672,12 @@ class WaccaBase:
|
||||
|
||||
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
|
||||
# This likely requies calculating standings at regular intervals and caching the results
|
||||
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(".")
|
||||
if int(ver_split[0]) >= 3:
|
||||
resp = UserMusicUpdateResponseV3()
|
||||
@ -831,18 +831,18 @@ class WaccaBase:
|
||||
return resp.make()
|
||||
|
||||
# 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]
|
||||
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]
|
||||
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)
|
||||
|
||||
def handle_user_mission_update_request(self, data: Dict) -> Dict:
|
||||
async def handle_user_mission_update_request(self, data: Dict) -> Dict:
|
||||
req = UserMissionUpdateRequest(data)
|
||||
page_status = req.params[1][1]
|
||||
|
||||
@ -860,7 +860,7 @@ class WaccaBase:
|
||||
|
||||
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)
|
||||
resp = UserGoodsPurchaseResponse()
|
||||
|
||||
@ -905,13 +905,13 @@ class WaccaBase:
|
||||
|
||||
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()
|
||||
|
||||
def handle_competition_status_update_request(self, data: Dict) -> Dict:
|
||||
async def handle_competition_status_update_request(self, data: Dict) -> Dict:
|
||||
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)
|
||||
|
||||
user_id = self.data.profile.profile_to_aime_user(req.profileId)
|
||||
@ -931,7 +931,7 @@ class WaccaBase:
|
||||
|
||||
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)
|
||||
|
||||
user_id = self.data.profile.profile_to_aime_user(req.profileId)
|
||||
@ -970,7 +970,7 @@ class WaccaBase:
|
||||
)
|
||||
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)
|
||||
|
||||
user_id = self.data.profile.profile_to_aime_user(req.profileId)
|
||||
@ -989,7 +989,7 @@ class WaccaBase:
|
||||
|
||||
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)
|
||||
resp = UserVipGetResponse()
|
||||
|
||||
@ -1021,7 +1021,7 @@ class WaccaBase:
|
||||
|
||||
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)
|
||||
|
||||
profile = self.data.profile.get_profile(req.profileId)
|
||||
|
@ -1,6 +1,6 @@
|
||||
import yaml
|
||||
import jinja2
|
||||
from twisted.web.http import Request
|
||||
from starlette.requests import Request
|
||||
from os import path
|
||||
from twisted.web.server import Session
|
||||
|
||||
|
@ -1,16 +1,19 @@
|
||||
from starlette.routing import Route
|
||||
import yaml
|
||||
import logging, coloredlogs
|
||||
from logging.handlers import TimedRotatingFileHandler
|
||||
import logging
|
||||
import json
|
||||
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 os import path
|
||||
import traceback
|
||||
import sys
|
||||
|
||||
from core import CoreConfig, Utils
|
||||
from core.title import BaseServlet
|
||||
from .config import WaccaConfig
|
||||
from .config import WaccaConfig
|
||||
from .const import WaccaConstants
|
||||
@ -23,7 +26,7 @@ from .handlers.base import BaseResponse, BaseRequest
|
||||
from .handlers.helpers import Version
|
||||
|
||||
|
||||
class WaccaServlet:
|
||||
class WaccaServlet(BaseServlet):
|
||||
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
|
||||
self.core_cfg = core_cfg
|
||||
self.game_cfg = WaccaConfig()
|
||||
@ -62,15 +65,12 @@ class WaccaServlet:
|
||||
coloredlogs.install(
|
||||
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]]]:
|
||||
return (
|
||||
[],
|
||||
[
|
||||
("render_POST", "/WaccaServlet/api/{api}/{endpoint}", {}),
|
||||
("render_POST", "/WaccaServlet/api/{api}/{branch}/{endpoint}", {})
|
||||
]
|
||||
)
|
||||
|
||||
def get_routes(self) -> List[Route]:
|
||||
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']),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
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]:
|
||||
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)}/WaccaServlet",
|
||||
self.core_cfg.title.hostname,
|
||||
f"http://{self.core_cfg.server.hostname}:{Utils.get_title_port(self.core_cfg)}/WaccaServlet",
|
||||
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:
|
||||
hash = md5(json.dumps(resp, ensure_ascii=False).encode()).digest()
|
||||
request.responseHeaders.addRawHeader(b"X-Wacca-Hash", hash.hex().encode())
|
||||
return json.dumps(resp).encode()
|
||||
return JSONResponse(resp, headers=["X-Wacca-Hash", hash.hex()])
|
||||
|
||||
api = matchers['api']
|
||||
branch = matchers.get('branch', '')
|
||||
endpoint = matchers['endpoint']
|
||||
api = request.path_params.get('api', '')
|
||||
branch = request.path_params.get('branch', '')
|
||||
endpoint = request.path_params.get('endpoint', '')
|
||||
client_ip = Utils.get_ip_addr(request)
|
||||
bod = await request.body()
|
||||
|
||||
if branch:
|
||||
url_path = f"{api}/{branch}/{endpoint}"
|
||||
@ -114,13 +114,13 @@ class WaccaServlet:
|
||||
func_to_find = f"handle_{api}_{endpoint}_request"
|
||||
|
||||
try:
|
||||
req_json = json.loads(request.content.getvalue())
|
||||
req_json = json.loads(bod)
|
||||
version_full = Version(req_json["appVersion"])
|
||||
req = BaseRequest(req_json)
|
||||
|
||||
except KeyError as e:
|
||||
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.status = 1
|
||||
@ -129,7 +129,7 @@ class WaccaServlet:
|
||||
|
||||
except Exception as e:
|
||||
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.status = 1
|
||||
@ -176,7 +176,7 @@ class WaccaServlet:
|
||||
|
||||
try:
|
||||
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}")
|
||||
return end(resp)
|
||||
|
@ -37,13 +37,13 @@ class WaccaLily(WaccaS):
|
||||
(210003, 0),
|
||||
]
|
||||
|
||||
def handle_advertise_GetNews_request(self, data: Dict) -> Dict:
|
||||
async def handle_advertise_GetNews_request(self, data: Dict) -> Dict:
|
||||
resp = GetNewsResponseV3()
|
||||
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)
|
||||
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)
|
||||
|
||||
@ -64,7 +64,7 @@ class WaccaLily(WaccaS):
|
||||
|
||||
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)
|
||||
resp = UserStatusGetV2Response()
|
||||
|
||||
@ -145,7 +145,7 @@ class WaccaLily(WaccaS):
|
||||
|
||||
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)
|
||||
resp = UserStatusLoginResponseV2()
|
||||
is_consec_day = True
|
||||
@ -189,7 +189,7 @@ class WaccaLily(WaccaS):
|
||||
|
||||
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)
|
||||
if req.appVersion.minor >= 53:
|
||||
resp = UserStatusGetDetailResponseV3()
|
||||
@ -440,10 +440,10 @@ class WaccaLily(WaccaS):
|
||||
|
||||
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()
|
||||
|
||||
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)
|
||||
req = UserStatusUpdateRequestV2(data)
|
||||
self.data.profile.update_profile_lastplayed(
|
||||
|
@ -39,7 +39,7 @@ class WaccaLilyR(WaccaLily):
|
||||
(210003, 0),
|
||||
]
|
||||
|
||||
def handle_housing_start_request(self, data: Dict) -> Dict:
|
||||
async def handle_housing_start_request(self, data: Dict) -> Dict:
|
||||
req = HousingStartRequestV2(data)
|
||||
allnet_region_id = None
|
||||
|
||||
@ -71,9 +71,9 @@ class WaccaLilyR(WaccaLily):
|
||||
resp = HousingStartResponseV1(region_id)
|
||||
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)
|
||||
resp = super().handle_user_status_create_request(data)
|
||||
resp = await super().handle_user_status_create_request(data)
|
||||
|
||||
self.data.item.put_item(
|
||||
req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210054
|
||||
@ -102,5 +102,5 @@ class WaccaLilyR(WaccaLily):
|
||||
|
||||
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()
|
||||
|
@ -47,12 +47,12 @@ class WaccaReverse(WaccaLilyR):
|
||||
(310006, 0),
|
||||
]
|
||||
|
||||
def handle_user_status_login_request(self, data: Dict) -> Dict:
|
||||
resp = super().handle_user_status_login_request(data)
|
||||
async def handle_user_status_login_request(self, data: Dict) -> Dict:
|
||||
resp = await super().handle_user_status_login_request(data)
|
||||
resp["params"].append([])
|
||||
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)
|
||||
resp = UserStatusGetDetailResponseV4()
|
||||
|
||||
@ -305,9 +305,9 @@ class WaccaReverse(WaccaLilyR):
|
||||
|
||||
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)
|
||||
resp = super().handle_user_status_create_request(data)
|
||||
resp = await super().handle_user_status_create_request(data)
|
||||
|
||||
self.data.item.put_item(
|
||||
req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 310001
|
||||
|
@ -31,6 +31,6 @@ class WaccaS(WaccaBase):
|
||||
super().__init__(cfg, game_cfg)
|
||||
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()
|
||||
return resp.make()
|
||||
|
Loading…
Reference in New Issue
Block a user