Files
BT412/context/steamification.md
T
CydandClaude Fable 5 335396912e Build: the BT412 compile gate -- one tree builds the pod game and the Steam game
option(BT412 ... ON) gates the steamification layer for the merge-back into
BT411, so the unified repo serves both fronts:

- Default (BT412=ON): pod/dev game + PadRIO / plasma / front end / lobby --
  all runtime-gated, so a -egg/-net launch still behaves as 4.11.
- Pod-minimal (-DBT412=OFF, build-pod/): compiles OUT L4PADRIO, L4PADBINDINGS,
  L4KEYLIGHT, L4PLASMASCREEN, L4STEAMTRANSPORT, btl4fe, btl4console, btl4lobby.
  Seam sites carry #ifdef BT412: L4CTRL.cpp PAD token (log + ignore), L4GREND.cpp
  L4PLASMA=SCREEN (log + ignore), L4VB16.cpp (inert PadRIO stub -- call sites
  identical in both flavors), btl4main.cpp (front-end/marshal/lobby blocks out;
  zero-arg launch = 4.11 behavior).
- The NetTransport seam ALWAYS compiles -- it is the wire for both flavors
  (WinsockNetTransport, verified byte-identical to the pre-seam arcade path).
- BT412_STEAM now requires BT412 (configure-time FATAL_ERROR; negative-tested).

Verified: OFF -- clean v143 link, solo DEV.EGG mission runs (31 subsystems
tick, gait, targeting). ON -- zero-arg launch engages the front-end menu.
STEAM -- links clean, ships steam_api.dll.

Also: README/context docs for the flavors + the merge-back; corrected the stale
Phase 3b DEFERRED note (the single-window cockpit landed 07-16) and the README's
leftover 0xBD3 valve-gate claim (it is a damage/explosion hub).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 15:45:45 -05:00

