Files
BT412/docs/BT412-ROADMAP.md
T
CydandClaude Fable 5 5ebb9a5906 Net: NetTransport seam + Steam transport (Workstream C.1)
The wire moves behind NetTransport (L4NETTRANSPORT): L4NET.CPP taken
from RP412 post-seam -- all ~24 Winsock call sites route through
NetTransport_Get() -- with BT's 3 BT_NET_TRACE blocks re-sited onto
their code anchors (they read message/packet metadata, not sockets, so
no collision). Default WinsockNetTransport = the arcade/LAN TCP wire.

SteamNetTransport (L4STEAMTRANSPORT, ISteamNetworkingSockets + FakeIP/
SDR) compiles under option(BT412_STEAM) (default OFF); Steamworks SDK
1.64 vendored at extern/steamworks_sdk_164. steam_appid.txt gitignored
(Spacewar 480 by hand until a real AppID). Ported gConsoleLossEndsMission
from RP412's APPMGR (default False = arcade re-listen).

Verified: default TCP build passes full loopback MP through the seam
(console -> egg msgID-3 chunks -> mesh complete -> both instances tick,
net-tx/net-rx traces fire through NetTransport_Get()); BT412_STEAM=ON
compiles + links against the SDK + boots solo. Live Steam session
deferred to Phase 6. (Phase 4 of docs/BT412-ROADMAP.md)

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

