diff --git a/example_config/pokken.yaml b/example_config/pokken.yaml index 225e980..423d9d3 100644 --- a/example_config/pokken.yaml +++ b/example_config/pokken.yaml @@ -2,8 +2,11 @@ server: hostname: "localhost" enable: True loglevel: "info" - port: 9000 - port_stun: 9001 - port_turn: 9002 - port_admission: 9003 - auto_register: True \ No newline at end of file + auto_register: True + enable_matching: False + stun_server_host: "stunserver.stunprotocol.org" + stun_server_port: 3478 + +ports: + game: 9000 + admission: 9001 diff --git a/titles/pokken/base.py b/titles/pokken/base.py index 306532d..50bc760 100644 --- a/titles/pokken/base.py +++ b/titles/pokken/base.py @@ -44,19 +44,19 @@ class PokkenBase: biwa_setting = { "MatchingServer": { "host": f"https://{self.game_cfg.server.hostname}", - "port": self.game_cfg.server.port, + "port": self.game_cfg.ports.game, "url": "/SDAK/100/matching", }, "StunServer": { - "addr": self.game_cfg.server.hostname, - "port": self.game_cfg.server.port_stun, + "addr": self.game_cfg.server.stun_server_host, + "port": self.game_cfg.server.stun_server_port, }, "TurnServer": { - "addr": self.game_cfg.server.hostname, - "port": self.game_cfg.server.port_turn, + "addr": self.game_cfg.server.stun_server_host, + "port": self.game_cfg.server.stun_server_port, }, - "AdmissionUrl": f"ws://{self.game_cfg.server.hostname}:{self.game_cfg.server.port_admission}", - "locationId": 123, + "AdmissionUrl": f"ws://{self.game_cfg.server.hostname}:{self.game_cfg.ports.admission}", + "locationId": 123, # FIXME: Get arcade's ID from the database "logfilename": "JackalMatchingLibrary.log", "biwalogfilename": "./biwa.log", } @@ -94,6 +94,7 @@ class PokkenBase: res.type = jackal_pb2.MessageType.LOAD_CLIENT_SETTINGS settings = jackal_pb2.LoadClientSettingsResponseData() + # TODO: Make configurable settings.money_magnification = 1 settings.continue_bonus_exp = 100 settings.continue_fight_money = 100 @@ -356,12 +357,11 @@ class PokkenBase: self, data: Dict = {}, client_ip: str = "127.0.0.1" ) -> Dict: """ - "sessionId":"12345678", - "A":{ - "pcb_id": data["data"]["must"]["pcb_id"], - "gip": client_ip - }, - "list":[] + "sessionId":"12345678", + "A":{ + "pcb_id": data["data"]["must"]["pcb_id"], + "gip": client_ip + }, """ return { "data": { @@ -379,10 +379,13 @@ class PokkenBase: ) -> Dict: return {} + 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: self.logger.info(f"Admission: JoinSession from {req_ip}") return { 'data': { - "id": 123 + "id": 12345678 } - } \ No newline at end of file + } diff --git a/titles/pokken/config.py b/titles/pokken/config.py index 84da8d2..d3741af 100644 --- a/titles/pokken/config.py +++ b/titles/pokken/config.py @@ -25,30 +25,6 @@ class PokkenServerConfig: ) ) - @property - def port(self) -> int: - return CoreConfig.get_config_field( - self.__config, "pokken", "server", "port", default=9000 - ) - - @property - def port_stun(self) -> int: - return CoreConfig.get_config_field( - self.__config, "pokken", "server", "port_stun", default=9001 - ) - - @property - def port_turn(self) -> int: - return CoreConfig.get_config_field( - self.__config, "pokken", "server", "port_turn", default=9002 - ) - - @property - def port_admission(self) -> int: - return CoreConfig.get_config_field( - self.__config, "pokken", "server", "port_admission", default=9003 - ) - @property def auto_register(self) -> bool: """ @@ -59,7 +35,51 @@ class PokkenServerConfig: self.__config, "pokken", "server", "auto_register", default=True ) + @property + def enable_matching(self) -> bool: + """ + If global matching should happen + """ + return CoreConfig.get_config_field( + self.__config, "pokken", "server", "enable_matching", default=False + ) + + @property + def stun_server_host(self) -> str: + """ + Hostname of the EXTERNAL stun server the game should connect to. This is not handled by artemis. + """ + return CoreConfig.get_config_field( + self.__config, "pokken", "server", "stun_server_host", default="stunserver.stunprotocol.org" + ) + + @property + def stun_server_port(self) -> int: + """ + Port of the EXTERNAL stun server the game should connect to. This is not handled by artemis. + """ + return CoreConfig.get_config_field( + self.__config, "pokken", "server", "stun_server_port", default=3478 + ) + +class PokkenPortsConfig: + def __init__(self, parent_config: "PokkenConfig"): + self.__config = parent_config + + @property + def game(self) -> int: + return CoreConfig.get_config_field( + self.__config, "pokken", "ports", "game", default=9000 + ) + + @property + def admission(self) -> int: + return CoreConfig.get_config_field( + self.__config, "pokken", "ports", "admission", default=9001 + ) + class PokkenConfig(dict): def __init__(self) -> None: self.server = PokkenServerConfig(self) + self.ports = PokkenPortsConfig(self) diff --git a/titles/pokken/index.py b/titles/pokken/index.py index 1140d41..3ccb3fc 100644 --- a/titles/pokken/index.py +++ b/titles/pokken/index.py @@ -1,8 +1,7 @@ from typing import Tuple from twisted.web.http import Request from twisted.web import resource -from twisted.internet import reactor, endpoints -from autobahn.twisted.websocket import WebSocketServerFactory +from twisted.internet import reactor import json, ast from datetime import datetime import yaml @@ -17,7 +16,7 @@ from .config import PokkenConfig from .base import PokkenBase from .const import PokkenConstants from .proto import jackal_pb2 -from .services import PokkenStunProtocol, PokkenAdmissionFactory, PokkenAdmissionProtocol +from .services import PokkenAdmissionFactory class PokkenServlet(resource.Resource): @@ -72,7 +71,7 @@ class PokkenServlet(resource.Resource): return ( True, - f"https://{game_cfg.server.hostname}:{game_cfg.server.port}/{game_code}/$v/", + f"https://{game_cfg.server.hostname}:{game_cfg.ports.game}/{game_code}/$v/", f"{game_cfg.server.hostname}/SDAK/$v/", ) @@ -93,21 +92,10 @@ class PokkenServlet(resource.Resource): return (True, "PKF1") def setup(self) -> None: - # TODO: Setup stun, turn (UDP) and admission (WSS) servers - reactor.listenUDP( - self.game_cfg.server.port_stun, PokkenStunProtocol(self.core_cfg, self.game_cfg, "Stun") - ) - - reactor.listenUDP( - self.game_cfg.server.port_turn, PokkenStunProtocol(self.core_cfg, self.game_cfg, "Turn") - ) - - factory = WebSocketServerFactory(f"ws://{self.game_cfg.server.hostname}:{self.game_cfg.server.port_admission}") - factory.protocol = PokkenAdmissionProtocol - - reactor.listenTCP( - self.game_cfg.server.port_admission, PokkenAdmissionFactory(self.core_cfg, self.game_cfg) - ) + 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, version: int = 0, endpoints: str = "" @@ -144,6 +132,9 @@ class PokkenServlet(resource.Resource): return ret def handle_matching(self, request: Request) -> bytes: + if not self.game_cfg.server.enable_matching: + return b"" + content = request.content.getvalue() client_ip = Utils.get_ip_addr(request) diff --git a/titles/pokken/services.py b/titles/pokken/services.py index ac12b72..952c232 100644 --- a/titles/pokken/services.py +++ b/titles/pokken/services.py @@ -1,8 +1,8 @@ from twisted.internet.interfaces import IAddress -from twisted.internet.protocol import DatagramProtocol from twisted.internet.protocol import Protocol from autobahn.twisted.websocket import WebSocketServerProtocol, WebSocketServerFactory -from datetime import datetime +from autobahn.websocket.types import ConnectionRequest +from typing import Dict import logging import json @@ -10,21 +10,6 @@ from core.config import CoreConfig from .config import PokkenConfig from .base import PokkenBase -class PokkenStunProtocol(DatagramProtocol): - def __init__(self, cfg: CoreConfig, game_cfg: PokkenConfig, type: str) -> None: - super().__init__() - self.core_config = cfg - self.game_config = game_cfg - self.logger = logging.getLogger("pokken") - self.server_type = type - - def datagramReceived(self, data, addr): - self.logger.debug( - f"{self.server_type} from from {addr[0]}:{addr[1]} -> {self.transport.getHost().port} - {data.hex()}" - ) - self.transport.write(data, addr) - -# 474554202f20485454502f312e310d0a436f6e6e656374696f6e3a20557067726164650d0a486f73743a207469746c65732e6861793174732e6d653a393030330d0a5365632d576562536f636b65742d4b65793a204f4a6b6d522f376b646d6953326573483548783776413d3d0d0a5365632d576562536f636b65742d56657273696f6e3a2031330d0a557067726164653a20776562736f636b65740d0a557365722d4167656e743a20576562536f636b65742b2b2f302e332e300d0a0d0a class PokkenAdmissionProtocol(WebSocketServerProtocol): def __init__(self, cfg: CoreConfig, game_cfg: PokkenConfig): super().__init__() @@ -33,27 +18,36 @@ class PokkenAdmissionProtocol(WebSocketServerProtocol): self.logger = logging.getLogger("pokken") self.base = PokkenBase(cfg, game_cfg) + + def onConnect(self, request: ConnectionRequest) -> None: + self.logger.debug(f"Admission: Connection from {request.peer}") + + def onClose(self, wasClean: bool, code: int, reason: str) -> None: + self.logger.debug(f"Admission: Connection with {self.transport.getPeer().host} closed {'cleanly ' if wasClean else ''}with code {code} - {reason}") def onMessage(self, payload, isBinary: bool) -> None: - msg = json.loads(payload) - self.logger.debug(f"WebSocket from from {self.transport.getPeer().host}:{self.transport.getPeer().port} -> {self.transport.getHost().port} - {msg}") + msg: Dict = json.loads(payload) + self.logger.debug(f"Admission: Message from {self.transport.getPeer().host}:{self.transport.getPeer().port} - {msg}") - handler = getattr(self.base, f"handle_admission_{msg['api'].lower()}") + api = msg.get("api", "noop") + handler = getattr(self.base, f"handle_admission_{api.lower()}") resp = handler(msg, self.transport.getPeer().host) + + if resp is None: + resp = {} if "type" not in resp: resp['type'] = "res" if "data" not in resp: resp['data'] = {} if "api" not in resp: - resp['api'] = msg["api"] + resp['api'] = api if "result" not in resp: resp['result'] = 'true' self.logger.debug(f"Websocket response: {resp}") self.sendMessage(json.dumps(resp).encode(), isBinary) -# 0001002c2112a442334a0506a62efa71477dcd698022002872655455524e2053796e6320436c69656e7420302e33202d20524643353338392f7475726e2d3132 class PokkenAdmissionFactory(WebSocketServerFactory): protocol = PokkenAdmissionProtocol @@ -64,7 +58,7 @@ class PokkenAdmissionFactory(WebSocketServerFactory): ) -> None: self.core_config = cfg self.game_config = game_cfg - super().__init__(f"ws://{self.game_config.server.hostname}:{self.game_config.server.port_admission}") + super().__init__(f"ws://{self.game_config.server.hostname}:{self.game_config.ports.admission}") def buildProtocol(self, addr: IAddress) -> Protocol: p = self.protocol(self.core_config, self.game_config)