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>
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.