Previously every game shared one always-on Xvnc/fluxbox/websockify
desktop, so starting two games at once meant they fought over focus on
the same screen and the same ALSA device. Now:
- server.sh no longer starts a shared desktop at all - it just runs
setup_server.py. There's no default display anymore.
- start_game() allocates a free display from GAME_DISPLAY_NUMS
(:90-:99, one per MAX_CONCURRENT_GAMES=10 slot), spins up a fresh
Xvnc+fluxbox+websockify for it, and launches the game with DISPLAY set
to that display. All four processes are tracked together per game.
- stop_game() tears down all four; is_running() does the same lazily if
the game exited on its own (crash/quit), so a slot doesn't stay stuck
just because nobody clicked Stop.
- Starting past the 10-slot cap is refused with an error shown on that
game's row instead of silently failing.
- Each running game's row gets its own "Open Screen" link (client-side
JS, since the port is only known once the game is actually started)
instead of one global noVNC link.
- run.sh publishes the whole 8090-8099 noVNC port range up front, since
Docker can't add port mappings to an already-running container.
Verified end-to-end: two different games running concurrently get fully
independent Xvnc/fluxbox/websockify/game process sets and noVNC
endpoints; stopping one leaves the other untouched; the capacity guard
correctly refuses a start at the limit.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NiNnj78HGx1KWyCCo39HSz