artemis/titles/idac/index.py

167 lines
5.7 KiB
Python

import json
import traceback
import inflection
import yaml
import logging
import coloredlogs
from os import path
from typing import Dict, 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.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
from titles.idac.matching import IDACMatching
class IDACServlet:
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
self.core_cfg = core_cfg
self.game_cfg = IDACConfig()
self.game_cfg.update(
yaml.safe_load(open(f"{cfg_dir}/{IDACConstants.CONFIG_NAME}"))
)
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 get_allnet_info(
cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str
) -> Tuple[bool, str, str]:
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, "", "")
if core_cfg.server.is_develop:
return (
True,
f"",
# requires http or else it defautls to https
f"http://{core_cfg.title.hostname}:{core_cfg.title.port}/{game_code}/$v/",
)
return (
True,
f"",
# requires http or else it defautls to https
f"http://{core_cfg.title.hostname}/{game_code}/$v/",
)
def render_POST(self, request: Request, version: int, url_path: str) -> bytes:
req_raw = request.content.getvalue()
url_split = url_path.split("/")
internal_ver = 0
endpoint = url_split[len(url_split) - 1]
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
if url_split[0] == "initiald":
header_application = self.decode_header(request.getAllHeaders())
req_data = json.loads(req_raw)
self.logger.info(f"v{version} {endpoint} request from {client_ip}")
self.logger.debug(f"Headers: {header_application}")
self.logger.debug(req_data)
# func_to_find = "handle_" + inflection.underscore(endpoint) + "_request"
func_to_find = "handle_"
for x in url_split:
func_to_find += f"{x.lower()}_" if not x == "" and not x == "initiald" else ""
func_to_find += f"request"
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")
resp = None
try:
handler = getattr(self.versions[internal_ver], func_to_find)
resp = handler(req_data, header_application)
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")
if resp is None:
resp = {"status_code": "0"}
self.logger.debug(f"Response {resp}")
return json.dumps(resp, ensure_ascii=False).encode("utf-8")
self.logger.warning(
f"IDAC unknown request {url_path} - {request.content.getvalue().decode()}"
)
return '{"status_code": "0"}'.encode("utf-8")
def decode_header(self, data: Dict) -> Dict:
app: str = data[b"application"].decode()
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:
endpoints.serverFromString(
reactor,
f"tcp:{self.game_cfg.server.matching}:interface={self.core_cfg.server.listen_address}",
).listen(server.Site(IDACMatching(self.core_cfg, self.game_cfg)))
reactor.listenUDP(
self.game_cfg.server.echo1,
IDACEchoUDP(self.core_cfg, self.game_cfg, self.game_cfg.server.echo1),
)
reactor.listenUDP(
self.game_cfg.server.echo2,
IDACEchoUDP(self.core_cfg, self.game_cfg, self.game_cfg.server.echo2),
)