artemis/titles/diva/index.py

136 lines
4.9 KiB
Python
Raw Normal View History

2024-01-09 08:07:04 +00:00
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
import zlib
import json
import base64
from os import path
from typing import Tuple, Dict, List
2023-02-26 04:40:50 +00:00
from titles.diva.handlers.base import *
from core.config import CoreConfig
from core.title import BaseServlet
from core.utils import Utils
from .config import DivaConfig
from .const import DivaConstants
from .base import DivaBase
2023-03-09 16:38:58 +00:00
class DivaServlet(BaseServlet):
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
super().__init__(core_cfg, cfg_dir)
self.game_cfg = DivaConfig()
if path.exists(f"{cfg_dir}/{DivaConstants.CONFIG_NAME}"):
2023-03-09 16:38:58 +00:00
self.game_cfg.update(
yaml.safe_load(open(f"{cfg_dir}/{DivaConstants.CONFIG_NAME}"))
)
self.base = DivaBase(core_cfg, self.game_cfg)
self.logger = logging.getLogger("diva")
log_fmt_str = "[%(asctime)s] Diva | %(levelname)s | %(message)s"
log_fmt = logging.Formatter(log_fmt_str)
2023-03-09 16:38:58 +00:00
fileHandler = TimedRotatingFileHandler(
"{0}/{1}.log".format(self.core_cfg.server.log_dir, "diva"),
encoding="utf8",
when="d",
backupCount=10,
)
fileHandler.setFormatter(log_fmt)
2023-03-09 16:38:58 +00:00
consoleHandler = logging.StreamHandler()
consoleHandler.setFormatter(log_fmt)
self.logger.addHandler(fileHandler)
self.logger.addHandler(consoleHandler)
2023-03-09 16:38:58 +00:00
self.logger.setLevel(self.game_cfg.server.loglevel)
2023-03-09 16:38:58 +00:00
coloredlogs.install(
level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str
)
2024-01-09 08:07:04 +00:00
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:
2024-01-09 08:07:04 +00:00
return (f"http://{self.core_cfg.server.hostname}:{Utils.get_title_port(self.core_cfg)}/DivaServlet/", self.core_cfg.server.hostname)
2024-01-09 08:07:04 +00:00
return (f"http://{self.core_cfg.server.hostname}/DivaServlet/", self.core_cfg.server.hostname)
@classmethod
def is_game_enabled(
2023-03-09 16:38:58 +00:00
cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str
) -> bool:
game_cfg = DivaConfig()
if path.exists(f"{cfg_dir}/{DivaConstants.CONFIG_NAME}"):
2023-03-09 16:38:58 +00:00
game_cfg.update(
yaml.safe_load(open(f"{cfg_dir}/{DivaConstants.CONFIG_NAME}"))
)
if not game_cfg.server.enable:
return False
2023-03-09 16:38:58 +00:00
return True
2024-01-12 17:18:23 +00:00
async def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
req_raw = await request.body()
url_header = request.headers
2023-03-09 16:38:58 +00:00
# Ping Dispatch
2024-01-12 17:18:23 +00:00
if "THIS_STRING_SEPARATES" in url_header:
binary_request = req_raw.splitlines()
binary_cmd_decoded = binary_request[3].decode("utf-8")
url_data = binary_cmd_decoded # for logging
2023-02-26 04:40:50 +00:00
req_cls = BaseBinaryRequest(binary_cmd_decoded)
2023-02-26 04:40:50 +00:00
else:
json_string = json.dumps(
req_raw.decode("utf-8")
) # Take the response and decode as UTF-8 and dump
b64string = json_string.replace(
r"\n", "\n"
) # Remove all \n and separate them as new lines
gz_string = base64.b64decode(b64string) # Decompressing the base64 string
2023-02-26 04:40:50 +00:00
try:
url_data = zlib.decompress(gz_string) # Decompressing the gzip
2023-02-26 04:40:50 +00:00
except zlib.error as e:
self.logger.error(f"Failed to defalte! {e} -> {gz_string}")
2024-01-12 17:18:23 +00:00
return PlainTextResponse("stat=0")
2023-02-26 04:40:50 +00:00
try:
req_cls = BaseRequest(url_data)
except DivaRequestParseException as e:
self.logger.error(e)
2024-01-12 17:18:23 +00:00
return PlainTextResponse("stat=0")
self.logger.debug(f"Request: {url_data}\nHeaders: {url_header}")
self.logger.info(
2023-11-20 16:29:10 +00:00
f"{req_cls.cmd} request from {req_cls.kc_serial}/{req_cls.b_serial} at {Utils.get_ip_addr(request)}"
)
2023-02-26 04:40:50 +00:00
handler_str = f"handle_{req_cls.cmd}_request"
2023-02-26 04:40:50 +00:00
if not hasattr(self.base, handler_str):
self.logger.warn(f"Unhandled cmd {req_cls.cmd}")
2024-01-12 17:18:23 +00:00
return PlainTextResponse(BaseResponse(req_cls.cmd, req_cls.req_id).make())
2023-02-26 04:40:50 +00:00
handler = getattr(self.base, handler_str)
2023-02-26 16:40:13 +00:00
response = handler(req_cls.raw)
if response is None or response == "":
response = BaseResponse(req_cls.cmd, req_cls.req_id).make()
2023-03-17 05:35:51 +00:00
if not response.startswith("cmd="):
response = f"cmd={req_cls.cmd}&req_id={req_cls.req_id}&stat=ok" + response
2023-03-17 05:35:51 +00:00
self.logger.debug(f"Response: {response}")
2024-01-12 17:18:23 +00:00
return PlainTextResponse(response)