pod: bundled per-game deployment (portable config, --exit-with, build-pod)

Phase 10: RIO hardware exists only on pods + dev boxes, so production is one
RIOJoy copy inside each podized game folder, no resident tray. ConfigLocator
makes a config.json beside the exe win over %APPDATA%; --exit-with <exe|pid>
(CompanionTarget/CompanionExit, 60s startup grace) tears down and quits when
the game exits; a starting --exit-with instance waits up to 15s for the
predecessor mutex instead of silently exiting. deploy/build-pod.ps1 emits
the ~4.5MB drop-in (app + portable config wrapping the profile + start
script, no drivers) - verified against the shipped Descent profile. 455
tests; PLAN.md Phase 10 + INPUT-INTEGRATION.md pod section.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-31 22:01:52 -05:00
co-authored by Claude Fable 5
parent 9a792193f9
commit 97caf124a6
10 changed files with 549 additions and 12 deletions
+39
View File
@@ -490,6 +490,45 @@ spec + client snippets in [`docs/FEEDBACK.md`](FEEDBACK.md). Delivers the
settings (JSON-only today); shipped client examples (SimHub plugin / DCS
export script) beyond the FEEDBACK.md snippets.
### Phase 10 — Pod-bundled deployment — code-complete ✅
Deployment topology decision (2026-07-31): RIO hardware exists only on **pods**
(the cockpit cabinets) and dev boxes — no freestanding end-user PCs. Production
model is therefore **one RIOJoy copy bundled inside each podized game's
folder**, started by the game's launch script and exiting with the game; no
resident RIOJoy runs on a pod, and the native games simply don't bundle one
(making the COM-port yield machinery vestigial in production). The resident
tray + auto-switch reclassifies as the development harness. 455 xUnit tests
total across the suite.
- **Portable config**: `ConfigLocator.Resolve` — a `config.json` beside the
exe wins over `%APPDATA%\RIOJoy\config.json`; `TrayApplicationContext.
ConfigPath` resolves through it, so `--import-profile` targets the same
store. A pod bundle needs no import step: its config *is* the profile.
- **`--exit-with <exe|pid>`** (`CompanionTarget.Parse` — pid, or a name
normalized like auto-switch triggers): the tray polls the companion on its
existing 1 s timer and quits through the normal teardown (ports released,
wallpaper restored, plasma blanked) once the game has run and then gone.
`CompanionExit` holds the pure decision — launch order isn't guaranteed, so
a never-seen companion only triggers exit after a 60 s startup grace
(also covers "game failed to launch"). Clock-free and unit-tested
(`tests/.../Hosting/CompanionExitTests`).
- **Instance handoff**: with `--exit-with`, a starting instance waits up to
15 s for the predecessor's single-instance mutex (game A's copy tearing
down while game B's starts) instead of the historical silent exit-0; plain
launches keep the instant-exit behavior. Abandoned mutex (predecessor
crash) counts as acquired.
- **`deploy/build-pod.ps1`**: emits the per-game drop-in — `riojoy\` app
(flavor-selectable net48/net40, Skia-pruned) + portable `config.json`
wrapping the game's profile document verbatim + `start-riojoy.bat`
(`--exit-with` prefilled from the profile's first trigger) + README-POD —
zipped as `RIOJoy-pod-<name>-<stamp>.zip` (~4.5 MB). Deliberately **no
drivers**: pods are provisioned once by the universal package. Verified by
building the Descent bundle and round-tripping its emitted config through
`ConfigStore.Load`.
-**Remaining:** on-pod verification of the full launch/handoff cycle
(launcher → game A → quit → game B); podize a first real game with the
bundle; revisit the universal zip's `install.bat` framing (dev-setup only)
once pod deploys are routine.
---
## Open items / risks