139 lines
6.1 KiB
Python
139 lines
6.1 KiB
Python
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)
|
|
|