# 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](#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 ] [-app rp|bt] [-lc|-mr] [-host ] [-res W H] [-nomanage] ``` - `-net ` 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 ` 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 [ 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-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\` 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.