import asyncio import os import logging import sys from server.server import GameServer from server.config import ServerConfig async def _run_tasks_with_optional_timeout(tasks): """Await tasks, optionally honoring RUN_SECONDS env var to cancel after a timeout.""" timeout_s = os.environ.get("RUN_SECONDS") if not timeout_s: await asyncio.gather(*tasks) return try: await asyncio.wait_for(asyncio.gather(*tasks), timeout=float(timeout_s)) except asyncio.TimeoutError: logging.info("Timeout reached (RUN_SECONDS=%s); stopping server tasks...", timeout_s) for t in tasks: t.cancel() await asyncio.gather(*tasks, return_exceptions=True) async def run_in_memory(): from server.transport import InMemoryTransport cfg = ServerConfig() server = GameServer(transport=InMemoryTransport(lambda d, p: server.on_datagram(d, p)), config=cfg) tasks = [asyncio.create_task(server.transport.run()), asyncio.create_task(server.tick_loop())] await _run_tasks_with_optional_timeout(tasks) async def run_quic(): from server.quic_transport import QuicWebTransportServer cfg = ServerConfig() host = os.environ.get("QUIC_HOST", "0.0.0.0") port = int(os.environ.get("QUIC_PORT", "4433")) cert = os.environ["QUIC_CERT"] key = os.environ["QUIC_KEY"] server = GameServer(transport=QuicWebTransportServer(host, port, cert, key, lambda d, p: server.on_datagram(d, p)), config=cfg) tasks = [asyncio.create_task(server.transport.run()), asyncio.create_task(server.tick_loop())] await _run_tasks_with_optional_timeout(tasks) async def run_webtransport(): from server.webtransport_server import WebTransportServer from server.static_server import start_https_static cfg = ServerConfig() host = os.environ.get("WT_HOST", os.environ.get("QUIC_HOST", "0.0.0.0")) port = int(os.environ.get("WT_PORT", os.environ.get("QUIC_PORT", "4433"))) cert = os.environ.get("WT_CERT") or os.environ["QUIC_CERT"] key = os.environ.get("WT_KEY") or os.environ["QUIC_KEY"] # Optional static HTTPS server for client assets static = os.environ.get("STATIC", "1") static_host = os.environ.get("STATIC_HOST", host) static_port = int(os.environ.get("STATIC_PORT", "8443")) static_root = os.environ.get("STATIC_ROOT", "client") httpd = None if static == "1": httpd, _t = start_https_static(static_host, static_port, cert, key, static_root) print(f"HTTPS static server: https://{static_host}:{static_port}/ serving '{static_root}'") server = GameServer(transport=WebTransportServer(host, port, cert, key, lambda d, p: server.on_datagram(d, p)), config=cfg) print(f"WebTransport server: https://{host}:{port}/ (HTTP/3)") try: tasks = [asyncio.create_task(server.transport.run()), asyncio.create_task(server.tick_loop())] await _run_tasks_with_optional_timeout(tasks) finally: if httpd is not None: httpd.shutdown() if __name__ == "__main__": try: if any(a in ("-h", "--help") for a in sys.argv[1:]): print( "Usage: python run.py [--mode mem|quic|wt|net] [--log-level LEVEL] [--run-seconds N]\n" " TLS (for wt/quic): set QUIC_CERT/QUIC_KEY or WT_CERT/WT_KEY env vars\n" " WT static server (MODE=wt): STATIC=1 [STATIC_HOST/PORT/ROOT]\n" "Examples:\n MODE=wt QUIC_CERT=cert.pem QUIC_KEY=key.pem python run.py\n MODE=mem python run.py" ) sys.exit(0) # Logging setup level = os.environ.get("LOG_LEVEL", "INFO").upper() logging.basicConfig(level=getattr(logging, level, logging.INFO), format="[%(asctime)s] %(levelname)s: %(message)s") mode = os.environ.get("MODE", "mem").lower() if mode == "wt": logging.info("Starting in WebTransport mode") asyncio.run(run_webtransport()) elif mode == "quic": logging.info("Starting in QUIC datagram mode") asyncio.run(run_quic()) elif mode == "net": logging.info("Starting in combined WebTransport+QUIC mode") from server.webtransport_server import WebTransportServer from server.quic_transport import QuicWebTransportServer from server.multi_transport import MultiTransport cfg = ServerConfig() host_wt = os.environ.get("WT_HOST", os.environ.get("QUIC_HOST", "0.0.0.0")) port_wt = int(os.environ.get("WT_PORT", os.environ.get("QUIC_PORT", "4433"))) host_quic = os.environ.get("QUIC_HOST", host_wt) port_quic = int(os.environ.get("QUIC_PORT", "4443")) cert = os.environ.get("WT_CERT") or os.environ.get("QUIC_CERT") key = os.environ.get("WT_KEY") or os.environ.get("QUIC_KEY") if not cert or not key: raise SystemExit("WT/QUIC cert/key required: set WT_CERT/WT_KEY or QUIC_CERT/QUIC_KEY") async def _run_net(): server: GameServer wt = WebTransportServer(host_wt, port_wt, cert, key, lambda d, p: server.on_datagram(d, p)) qu = QuicWebTransportServer(host_quic, port_quic, cert, key, lambda d, p: server.on_datagram(d, p)) m = MultiTransport(wt, qu) server = GameServer(transport=m, config=cfg) await asyncio.gather(m.run(), server.tick_loop()) asyncio.run(_run_net()) else: logging.info("Starting in in-memory transport mode") asyncio.run(run_in_memory()) except KeyboardInterrupt: pass async def _run_tasks_with_optional_timeout(tasks): """Await tasks, optionally honoring RUN_SECONDS env var to cancel after a timeout.""" timeout_s = os.environ.get("RUN_SECONDS") if not timeout_s: await asyncio.gather(*tasks) return try: await asyncio.wait_for(asyncio.gather(*tasks), timeout=float(timeout_s)) except asyncio.TimeoutError: logging.info("Timeout reached (RUN_SECONDS=%s); stopping server tasks...", timeout_s) for t in tasks: t.cancel() await asyncio.gather(*tasks, return_exceptions=True)