Files
pirate-dice/server/matchmaker.py
2026-09-07 17:38:17 +02:00

79 lines
2.2 KiB
Python

"""Räume verwalten: Code vergeben, headless Spielserver starten, aufräumen.
Ein Raum ist ein eigener Godot-Prozess auf einem festen UDP-Port. Der Code
zeigt nur auf diesen Port. Läuft der Prozess nicht mehr, ist der Code frei.
"""
import os
import random
import subprocess
from pathlib import Path
from fastapi import FastAPI, HTTPException
# Ohne I, O, 0 und 1: die verwechselt man beim Vorlesen
ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
CODE_LAENGE = 4
GODOT = os.getenv("GODOT", "godot")
SPIEL_PFAD = Path(os.getenv("SPIEL_PFAD", "/app/spiel"))
# Was die Clients als Adresse bekommen
SPIEL_HOST = os.getenv("SPIEL_HOST", "pirate-dice.marha.de")
PORT_VON = int(os.getenv("PORT_VON", "8911"))
PORT_BIS = int(os.getenv("PORT_BIS", "8940"))
app = FastAPI(title="Pirate Dice Matchmaker")
## code -> {"port": int, "proc": Popen}
raeume: dict = {}
def aufraeumen() -> None:
"""Beendete Räume vergessen. Godot beendet sich selbst, wenn es leer ist."""
for code in [c for c, r in raeume.items() if r["proc"].poll() is not None]:
del raeume[code]
def freier_port() -> int:
belegt = {r["port"] for r in raeume.values()}
for port in range(PORT_VON, PORT_BIS + 1):
if port not in belegt:
return port
raise HTTPException(status_code=503, detail="Alle Räume belegt")
def neuer_code() -> str:
while True:
code = "".join(random.choice(ALPHABET) for _ in range(CODE_LAENGE))
if code not in raeume:
return code
@app.get("/api/health")
def health():
aufraeumen()
return {"ok": True, "raeume": len(raeume)}
@app.post("/api/raum")
def raum_anlegen():
aufraeumen()
port = freier_port()
code = neuer_code()
proc = subprocess.Popen(
[GODOT, "--headless", "--path", str(SPIEL_PFAD), "--", "server", str(port)],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
raeume[code] = {"port": port, "proc": proc}
return {"code": code, "host": SPIEL_HOST, "port": port}
@app.get("/api/raum/{code}")
def raum_suchen(code: str):
aufraeumen()
raum = raeume.get(code.strip().upper())
if not raum:
raise HTTPException(status_code=404, detail="Code unbekannt")
return {"code": code.upper(), "host": SPIEL_HOST, "port": raum["port"]}