# TODO - ~~Reduce image size of `jayfield/dosbox-novnc` (currently ~2.3GB).~~ Done in two steps: 1. Merged the game download/extract steps and the unzip/smbclient install into a single layer (so the downloaded zips and the packages only needed to extract them no longer persist in the final image). 2.31GB -> 1.6GB. 2. Stopped baking the games into the image at all (927MB of that was just t7g's two CD ISOs). `run.sh` mounts a named volume (`dosbox-games`) so installed games persist across `--rm` restarts. 1.6GB -> ~690MB. Container now needs network access to `vlda-01` at runtime (not just build time) to install games. 3. Dropped the auto-download at container startup entirely. `scripts/setup_server.py` is a small stdlib-only HTTP server that serves an HTML page on `${SETUP_PORT}` (`70${DISPLAY_NUM}`, published as `7099` by `run.sh`) listing every `*.zip` on the SMB share with an Installed/Install status per game; the user clicks "Install" to fetch+extract a specific game into `${GAMES_HOME}` on demand. - **Manifest-driven games + Start/Stop/Uninstall**: games are no longer discovered from raw `*.zip` files on the share. `setup_server.py` lists `*.json` manifests instead (one per game, in the `Games\dosbox\` catalog directory), each declaring `name`/`title`/`zip_path`/`start_cmd`. The web page grew Start/Stop/Uninstall buttons alongside Install, driven entirely by that manifest — nothing about a game's identity or how to run it is hardcoded in the image anymore. Started as a `stuntcar`-only pilot, now extended to 6 games (see the SCUMM item below). `t7g` still has no manifest and won't show up in the setup page until one is added (`t7g.json` with a `start_cmd` for its `dosbox-0.74-3.conf`). `game.sh`/`scripts/start_game.sh` (the old `docker exec`-based launch path, with its hardcoded `DOSBOX_VERSION`/`run.bat` convention) were intentionally left untouched during the pilot and still overlap with the new Start button — worth reconciling (or removing) once every remaining game has a manifest. - ~~Create manifests for SCUMM engine games~~ Done: `scummvm` added to the Dockerfile (`/usr/games/scummvm`, not on `$PATH` by default under a non-login shell — manifests use the absolute path). Manifests live in the usual `Games\dosbox\*.json` catalog directory on the share, but their `zip_path` now points at wherever the game's zip actually already lived (`Games\Monkey Island\...`, `Games\Indiana Jones\...`, etc.) — this required generalizing the manifest schema from a bare `zip` filename (implicitly under `Games\dosbox\`) to a full `zip_path` relative to the share root, since these games weren't colocated with dosbox's. Added and verified end-to-end (install/start/stop/uninstall) via `monkey2`: - `monkey` — The Secret of Monkey Island - `monkey2` — Monkey Island 2: LeChuck's Revenge - `atlantis` — Indiana Jones and the Fate of Atlantis - `indy3` — Indiana Jones and the Last Crusade - `tentacle` — Day of the Tentacle Skipped the German CD release of Day of the Tentacle on the share (`Day Of The Tentacle (CD DOS, German).zip`) — unlike every other zip here, it doesn't wrap its contents in a single top-level folder (loose files at the zip root plus a stray `MANIAC/` dir for the embedded Maniac Mansion easter egg), so it doesn't fit the current "zip's top-level folder == install name" extraction convention. Also skipped Curse of Monkey Island (much larger, untested) — could be added the same way if wanted. ScummVM's own game-target IDs (`monkey`, `monkey2`, `atlantis`, `indy3`, `tentacle`) happened to exactly match each zip's existing top-level folder name, so no change was needed to the install/extraction logic itself, only to where zips are looked up from. - ~~Handle multiple games running at once~~ Done: each running game now gets its own ephemeral X session (`Xvnc`+`fluxbox`+`websockify`, its own display `:90`-`:99` and noVNC port `8090`-`8099`) spun up by `start_game()` and torn down by `stop_game()` (or lazily on the next page load, if the game exited/crashed on its own) — instead of every game sharing one screen and fighting over focus/audio. Capped at `MAX_CONCURRENT_GAMES = 10`; starting an 11th game while all 10 slots are in use is refused with an error shown on its row rather than silently doing nothing. There is no more a single shared "default" desktop or global noVNC link — `server.sh` no longer starts Xvnc/fluxbox/websockify at container startup at all, only `setup_server.py` itself; each game's own "Open Screen" link appears on its row only while it's running, built with a little client-side JS (the noVNC port differs from the setup port and can't be known server-side without knowing which hostname the browser used to reach the container). `run.sh` publishes the whole `8090-8099` range up front, since Docker can't add port mappings to an already-running container. Verified end-to-end with two different games running concurrently (separate processes, separate displays, separate noVNC ports, independent stop/teardown) and the 10-slot cap logic. - Get game sound actually audible during play. `--device /dev/snd` is passed through and `alsa-utils` is installed, so DOSBox can write to an ALSA device inside the container, but VNC/noVNC only ever streams video, not audio — nothing currently carries that sound out to the browser. Target latency is a few tens of ms, not seconds — that rules out the "obvious" approach of a PulseAudio null sink piped through `ffmpeg` into a compressed (mp3/ogg) HTTP/Icecast-style stream consumed by an `