Files
CydandClaude Fable 5 91640dcbf2 XP11: whole suite on net40 — Console + vPOD run on XP SP3 through Win11
The Launcher's XP11 port (8730b9b) now extends to everything: one net40
flavor across Console, vPOD, Contract, and SecureConfig (Newtonsoft.Json
everywhere; the net48/System.Text.Json legs and their #if splits are gone
since nothing consumed them).

Console (net40, single TFM like the Launcher):
- The ~31 BinaryFormatter bitmap blobs in the .resx files became raw
  embedded files under assets/icons/ (extracted byte-faithfully via a
  serialization surrogate — the animated square_throbber.gif survives),
  loaded by Properties.Resources.EmbeddedBitmap/EmbeddedIcon. Reason:
  System.Resources.Extensions' DeserializingResourceReader is net461+
  and cannot load on net40. Strings stay in the .resx.
- IReadOnlyList -> IList in AppRegistry (net45+ interface).

vPOD (net40, single TFM):
- Zip extraction now shares the Launcher's MiniZip.cs (linked source), so
  the diff-test install round-trip exercises it against ZipArchive zips.
- RPC args as JTokens; LaunchApps.json persistence via Newtonsoft;
  Thread.VolatileRead instead of Volatile.Read.

Contract/SecureConfig: net40-only; Client/** (PodManagerConnection) now
ships in the one build. The Launcher package gains
TeslaSecureConfiguration.dll as a dependency of the client half.

Tests: the net48 xunit host loads the net40 assemblies (both CLR4), so
the suite exercises exactly what ships — 106/106 green. Also verified
live: net40 console provisioned, managed, and ran a full RP mission
against net40 vPOD (beacon/passphrase/RSA, 53290 RPC, egg load,
Run/Stop Mission).

Version: 4.11.4.3 across Launcher, Console, and vPOD (vPOD joins the
suite version line; was 1.0.0). Ship the dotNetFx40 redistributable in
Launcher/assets for XP-era pods.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 21:01:34 -05:00
..

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 StateQuery with a StateResponse, reporting the game (ApplicationID) and the current ApplicationState.
  • 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 to WaitingForEgg without 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 autoRestart product 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 EggFileMessage chunks), 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 ApplicationID the pod reports, live, so one vPOD can stand in for either game. (-app rp|bt sets 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|bt which game to report initially (also switchable in the UI).
  • -lc / -mr live-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 H accepted and ignored (real clients take it; kept for drop-in launch compatibility).
  • -nomanage vPOD-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):

  1. Run vPOD (no -nomanage). It broadcasts RQST beacons and displays a Request ID and Passphrase (the real pod shows these on its screen).
  2. 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.
  3. 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 050% receive / 5095% 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-owned C:\Games isn't writable by your account, the install reports Failed — fix the folder's ACL or run vPOD elevated). Uninstalling a product removes its C:\Games\<product> folder, like the real launcher. A packaged postinstall.bat is logged and removed unrun by default; the "Run postinstall.bat after install" checkbox makes the install execute it (via cmd /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: an autoRestart entry 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.