artemis/titles/idac/index.py

195 lines
6.8 KiB
Python
Raw Normal View History

2023-10-01 01:54:23 +00:00
import json
import traceback
2024-01-09 08:07:04 +00:00
from starlette.routing import Route
from starlette.requests import Request
from starlette.responses import JSONResponse
2023-10-01 01:54:23 +00:00
import yaml
import logging
import coloredlogs
from os import path
from typing import Dict, List, Tuple
2023-10-01 01:54:23 +00:00
from logging.handlers import TimedRotatingFileHandler
2024-01-09 17:41:47 +00:00
import asyncio
2023-10-01 01:54:23 +00:00
from core.config import CoreConfig
2024-01-09 17:41:47 +00:00
from core.title import BaseServlet, JSONResponseNoASCII
2023-10-01 01:54:23 +00:00
from core.utils import Utils
from titles.idac.base import IDACBase
from titles.idac.season2 import IDACSeason2
from titles.idac.config import IDACConfig
from titles.idac.const import IDACConstants
from titles.idac.echo import IDACEchoUDP
2024-01-09 08:07:04 +00:00
class IDACServlet(BaseServlet):
2023-10-01 01:54:23 +00:00
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
self.core_cfg = core_cfg
self.game_cfg = IDACConfig()
if path.exists(f"{cfg_dir}/{IDACConstants.CONFIG_NAME}"):
self.game_cfg.update(
yaml.safe_load(open(f"{cfg_dir}/{IDACConstants.CONFIG_NAME}"))
)
2023-10-01 01:54:23 +00:00
self.versions = [
IDACBase(core_cfg, self.game_cfg),
IDACSeason2(core_cfg, self.game_cfg)
]
self.logger = logging.getLogger("idac")
log_fmt_str = "[%(asctime)s] IDAC | %(levelname)s | %(message)s"
log_fmt = logging.Formatter(log_fmt_str)
fileHandler = TimedRotatingFileHandler(
"{0}/{1}.log".format(self.core_cfg.server.log_dir, "idac"),
encoding="utf8",
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.game_cfg.server.loglevel)
coloredlogs.install(
level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str
)
@classmethod
def is_game_enabled(cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str) -> bool:
2023-10-01 01:54:23 +00:00
game_cfg = IDACConfig()
if path.exists(f"{cfg_dir}/{IDACConstants.CONFIG_NAME}"):
game_cfg.update(
yaml.safe_load(open(f"{cfg_dir}/{IDACConstants.CONFIG_NAME}"))
)
if not game_cfg.server.enable:
return False
return True
2024-01-09 08:07:04 +00:00
def get_routes(self) -> List[Route]:
return [
2024-01-09 17:41:47 +00:00
Route("/{version:int}/initiald/{category:str}/{endpoint:str}", self.render_POST, methods=["POST"]),
2024-01-09 17:52:53 +00:00
Route("/{version:int}/initiald-matching/{endpoint:str}", self.render_matching, methods=["POST"]),
2024-01-09 08:07:04 +00:00
]
def get_allnet_info(
self, game_code: str, game_ver: int, keychip: str
) -> Tuple[bool, str, str]:
title_port_int = Utils.get_title_port(self.core_cfg)
t_port = f":{title_port_int}" if title_port_int and not self.core_cfg.server.is_using_proxy else ""
2023-10-01 01:54:23 +00:00
return (
f"",
# requires http or else it defaults to https
2024-01-09 17:41:47 +00:00
f"http://{self.core_cfg.server.hostname}{t_port}/{game_ver}/",
2023-10-01 01:54:23 +00:00
)
2024-01-09 08:07:04 +00:00
async def render_POST(self, request: Request) -> bytes:
req_raw = await request.body()
2023-10-01 01:54:23 +00:00
internal_ver = 0
2024-01-09 08:07:04 +00:00
version: int = request.path_params.get('version')
category: str = request.path_params.get('category')
endpoint: str = request.path_params.get('endpoint')
2023-10-01 01:54:23 +00:00
client_ip = Utils.get_ip_addr(request)
if version >= 100 and version < 140: # IDAC Season 1
internal_ver = IDACConstants.VER_IDAC_SEASON_1
elif version >= 140 and version < 171: # IDAC Season 2
internal_ver = IDACConstants.VER_IDAC_SEASON_2
2024-01-09 08:07:04 +00:00
header_application = self.decode_header(request.headers.get("application", ""))
2023-10-01 01:54:23 +00:00
req_data = json.loads(req_raw)
2023-10-01 01:54:23 +00:00
self.logger.info(f"v{version} {endpoint} request from {client_ip}")
self.logger.debug(f"Headers: {header_application}")
self.logger.debug(req_data)
2023-10-01 01:54:23 +00:00
# func_to_find = "handle_" + inflection.underscore(endpoint) + "_request"
func_to_find = "handle_"
func_to_find += f"{category.lower()}_" if not category == "" else ""
func_to_find += f"{endpoint.lower()}_request"
2023-10-01 01:54:23 +00:00
if not hasattr(self.versions[internal_ver], func_to_find):
self.logger.warning(f"Unhandled v{version} request {endpoint}")
2024-01-09 08:07:04 +00:00
return JSONResponse('{"status_code": "0"}')
2023-10-01 01:54:23 +00:00
resp = None
try:
handler = getattr(self.versions[internal_ver], func_to_find)
2024-01-09 19:42:17 +00:00
resp = await handler(req_data, header_application)
2023-10-01 01:54:23 +00:00
except Exception as e:
traceback.print_exc()
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
2024-01-09 08:07:04 +00:00
return JSONResponse('{"status_code": "0"}')
2023-10-01 01:54:23 +00:00
if resp is None:
resp = {"status_code": "0"}
2023-10-01 01:54:23 +00:00
self.logger.debug(f"Response {resp}")
2024-01-09 17:41:47 +00:00
return JSONResponseNoASCII(resp)
async def render_matching(self, request: Request):
url: str = request.path_params.get("endpoint")
ver: int = request.path_params.get("version")
client_ip = Utils.get_ip_addr(request)
req_data = await request.json()
header_application = self.decode_header(request.headers.get('application', ''))
user_id = int(header_application["session"])
# self.getMatchingStatus(user_id)
self.logger.info(
f"IDAC Matching request from {client_ip}: {url} - {req_data}"
)
resp = {"status_code": "0"}
if url == "/regist":
self.queue = self.queue + 1
elif url == "/status":
if req_data.get("cancel_flag"):
self.queue = self.queue - 1
self.logger.info(
f"IDAC Matching endpoint {client_ip} had quited"
)
resp = {
"status_code": "0",
# Only IPv4 is supported
"host": self.game_config.server.matching_host,
"port": self.game_config.server.matching_p2p,
"room_name": "INDTA",
"state": 1,
}
2023-10-01 01:54:23 +00:00
2024-01-09 17:41:47 +00:00
self.logger.debug(f"Response {resp}")
return JSONResponseNoASCII(resp)
2023-10-01 01:54:23 +00:00
2024-01-09 08:07:04 +00:00
def decode_header(self, app: str) -> Dict:
2023-10-01 01:54:23 +00:00
ret = {}
for x in app.split(", "):
y = x.split("=")
ret[y[0]] = y[1].replace('"', "")
return ret
def setup(self):
if self.game_cfg.server.enable:
2024-01-09 17:41:47 +00:00
loop = asyncio.get_running_loop()
asyncio.create_task(
loop.create_datagram_endpoint(
lambda: IDACEchoUDP(),
local_addr=(self.core_cfg.server.listen_address, self.game_cfg.server.echo1)
)
2023-10-01 01:54:23 +00:00
)
2023-11-13 17:14:34 +00:00
2024-01-09 17:41:47 +00:00
self.logger.info(f"Matching listening on {self.game_cfg.server.matching} with echo on {self.game_cfg.server.echo1}")