Network-agnostic, air-gapped pod install for the two DOSBox titles (BT/RP 4.10), fitting the existing TeslaConsole/TeslaLauncher pod-bay architecture. emulator/DEPLOYMENT-PLAN.md Full design: bridge on the one NIC (pods form a source-proven P2P TCP mesh, so NAT/slirp is out); launcher keeps the bay IP, the DOSBox guest bridges at bayIP+100 (egg/mesh/game endpoint); two-edit +100 convention (postinstall + console DOSBox flag); static, air-gapped, <=32 pods. emulator/pod-launch/ (C# net8 supervising entry-point) Creates a Job Object (KILL_ON_JOB_CLOSE), launches DOSBox-X + the render bridge into it and blocks -- kill this process and both die (kernel-enforced, even on a hard TerminateProcess of a hung session; verified). Mode dispatch: bt/rp wired (also serve camera + live mission-review via egg hostType); review + diagnostics recognized but fail clean until their DOS launch args are known. emulator/deploy/ (install side) postinstall.bat (thin elevated wrapper) + configure.ps1 (NIC detect, realnic bind as a contiguous letter-leading GUID fragment, game IP = bayIP+100, stable MAC, render net_*.conf templates, stamp WATTCP.CFG my_ip) + tokenized conf templates + package.ps1 (assemble the zip). Render/bind/stamp + packaging verified against a scratch tree. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
9.3 KiB
Pod-bay deployment plan (network-agnostic archive)
How the two emulated titles (BattleTech + Red Planet, the only DOSBox games) ship into the existing TeslaConsole / TeslaLauncher pod-bay architecture as a self-contained, air-gapped, statically-addressed install. Everything here is settled unless marked OPEN.
Built so far: the supervising launch entry-point (pod-launch/, C# Job-Object
supervisor -- kill-cascade verified) and the install-side artifacts
(deploy/: postinstall.bat, configure.ps1, net_*.conf.tmpl -- rendering +
NIC-bind + bayIP+100 + WATTCP-stamp verified against a scratch tree). Remaining
build work is in the OPEN list.
The existing contract (do NOT change it)
- A pod bay is a Windows PC on a self-contained, air-gapped network (no venue LAN, no internet) shared by the console + up to 32 pods (~140 units left worldwide -- a bounded preservation fleet, not a scaling problem).
- All addresses are static, assigned by TeslaConsole. No DHCP.
- TeslaConsole -> TeslaLauncher: transmits a zip; the zip has ONE folder +
a
postinstall.batat its root; TeslaLauncher extracts to the games root (C:\gamesalmost always) and runspostinstall.batelevated. - TeslaConsole registers/invokes the launch -- it tells TeslaLauncher to
execute a command in the game dir with args (args select game/mode).
postinstall.batdoes NOT register anything. - TeslaLauncher also STOPS the app -- e.g. on a console "kill" call when a DOSBox hangs. Its control path must stay alive independent of DOSBox.
- Only BT/RP 4.10 are emulated (DOSBox); the rest of the catalog runs native (preservation project). For every native title, TeslaLauncher and the game share the bay's single IP as one process space; these two are the only exception, because the game's TCP stack lives inside DOSBox, separate from the host.
Network model -- THE core of this plan
Why a bridge, not slirp/NAT (settled, source-proven)
The pods form a direct peer-to-peer TCP mesh, not star-through-console:
each pod TCP_OPENs to the other pods' real IPs from the egg roster
(MasterMode/SlaveMode decides who dials), tracked separately from the
console stream. Evidence: NETNUB_TCP_OPEN ("Opens a TCP stream to another
computer", NETNUB.HPP), OpenConnection(...,int internet_address),
NetworkStartupMode{SlaveMode,MasterMode}, numberOfMungaHostsConnected vs
numberOfConsoleHostsConnected (L4NET.HPP). See memory pod-multiplayer-mesh.
=> NAT can't carry a pod dialing a same-subnet peer, so the emulated NIC must be
a real bridged host on the segment.
Two IPs per bay -- launcher keeps the bay IP; the DOS game gets bayIP+100
The launcher's IP never moves. The DOSBox guest gets a second address so it can be a first-class host without stealing the bay IP:
| Role | Address | Owner | Used for |
|---|---|---|---|
| Bay / control IP | e.g. 10.0.0.5 (static, console-assigned) |
host Windows / TeslaLauncher | zip, launch, kill -- unchanged from native titles; alive even if DOSBox hangs |
| Game IP | bayIP + 100 = 10.0.0.105 |
DOSBox guest (bridged, own emulated-NIC MAC) | egg identity, mesh peer target, :1501 |
Both live at once on the one physical NIC (host MAC = bay IP, emulated NE2000
MAC = game IP). The +100 offset on the last octet is safe forever at this
scale: 32 bays fit in .1-.32, games in .101-.132, no wrap, no collision.
The +100 convention -- two edits, both derive the same value
postinstall.batreads the host's static IP and stamps the guestWATTCP.CFG my_ip = bayIP+100(netmask255.255.255.0, gateway harmless -- air-gapped, no routing; the mesh is all same-subnet L2).- TeslaConsole -- a per-title "runs in DOSBox" flag. When set, apply
+100at the single point where the console resolves a bay to its game endpoint, so BOTH inherit it:- the roster entries written into the egg (peers dial
+100), AND - the address the console itself dials to deliver the egg / poll
StateQuery / send StopMission (
bayIP+100:1501, not the bay IP -- else the SYN hits the launcher, not the DOS game).
- the roster entries written into the egg (peers dial
Blanket +100 is valid because every host in a BT/RP egg is itself a DOSBox
pod. Only the BT/RP 4.10 engine is emulated, and ALL of its roles -- cockpit,
camera ship, mission review -- are that same one DOSBoxed binary; every other
catalog title runs native. So a BT/RP mission roster is entirely emulated and
takes +100 uniformly. The console's own address is never a roster entry (pods
reply on the console's inbound socket), so it's never offset.
What this retires (dev-only artifacts, gone in deployment)
SendToRxAdapters, the Windows Network Bridge, the two-TAP setup, and
tap2_mirror.py all existed ONLY because dev co-located the console on the pod
PC and had to loop pcap frames back to the host stack. On the real air-gapped
segment the console is a separate machine and hears every pod over the wire.
Also gone: the machine-specific realnic=DB5521D GUID (postinstall binds the
one NIC) and the 200.0.0.x hardcoding (postinstall stamps the assigned IPs).
The archive (self-contained -- air-gap forbids any download)
Single root folder + postinstall.bat. Everything the install needs is inside:
dosbox-x.exe+ DLLs (bundle the VC++ runtime; don't install it separately)ALPHA_1\game content (REL410\BT, REL410\RP, SB16, GAUGE, ...) +net-boot\drivers (LSL/NE2000/ODIPKT/NET.CFG @ PORT 340/INT 10, loop.bat)- Npcap silent installer (bundled)
- The renderer (OPEN: freeze to one exe vs bundle embeddable Python -- decide with David; "decide later")
net_*.conftemplates with@@ROOT@@/@@REALNIC@@tokens, launch dispatcher,postinstall.bat
postinstall.bat responsibilities (elevated, one-shot, no network fetch)
Implemented in deploy/ (postinstall.bat = thin wrapper; configure.ps1 does
2-4):
- Install Npcap silently (
/S, from the bundled installer -- the edition whose installer supports unattended install). - Bind the NIC: pick the one active adapter, write a safe letter-leading
realnic=fragment (a contiguous substring of ONE GUID block, so it matches\Device\NPF_{GUID}; avoids the leading-digit-as-index trap). - Stamp game identity:
WATTCP.CFG my_ip = <host static IP> + 100in everyALPHA_1\...\WATTCP.CFG, plus a stablemacaddr = 02:00:<gameIP hex>. - Relocate paths: render
net_*.conf.tmpl->net_*.conf, filling@@ROOT@@(the package dir),@@REALNIC@@,@@MACADDR@@.
NOT done here: registering the launch entry-point (console does that), firewall rules, SendToRx, TAP, or mirror.
Launch dispatch
TeslaConsole invokes a single entry-point in the game dir with args
selecting game/mode. The dispatcher maps args -> conf + renderer:
bt/looped -> net_loop.conf; rp -> net_rp.conf; the "alternate games/
modes" map onto the decoded ExitCodeID set (RunBattleTech/RunRedPlanet +
SinglePlayer variants, RunCamera, RunMissionReview, test patterns,
TestPlasmaDisplay, ResetRIO).
The entry-point MUST be a supervising parent, not fire-and-forget -- it stays authoritative for the whole session, lifetime == the game's:
- It creates a Windows Job Object with
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, launches DOSBox-X and the render bridge into the job, then blocks on DOSBox (also doing the window focus/layout, folding infocus_dosbox.ps1). - Kill the entry-point -> DOSBox + renderer die with it. The kernel enforces
the teardown when the job's last handle closes (the instant the entry-point
process dies), so it holds even on a hard
TerminateProcess-- which is exactly the hung-DOSBox case, where no cooperative shutdown can be relied on. No orphaned emulator/renderer, ever. - When DOSBox exits on its own (mission end), the supervisor tears down the job (renderer too) and returns DOSBox's exit code.
Ship the entry-point as a small compiled supervisor exe (C#/C++) that creates
the job, CreateProcesses DOSBox + the bridge into it, and blocks on
WaitForSingleObject(dosbox). It must NOT be a batch file / cmd.exe, nor use
start or Start-Process -- all of those DECOUPLE from the child: a started
/ Start-Processed DOSBox is an independent process, and even a foreground
dosbox.exe under cmd.exe is NOT killed when cmd is (Windows doesn't reap
children with the parent gone, absent a job object). The current
launch_pod.ps1/pod_deploy.ps1 do exactly this wrong (Start-Process +
pid-files, then exit, leaving DOSBox running) and must be replaced by the
supervisor.
Kill flow: console -> TeslaLauncher terminates the entry-point process ->
job closes -> DOSBox + renderer die. Graceful mission-end still flows to the game
as StopMission on :1501, after which the supervisor exits with DOSBox.
Provisioning rules
- Bays static in
.1-.32, game endpoints auto-fall in.101-.132, console + its special hosts elsewhere in the /24. All on one flat, air-gapped L2 segment. - Dumb unmanaged switch; no venue cooperation needed.
OPEN items
- Renderer packaging -- freeze (PyInstaller, one exe) vs embeddable Python (with David).
- Per-rig display RECTS (already isolated in
pod_deploy.ps1; deployment variant of the window layout).
Related: NET-NOTES.md, LAUNCH.md, ENV-VARS.md, memory
pod-multiplayer-mesh, pod-deployment.