Sync from GitHub main #1

Open
nomad wants to merge 145 commits from sync/from-github into main
Showing only changes of commit 86ec79eeb9 - Show all commits
+58 -9
View File
@@ -15,7 +15,10 @@ from pathlib import Path
from typing import Any, Callable
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 transport_server import TransportAwareFalloutTogetherServer
_IPV4_RE = re.compile(r"^\d{1,3}(?:\.\d{1,3}){3}$")
@@ -32,6 +35,8 @@ class ServerConfig:
log_verbosity: str = "info"
admin_port: int = DEFAULT_ADMIN_PORT
bans_path: str | None = None
enable_gns_transport: bool = False
gns_bridge_path: str | None = None
@dataclass
@@ -76,11 +81,13 @@ class ServerService:
self.config = config or ServerConfig()
self._server: FalloutTogetherServer | None = None
self._admin: AdminServer | None = None
self._gns_adapter: GnsGameplayAdapter | None = None
self._log_listeners: list[Callable[..., None]] = []
self._log_lock = threading.RLock()
self._serve_thread: threading.Thread | None = None
self._running = False
self._stop_requested = False
self._stop_event = threading.Event()
def add_log_listener(self, callback: Callable[..., None]) -> None:
"""Register a callback for server log messages."""
@@ -114,7 +121,7 @@ class ServerService:
return str(Path(__file__).resolve().parent / "bans.json")
def _create_server(self) -> FalloutTogetherServer:
return FalloutTogetherServer(
return TransportAwareFalloutTogetherServer(
host=self.config.host,
port=self.config.port,
server_name=self.config.server_name,
@@ -137,6 +144,41 @@ class ServerService:
self._admin.stop()
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:
"""Start the server in a background thread."""
if self._running:
@@ -144,6 +186,7 @@ class ServerService:
return
self._stop_requested = False
self._stop_event.clear()
self._server = self._create_server()
self._server.add_log_listener(self._dispatch_log)
self._running = True
@@ -159,25 +202,29 @@ class ServerService:
raise RuntimeError("Server is already running.")
self._stop_requested = False
self._stop_event.clear()
self._server = self._create_server()
self._server.add_log_listener(self._dispatch_log)
self._running = True
self._start_admin()
self._run_server_loop()
def _run_server_loop(self) -> None:
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:
self._stop_gns()
self._stop_admin()
self._running = False
def _serve_forever(self) -> None:
"""Internal serve_forever for background thread."""
try:
if self._server:
self._server.serve_forever()
finally:
self._stop_admin()
self._running = False
"""Internal server loop for background thread."""
self._run_server_loop()
def stop(self) -> None:
"""Stop the server."""
@@ -185,11 +232,13 @@ class ServerService:
return
self._stop_requested = True
self._stop_event.set()
if not self._running and self._server is None:
self._dispatch_log("Server is not running.")
return
self._stop_admin()
self._stop_gns()
if self._server is not None:
self._server.stop()