293 lines
21 KiB
Markdown
Raw 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]:** each checkout talks ONLY to its own gitea remote — `C:\VWE\BT412`
`origin` = `VWE/BT412.git`, `C:\VWE\BT411` `origin` = `VWE/BT411.git`. Cross-repo sync
is **local-path only** (`git fetch C:/VWE/BT412 master` from BT411 and vice versa) —
never a cross remote. Forked at BT411 `4e72f0c` (2026-07-14); **merged back 2026-07-17**
(see the merge-back section below) — BT411 is now the unified repo carrying both lines
behind the `BT412` compile gate.
## Merge-back into BT411 + the `BT412` compile gate (2026-07-17)
The whole steamification line was merged **back into BT411** so one repo serves both
fronts (pod work + internet MP). Mechanics [T2]:
- BT411's head was exactly `abed41e` — the catch-up point BT412 had already merged —
so the merge was a clean **fast-forward** of BT411 onto BT412's line via a
local-path fetch (no gitea remote touched from either side). BT411's dirty
`run/run.cmd` bench edit (pod `-net 1501` restart loop) was stashed and restored.
- **Compile gate:** CMake `option(BT412 ... ON)` + compile definition `BT412` on all
three targets. Gated OUT under `-DBT412=OFF` (the pod-minimal flavor, `build-pod/`):
`L4PADRIO`, `L4PADBINDINGS`, `L4KEYLIGHT`, `L4PLASMASCREEN`, `L4STEAMTRANSPORT`,
`btl4fe`, `btl4console`, `btl4lobby`, plus `#ifdef BT412` seams in `L4CTRL.cpp`
(PAD token), `L4GREND.cpp` (`L4PLASMA=SCREEN`), `L4VB16.cpp` (inert `PadRIO` stub),
`btl4main.cpp` (front-end/marshal/lobby blocks; zero-arg = 4.11 behavior). The
**NetTransport seam always compiles** (the wire for both flavors). `BT412_STEAM`
requires `BT412` (configure-time `FATAL_ERROR`, negative-tested). All three flavors
verified: OFF solo mission un-regressed (31 subsystems, gait, targeting); ON zero-arg
front end engages; STEAM links + ships `steam_api.dll`. Details:
[[build-and-run]] §Build flavors.
- `handoff/padrio/` (the interim BT411 transfer copy, commit `2e475f4`) was retired —
superseded by the merge itself (the real modules now live in BT411; a drifting
duplicate in the same tree would be a hazard).
## BT411 catch-up merge (2026-07-14)
Merged BT411's post-fork line (`4e72f0c..abed41e`, 91 commits) into BT412 via a
**local-path** merge — brought the whole **audio-fidelity system** (engine `AUD*` +
`L4AUD*` + `audiopresets.cpp` + ~600 content `.wav`s, `docs/AUDIO_FIDELITY.md`),
missiles/rear-fire/HUD/gyro/gait tasks (#6668), `FOGDAY.EGG`, and refreshed context
docs. Only **5 files** overlapped the steamification; resolutions:
- `.gitignore`, `CMakeLists.txt`, `mech4.cpp` — trivial / auto-merged (the
`deviceOwnsInput` gating survived).
- `L4NET.CPP` — took BT411's task-#50 fix (don't close the game listener on console
loss) over the seam's adaptation of the old buggy close; the point-to-point send
stays on `NetTransport_Get()->Connect()`.
- `L4NETTRANSPORT.cpp` — folded BT411's **TCP_NODELAY** latency fix into
`WinsockNetTransport::Connect` (the seam already had BT411's retry-while-refused +
non-blocking). Accept-side game-socket NODELAY stayed at the L4NET call site.
- `mechmppr.cpp` — combined the `device_owns_input` gating with BT411's task-#68
look-behind ('V' rear-view), gating the `lookBehind` write too (a live RIO device
feeds it through its mapped button).
Verified after merge: clean build; solo front-end mode; loopback MP through the seam
(mesh completes, both tick, replication, no NODELAY warnings). [T2]
## 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`) — DONE 2026-07-16**
(initially deferred 2026-07-14; the blockers fell). Landed across commits `0c964c0`
`59c454b` + `2b238a8`: the 1920×1080 single-window cockpit arrangement (RP412 layout)
via the `L4VB16.cpp` hand-merge, on-screen clickable lamp-lit RIO button banks framing
the MFDs (4-above/4-below strips — BT-derived placement, not vRIO geometry, which has
no BT data), green MFD phosphor tint, button-lamp bitfield decode (subdued idle
baseline), `L4CONTROLS=PAD` as the cockpit default (before the DEV keyboard default),
the gauge NULL-source guard extended to the split path, and the backtick (`) toggle
for the secondary displays. Mouse presses inject into PadRIO as screen buttons
(`BTPadRIOScreenButton`, WndProc → `L4VB16.cpp`). [T2]
- **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) — DONE 2026-07-16 (5a5d).** Interactive menu + local egg builder
+ in-process marshal + results + single-binary loop all landed and boot-verified.
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).
**Completed since (2026-07-16):**
- Interactive on-screen catalog **menu** (green-on-black GDI, `btl4fe.cpp`): map / mech /
color / time / weather / length groups + pilot-name edit, framed LAUNCH (and, under
Steam, HOST / JOIN) buttons. Zero-arg launch shows it; a chosen LAUNCH builds the egg.
- In-process **LocalConsole marshal** (`btl4console.{hpp,cpp}`): NOT a thread — it runs on
the engine's per-frame hook (`gPerFrameHook`, added to `engine/MUNGA/APPMGR.*`, called on
the game thread in `RunMissions`), so its `application->Dispatch(StopMissionMessage)` is
safe. Owns the mission clock and stops it at the chosen `length=`; snapshots
per-pilot kills/deaths at the stop (`BTResolveRosterPilot`/`BTPilotKills`/`BTPilotDeaths`).
- **Score intake + results screen**: BT's own kills/deaths model (no `gConsoleScoreSink` —
that is RP-layer; BT has no working mission-end wire flow, msgID 0x18 stays a stub). A
GDI "MISSION COMPLETE" scoreboard (`BTFrontEnd_ShowResults`) reads the marshal snapshot
(`BTLocalConsole_GetResult`/`GetResultName`).
- **Single-binary loop** = RELAUNCH per mission (`CreateProcessW` after the marshal stop),
NOT an in-process `for(;;)` — in-process re-init crashes on the stale reconstructed
`gBT*` globals (cdb-traced to `BTL4VideoRenderer::MakeEntityRenderables` →
`gBTTerrainEntity`). Verified: 3 distinct PIDs across the menu→mission→results→menu cycle.
- WinMain launch-mode branch (`BTFrontEnd_LastLaunchMode()`): 0 solo, 1 lobby host, 2
lobby member (see Phase 6).
- **Phase 6 (Steam lobby) — CODE LANDED 2026-07-16; live 3-machine test DEFERRED.**
`game/reconstructed/btl4lobby.{hpp,cpp}` (ported from RP412 `RPL4LOBBY`) builds both gates
— stubs without `BT412_STEAM`, full `ISteamMatchmaking` room under it (both configs
link clean). What works: the room screen; member data (`ip`/`cp`/`gp`/`nm`/`vh`/`cl`/`bd` =
FakeIP + fake console/game ports + persona + mech/color/badge); the nonced `go` launch
roster → `SteamNetTransport_RegisterPeer` for every peer; `Push`/`PullMissionResults` over
lobby data (`res` key) rebuilding the shared score sheet via `BTLocalConsole_ClearResults`/
`InjectResult`. The menu offers HOST/JOIN when `BTLobby_Available()` (Steam transport up,
installed by WinMain on `BT412STEAM` env). A host launch bakes every member into the egg's
`[pilots]` (mesh `FakeIP:1502` addresses, round-robin drop zones) via
`BTFrontEnd_SetHostedPilots`; a member enters as a network pod on `:1501`
(`SetNetworkCommonFlatAddress` + `gConsoleLossEndsMission=True`).
**Host→member wire egg-feed marshal — DONE (2026-07-17):**
`BTLocalConsole_InstallNetworkMission` (btl4console.cpp) makes the owner play the arcade console
in-process over the NetTransport seam (Winsock TCP or Steam SDR): connect to each member's
console channel, feed the chunked egg (`NetworkManager::ReceiveEggFileMessage`, \n→NUL wire image
like `tools/btconsole.py`) retry-until-ACK, poll state (`StateQueryMessage`), and when the whole
mesh is staged at `WaitingForLaunch` dispatch `RunMission` to all + locally at once; `StopMission`
at expiry. The owner's own pod is fed via `L4NetworkManager::FeedLocalEgg`. Scores are BT's
kills/deaths snapshotted from the (meshed) roster at the stop — NO EndMission wire intake (BT's
mission-end flow is a stub; the mesh already put every pilot in the host's roster).
- **Engine change (ported from RP412):** `gConsoleMarshalsLaunch` (APPMGR.h/.cpp) + the
`!gConsoleMarshalsLaunch &&` guard in APP.cpp's WaitingForLaunch self-launch — holds the owner
(which has no console connection to itself) at WaitingForLaunch so the in-process console
launches the mesh together instead of the owner self-launching early. Default False = stock
behavior; solo boot un-regressed.
- **WinMain host path** honors `BT412HOSTPODS` (+`BT412HOSTPORT`) for the lobby host AND a classic
LAN host: `SetNetworkCommonFlatAddress` + `InstallNetworkMission(secs, "frontend.egg", pods)`,
falling back to a solo marshal if no member is reachable.
- **Verified (loopback):** two `btl4.exe -net` pods fed by `tools/btconsole.py` reach
"All connections completed!" and run after the engine change — the exact protocol + launch
handshake the marshal uses. Both gates build+link; the Release dist boots and the Steam
transport comes up (FakeIP allocated).
- **Remaining:** the live multi-machine Steam mission (FakeIP mesh + pilot-slot matching across
machines) — untestable here (needs Steam + ≥2 accounts/machines). Procedure:
`docs/STEAM-3-MACHINE-TEST.md`. The lobby object does NOT survive the per-mission relaunch
(single-binary limitation — everyone returns to a fresh menu, not the room).
- **Phase 7 (packaging) — IN PROGRESS.** `pack-dist.ps1` landed (assembles a runnable
Steam test dist; wraps the zip in a single `BT412/` folder). KeyLight landed with
Phase 2 (`L4KEYLIGHT`, gate `BT412KEYLIGHT`). Remaining: release proper (real Steam
AppID, store assets, depot config) + the Phase 6 live 3-machine test.
- **Merge-back into BT411 — DONE 2026-07-17** (see the merge-back section above): one
repo, both lines, behind the `BT412` compile gate.
## 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)