- InstallProduct now extracts into the real C:\Games (the launcher's GAMES_DIR) so deployed products land where their catalog launch entries point; uninstall removes the real product folder. Tests pass an isolated games root through the VirtualLauncher ctor. - New "Actually launch apps (real processes)" toggle (off by default = simulated PIDs): LaunchApp starts the entry's exe exactly like the Agent (same start info, same registered-but-not-installed error), Kill*/ Uninstall/Wipe terminate the real processes (kill before folder delete), and self-exited apps are pruned from GetLaunchedApps/FullUpdate. Real processes die with the machine: power off, reboot, or closing vPOD. The Agent's autoRestart watchdog is deliberately not emulated. - Pod Power and Mimicking Game groups swapped (game left, power right). - Provisioning round-trip test now skips as inconclusive when a running TeslaConsole holds UDP 53291 instead of failing the suite; two new tests cover real launch/kill (ping.exe) and the missing-exe error path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
9.2 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 but never executed. - 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. Real processes also die when the pod is powered off / rebooted / the vPOD window closes. 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), and the Agent's
autoRestart watchdog (a real-launched app that dies is pruned from the
running list, not relaunched).
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 a catalog product (Console\RedPlanet\Apps.xml, id 0041C870-…) with Game
Client / Live Camera / Mission Review entries, so it appears in Manage Site →
Install Product like any game. 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 the catalog entry launches C:\Games\vPOD\vPOD.exe.
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.