artemis/titles/pokken/index.py

185 lines
6.2 KiB
Python
Raw Permalink Normal View History

from typing import Tuple, List, Dict
2023-02-17 07:09:26 +00:00
from twisted.web.http import Request
2023-03-12 04:42:12 +00:00
from twisted.web import resource
from twisted.internet import reactor
2023-03-12 04:42:12 +00:00
import json, ast
from datetime import datetime
2023-02-17 07:09:26 +00:00
import yaml
import logging, coloredlogs
from logging.handlers import TimedRotatingFileHandler
2023-03-12 04:42:12 +00:00
import inflection
2023-02-27 21:51:17 +00:00
from os import path
2023-02-28 05:41:32 +00:00
from google.protobuf.message import DecodeError
2023-02-17 07:09:26 +00:00
from core import CoreConfig, Utils
from core.title import BaseServlet
from .config import PokkenConfig
from .base import PokkenBase
from .const import PokkenConstants
from .proto import jackal_pb2
from .services import PokkenAdmissionFactory
2023-02-17 07:09:26 +00:00
2023-03-09 16:38:58 +00:00
class PokkenServlet(BaseServlet):
2023-02-17 07:09:26 +00:00
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
super().__init__(core_cfg, cfg_dir)
2023-02-17 07:09:26 +00:00
self.config_dir = cfg_dir
self.game_cfg = PokkenConfig()
if path.exists(f"{cfg_dir}/pokken.yaml"):
self.game_cfg.update(yaml.safe_load(open(f"{cfg_dir}/pokken.yaml")))
2023-02-17 07:09:26 +00:00
self.logger = logging.getLogger("pokken")
if not hasattr(self.logger, "inited"):
log_fmt_str = "[%(asctime)s] Pokken | %(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, "pokken"),
encoding="utf8",
when="d",
backupCount=10,
)
2023-02-17 07:09:26 +00:00
fileHandler.setFormatter(log_fmt)
2023-03-09 16:38:58 +00:00
2023-02-17 07:09:26 +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
2023-02-17 07:09:26 +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
)
2023-02-17 07:09:26 +00:00
self.logger.inited = True
self.base = PokkenBase(core_cfg, self.game_cfg)
@classmethod
def is_game_enabled(cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str) -> bool:
game_cfg = PokkenConfig()
2023-03-09 16:38:58 +00:00
if path.exists(f"{cfg_dir}/{PokkenConstants.CONFIG_NAME}"):
2023-03-09 16:38:58 +00:00
game_cfg.update(
yaml.safe_load(open(f"{cfg_dir}/{PokkenConstants.CONFIG_NAME}"))
)
if not game_cfg.server.enable:
return False
return True
def get_endpoint_matchers(self) -> Tuple[List[Tuple[str, str, Dict]], List[Tuple[str, str, Dict]]]:
2023-03-09 16:38:58 +00:00
return (
[],
[
("render_POST", "/pokken/", {}),
("handle_matching", "/pokken/matching", {}),
]
)
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/",
2023-03-09 16:38:58 +00:00
)
2023-03-05 03:46:26 +00:00
def get_mucha_info(self, core_cfg: CoreConfig, cfg_dir: str) -> Tuple[bool, str]:
2023-03-05 03:46:26 +00:00
game_cfg = PokkenConfig()
2023-03-09 16:38:58 +00:00
2023-03-05 03:46:26 +00:00
if path.exists(f"{cfg_dir}/{PokkenConstants.CONFIG_NAME}"):
2023-03-09 16:38:58 +00:00
game_cfg.update(
yaml.safe_load(open(f"{cfg_dir}/{PokkenConstants.CONFIG_NAME}"))
)
2023-03-05 03:46:26 +00:00
if not game_cfg.server.enable:
return (False, "")
2023-03-09 16:38:58 +00:00
2023-04-12 06:34:29 +00:00
return (True, "PKF1")
2023-03-09 16:38:58 +00:00
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)
)
2023-02-17 07:09:26 +00:00
def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
2023-02-28 05:41:32 +00:00
content = request.content.getvalue()
if content == b"":
self.logger.info("Empty request")
return b""
2023-02-17 07:09:26 +00:00
pokken_request = jackal_pb2.Request()
2023-02-28 05:41:32 +00:00
try:
pokken_request.ParseFromString(content)
except DecodeError as e:
2023-08-08 14:17:56 +00:00
self.logger.warning(f"{e} {content}")
2023-02-28 05:41:32 +00:00
return b""
endpoint = jackal_pb2.MessageType.DESCRIPTOR.values_by_number[
pokken_request.type
].name.lower()
2023-04-12 06:34:29 +00:00
self.logger.debug(pokken_request)
2023-03-09 16:38:58 +00:00
2023-02-17 07:09:26 +00:00
handler = getattr(self.base, f"handle_{endpoint}", None)
if handler is None:
2023-08-08 14:17:56 +00:00
self.logger.warning(f"No handler found for message type {endpoint}")
2023-02-17 07:09:26 +00:00
return self.base.handle_noop(pokken_request)
2023-03-12 20:30:57 +00:00
self.logger.info(f"{endpoint} request from {Utils.get_ip_addr(request)}")
2023-03-12 01:02:58 +00:00
ret = handler(pokken_request)
return ret
def handle_matching(self, request: Request, game_code: str, matchers: Dict) -> bytes:
if not self.game_cfg.server.enable_matching:
return b""
2023-03-12 04:42:12 +00:00
content = request.content.getvalue()
client_ip = Utils.get_ip_addr(request)
2023-03-12 04:42:12 +00:00
if content is None or content == b"":
self.logger.info("Empty matching request")
return json.dumps(self.base.handle_matching_noop()).encode()
json_content = ast.literal_eval(
content.decode()
.replace("null", "None")
.replace("true", "True")
.replace("false", "False")
)
2023-03-12 04:42:12 +00:00
self.logger.info(f"Matching {json_content['call']} request")
self.logger.debug(json_content)
handler = getattr(
self.base,
f"handle_matching_{inflection.underscore(json_content['call'])}",
None,
)
2023-03-12 04:42:12 +00:00
if handler is None:
2023-08-08 14:17:56 +00:00
self.logger.warning(
f"No handler found for message type {json_content['call']}"
)
2023-03-12 04:42:12 +00:00
return json.dumps(self.base.handle_matching_noop()).encode()
2023-03-12 04:42:12 +00:00
ret = handler(json_content, client_ip)
2023-03-12 04:42:12 +00:00
if ret is None:
ret = {}
2023-03-12 04:42:12 +00:00
if "result" not in ret:
ret["result"] = "true"
if "data" not in ret:
ret["data"] = {}
if "timestamp" not in ret:
ret["timestamp"] = int(datetime.now().timestamp() * 1000)
2023-03-12 04:42:12 +00:00
self.logger.debug(f"Response {ret}")
return json.dumps(ret).encode()