Sync from GitHub main #1
@@ -15,7 +15,10 @@ from pathlib import Path
|
|||||||
from typing import Any, Callable
|
from typing import Any, Callable
|
||||||
|
|
||||||
from admin_server import AdminServer, DEFAULT_ADMIN_PORT
|
from admin_server import AdminServer, DEFAULT_ADMIN_PORT
|
||||||
|
from gns_gameplay_adapter import GnsGameplayAdapter
|
||||||
|
from gns_transport import GnsServerTransport
|
||||||
from server_core import FalloutTogetherServer
|
from server_core import FalloutTogetherServer
|
||||||
|
from transport_server import TransportAwareFalloutTogetherServer
|
||||||
|
|
||||||
|
|
||||||
_IPV4_RE = re.compile(r"^\d{1,3}(?:\.\d{1,3}){3}$")
|
_IPV4_RE = re.compile(r"^\d{1,3}(?:\.\d{1,3}){3}$")
|
||||||
@@ -32,6 +35,8 @@ class ServerConfig:
|
|||||||
log_verbosity: str = "info"
|
log_verbosity: str = "info"
|
||||||
admin_port: int = DEFAULT_ADMIN_PORT
|
admin_port: int = DEFAULT_ADMIN_PORT
|
||||||
bans_path: str | None = None
|
bans_path: str | None = None
|
||||||
|
enable_gns_transport: bool = False
|
||||||
|
gns_bridge_path: str | None = None
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -76,11 +81,13 @@ class ServerService:
|
|||||||
self.config = config or ServerConfig()
|
self.config = config or ServerConfig()
|
||||||
self._server: FalloutTogetherServer | None = None
|
self._server: FalloutTogetherServer | None = None
|
||||||
self._admin: AdminServer | None = None
|
self._admin: AdminServer | None = None
|
||||||
|
self._gns_adapter: GnsGameplayAdapter | None = None
|
||||||
self._log_listeners: list[Callable[..., None]] = []
|
self._log_listeners: list[Callable[..., None]] = []
|
||||||
self._log_lock = threading.RLock()
|
self._log_lock = threading.RLock()
|
||||||
self._serve_thread: threading.Thread | None = None
|
self._serve_thread: threading.Thread | None = None
|
||||||
self._running = False
|
self._running = False
|
||||||
self._stop_requested = False
|
self._stop_requested = False
|
||||||
|
self._stop_event = threading.Event()
|
||||||
|
|
||||||
def add_log_listener(self, callback: Callable[..., None]) -> None:
|
def add_log_listener(self, callback: Callable[..., None]) -> None:
|
||||||
"""Register a callback for server log messages."""
|
"""Register a callback for server log messages."""
|
||||||
@@ -114,7 +121,7 @@ class ServerService:
|
|||||||
return str(Path(__file__).resolve().parent / "bans.json")
|
return str(Path(__file__).resolve().parent / "bans.json")
|
||||||
|
|
||||||
def _create_server(self) -> FalloutTogetherServer:
|
def _create_server(self) -> FalloutTogetherServer:
|
||||||
return FalloutTogetherServer(
|
return TransportAwareFalloutTogetherServer(
|
||||||
host=self.config.host,
|
host=self.config.host,
|
||||||
port=self.config.port,
|
port=self.config.port,
|
||||||
server_name=self.config.server_name,
|
server_name=self.config.server_name,
|
||||||
@@ -137,6 +144,41 @@ class ServerService:
|
|||||||
self._admin.stop()
|
self._admin.stop()
|
||||||
self._admin = None
|
self._admin = None
|
||||||
|
|
||||||
|
def _start_gns(self) -> None:
|
||||||
|
if not self.config.enable_gns_transport or self._server is None:
|
||||||
|
return
|
||||||
|
if self._gns_adapter is not None:
|
||||||
|
return
|
||||||
|
|
||||||
|
transport = GnsServerTransport(
|
||||||
|
self.config.host,
|
||||||
|
self._server.port,
|
||||||
|
library_path=self.config.gns_bridge_path,
|
||||||
|
)
|
||||||
|
if transport.local_port != self._server.port:
|
||||||
|
actual_port = transport.local_port
|
||||||
|
transport.close()
|
||||||
|
raise RuntimeError(
|
||||||
|
f"GNS transport bound UDP {actual_port}, expected UDP {self._server.port}."
|
||||||
|
)
|
||||||
|
|
||||||
|
adapter = GnsGameplayAdapter(self._server, transport)
|
||||||
|
try:
|
||||||
|
adapter.start()
|
||||||
|
except Exception:
|
||||||
|
transport.close()
|
||||||
|
raise
|
||||||
|
self._gns_adapter = adapter
|
||||||
|
self._dispatch_log(
|
||||||
|
f"GameNetworkingSockets gameplay transport listening on UDP {self._server.port}."
|
||||||
|
)
|
||||||
|
|
||||||
|
def _stop_gns(self) -> None:
|
||||||
|
adapter = self._gns_adapter
|
||||||
|
self._gns_adapter = None
|
||||||
|
if adapter is not None:
|
||||||
|
adapter.stop()
|
||||||
|
|
||||||
def start(self) -> None:
|
def start(self) -> None:
|
||||||
"""Start the server in a background thread."""
|
"""Start the server in a background thread."""
|
||||||
if self._running:
|
if self._running:
|
||||||
@@ -144,6 +186,7 @@ class ServerService:
|
|||||||
return
|
return
|
||||||
|
|
||||||
self._stop_requested = False
|
self._stop_requested = False
|
||||||
|
self._stop_event.clear()
|
||||||
self._server = self._create_server()
|
self._server = self._create_server()
|
||||||
self._server.add_log_listener(self._dispatch_log)
|
self._server.add_log_listener(self._dispatch_log)
|
||||||
self._running = True
|
self._running = True
|
||||||
@@ -159,25 +202,29 @@ class ServerService:
|
|||||||
raise RuntimeError("Server is already running.")
|
raise RuntimeError("Server is already running.")
|
||||||
|
|
||||||
self._stop_requested = False
|
self._stop_requested = False
|
||||||
|
self._stop_event.clear()
|
||||||
self._server = self._create_server()
|
self._server = self._create_server()
|
||||||
self._server.add_log_listener(self._dispatch_log)
|
self._server.add_log_listener(self._dispatch_log)
|
||||||
self._running = True
|
self._running = True
|
||||||
self._start_admin()
|
self._start_admin()
|
||||||
|
self._run_server_loop()
|
||||||
|
|
||||||
|
def _run_server_loop(self) -> None:
|
||||||
try:
|
try:
|
||||||
self._server.serve_forever()
|
if self._server is None:
|
||||||
|
return
|
||||||
|
self._server.start()
|
||||||
|
self._start_gns()
|
||||||
|
while not self._stop_requested and self._server.is_running():
|
||||||
|
self._stop_event.wait(0.1)
|
||||||
finally:
|
finally:
|
||||||
|
self._stop_gns()
|
||||||
self._stop_admin()
|
self._stop_admin()
|
||||||
self._running = False
|
self._running = False
|
||||||
|
|
||||||
def _serve_forever(self) -> None:
|
def _serve_forever(self) -> None:
|
||||||
"""Internal serve_forever for background thread."""
|
"""Internal server loop for background thread."""
|
||||||
try:
|
self._run_server_loop()
|
||||||
if self._server:
|
|
||||||
self._server.serve_forever()
|
|
||||||
finally:
|
|
||||||
self._stop_admin()
|
|
||||||
self._running = False
|
|
||||||
|
|
||||||
def stop(self) -> None:
|
def stop(self) -> None:
|
||||||
"""Stop the server."""
|
"""Stop the server."""
|
||||||
@@ -185,11 +232,13 @@ class ServerService:
|
|||||||
return
|
return
|
||||||
|
|
||||||
self._stop_requested = True
|
self._stop_requested = True
|
||||||
|
self._stop_event.set()
|
||||||
if not self._running and self._server is None:
|
if not self._running and self._server is None:
|
||||||
self._dispatch_log("Server is not running.")
|
self._dispatch_log("Server is not running.")
|
||||||
return
|
return
|
||||||
|
|
||||||
self._stop_admin()
|
self._stop_admin()
|
||||||
|
self._stop_gns()
|
||||||
if self._server is not None:
|
if self._server is not None:
|
||||||
self._server.stop()
|
self._server.stop()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user