Files
BT412/context/steamification.md
CydandClaude Fable 5 0faf6a0072 Front end: local mission-egg builder + front-end mode (Workstream B foundation)
The core 'single-player without the operator console' capability: a
zero-argument launch now builds its own mission and plays.

- game/reconstructed/btl4fe.{hpp,cpp}: the BattleTech catalog (8 maps,
  8 mechs + variants, colors) and BTFeMission_WriteEgg -- emits the exact
  console egg format (verified against content/MP.EGG): [mission],
  [ordinals] + the 4 static rank-place plasma bitmaps, [pilots],
  per-pilot loadout sections, and [largebitmap]/[smallbitmap] with
  GDI-rendered 128x32 / 64x16 pilot-name plasma bitmaps.
- L4APP.H: SetEggNotationFileName / SetNetworkCommonFlatAddress setters
  (from RP412) so the front end feeds the standard -egg load path.
- btl4main.cpp WinMain: front-end mode -- no -egg and no -net builds
  frontend.egg locally (BTFrontEnd_Run) and boots it. -egg/-net runs
  are untouched.

Verified: no-args launch -> '[frontend] built frontend.egg' -> mission
loads and runs; the generated egg is structurally identical to MP.EGG
(32x32/16x16 name bitmaps with real glyph pixels, all sections present).

Remaining in Phase 5 (deferred): the interactive on-screen catalog menu,
the in-process LocalConsole marshal (mission clock + StopMission at
expiry + single-binary loop), and the score-intake/results screen (new
for BT -- no mission-end flow exists yet). (Phase 5 of the roadmap)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 08:44:37 -05:00