194 lines
12 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.
# BT 4.12 Roadmap — Steam + Internet Multiplayer
Working plan for turning the 4.11 pod port into a consumer Steam title, following the
recipe proven end-to-end by **RP412** (Red Planet 4.11 → 4.12; see
`C:\vwe\RP412\docs\RP412-ROADMAP.md` and its git history — the reference implementation
for every workstream here). Status: plan authored 2026-07-14. Items marked
**[investigate]** need archaeology before they become concrete tasks.
## Where 4.11 stands
- **Game**: `btl4.exe`, 32-bit Win32, DirectX 9, CMake + VS2019 BuildTools (v142),
DXSDK June 2010, `/FORCE:MULTIPLE`. Full single-player combat loop
(drive → animate → target → fire → damage → destroy → respawn) on all 8 maps.
- **Engine**: the same WinTesla MUNGA/MUNGA_L4 engine as Red Planet — the MUNGA core is
file-identical to RP's; BT's L4 layer adds bgfload/image + BT render work
(L4D3D/L4VIDEO) and dev-gauge docking in L4VB16.
- **Networking**: same arcade stack — `MUNGA/NETWORK|HOSTMGR|INTEREST`, WinSock2 TCP in
`MUNGA_L4/L4NET.CPP`. Two-instance loopback MP verified including cross-pod combat,
kills, respawn, gait, and weapon visuals. Console feeder: `tools/btconsole.py`
(egg msgID 3 in 1040-byte chunks, RunMission msgID 5 ×2, must stay connected).
- **Input/output**: pod hardware is a serial **RIO** board (stick X/Y, throttle, pedals,
fire/config buttons 0x40/0x45/0x46/0x47 + MFD aux buttons) — `L4RIO`/`L4SERIAL`. Dev
box uses keyboard debug controls (WASD etc.). Displays: **7 monitors** (main 800×600
3D view, radar 640×480, five mono MFDs as one spanned 1280×480 surface) + the
**128×32 plasma** (identical hardware to RP's; `L4PLASMA`). `BT_DEV_GAUGES` docks the
gauge strip into the main window (dev mode).
- **Session control**: missions are **egg** files (NotationFile); the operator console
configures and launches over TCP 1501. No in-game menu; no mission-end flow (mission
review msgID 0x18 is a stub, `scoreAward=0`); live kill/death tally exists on the
Comm MFD `pilotList`.
## Defaults (decided at planning, change only deliberately)
- Gates mirror RP412 with a BT412 prefix: `BT412_STEAM`, `BT412KEYLIGHT`, env
`BT412STEAM=1`. Exe stays `btl4.exe`; CMake project is `bt412`.
- AppID: Spacewar **480** via hand-created `steam_appid.txt` (gitignored, never packed)
until a real AppID exists.
- Mech catalog: base 8 (`avatar bhk1 loki madcat owens sunder thor vulture`) + variants
(`ava1 lok1 lok2 mad1 mad2 own1 snd1 thr1 vul1 blkhawk`); trim any that fail to load.
- Results v1: read the live Comm-MFD `pilotList` tally (kills + observed deaths) at
stop; EndMission wire scores when remote-pod marshaling needs them.
- `L4VB16.cpp` end-state: `MFDSplitView` is the successor to the dev-gauge dock; keep
`BT_DEV_GAUGES` gated until parity, then retire.
- **This repo never touches the BT411 remote.** `origin` = `VWE/BT412.git` only.
## Phase 1 — Toolchain: VS2022 (v143)
RP412's step 1: unblocks everything, no design risk.
- Configure `-G "Visual Studio 17 2022" -A Win32` (drop the 2019
`CMAKE_GENERATOR_INSTANCE`). **Stay Win32** — DXSDK June 2010 x86 libs are the hard
constraint. BT's CMake has no `/Zp1`, so RP's packing-mismatch defines don't apply.
- Apply RP412 BUILD.md §3 source fixes as v143 errors demand: `MUNGA/TIME.h` Time
copy-ctor; `MUNGA/NETWORK.h` inline `operator==`; `L4DINPUT.cpp` DIEnum callback
renames; `CAMMGR.cpp` `std::ios::in`; `legacy_stdio_definitions.lib` for dxerr.
- [investigate] RP412's `L4D3D.cpp` NULL-texture AddRef guard — BT's L4D3D is heavily
rebuilt; port the guard if the crash pattern survives.
- **Verify**: v143 build boots DEV.EGG; two-instance loopback + btconsole.py — drive,
fire, cross-pod kill, respawn.
## Phase 2 — Input layer: RIOBase / PadRIO (Workstream A.1)
Rule for every touched engine file: 3-way check (BT copy vs RP411 baseline vs RP412);
identical-to-baseline → take RP412's file verbatim, diverged → hand-apply the diff.
- Port `L4RIO.h/.cpp` (RIOBase split), new `L4PADRIO.*`, `L4PADBINDINGS.*`; the
`L4CTRL.cpp` hook (`new PadRIO()` for token `PAD`). BT never customized these —
expected clean.
- CMake: explicit source lines (no globs); link `xinput9_1_0.lib`.
- Confirm `L4CONTROLS=PAD` yields `primaryControlType=PrimaryRIO` so the stock
`MechRIOMapper` engages unchanged (`btl4app.cpp MakeViewpointEntity`) — RP precedent:
stock VTVRIOMapper ran against PadRIO unmodified.
- Author the default BT `bindings.txt` (vRIO grammar): stick/throttle/pedal axes, fire
buttons, MFD aux banks.
- Fix desktop-unreachable controls: secondary-MFD view cycling (mapper cases
0x13d/0x13e are dead DOS F3/F4 codes). Weapon regrouping comes alive via the RIO
path for free.
- **Verify**: full mech drive+fight from pad+keyboard, no COM ports; pad hot-plug;
`L4CONTROLS=RIO` pod regression still initializes.
## Phase 3 — Cockpit on the desktop (Workstream A.2)
- **3a PlasmaScreen** (low risk, land first): `L4PLASMASCREEN.*` verbatim + the
`L4GREND.cpp` selector diff. `L4PLASMA=SCREEN`.
- **3b Single-window MFD split** (`L4MFDSPLIT=1`) — **the HIGH-risk hand-merge**:
RP412 rebuilt `L4VB16.cpp` ~38464046 (splitViews, fixed 1920×1080 canvas,
3D Present into the viewscreen pane via `hDestWindowOverride`); BT rebuilt the same
region for dev-gauge docking (`BT_DEV_GAUGES*`, `BT_GAUGE_*`, `BT_ALL` — 15 sites).
Write 3-way diff notes into `context/steamification.md` **before** editing.
`L4MFDVIEW.*` copied as skeleton; replace RP's pod tables with BT's display set
(viewscreen, radar, five mono MFDs sliced from the 1280×480 span, plasma glass).
- **[investigate]** BT cockpit button-bank geometry: no vRIO `CockpitLayout` exists for
the BT pod. Sources: `content/GAUGE/l4gauge.cfg`, pod photos, ask Nick. Land
panes-without-buttons first; clicks inject `PadRIO::SetScreenButton`, lamps light
from `GetLampState` once geometry lands.
- **Verify**: one cockpit window, 3D presents into its pane (smoke BT's rebuilt L4D3D
present path EARLY), live MFDs/radar/plasma; pod multi-surface path (`L4GAUGE`)
unbroken.
> **Status (2026-07-14):** 3a landed. **3b DEFERRED** — blocked on (1) the missing BT
> cockpit geometry and (2) BT's own MFD dev-composite being an unfinished upstream
> beachhead (`docs/GAUGE_COMPOSITE.md`: only radar/secondary composites; the five mono
> MFDs don't render yet). Prereq order when resumed: finish the MFD dev-composite →
> obtain BT cockpit geometry → hand-merge `MFDSplitView`. Details in
> `context/steamification.md`. Phase 4 does not depend on 3b.
## Phase 4 — NetTransport seam + Steam transport (Workstream C.1)
- `L4NETTRANSPORT.*` verbatim (abstract seam + `WinsockNetTransport` default;
SOCKADDR_IN-shaped addresses keep egg `[pilots] ip[:port]` working in both worlds).
- `L4NET.CPP`: apply RP412's refactor (~2226 Winsock sites → `NetTransport_Get()`);
**re-site the 5 `BT_NET_TRACE` blocks** onto the seam calls.
- `L4STEAMTRANSPORT.*` near-verbatim, gate `BT412_STEAM`; vendor
`extern/steamworks_sdk_164/` (copy from RP412).
- CMake: seam always compiled; `option(BT412_STEAM)` gates the transport TU + define +
SDK include + `steam_api.lib` + post-build dll copy.
- **Verify**: (1) loopback + btconsole.py full MP parity through the seam (TCP);
(2) Steam smoke — transport installs, FakeIP allocated, graceful TCP degrade when
Steam is absent; (3) real Steam-sockets session (may defer to Phase 6).
> **Status (2026-07-14): DONE.** Seam landed; `L4NET.CPP` from RP412 post-seam with
> BT's 3 `BT_NET_TRACE` blocks re-sited; `gConsoleLossEndsMission` engine global
> ported. `option(BT412_STEAM)` gates the Steam TU + SDK. Verified: default TCP build
> passes full loopback MP through the seam (console → egg → mesh → both tick, traces
> fire through `NetTransport_Get()`); `BT412_STEAM=ON` compiles + links against the
> vendored SDK + boots solo. Live Steam session deferred to Phase 6 (needs the lobby +
> 3 machines). Details in `context/steamification.md`.
## Phase 5 — Front end + LocalConsole marshal (Workstream B — bulk of new code)
- **5a `game/reconstructed/btl4fe.cpp`** (skeleton: RP412 `RP_L4/RPL4FE.cpp`): no
`-egg`/`-net` → setup menu. Catalog: 8 maps
(`cavern grass rav polar3 polar4 arena1 arena2 dbase`), mechs per defaults,
scenario=freeforall, time/weather/temperature/length, pilot name.
`BTMission::ToEggString` against the verified egg format (`content/MP.EGG`,
`DEV.EGG`) incl. GDI-rendered 128×32 `[ordinals]`/name plasma bitmaps → the standard
egg-load path.
- **5b `game/reconstructed/btl4console.cpp`** (skeleton: `RPL4CONSOLE.cpp`): in-process
console on its own thread over NetTransport — egg chunks + ACK, RunMission ×2
(mirror btconsole.py), stays connected (fixes the console-loss listener bug for
solo), owns the mission clock, StopMission at `length=` expiry, launcher-role loop,
`InstallNetworkRace` marshal for remote pods.
- **5c Score intake — genuinely new for BT** (no mission-end flow exists): a
`gConsoleScoreSink` equivalent through `BTPlayer`; v1 results screen = kills/deaths
per pilot from the Comm-MFD `pilotList` tally at stop.
- **5d `game/btl4main.cpp` WinMain wiring** (skeleton: RP412 `RPL4.CPP` diff):
front-end-mode detection, single-binary `for(;;)` loop (results → menu → launch),
host/member branch, self-documenting `environ.ini` parsing, minidump crash filter,
`SC_KEYMENU` swallow, `BT412STEAM` env → `SteamNetTransport_Install()`.
- **Verify**: no-args boot → menu → LAUNCH → mission → stop at set length → results →
fresh menu; `-egg DEV.EGG` stays unmarshaled (dev shortcut); btconsole.py external
feed still works; marshaled 2-instance loopback collects both score lines.
## Phase 6 — Steam lobby (Workstream C.2)
- **`game/reconstructed/btl4lobby.cpp`** (skeleton: `RPL4LOBBY.cpp` — mechanics
generic): lobby owner = console; member data = FakeIP + fake console/game ports +
persona + loadout (**mech**/color/badge); nonced launch roster → `RegisterPeer` all →
owner hosts via egg+marshal, members join as network pods; Push/PullRaceResults
distribute the score sheet. FE gains HOST/JOIN screens.
- Author `docs/STEAM-3-MACHINE-TEST.md` for BT (adapt RP412's; keep its gotchas:
disable Steam Input on 480, one Steam account per machine, abort key).
- **Verify**: 3-machine Steam test — lobby → mesh → marshaled timed mission →
kills/deaths → stop → results everywhere → rematch. Known MP fidelity gaps (peer
warp spheres, missile clusters, PNAME/PLACE billboards) do not block.
## Phase 7 — Polish, packaging, release
- KeyLight RGB mirror: `L4KEYLIGHT.*` verbatim (C++/WinRT TU — per-file C++17 props),
gate `BT412KEYLIGHT`.
- `pack-dist.ps1` adapted: `btl4.exe`+pdb, `BTL4.RES`, `BTDPL.INI`,
`VIDEO/ GAUGE/ AUDIO/`, eggs, `steam_api.dll`, OpenAL runtime, generated commented
`environ.ini`, `start-windowed.bat`, README.txt, refuse-while-running.
- `tools/two-pod-test.ps1` adapted (feeder = btconsole.py). Version banner `4.12.x`;
tag the release on gitea.
- **Verify**: dist zip runs on a clean machine (no DXSDK); solo loop; packaged
2-machine Steam session.
## Copy matrix (from RP412)
| Treatment | Files |
|-----------|-------|
| **Verbatim** (after 3-way check) | `L4PADRIO.*`, `L4PADBINDINGS.*`, `L4PLASMASCREEN.*`, `L4NETTRANSPORT.*`, `L4STEAMTRANSPORT.*` (gate rename), `L4KEYLIGHT.*` (gate rename), `extern/steamworks_sdk_164/` |
| **Diff-apply / hand-merge** | `L4RIO.*`, `L4CTRL.cpp`, `L4GREND.cpp` (expected clean); `L4NET.CPP` (medium — BT_NET_TRACE); `L4VB16.cpp` (**high** — dev-gauge collision); `L4VIDEO.*`; v143 source fixes |
| **Adapt** (RP skeleton, BT tables/fields) | `L4MFDVIEW.*`, `btl4lobby`, `pack-dist.ps1`, `two-pod-test.ps1`, `environ.ini` template, STEAM-3-MACHINE-TEST doc |
| **New** | `btl4fe`, `btl4console`, score sink + results screen, BT `bindings.txt`, CMake Steam gating, `context/steamification.md` |
## Standing regressions (after every engine-file merge)
1. Solo: `run\run.cmd` boots DEV.EGG; drive → fire → kill → respawn works.
2. Loopback MP via btconsole.py keeps entity/combat replication.
3. Pod paths (`L4CONTROLS=RIO`, `L4GAUGE`, `L4PLASMA=com2`) still compile and
initialize.