- Adopt the launch-key convention (documented in the Apps.xml header):
fresh Guid = product id, first Launch key reuses it, each extra entry
increments the last hex digit (wrapping F->0). Never a -1 suffix:
keys parse as System.Guid and silently collapse to Guid.Empty.
- Rewrite BT411 LC/MR + RIOJoy keys to the convention. RP4.11 LC/MR stay
pinned to the original console's hardcoded Guids (SiteManagement
constants + diff tests).
- Remove vPOD from the shipped catalog: dev tool, never a console-deployed
product (README documents the ad-hoc Add Product path instead).
- Add TeslaRel410 (DOSBox-X preservation pods): six entries, BT/RP 4.10 x
GameClient/LC/MR, all C:\Games\TeslaPod410\pod-launch.exe with mode
bt/rp. LC/MR boot identically (role assigned via egg hostType) and no
{res} token (output size fixed per rig at postinstall).
- CatalogTests: now 5 products / 14 entries; full diff suite 106/106.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
9.7 KiB
vPOD — virtual pod / game-client stand-in
A test tool that impersonates a Tesla game client (Red Planet's rpl4opt.exe
or BattleTech's btl4.exe) so the operator consoles can be exercised without
real cockpit hardware. It speaks the Munga command/control protocol as a
server on TCP 1501 — the console connects to it exactly as it would a real pod —
emulates the pod ApplicationState machine, reassembles the streamed egg, and
shows everything on a live display.
It also impersonates the pod's TeslaLauncher service (the "Launcher / Site Management" column), so the console's Manage Site — provisioning, Install / Uninstall Product, launch/kill, volume, restart/shutdown — can be tested end-to-end with no cockpit and no console changes. See Site management / virtual launcher.
What it does
- Listens on TCP 1501 (configurable) and answers the console's
StateQuerywith aStateResponse, reporting the game (ApplicationID) and the currentApplicationState. - Walks the mission lifecycle the console drives it through:
WaitingForEgg → LoadingMission → WaitingForLaunch → LaunchingMission → RunningMission → …, reacting to the egg stream and to Run / Stop / Abort / Suspend / Resume messages, and acknowledging the egg. - End-mission graceful exit + watchdog restart — on the console's end-mission
command the "game exe" exits (the listener closes, the console's connection
drops), then a watchdog relaunches it a moment later and it comes back up in
WaitingForEgg. This is the real pod's per-game cycle (autoRestart); the Restart game after mission ends (watchdog) checkbox (on by default) toggles it — unchecked, the pod just returns toWaitingForEggwithout exiting. - Pod Power (Power On / Power Off) — the whole virtual machine: Power Off
darkens the game listener AND the launcher / site-management side; Power On
boots the launcher (or provisioning beacons) and auto-starts the game, like a
real pod booting with an
autoRestartproduct installed. - Start Game / Stop Game — just the emulated game "exe", separate from pod
power: Stop Game closes the Munga listener (the console's game connection
drops) while the pod and its launcher service stay up — the state of a pod
whose game client crashed or was killed. Reset returns a running game to
WaitingForEgg. - Reassembles and shows the egg the console streams (the
EggFileMessagechunks), one field per line, with a summary line (adventure / map / scenario / pilot count). The last egg is kept across missions/restarts (so it can be copied for dev use) until the Clear button empties the viewer. - Game toggle — a Red Planet ⇄ BattleTech switch on the window changes which
ApplicationIDthe pod reports, live, so one vPOD can stand in for either game. (-app rp|btsets the initial choice.) - A newest-first protocol log of the traffic.
Running it
vPOD.exe [-net <port>] [-app rp|bt] [-lc|-mr] [-host <id>] [-res W H] [-nomanage]
-net <port>Munga control port (default 1501).-app rp|btwhich game to report initially (also switchable in the UI).-lc/-mrlive-camera / mission-review role (cosmetic; the state model is identical to a game machine).-host <id>responding host id reported in state responses (default 1).-res W Haccepted and ignored (real clients take it; kept for drop-in launch compatibility).-nomanagevPOD-only: disable the virtual launcher / site-management side (no provisioning beacons, no TCP 53290 listener) — e.g. when a real TeslaLauncher runs on the same machine, or for a second vPOD instance.
Site management / virtual launcher
vPOD's right-hand column is a stand-in for the pod's TeslaLauncher service:
the OFB-encrypted, framed-JSON ILauncherService RPC on TCP 53290 that the
console's Manage Site and squad/pod panel talk to (Tesla.Contract /
Tesla.SecureConfig are the same shared wire libraries both real ends use).
Provisioning (first run)
The console only talks to a pod's launcher after minting a 32-byte session key for it via the SecureConfig Configure flow, so an unprovisioned vPOD behaves like a freshly-imaged pod — minus the NIC/registry changes (the assigned network config is shown in the window but never applied):
- Run vPOD (no
-nomanage). It broadcastsRQSTbeacons and displays a Request ID and Passphrase (the real pod shows these on its screen). - In the console: Manage Site — a "Configure <Request ID>" button appears at the bottom. Click it, enter the pod's name/squad, its IP (127.0.0.1 when vPOD runs on the console machine — accepted), any subnet (e.g. 255.255.255.0), and the passphrase from vPOD's window.
- The console sends the encrypted config + session key; vPOD stores the key
and starts the launcher RPC. The pod row goes healthy (
Idle [<n> ms]).
The key persists in %LocalAppData%\vPOD\TeslaKeyStore.key (launcher format),
so provisioning is one-time. Reprovision resets to a fresh pod — drops the
key, clears the installed-apps store (LaunchApps.json) and returns to beacon
mode — pair it with deleting the pod in Manage Site (the console's
Reconfigure… does both ends automatically: its ClearStore makes vPOD do
the same wipe and beacon again). Extracted packages under C:\Games are left
on disk either way.
Product deployments
Right-click the pod row → Install Product ▸ works exactly as against a real
pod: the console streams the package zip out-of-band on a second 53290
connection while polling progress on the first; vPOD extracts it into the real
C:\Games — the launcher's games root, so products land where their catalog
launch entries point — and reports the launcher's usual 0–50% receive /
50–95% extract / 99% Complete progression. Then:
- Installed apps land in the column's list (and in
GetInstalledApps, so the Uninstall menu populates). Registrations persist in%LocalAppData%\vPOD\LaunchApps.json. - Packages extract into
C:\Games(created on first install if missing; if an admin-ownedC:\Gamesisn't writable by your account, the install reports Failed — fix the folder's ACL or run vPOD elevated). Uninstalling a product removes itsC:\Games\<product>folder, like the real launcher. A packagedpostinstall.batis logged and removed unrun by default; the "Run postinstall.bat after install" checkbox makes the install execute it (viacmd /c, waited on up to 5 min) before deleting it, like the real Agent — off by default because it runs package script code on the host machine. - Launch/Kill from the squad panel simulate PIDs by default. The
"Actually launch apps (real processes)" checkbox switches to the real
Agent's behavior: LaunchApp starts the entry's exe from
C:\Games(missing exe → the same "registered but not yet installed" error a real pod gives), Kill* terminate the processes, and apps that exit or crash on their own disappear from the console's running list. The indented "Auto-restart after the app exits (watchdog)" checkbox (on by default) adds the Agent's watchdog: anautoRestartentry that exits on its own relaunches after 2 s, while console-ordered kills stay down — exactly the real pod's behavior. Real processes also die when the pod is powered off / rebooted / the vPOD window closes (which also cancels pending watchdog restarts). Caveat: launching a deployed vPOD or a real game client this way will fight the running vPOD for ports 1501/53290. Volume round-trips; Restart/Shutdown power-cycle the virtual pod (dark for a few seconds on restart).
Ports
| Port | Proto | Direction | Purpose |
|---|---|---|---|
| 1501 | TCP | console → vPOD | Munga game control (existing) |
| 53290 | TCP | console → vPOD | Launcher RPC (ILauncherService) |
| 53291 | UDP | vPOD → console (broadcast) | RQST provisioning beacon |
| 53292 | UDP+TCP | console → vPOD | RPLY config broadcast + RSA key exchange |
Same-machine testing needs no firewall changes (loopback). Running vPOD on a different machine needs inbound allows for TCP 1501/53290/53292 and UDP 53292 on the vPOD machine (the console installer already opens its own side).
Not emulated: the console's remote Windows-service control (ServiceController
over SCM/SMB, used by some SitePanel service start/stop paths — dormant against
real pods too, since it queries service name TeslaLauncherService while the
launcher registers as Tesla Application Launcher).
An end-to-end loopback test of this server against the console's real
PodManagerConnection client lives in the differential suite:
Console/tests/TeslaConsole.DiffTests/VPodLauncherServerTests.cs.
Deploying from the console (Manage Site → Install Product)
vPOD is not in the shipped catalog (Console\RedPlanet\Apps.xml) — it was
removed 2026-07-11 since it is a dev tool, never a console-deployed product. To
push it to a pod for testing, add it locally via Site Management → Add New
Product... (or hand-edit Apps.xml) and build the deployable package first:
pwsh -File pack.ps1 # produces dist\vPOD.zip
The zip lays out vPOD\vPOD.exe (+ Munga Net.dll) so the launcher extracts it
to C:\Games\vPOD and a launch entry pointing at C:\Games\vPOD\vPOD.exe works
as-is.
Testing locally against the console
The default site ships a local pod at 127.0.0.1. Run vPOD on the console
machine, open a game window (e.g. Games → Red Planet: Death Race), and enable
the local pod — the console connects to 127.0.0.1:1501 (vPOD), and you can
drive Load → Run → Stop and watch vPOD's state and egg viewer follow along.