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
96 lines
6.8 KiB
Markdown
96 lines
6.8 KiB
Markdown
# 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 `<audio>` tag: codec frame buffering plus the `<audio>` element's own
|
|
jitter buffer realistically puts that in the 1-3s range, no matter how it's tuned.
|
|
|
|
Concrete approach instead: PulseAudio null sink → capture raw/lightly-buffered PCM (small
|
|
frames, e.g. `parec` with a short `--latency`) → push those frames to the browser over a
|
|
plain WebSocket (new endpoint alongside `setup_server.py`, or a small dedicated process) →
|
|
browser side, feed them straight into the Web Audio API via an `AudioWorkletNode` (not
|
|
`<audio>`, not `ScriptProcessorNode` — that's deprecated and has worse latency) for
|
|
near-real-time scheduled playback. No container/codec framing in the path at all.
|
|
|
|
Build this to be reusable across the other noVNC-family projects (`docker-xserver-novnc`,
|
|
`docker-sdr-novnc`), not dosbox-specific — it's a "browser audio + noVNC" concern, nothing
|
|
about it is really about DOSBox. Follow the `docker-common/scripts/signals.sh` precedent
|
|
(see the `docker-common` repo): keep the generic PulseAudio-sink/WebSocket/AudioWorklet
|
|
piece project-agnostic there, then copy it (not symlink) into each project's own `scripts/`
|
|
the same way signal handling is shared, so a fix in one place gets propagated by hand to
|
|
the others.
|