Files
BT412/docs/BT412-ROADMAP.md
T
CydandClaude Opus 4.8 a2949166ea Net: Steam lobby (btl4lobby) + launch-mode wiring (Workstream C.2)
Port RP412's RPL4LOBBY to BT: an ISteamMatchmaking room stands in for the
arcade Site-Management screen. Compiled both gates -- stubs without
BT412_STEAM, the full room under it (both configs build + link clean).

Lobby (game/reconstructed/btl4lobby.*):
- Room screen (green-on-black, like the menu), owner = console.
- Member data: FakeIP + fake console/game ports + persona + mech/color/badge
  (ip/cp/gp/nm/vh/cl/bd keys).
- Nonced "go" launch roster -> SteamNetTransport_RegisterPeer for every peer.
- Push/PullRaceResults over lobby data rebuild the shared score sheet.

Front end (btl4fe.*): HOST/JOIN buttons when BTLobby_Available() (Steam
transport up); the menu loop exits on a steam action and BTFrontEnd_Run
routes the lobby outcome -- host builds the egg with every member as a
[pilots] mesh entry (BTFrontEnd_SetHostedPilots), member returns launch
mode 2. Results screen prefers the marshal's collated result names.

Marshal (btl4console.*): add GetResultName / ClearResults / InjectResult so
the lobby owner can refill the sheet from the collated wire scores; Result
gains a name field.

WinMain (btl4main.cpp): install the Steam transport on BT412STEAM env
(before the front end, so the lobby is offered); branch on
BTFrontEnd_LastLaunchMode() -- host owns the marshal clock + joins the mesh,
member enters as a network pod on :1501 (SetNetworkCommonFlatAddress +
gConsoleLossEndsMission); Push (host) / Pull (member) results, then relaunch.

CMake: btl4lobby.cpp joins bt410_l4; that lib gets BT412_STEAM + the
Steamworks include under the gate. build-steam/ gitignored.

Deferred (untestable here -- needs Steam + multiple machines): the
host->member wire egg-feed marshal (InstallNetworkRace); a member currently
waits for a console connection nothing supplies, so a live host+member race
is blocked on it. The lobby object does not survive the per-mission
relaunch. docs/STEAM-3-MACHINE-TEST.md for BT not yet authored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 23:18:36 -05:00

224 lines
14 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.
> **Status (2026-07-16): DONE (5a5d).** The zero-argument launch shows the interactive
> green-on-black catalog menu (`game/reconstructed/btl4fe.*`: map/mech/color/time/weather/
> length + pilot name), builds `frontend.egg` (verified structurally identical to MP.EGG,
> GDI-rendered plasma name bitmaps), and boots a playable mission. The in-process
> LocalConsole marshal (`game/reconstructed/btl4console.*`) owns the mission clock off the
> engine per-frame hook (`gPerFrameHook`, added to `APPMGR`) and dispatches
> `Application::StopMissionMessage` at the chosen length; it snapshots per-pilot
> kills/deaths at the stop (BT's own score model — msgID 0x18 stays a stub). The
> single-binary loop is a RELAUNCH per mission (`CreateProcessW`; avoids the stale
> `gBT*`-global re-init crash), showing a GDI "MISSION COMPLETE" scoreboard between races.
> WinMain (`game/btl4main.cpp`) wires front-end-mode detection, the marshal arm, the
> results screen + relaunch, and the launch-mode branch (solo/host/member). Details in
> `context/steamification.md`.
## 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.
> **Status (2026-07-16): CODE LANDED; live 3-machine test DEFERRED (no Steam + multi-machine
> here).** `game/reconstructed/btl4lobby.*` ships (compiled both gates: stubs without
> `BT412_STEAM`, full ISteamMatchmaking room under it). Built: the room screen (green-on-black,
> like the menu), member data exchange (FakeIP + fake console/game ports + persona +
> mech/color/badge), the nonced "go" launch roster → `SteamNetTransport_RegisterPeer` all peers,
> and Push/PullRaceResults over lobby data. The menu shows HOST/JOIN when `BTLobby_Available()`
> (Steam transport up); a launch routes through `BTFrontEnd_LastLaunchMode()`: **host** builds the
> egg with every member as a `[pilots]` entry + owns the marshal clock, **member** enters as a
> network pod on `:1501` (`SetNetworkCommonFlatAddress` + `gConsoleLossEndsMission`). WinMain
> installs the Steam transport on `BT412STEAM` env. **Remaining / deferred:** the host→member
> **wire egg-feed marshal** (`InstallNetworkRace` — the owner streaming eggs to remote pods) is
> NOT built; a member currently waits for a console connection that only that marshal provides, so
> a live host+member race needs it. The lobby OBJECT does not survive the per-mission relaunch
> (documented single-binary limitation — everyone lands on a fresh menu, not back in the room).
> `docs/STEAM-3-MACHINE-TEST.md` for BT is not yet authored. Details in `context/steamification.md`.
## 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.