79 lines
2.2 KiB
Python
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"]}
|