PC3 Phase 2.2: game-server orchestration + POST /api/worlds/{id}/join

- backend/app/services/game_servers.py: an in-process supervisor. One
  headless Godot dedicated-server process per world, keyed by world_id,
  spawned on demand from the join endpoint, port-allocated from a
  configured range, with a background reaper that kills a world's server
  once its status file reports 0 players for the idle timeout. Off by
  default (GAME_SERVERS_ENABLED); the checkpoint/playtest launcher enables it.
- POST /api/worlds/{id}/join -> {world_id, host, port}; 404 for
  unknown/hidden worlds, 503 when orchestration is disabled. Auth required.
- main.py lifespan starts/stops the supervisor.
- config.py: GODOT_BIN, GAME_PROJECT_DIR, GAME_SERVER_HOST, port range,
  idle timeout.
- tests/test_world_join.py (auth / 404 / visibility / 503) -- suite 72 passed.
- checkpoint runner also runs the 2p multiplayer shell test.

Verified end to end: enabled orchestration, POST /join spawned the Godot
server, a real client connected to the returned address and moved; backend
shutdown reaped the process.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015jtxAiu5dAW4WxCNdGkrkJ
This commit is contained in:
2026-09-05 22:02:55 +00:00
parent 682f1f89f7
commit 260b7a38da
5 changed files with 286 additions and 2 deletions
+10
View File
@@ -156,6 +156,16 @@ def main():
except subprocess.TimeoutExpired:
game_server.kill()
results["godot smoke tests"] = godot_ok
# 2-player multiplayer test (its own shell harness: server + 2
# real client processes).
mp_script = ROOT / "scripts" / "run_multiplayer_2p_test.sh"
if mp_script.exists():
print("\n--- scripts/run_multiplayer_2p_test.sh ---")
mp_env = os.environ.copy()
mp_env["GODOT_BIN"] = godot_bin
r = run(["bash", str(mp_script)], env=mp_env)
results["multiplayer 2p test"] = r.returncode == 0
finally:
server_proc.terminate()
try: