# Deploying and commanding a game in a Tesla pod bay **Audience:** developers of games being brought to the Tesla cockpit pods — current and future titles alike. This is the integration contract from the game's point of view: what your package must look like so the operator console can **deploy** it to pods, and what your executable must speak so the console can **command** it through a mission. Everything here is implemented and validated in this repo (TeslaSuite v4.11.4.x) — file references point at the authoritative source. --- ## 1. The big picture: two independent channels A pod (cockpit PC) runs two things that matter to you: | Channel | Port | Who listens | Purpose | |---|---|---|---| | **Launcher RPC** | TCP **53290** | `TeslaLauncher.exe` (pod tray app) | Deploy: install/uninstall packages, register launch entries, launch/kill your exe, volume, reboot | | **Munga game control** | TCP **1501** | **your game exe** | Command: mission load (the "egg"), run/stop/abort/suspend/resume, state polling, in-mission events | These are separate. The launcher channel is fully game-agnostic — any exe can be deployed and launched with **zero code changes** to your game. The Munga channel is what your game implements if the console is to drive missions in it. That split gives two integration tiers: - **Tier 0 — deploy + launch only.** The console installs your package, starts and stops your exe, and keeps it alive (watchdog). Your game runs its own show. Examples in the shipped catalog: BattleTech Firestorm, RIOJoy. Requires only §2 (a package + a catalog entry). - **Tier 1 — full mission command.** Your game is a Munga TCP server; the console streams it a mission egg, drives the state machine, and receives scoring events. Examples: Red Planet 4.11 (`rpl4opt.exe`), BattleTech 4.11 (`btl4.exe`), TeslaRel410 (supervisor wrapping the DOS games). Requires §2 + §3, plus a console-side game module (§3.7). There is one escape hatch: a game that Munga control would not serve well may ship its own dedicated **game console** instead (§3.9) — deployment still goes through Tier 0 unchanged. --- ## 2. Deployment spec (Tier 0 — every game needs this) ### 2.1 The package zip The console's **Manage Site → Install Product** streams a zip to the pod; the launcher extracts it into the games root **`C:\Games`** (the zip is opened at that root, not inside a product folder). Lay the zip out as: ``` YourGame.zip ├── YourGame\ ← your product folder → becomes C:\Games\YourGame\ │ ├── yourgame.exe │ ├── (data files...) │ └── pre-uninstall.bat ← optional; run on uninstall (driver/config removal) └── postinstall.bat ← optional; at ZIP ROOT; run once after extract, then deleted ``` Rules and lifecycle (implementation: [Launcher/TeslaLauncher.cs](Launcher/TeslaLauncher.cs), [Launcher/MiniZip.cs](Launcher/MiniZip.cs)): - **Everything must extract under one `C:\Games\\` folder** (plus the optional root `postinstall.bat`). Uninstall deletes `C:\Games\` recursively — don't scatter files elsewhere unless `postinstall.bat` puts them there and `pre-uninstall.bat` removes them. - **`postinstall.bat`** (zip root) runs after extraction with the launcher's token — the kiosk account is an Administrator, so driver installs work (RIOJoy installs ViGEmBus this way). It is waited on, then deleted. - **`pre-uninstall.bat`** (inside your product folder) runs before the folder is deleted on uninstall. - Zip format: stored + deflate (+ ZIP64) — the launcher uses its own extractor (`MiniZip.cs`), no exotic compression methods. - Install progress reported to the operator: 0–50% receive, 50–95% extract, ~96% postinstall, 99–100% complete. - **OS range:** pods run **Windows XP SP3 through Windows 11** on one image. The *suite itself* is deliberately held to the XP floor (one net40 binary set) to retain compatibility with the original cockpit hardware. For new games, XP support is a **nice-to-have, not a requirement**: meeting it (native exes: x86 + XP-safe API surface; .NET exes: net40 — runs in-place on Win10/11's 4.8 runtime) lets your game reach the original-hardware pods too. If you skip it, note modern-pods-only in your catalog entry's comment so operators don't push it to XP-era machines. Existing package builders to crib from: [vPOD/pack.ps1](vPOD/pack.ps1) (minimal) and TeslaRel410's `deploy\package.ps1` (in its own repo — produces an Install-Product-ready zip with postinstall). ### 2.2 The catalog entry (`Apps.xml`) The console's product menu is data-driven from [Console/RedPlanet/Apps.xml](Console/RedPlanet/Apps.xml) (parser: [Console/TeslaConsole/AppRegistry.cs](Console/TeslaConsole/AppRegistry.cs)). Your game ships as one `` element: ```xml ← "true" only if you have LC/MR roles ← GameClient|LiveCamera|MissionReview|None ``` - **Key convention** (documented in the Apps.xml header — follow it exactly): generate ONE fresh Guid for the product id; the first `` reuses it; each additional `` increments the **last hex digit** (+1, +2…, wrapping F→0). Never append `-1`/`-2` to the string — keys parse as `System.Guid` and a suffixed string silently collapses to `Guid.Empty`. - **`{res}`** in `args` expands to ` -res W H` when the operator picks a custom resolution, else to nothing. Only use it if your exe accepts `-res W H` (the RP411 engine convention); a game with different resolution flags just pins them in `args`. - **`autoRestart="true"`** enables the pod watchdog (§2.3). Almost always what you want for a game client. - **`hostTypeDialog="true"` + per-entry `hostType`** is for games with separate live-camera / mission-review roles (RP/BT use `-lc` / `-mr` flags) — see §4 for what those stations do. A plain game ships one entry with `hostType="None"` and `hostTypeDialog="false"`. - XML gotcha: comments in this file must not contain `--` — `XmlDocument.Load` throws and the whole catalog comes up empty. Registering entries on pods does **not** require reinstalling files: the console's **Register Product on Pods** context action pushes the catalog's launch entries to connected pods over the `InstallApp` RPC. The wire shape is `LaunchData { LaunchPair{LaunchKey, DisplayName}, WorkingDirectory, ExeFile, Arguments, AutoRestart }` ([Contract/WireContract.cs](Contract/WireContract.cs)). ### 2.3 Launch / kill / watchdog semantics What the launcher does with your entry ([Launcher/TeslaLauncher.cs](Launcher/TeslaLauncher.cs)): - **LaunchApp** starts `exe` with `args`, working directory = `workingDirectory` or the exe's folder. Missing exe → clean "registered but not yet installed" error at the console (register-first / install-later is supported). - **Kill** terminates the process. Console-ordered kills stay down. - **Watchdog:** an `autoRestart` entry whose process **exits on its own** is relaunched ~2 s later. The original games lean on this for their per-mission cycle: `rpl4opt`/`btl4` **terminate after each mission** and the watchdog brings a fresh process up waiting for the next egg. That exit-and-relaunch cycle is **not strictly required** (FireStorm doesn't do it): a game may instead stay resident and return itself to a dark waiting state, ready for the next group (§3.6). Either way, design your exe so a cold start goes straight to that ready state with no menus in the way — the watchdog is still your crash recovery. - Your process runs in the auto-logged-in kiosk session (account `Firestorm`, Administrator, UAC disabled) — desktop, audio, and DirectX/OpenAL are all available. Firewall is disabled on pods; don't ship your own rules. --- ## 3. Command spec (Tier 1 — the Munga protocol) Reference implementations, in order of usefulness: - **[vPOD/MungaPodServer.cs](vPOD/MungaPodServer.cs)** — the framing, complete and commented (vPOD is a working software pod; the console can't tell it from a real one). - **[vPOD/PodSimulator.cs](vPOD/PodSimulator.cs)** — the pod-side state machine and egg handling. - **[Console/TeslaConsole/MungaGame.cs](Console/TeslaConsole/MungaGame.cs)** — the console side you're talking to. - The typed message classes live in the vendored `Console/lib/Munga Net.dll`; the C++ originals are in the RP411 game repo. ### 3.1 Transport **Your game is the TCP server.** Listen on **TCP 1501**; the console connects to `:1501` and keeps one connection open. One console at a time (a new connection replaces the old — see `MungaPodServer.AcceptLoop`). Convention: the port is passed on your command line (`-net 1501`) rather than hardcoded. ### 3.2 Framing (little-endian throughout) Every message, both directions: ``` [16-byte NetworkPacketHeader][MungaMessage] header: int32 ClientID | int32 GameID | int32 FromHost | int32 Timestamp(ms tick) message: int32 MessageLength | int32 MessageID | int32 Flags | body... ``` `MessageLength` counts the 12-byte message base **but not** the 16-byte header. Messages are dispatched by **(ClientID, MessageID)** pairs. ### 3.3 Message set Console → pod (what you must accept): | ClientID | MessageID | Message | Your reaction | |---|---|---|---| | Application | 3 | `StateQuery` | reply `StateResponse(host, state, appId)` | | Application | 4 | `CheckLoad` | (load probe) | | Application | 5 | `RunMission` | `WaitingForLaunch → LaunchingMission → RunningMission` | | Application | 6 | `StopMission` | `RunningMission → EndingMission → exit` (§3.6) | | Application | 8 | `SuspendMission` | `RunningMission → SuspendingMission` (pause) | | Application | 9 | `ResumeMission` | `SuspendingMission → ResumingMission → RunningMission` | | Application | 10 | `LoadMission` | `WaitingForEgg → LoadingMission` | | Application | 11 | `AbortMission` | `AbortingMission → WaitingForEgg` (no results) | | Application | 12 | `LightsOutMission` | cockpit lights-out | | NetworkManager | 3 | `EggFile` | egg chunk — buffer it (§3.5) | Pod → console (what you send): `StateResponse` (answer to every `StateQuery`), `AcknowledgeEggFile` (NetworkManager 4, once the egg is complete), and the in-mission event messages (§3.6). ### 3.4 Identity and state - **`ApplicationID`** — which game this pod is running, reported in every `StateResponse`. The enum lives in `Munga Net.dll`: `RPL4 = 0` (Red Planet), `BTL4 = 1` (BattleTech), plus `NDL4`. **A brand-new title needs a new value** agreed with the TeslaSuite side (the console maps `ApplicationID` → game module), or it reuses an existing one if it's a port of that game. - **`ApplicationState`** — the cockpit state machine. The values the console drives/observes: `InitializingState, WaitingForEgg, LoadingMission, WaitingForLaunch, LaunchingMission, RunningMission, SuspendingMission, ResumingMission, EndingMission, AbortingMission, CreatingMission`. - The console polls `StateQuery` about **once per second** and gates every operator action on your reported state. Report honestly — the console's UI ("busy, must stop first", ready-to-run, etc.) is driven entirely by it. The normal lifecycle: ``` boot → InitializingState → WaitingForEgg ← egg streamed (console sends it when it sees WaitingForEgg) → LoadingMission → WaitingForLaunch (send AcknowledgeEggFile) ← RunMission → LaunchingMission → RunningMission ← StopMission (once, at mission end) → EndingMission → back to WaitingForEgg, either by: exiting (watchdog relaunches a fresh process — the original games), or resetting in-process to a dark waiting state ``` ### 3.5 The egg (mission definition) The console streams the mission as **`EggFileMessage` chunks of ≤1000 bytes** (`index, totalLength, thisLength, buffer`). Reassemble by index until `totalLength` bytes have arrived, then send **`AcknowledgeEggFileMessage`** and move to `LoadingMission`. Content: ASCII, INI-style sections, with every `key=value` separated by **NUL** (`\0`) on the wire (the console builds it with `\n` separators and replaces them before encoding). Parse by section name — section **order is not guaranteed**. General shape (full field-by-field spec for an existing game: [410console/battletech-port/BATTLETECH-PORT-SPEC.md](410console/battletech-port/BATTLETECH-PORT-SPEC.md) §2): ``` [mission] adventure= map= scenario= time= weather= temperature= length=... [pilots] pilot= ← one line per participant [] hostType= name= vehicle= dropzone= color= ... ← per-participant [ordinals] 1st–4th place plasma bitmaps (128×32) [BitMap::Large::] 128×32 pilot-name plasma bitmap [BitMap::Small::] 64×16 variant ``` - Participants are keyed by **pod IP**. `hostType` assigns the pod's role: `0` = game machine, `2` = mission review / camera, `3` = console ([Console/TeslaConsole/HostType.cs](Console/TeslaConsole/HostType.cs)). - The `[BitMap::*]`/`[ordinals]` sections are pre-rendered graphics for the cockpit's 128×32 plasma scoreboard — the console authors them; your game just forwards them to the plasma display if the cockpit has one. - Egg *content* is game-specific; the envelope above (chunking, ack, NUL delimiting, sections) is fixed. ### 3.6 Mission end, events, results - **In-mission events** are pod → console `MungaMessage`s. Red Planet sends `Scored / Killed / Damaged / Boost / ScoreUpdate`; BattleTech's set maps its DamageMatrix/KillMarker model. Your game defines its own set, but the console module (§3.7) must know how to decode it — coordinate the two. - **Mission end / egress:** the console is the **sole mission timekeeper**. It sends **one** `StopMission` when mission time expires. Any end-of-mission ritual (RP/BT hold pilots in the cockpit ~30 s of egress) is the *game's* own behavior after receiving it — the console does not send a second stop. After egress the game must end up back in a **dark waiting state** reporting `WaitingForEgg`, ready for the next group. The original games get there by **exiting** — the launcher watchdog relaunches a fresh process (§2.3) and the console reconnects — but staying resident and resetting in-process is equally valid (FireStorm-style); the console only acts on the state you report and tolerates either a dropped-and-reconnected or a continuously open socket. - `AbortMission` is the operator bailing out: return to `WaitingForEgg` (via `AbortingMission`), no results expected. ### 3.7 The console side of Tier 1 Commanding a game isn't only pod-side work — the console needs a per-game module that builds the egg and provides the mission UI: `Console/TeslaConsole./` mirroring [Console/TeslaConsole.RedPlanet/](Console/TeslaConsole.RedPlanet/) (mission classes + `ToEggString()`, a config XML catalog of maps/vehicles/ scenarios, the game pane driving `MungaGame`). The BattleTech port spec ([BATTLETECH-PORT-SPEC.md](410console/battletech-port/BATTLETECH-PORT-SPEC.md)) is the worked example of adding one — budget for it in your plan, and open the conversation with the TeslaSuite maintainers early (ApplicationID assignment, event vocabulary, egg fields). ### 3.8 If your game already has its own control protocol Precedent: BattleTech FireStorm (MechWarrior 4) speaks **CTCL** on ports 1000/1001, not Munga — researched and parked in [FIRESTORM-CTCL.md](FIRESTORM-CTCL.md). The standing guidance: **do not teach the console a second protocol**. Put an adapter on the pod that speaks Munga to the console and your native protocol to the game (TeslaRel410 does exactly this: `pod-launch.exe` supervises the DOS game and fronts for it). The console then sees a normal Munga pod. ### 3.9 Escape hatch: a dedicated game console If Munga control — direct or through a §3.8 adapter — would be **limiting or detrimental to the game experience** (the mission model doesn't map to the egg/state machine, the game needs richer or real-time operator control than load/run/stop, the adapter would cost fidelity), you may instead build a separate **game console**: a purpose-built operator application for your game, run on the console computer alongside TeslaConsole. FireStorm is the precedent — MechWarrior 4 venues ran their own dedicated console rather than bending the game to the Tesla mission model ([FIRESTORM-CTCL.md](FIRESTORM-CTCL.md)). Rules if you take this path: - **It ships inside the same deployment zip as the pod-side game.** One Install Product archive is the whole product — no separate installer, no side-channel distribution. Put it in a subfolder of your product (e.g. `YourGame\GameConsole\`); the pod-side extraction just carries the folder along, and the operator runs it from that same archive on the console machine. - It runs on the **console computer**, never on pods. - **TeslaConsole still owns deployment and process lifecycle** (§2): install/uninstall, launch/kill, and the watchdog all stay on the launcher channel. Your game console owns only in-game command — it is a replacement for §3.1–3.7, not for §2. - Don't collide with the suite's ports on either end: 1501, 53290, 53291/53292 are spoken for. --- ## 4. Presentation: live camera, mission review, score sheets A pod bay is more than cockpits: spectators watch the current game on a bay display, players coming out of the pods watch a replay at the mission-review station, and everyone walks away with a printed score sheet. Plan for all three. ### 4.1 The bay address plan Modern bay deployments follow the FireStorm/CTCL site layout: squads of eight pods per address decade, with the `9`/`10` slots of each decade reserved for stations. | Address | Station | |---|---| | `x.x.x.1–8` | Pods, squad 1 | | `x.x.x.9` | **Live camera** | | `x.x.x.10` | **Operator console** | | `x.x.x.11–18` | Pods, squad 2 | | `x.x.x.19` | **Mission review** | | `x.x.x.20` | **Score-sheet printer** | | `x.x.x.21–28` | Pods, squad 3 | | `x.x.x.31–38` | Pods, squad 4 — and so on: pods at `1–8` of every further decade, `9`/`10` slots reserved for future stations | (Legacy RP/BT-era installs used the `200.0.0.x` scheme with the console at `.1` and pods from `.11`. The suite doesn't hardcode either — addressing is per-site via Manage Site — but new bays should follow the plan above.) ### 4.2 Live camera — presenting the current game The live-cam station runs your game exe in a **spectator role**, rendering the running mission on the bay display. The RP/BT model, which the console generalizes: - The station is a pod like any other — installed, launched, watchdogged — whose catalog launch entry has `hostType="LiveCamera"`; RP/BT pass a `-lc` flag so the exe boots into the camera role (§2.2). - The console enrolls every enabled camera station in the mission as a **camera participant**: it receives the same egg and walks the same Munga state machine as a game pod, but its participant block says `hostType=2`, `vehicle=camera`, `name=Camera`, `loadzones=0` ([RPCamera.cs](Console/TeslaConsole.RedPlanet/RPCamera.cs)). - Your game's job in the role: observe the running mission and render a spectator view — no cockpit input, no scoring participation. The camera's view of the action travels over the game's own network traffic between pods; the console only issues the egg and state commands. ### 4.3 Mission review — replaying the finished game The mission-review station **replays the completed mission** for the players who just climbed out. To the console it looks exactly like the live cam — a `hostType="MissionReview"` catalog entry (`-mr` flag in RP/BT), enrolled as an egg camera participant — the difference is entirely inside your game: the MR role captures the mission as it runs and replays it on demand afterwards. **Replay capture and playback are the game's responsibility.** The console does not record or transport replay data: RP/BT capture from the game's own network traffic; FireStorm writes `{guid}.mr` files pod-side and its console points the review station at them ([FIRESTORM-CTCL.md](FIRESTORM-CTCL.md)). ### 4.4 Score sheets Players get a printed score sheet. How it works for a Tier 1 game: - During the mission the console builds results from your **in-mission event messages** (§3.6) via the game module's mission recorder ([RPMissionRecorder.cs](Console/TeslaConsole.RedPlanet/RPMissionRecorder.cs)). The event vocabulary is what makes score sheets possible — a game that reports no events has nothing to print. - The game module renders the results as a print document ([RPPrintDocument.cs](Console/TeslaConsole.RedPlanet/RPPrintDocument.cs)). The operator's **Auto Print** checkbox prints each mission as it ends; **Print Last Mission** reprints on demand. Output goes to the bay's score-sheet printer (`x.x.x.20`, a network printer configured on the console machine). - A §3.9 dedicated game console owns its own scoring and printing (FireStorm: the game writes `{guid}.pr` files pod-side, the console hands them to the `mw4print` helper) — it should print to the same bay printer. --- ## 5. Pod environment reference | Fact | Value | |---|---| | OS range | Windows XP SP3 → Windows 11, one binary set (.NET products: net40) | | Session | auto-login kiosk account `Firestorm` (Administrator, UAC off) | | Games root | `C:\Games\\` | | Launcher state | `\TeslaLauncher\` (`LaunchApps.xml`, key store, log) | | Network plan | squads of 8 pods per address decade; live cam `.9`, console `.10`, mission review `.19`, printer `.20` — see §4.1 | | Ports | 1501 TCP game control (your game listens) · 53290 TCP launcher RPC · 53291/53292 UDP first-boot provisioning | | Cockpit extras | 128×32 plasma scoreboard on COM2; RIO board cockpit controls — native integration preferred, RIOJoy shim optional (§5.1) | | Firewall | disabled by the pod installer | ### 5.1 Cockpit controls: the RIO board The cockpit's controls come in through the **RIO board**. Two integration paths: - **Native RIO integration — preferred.** Talk to the board directly, as the original games do (FireStorm's `CRIOMAIN.CPP` is the documented precedent). This is the only path that reaches the board's **output side** — the **cockpit lighting** — which native games get as a feedback channel to the player. A game that only reads a gamepad can't touch it. - **RIOJoy — optional shim.** A deployable catalog product that feeds RIO board input into a virtual gamepad (RioGamepad HID via the ViGEmBus driver; Win10+ only) with per-game mapping profiles. Zero game-side changes — if your engine already reads a standard gamepad, a RIOJoy profile gets a cockpit playable. **Input only:** no lighting, no feedback. A RIOJoy profile is a fine way to get playable quickly during development; plan on native RIO integration for the real deployment so the cockpit lighting works for your game. (The board interface and the feeder implementation live in the RIOJoy repo.) --- ## 6. Testing your integration without a pod bay - **[vPOD/](vPOD/)** is a full software pod: it emulates the launcher side (install your zip into a real `C:\Games`, launch/kill with the real watchdog semantics) *and* the Munga side. Two ways to use it: 1. **Test your package/catalog entry:** run vPOD on any machine, provision it from the console (README walk-through), Install Product your zip, launch — with "Actually launch apps" checked your real exe runs. 2. **Test your Munga implementation:** point the console at your game instead of vPOD (the shipped site has a `local` pod at `127.0.0.1` — run your exe with `-net 1501` on the console machine). vPOD's egg viewer is also handy: drive a mission at vPOD, copy the egg it captures, and use it as a fixture for your parser. - Console-side changes are pinned by the differential suite ([Console/tests/TeslaConsole.DiffTests](Console/tests/TeslaConsole.DiffTests)). --- ## 7. Checklists **To make your game deployable (Tier 0):** - [ ] Package zip: `\` folder + optional root `postinstall.bat`, optional `\pre-uninstall.bat` (§2.1) - [ ] Runs on your target pod OS range — XP SP3 support is a nice-to-have that reaches the original hardware (x86 + net40 if .NET); if skipped, catalog comment says modern-pods-only - [ ] `Apps.xml` `` entry, key convention respected (§2.2) - [ ] Cold start reaches gameplay/ready state unattended (kiosk + watchdog) - [ ] Cockpit controls wired: native RIO integration preferred (enables lighting feedback), RIOJoy profile acceptable (§5.1) - [ ] Install → launch → kill → uninstall verified against vPOD **To make your game commandable (Tier 1), additionally** *(or, if Munga control would hurt the game: a dedicated game console per §3.9, shipped in the same deployment zip)*: - [ ] TCP server on 1501 (`-net` arg), Munga framing per §3.2 - [ ] `StateQuery` → `StateResponse` with an agreed `ApplicationID` - [ ] Egg reassembly + `AcknowledgeEggFile` + state walk to `RunningMission` - [ ] `Stop/Abort/Suspend/Resume` honored; after mission end, back to a dark `WaitingForEgg` — by exiting (watchdog relaunch) or by in-process reset - [ ] Event vocabulary agreed with the console module — rich enough for score sheets (§4.4) - [ ] Live-camera and mission-review roles: spectator rendering + replay capture/playback in the game (§4.2–4.3), LC/MR catalog entries (§2.2) - [ ] Console game module exists or is planned (§3.7)