pokken: Switch to using external STUN server

This commit is contained in:
Hay1tsme 2023-06-25 01:09:23 -04:00
parent ec9ad1ebb0
commit 514f786e2d
5 changed files with 97 additions and 86 deletions

View File

@ -2,8 +2,11 @@ server:
hostname: "localhost" hostname: "localhost"
enable: True enable: True
loglevel: "info" loglevel: "info"
port: 9000 auto_register: True
port_stun: 9001 enable_matching: False
port_turn: 9002 stun_server_host: "stunserver.stunprotocol.org"
port_admission: 9003 stun_server_port: 3478
auto_register: True
ports:
game: 9000
admission: 9001

View File

@ -44,19 +44,19 @@ class PokkenBase:
biwa_setting = { biwa_setting = {
"MatchingServer": { "MatchingServer": {
"host": f"https://{self.game_cfg.server.hostname}", "host": f"https://{self.game_cfg.server.hostname}",
"port": self.game_cfg.server.port, "port": self.game_cfg.ports.game,
"url": "/SDAK/100/matching", "url": "/SDAK/100/matching",
}, },
"StunServer": { "StunServer": {
"addr": self.game_cfg.server.hostname, "addr": self.game_cfg.server.stun_server_host,
"port": self.game_cfg.server.port_stun, "port": self.game_cfg.server.stun_server_port,
}, },
"TurnServer": { "TurnServer": {
"addr": self.game_cfg.server.hostname, "addr": self.game_cfg.server.stun_server_host,
"port": self.game_cfg.server.port_turn, "port": self.game_cfg.server.stun_server_port,
}, },
"AdmissionUrl": f"ws://{self.game_cfg.server.hostname}:{self.game_cfg.server.port_admission}", "AdmissionUrl": f"ws://{self.game_cfg.server.hostname}:{self.game_cfg.ports.admission}",
"locationId": 123, "locationId": 123, # FIXME: Get arcade's ID from the database
"logfilename": "JackalMatchingLibrary.log", "logfilename": "JackalMatchingLibrary.log",
"biwalogfilename": "./biwa.log", "biwalogfilename": "./biwa.log",
} }
@ -94,6 +94,7 @@ class PokkenBase:
res.type = jackal_pb2.MessageType.LOAD_CLIENT_SETTINGS res.type = jackal_pb2.MessageType.LOAD_CLIENT_SETTINGS
settings = jackal_pb2.LoadClientSettingsResponseData() settings = jackal_pb2.LoadClientSettingsResponseData()
# TODO: Make configurable
settings.money_magnification = 1 settings.money_magnification = 1
settings.continue_bonus_exp = 100 settings.continue_bonus_exp = 100
settings.continue_fight_money = 100 settings.continue_fight_money = 100
@ -356,12 +357,11 @@ class PokkenBase:
self, data: Dict = {}, client_ip: str = "127.0.0.1" self, data: Dict = {}, client_ip: str = "127.0.0.1"
) -> Dict: ) -> Dict:
""" """
"sessionId":"12345678", "sessionId":"12345678",
"A":{ "A":{
"pcb_id": data["data"]["must"]["pcb_id"], "pcb_id": data["data"]["must"]["pcb_id"],
"gip": client_ip "gip": client_ip
}, },
"list":[]
""" """
return { return {
"data": { "data": {
@ -379,10 +379,13 @@ class PokkenBase:
) -> Dict: ) -> Dict:
return {} 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: def handle_admission_joinsession(self, data: Dict, req_ip: str = "127.0.0.1") -> Dict:
self.logger.info(f"Admission: JoinSession from {req_ip}") self.logger.info(f"Admission: JoinSession from {req_ip}")
return { return {
'data': { 'data': {
"id": 123 "id": 12345678
} }
} }

View File

@ -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 @property
def auto_register(self) -> bool: def auto_register(self) -> bool:
""" """
@ -59,7 +35,51 @@ class PokkenServerConfig:
self.__config, "pokken", "server", "auto_register", default=True 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): class PokkenConfig(dict):
def __init__(self) -> None: def __init__(self) -> None:
self.server = PokkenServerConfig(self) self.server = PokkenServerConfig(self)
self.ports = PokkenPortsConfig(self)

View File

@ -1,8 +1,7 @@
from typing import Tuple from typing import Tuple
from twisted.web.http import Request from twisted.web.http import Request
from twisted.web import resource from twisted.web import resource
from twisted.internet import reactor, endpoints from twisted.internet import reactor
from autobahn.twisted.websocket import WebSocketServerFactory
import json, ast import json, ast
from datetime import datetime from datetime import datetime
import yaml import yaml
@ -17,7 +16,7 @@ from .config import PokkenConfig
from .base import PokkenBase from .base import PokkenBase
from .const import PokkenConstants from .const import PokkenConstants
from .proto import jackal_pb2 from .proto import jackal_pb2
from .services import PokkenStunProtocol, PokkenAdmissionFactory, PokkenAdmissionProtocol from .services import PokkenAdmissionFactory
class PokkenServlet(resource.Resource): class PokkenServlet(resource.Resource):
@ -72,7 +71,7 @@ class PokkenServlet(resource.Resource):
return ( return (
True, 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/", f"{game_cfg.server.hostname}/SDAK/$v/",
) )
@ -93,21 +92,10 @@ class PokkenServlet(resource.Resource):
return (True, "PKF1") return (True, "PKF1")
def setup(self) -> None: def setup(self) -> None:
# TODO: Setup stun, turn (UDP) and admission (WSS) servers if self.game_cfg.server.enable_matching:
reactor.listenUDP( reactor.listenTCP(
self.game_cfg.server.port_stun, PokkenStunProtocol(self.core_cfg, self.game_cfg, "Stun") self.game_cfg.ports.admission, PokkenAdmissionFactory(self.core_cfg, self.game_cfg)
) )
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)
)
def render_POST( def render_POST(
self, request: Request, version: int = 0, endpoints: str = "" self, request: Request, version: int = 0, endpoints: str = ""
@ -144,6 +132,9 @@ class PokkenServlet(resource.Resource):
return ret return ret
def handle_matching(self, request: Request) -> bytes: def handle_matching(self, request: Request) -> bytes:
if not self.game_cfg.server.enable_matching:
return b""
content = request.content.getvalue() content = request.content.getvalue()
client_ip = Utils.get_ip_addr(request) client_ip = Utils.get_ip_addr(request)

View File

@ -1,8 +1,8 @@
from twisted.internet.interfaces import IAddress from twisted.internet.interfaces import IAddress
from twisted.internet.protocol import DatagramProtocol
from twisted.internet.protocol import Protocol from twisted.internet.protocol import Protocol
from autobahn.twisted.websocket import WebSocketServerProtocol, WebSocketServerFactory from autobahn.twisted.websocket import WebSocketServerProtocol, WebSocketServerFactory
from datetime import datetime from autobahn.websocket.types import ConnectionRequest
from typing import Dict
import logging import logging
import json import json
@ -10,21 +10,6 @@ from core.config import CoreConfig
from .config import PokkenConfig from .config import PokkenConfig
from .base import PokkenBase 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): class PokkenAdmissionProtocol(WebSocketServerProtocol):
def __init__(self, cfg: CoreConfig, game_cfg: PokkenConfig): def __init__(self, cfg: CoreConfig, game_cfg: PokkenConfig):
super().__init__() super().__init__()
@ -33,27 +18,36 @@ class PokkenAdmissionProtocol(WebSocketServerProtocol):
self.logger = logging.getLogger("pokken") self.logger = logging.getLogger("pokken")
self.base = PokkenBase(cfg, game_cfg) 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: def onMessage(self, payload, isBinary: bool) -> None:
msg = json.loads(payload) msg: Dict = json.loads(payload)
self.logger.debug(f"WebSocket from from {self.transport.getPeer().host}:{self.transport.getPeer().port} -> {self.transport.getHost().port} - {msg}") 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) resp = handler(msg, self.transport.getPeer().host)
if resp is None:
resp = {}
if "type" not in resp: if "type" not in resp:
resp['type'] = "res" resp['type'] = "res"
if "data" not in resp: if "data" not in resp:
resp['data'] = {} resp['data'] = {}
if "api" not in resp: if "api" not in resp:
resp['api'] = msg["api"] resp['api'] = api
if "result" not in resp: if "result" not in resp:
resp['result'] = 'true' resp['result'] = 'true'
self.logger.debug(f"Websocket response: {resp}") self.logger.debug(f"Websocket response: {resp}")
self.sendMessage(json.dumps(resp).encode(), isBinary) self.sendMessage(json.dumps(resp).encode(), isBinary)
# 0001002c2112a442334a0506a62efa71477dcd698022002872655455524e2053796e6320436c69656e7420302e33202d20524643353338392f7475726e2d3132
class PokkenAdmissionFactory(WebSocketServerFactory): class PokkenAdmissionFactory(WebSocketServerFactory):
protocol = PokkenAdmissionProtocol protocol = PokkenAdmissionProtocol
@ -64,7 +58,7 @@ class PokkenAdmissionFactory(WebSocketServerFactory):
) -> None: ) -> None:
self.core_config = cfg self.core_config = cfg
self.game_config = game_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: def buildProtocol(self, addr: IAddress) -> Protocol:
p = self.protocol(self.core_config, self.game_config) p = self.protocol(self.core_config, self.game_config)