198 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: steamification
title: "BT412 Steamification — status, seams, and merge notes"
status: living
source_sections: "docs/BT412-ROADMAP.md; RP412 repo (C:\\vwe\\RP412) git history + docs"
related_topics: [pod-hardware, multiplayer, gauges-hud, build-and-run, wintesla-port]
key_terms: [egg, RIO, console]
open_questions:
- "BT cockpit button-bank geometry source (no vRIO CockpitLayout exists for BT): l4gauge.cfg / pod photos / Nick"
- "Does BT's rebuilt L4D3D still have RP's NULL-texture AddRef crash pattern?"
- "Does L4CONTROLS=PAD set primaryControlType=PrimaryRIO so MechRIOMapper engages unchanged?"
---
# BT412 Steamification — status, seams, and merge notes
BT412 = BT411 made distributable on Steam and playable over the internet with no pod
hardware, following the recipe RP412 proved end-to-end (RP411 → RP412). The full plan
and phase order: `docs/BT412-ROADMAP.md`. RP412 (`C:\vwe\RP412`) is the reference
implementation — its git history from commit `467934c` onward is the steamification,
grouped roughly: toolchain → PadRIO input → MFD-split cockpit → front end/LocalConsole
→ NetTransport/Steam sockets → lobby → bindings/KeyLight → packaging.
**Repo rule [T0]:** `origin` = `VWE/BT412.git` only. This repo must never touch the
BT411 remote (no cross-pull remote — deliberate difference from RP412's `rp411`
pattern). Forked at BT411 `4e72f0c` (2026-07-14).
## Status ledger
- **Phase 0 bootstrap — DONE 2026-07-14.** Tree copied (working tree + full history,
`build/` excluded), origin re-pointed, fast-forwarded to `4e72f0c`, dev-state
`run/run.cmd` working-tree edit discarded (committed version is the documented solo
launcher), identity pass (README/CLAUDE.md/CMake `project(bt412)`), this topic +
roadmap authored.
- **Phase 1 (VS2022 v143) — DONE 2026-07-14.** Generator moved to
`Visual Studio 17 2022` (v143, Win32). One real change was forced: the v143 linker
fails the image on unresolved externals even under `/FORCE`, so the dead
offline-tool ladders in `game/reconstructed/mech3.cpp` are neutralized behind
`BT412_OFFLINE_TOOLS` (default off) and the exe now links `/FORCE:MULTIPLE`
(UNRESOLVED retired — real unresolved symbols are hard errors now). None of RP412's
other v143 source fixes (TIME.h, NETWORK.h, L4DINPUT, CAMMGR) were needed — BT's
tree was already v142-clean. Verified: clean build; solo DEV.EGG runs (gait/target/
31-subsystem ticks); two-instance loopback MP via btconsole.py — mesh forms
(console + 1502↔1602 game ports, "All connections completed!"), mission runs both
sides, each world ticks its master + the remote replicant. [T2]
- **Phase 2 (PadRIO input layer) — DONE 2026-07-14.** Ported from RP412:
`L4RIO.h` (RIOBase split; serial RIO re-parented), `L4CTRL.h` (`rioPointer` is
`RIOBase*`), `L4CTRL.cpp` PAD-token hook (→ `primaryControlType=PrimaryRIO`, so the
stock `MechRIOMapper` engages — confirmed), new `L4PADRIO.*` / `L4PADBINDINGS.*` /
`L4KEYLIGHT.*` (gates renamed `BT412KEYLIGHT`; KeyLight TU builds `/std:c++17
/permissive-` per-file). Default `bindings.txt` = the shared Tesla board layout
(numpad flight, letter rows = MFD banks, F-keys = Secondary columns, joystick head
0x40-0x47 on Space/arrows/pad). `content/bindings.txt` is gitignored (written on
first PAD run).
**BT-side changes PadRIO forced:** (1) both keyboard input bridges (mech4.cpp write
of mapper attrs; mechmppr.cpp `BT_KEY_BRIDGE`) now stand down when a RIO device
exists (`bridge_controls->rioPointer != 0`) — they clobbered the engine push every
frame; M mode-cycle / X recenter conveniences stay live. (2) **the mapper attribute
chain was off by one** — see [[reconstruction-gotchas]] §11 (short-parent-chain
positional trap): the `.CTL` stick mapping wrote `throttlePosition`. Fixed with a
pad slot + binary-locked enum. This was a latent REAL-POD bug (Phase 8 would have
hit it).
Verified live: `L4CONTROLS=PAD` → throttle lever ramps (rate axis, sticky), stick
deflects (turnDemand=1 + the authentic speed-vs-turn clamp: 61.5→22.0 u/s while
turning), mech drives across the map, XInput pad detected + 3 s re-probe, bindings
parse (59 key buttons/8 axes/12 pad buttons/5 pad axes). Keyboard fallback
regression-verified headless (`BT_FORCE_THROTTLE=1` walks). New diagnostics:
`BT_CTRLMAP_LOG=1` (streamed-mapping dump), `BT_STICK_LOG=1` (RIO stick push trace).
- **Phase 3a (PlasmaScreen, `L4PLASMA=SCREEN`) — DONE 2026-07-14.** `L4PLASMASCREEN.*`
copied verbatim; `L4GREND.cpp` selector diff applied (BT's only divergence there is a
comment at line 440 — no collision). Renders BT's 128×32 plasma into a desktop window.
Verified: a window titled "Plasma Display" opens and the game ticks normally beside it
(`L4GAUGE=640x480x16 L4PLASMA=SCREEN`). Note: the `L4GAUGE` gauge path binds a listen
socket at startup → a first-run Windows Firewall prompt (expected; unrelated to plasma).
- **Phase 3b (single-window MFD-split cockpit, `L4MFDSPLIT=1`) — DEFERRED 2026-07-14.**
Two blockers make this the wrong thing to land now:
1. **No BT cockpit-layout geometry.** RP412's `L4MFDVIEW` button-bank anchors/addresses
come from vRIO's `CockpitLayout` for the Red Planet pod; vRIO has **zero** BattleTech
data. The BT pod's panel geometry has no reference (needs pod photos / `l4gauge.cfg`
mining / Nick) — the clickable lamp-lit banks can't be authentically placed.
2. **BT's MFD compositing is an unfinished upstream beachhead.** Per `docs/GAUGE_COMPOSITE.md`,
only the radar/secondary surface composites on a dev box; the five mono MFDs don't
render yet (port-name mismatch, Step 2). RP412's split panes would be empty until that
upstream work lands.
3. **Merge is HIGH-risk** (quantified above): BT's 521-line L4VB16 divergence (dev-gauge
docking + a 476-line block) overlaps RP412's 402-line split rewrite in the ctor and
`Update` regions — a true hand-merge, not a drop-in. The 3-way diffs are staged in the
scratchpad (`rp412-L4VB16.diff`, `bt-L4VB16.diff`).
Prerequisite order when resumed: finish BT's MFD dev-composite (GAUGE_COMPOSITE.md Steps
23) → obtain BT cockpit geometry → then hand-merge `MFDSplitView`. Until then the game
ships single-window with the dev-gauge dock (`BT_DEV_GAUGES`) as its cockpit composite.
- **Phase 4 (NetTransport seam + Steam transport) — DONE 2026-07-14.** `L4NET.CPP`
taken from RP412 post-seam (all ~24 Winsock call sites now route through
`NetTransport_Get()`); BT's 3 `BT_NET_TRACE` blocks re-sited onto their code anchors
(the drop path, the point-to-point send before `NetTransport_Get()->Send`, the
pad-buffer receive) — they read message/packet metadata, not sockets, so no
collision. `L4NETTRANSPORT.*` (seam + `WinsockNetTransport` default) and
`L4STEAMTRANSPORT.*` (gate renamed `BT412_STEAM`, self-test env `BT412STEAMSELFTEST`)
copied. `extern/steamworks_sdk_164/` vendored (headers + win32 redistributable_bin).
One engine global ported from RP412's APPMGR: `gConsoleLossEndsMission` (default
False = arcade re-listen; the marshaled/lobby path sets it in Phase 5/6).
CMake: seam always compiled; `option(BT412_STEAM)` (default OFF) gates the transport
define + `sdk/public` include + `steam_api.lib` + post-build `steam_api.dll` copy.
`steam_appid.txt` gitignored (Spacewar 480 by hand until a real AppID).
Verified: (1) default TCP build — loopback MP through the seam is byte-for-byte the
old behavior (console connects, egg msgID-3 chunks delivered, "All connections
completed!", both instances tick, `net-tx`/`net-rx` traces fire through
`NetTransport_Get()`); (2) `BT412_STEAM=ON` config + build (compile-check of the
Steam transport TU against the vendored SDK; a live Steam session needs the Phase 6
lobby + 3 machines).
- **Phase 5 (front end) — FOUNDATION DONE 2026-07-14; marshal/menu/results IN PROGRESS.**
Landed the local **mission-egg builder** — the core "single-player without the
operator console" capability:
- `game/reconstructed/btl4fe.{hpp,cpp}`: catalogs (8 maps, 8 mechs + variants,
colors), a `BTFeMission` config struct, and `BTFeMission_WriteEgg` that emits the
exact console egg format (verified against `content/MP.EGG`): `[mission]`,
`[ordinals]` + the 4 static rank-place plasma bitmaps, `[pilots]`, per-pilot
loadout sections, `[largebitmap]`/`[smallbitmap]` with **GDI-rendered** 128×32 /
64×16 pilot-name plasma bitmaps, and the role models.
- Two engine setters ported from RP412's L4APP.H (`SetEggNotationFileName`,
`SetNetworkCommonFlatAddress`) so the front end feeds the standard `-egg` path.
- `game/btl4main.cpp` WinMain: **front-end mode** — no `-egg` and no `-net` builds
`frontend.egg` locally and points the engine at it (`BTFrontEnd_Run`). `-egg`/`-net`
runs are untouched.
Verified: zero-argument launch → `[frontend] built frontend.egg` → mission loads and
runs (ticks, gait, targeting); the generated egg is structurally identical to MP.EGG
(32×32/16×16 name bitmaps with real glyph pixels, all sections present).
**Remaining in Phase 5** (each substantial, deferred): interactive on-screen catalog
menu (RP412 `RPL4FE` is 1536 lines of render-loop UI); the in-process **LocalConsole
marshal** thread (mission clock + `StopMissionMessage` at expiry + single-binary
menu→race→results→menu loop); and the **score-intake + results screen**, which is
genuinely NEW for BT — BT has no working mission-end flow (mission-review msgID 0x18
is a stub, `scoreAward=0`); v1 should collect kills/deaths from the live Comm-MFD
`pilotList` tally at stop (see [[gauges-hud]], [[multiplayer]]).
- Phases 67: Steam lobby (`btl4lobby`) + packaging remain.
## The seams (what plugs in where) [T0 unless noted]
- **Input**: `LBE4ControlsManager` PUSH model; `L4CTRL.cpp` parses `L4CONTROLS` tokens;
RP412 adds token `PAD``new PadRIO()` (a `RIOBase` implementation from
XInput+keyboard), and the stock RIO mapper runs against it unchanged. BT's mapper
selection: `btl4app.cpp MakeViewpointEntity` switch on
`controls->primaryControlType` (+0xc98) → `MechRIOMapper` for PrimaryRIO(4).
- **Plasma**: BT's plasma is identical hardware to RP's (128×32, `L4PLASMA.h`);
RP412's `PlasmaScreen` renders the same `Video8BitBuffered` surface into a desktop
window, selected in `L4GREND.cpp` on `L4PLASMA=SCREEN`.
- **Displays**: BT pod = main 800×600 3D view + 640×480 radar + five mono MFDs on one
spanned 1280×480 surface (+ plasma). All cockpit surfaces are bit-plane masks over
one shared `SVGA16` pixelBuffer (see [[gauges-hud]], [[pod-hardware]]). RP412's
`MFDSplitView` (`L4MFDVIEW.*` + `L4VB16.cpp` split mode, `L4MFDSPLIT=1`) composes
child panes on a fixed 1920×1080 canvas; 3D presents into the viewscreen pane via
`Present(hDestWindowOverride)`.
- **Networking**: arcade stack unchanged (see [[multiplayer]]); RP412 moved the wire
behind `NetTransport` (`L4NETTRANSPORT.*`), default `WinsockNetTransport`, Steam
implementation `L4STEAMTRANSPORT.*` (`ISteamNetworkingSockets`, FakeIP + SDR,
degrade-to-TCP on any failure). Addresses stay SOCKADDR_IN-shaped so the egg
`[pilots] ip[:port]` roster works over both wires.
- **Session**: console protocol (egg msgID 3, 1040-byte chunks + ACK; RunMission
msgID 5 ×2; console must stay connected) — `tools/btconsole.py` is the working
feeder and the spec for the in-process LocalConsole marshal (Phase 5).
## Known merge risks (from the 3-way BT411 / RP411-baseline / RP412 comparison)
**Quantified 2026-07-14 [T1]** — BT-vs-RP411-baseline divergence measured with
`diff --strip-trailing-cr` (baseline extracted from RP412 history, `git show
3b8b729:MUNGA_L4/<f>`); RP412-side change sizes from `git diff 3b8b729..HEAD`:
| File | BT vs baseline | RP412 change | Treatment |
|------|----------------|--------------|-----------|
| `L4RIO.h` | identical | 125 lines (RIOBase split) | RP412 verbatim |
| `L4RIO.cpp` | identical | 0 | nothing to do |
| `L4CTRL.h`, `L4NET.H`, `L4PLASMA.*`, `L4KEYBD.h` | identical | small | RP412 verbatim |
| `L4CTRL.cpp` | 29 diff lines | 17 (PadRIO hook) | apply RP412 diff |
| `L4GREND.cpp` | 6 diff lines | 15 (PlasmaScreen selector) | apply RP412 diff |
| `L4VIDEO.cpp` | **2725** diff lines (BT render work) | only 14 | apply RP412 diff — small despite BT divergence |
| `L4VIDEO.h` | 29 diff lines | 7 | apply RP412 diff |
| `L4NET.CPP` | 23 diff lines (BT_NET_TRACE ×5) | 345 (seam refactor) | **MEDIUM** — apply seam refactor, re-site traces |
| `L4VB16.cpp` | **514** diff lines (dev-gauge dock) | 350 (split view) | **HIGH** — true hand-merge |
| `L4VB16.h` | 14 diff lines | 32 | hand-merge with L4VB16.cpp |
- **`L4VB16.cpp` — HIGH.** RP412 rebuilt ~38464046 (splitViews/canvas/present); BT
rebuilt the same window-management region for dev-gauge docking (`BT_DEV_GAUGES`,
`BT_DEV_GAUGES_WINDOW`, `BT_DEV_GAUGES_DOCK`, `BT_GAUGE_SCALE`, `BT_GAUGE_SEC_ROT`,
`BT_ALL` — 15 sites). Hand-merge; write the 3-way diff notes HERE before editing.
- **`L4NET.CPP` — MEDIUM.** RP412 rerouted all ~2226 Winsock call sites through
`NetTransport_Get()`; BT's 5 `BT_NET_TRACE` diagnostic blocks sit exactly on those
send/receive sites and must be re-sited onto the seam calls.
- **CMake**: engine sources are explicit per-file lines (no globs) — every ported
`.cpp` needs its own `add_library` line or it silently doesn't compile. New deps:
`xinput9_1_0.lib` (PadRIO), C++/WinRT (KeyLight, per-file C++17), Steamworks SDK +
`steam_api.lib` behind `option(BT412_STEAM)`.
- **No `/Zp1` in BT's build** (RP's engine builds `/Zp1`): RP412's packing-mismatch
workarounds (`WINDOWS_IGNORE_PACKING_MISMATCH`, KeyLight default-packing carve-out,
`pack(push,8)` around Steam headers) are no-ops here — keep them for verbatim-ness,
but eyeball any copied code for /Zp1-layout assumptions.
## Key Relationships
- Uses: [[pod-hardware]] · [[multiplayer]] · [[gauges-hud]] · [[build-and-run]]
- Feeds: [[open-questions]] (steamification investigate items)