The mission-control menu was missing player experience + many options. Added the complete set recovered from the Mac 4.10 operator console's BT:: adventure tree (TeslaSuite/410console/.../Console.ini) -- the tag= values are the egg fields; cross-checked vs the port spec + reference/cavern.egg: NEW groups: SCENARIO (Free For All / No Return respawn role), EXPERIENCE (novice/standard/veteran/expert), BADGE (7 team emblems), PATCH (8 badge colours), DROP ZONE (one..five), ADV. DAMAGE (on/off). CORRECTED: TIME (evening, not 'dusk'); COLOR (full 7: +Brown/Green/Grey/Tan, Red->Crimson, Gray->Grey); MECH (full 18 with console friendly names -> tags, was 8). btl4fe.hpp: BTFePilot gains experience/badge/patch/dropzone/advancedDamage; BTFeMission gains roleKey/roleModel. The egg writer emits the selected values (were hardcoded expert/VGL/Yellow/one/advancedDamage=1); No-Return role model = the game's 8.3 resource 'noretun'. 5-column layout (mission | mech | pilot | emblem/patch | session+controls); loadout persists across the relaunch (fe_last.ini, all 14 groups). Verified: menu drives (click Expert+Davion -> egg experience=expert badge=Davion, persisted), and the generated egg parses + runs a full mission (no egg errors, drive 61.501). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
207 lines
14 KiB
Markdown
207 lines
14 KiB
Markdown
# GLASS_COCKPIT — running ledger (desktop developer layer)
|
||
|
||
Detailed running log for the `glass-cockpit` branch work: PadRIO, on-screen panel/plasma,
|
||
miniconsole, Steam transport/lobby. Curated digest: `context/glass-cockpit.md` (read that
|
||
first); this file carries the step-by-step detail, evidence, and verification transcripts.
|
||
|
||
---
|
||
|
||
## 2026-07-17 — Step 1: branch + compile gates + scaffolding
|
||
|
||
- Branch `glass-cockpit` created from master @ `e2c21c4`.
|
||
- `CMakeLists.txt`: `option(BT_GLASS OFF)` + `option(BT_STEAM OFF)`; `BT_STEAM` without
|
||
`BT_GLASS` is a configure-time FATAL_ERROR; gates append to `BT_DEFS` (flows to all three
|
||
targets); `STATUS` line prints gate state at configure. Gated `target_sources` blocks will be
|
||
added per-TU as they land (CMake would reject nonexistent source files now).
|
||
- `.gitignore`: `build-pod/`, `build-steam/`, `content/bindings.txt`, `content/frontend.egg`,
|
||
`content/steam_appid.txt`.
|
||
- Verification (this step): defaults configure prints `BT_GLASS=OFF BT_STEAM=OFF` and builds
|
||
(purity build); `-DBT_GLASS=ON` and ON/ON configures print correct gate state; the
|
||
BT_STEAM-without-BT_GLASS negative test FATAL_ERRORs. Full 3-config BUILD matrix becomes
|
||
meaningful from step 2 (first gated code) and is re-run there.
|
||
|
||
## 2026-07-17 — Step 2a: the .CTL positional-attribute off-by-one — CONFIRMED and FIXED (ungated)
|
||
|
||
**The bug [T2, live-verified]:** the streamed `.CTL` "L4" mapping resource carries the 1995
|
||
binary's positional attribute ids (stick=3, throttle=4, ...), resolved at install time by
|
||
`Simulation::GetAttributePointer(id)` → `AttributeIndexSet::Find(id)` = `attributeIndex[id-1]`
|
||
(positional). `MechControlsMapper` chained its enum on `Subsystem::NextAttributeID == 2`, but
|
||
the binary's chain reaches 3 (one base attribute we don't publish) — so EVERY streamed mapper
|
||
record landed one member late.
|
||
|
||
**Evidence (new `BT_CTRLMAP_LOG=1` diagnostic in `LBE4ControlsManager::CreateStreamedMappings`,
|
||
`engine/MUNGA_L4/L4CTRL.cpp` — permanent, env-gated, default off).** Before the fix
|
||
(`build-pod` Debug, `-egg DEV.EGG`, dev platform):
|
||
```
|
||
rec 0 subsys 0 group 1(Joy) elem 0 attrID 3 -> +0xe4 (= throttlePosition: WRONG, stick intended)
|
||
rec 1 subsys 0 group 0(Scalar) elem 0 attrID 4 -> +0xe8 (= pedalsPosition: WRONG, throttle intended)
|
||
rec 2 subsys 0 group 3(Button) elem 63 attrID 6 -> +0xf0 (= speedDemand: WRONG, reverse intended)
|
||
rec 4-6 attrID 10/11/12 -> lookRight/lookBehind/lookDown (one late; LookLeft/Right/Behind intended)
|
||
```
|
||
Spacing attr3→attr4 was 4 bytes (Scalar) where stick→throttle must be 8 (ControlsJoystick).
|
||
The weapons chain (attrID 19 → +0x31c TriggerState) was already correct — mechweap was
|
||
pad-aligned in task #16 (gotcha #11). Torso (subsys 17) also aligned (MechSubsystem publishes
|
||
binary id 2). ONLY the mapper chain (subsys 0) was off. On the dev box the keyboard INPUT
|
||
BRIDGE (writes the mapper members directly, after the engine push) masked it; on a REAL POD the
|
||
serial RIO throttle scalar would have driven `pedalsPosition` and the stick x/y would have
|
||
driven throttle+pedals — the Phase-8 landmine prior art predicted.
|
||
|
||
**The fix (ungated, pod-correctness — user-approved 2026-07-17):** `mechmppr.hpp` pins the enum
|
||
to the binary numbering (`MechControlsMapperPadFirstAttributeID = 2`, stick=3 ... PilotArray=
|
||
0x16); `mechmppr.cpp` pads id 2 with a valid never-bound entry (the mech.cpp attrPad idiom) and
|
||
locks the base + pinned ids with `static_assert`s. `L4MechControlsMapper::TargetRangeExponent`
|
||
(chained) moves 0x16→0x17, matching the binary's own arithmetic; it is bound by name (audio
|
||
zoom-blip), so no consumer shifts.
|
||
|
||
**After the fix:** `attrID 3 -> +0xdc` (stick), `attrID 4 -> +0xe4` (throttle, 8-byte spacing
|
||
✓), `attrID 6 -> +0xec` (reverse), `10/11/12 -> +0xfc/+0x100/+0x104` (the look trio); torso +
|
||
weapons unchanged. Regression: headless forced-walk run (`BT_FORCE_THROTTLE=1`) drives normally
|
||
— `[mppr] thr=1 -> speedDemand=61.501`, gait cycles, position advances, 31/31 subsystems
|
||
simulated, no faults.
|
||
|
||
## 2026-07-17 — Steps 2b-2e: PadRIO, stand-downs, displays, preset — DONE + verified
|
||
|
||
**2b (RIOBase + PadRIO, commit ac57a47):** `L4RIO.h` splits abstract `RIOBase` (enums, the five
|
||
analog Scalars, pure `GetNextEvent`/`SetLamp`, no-op serial ops, NEW `IsOperational()`) out of
|
||
serial `RIO` (`: PCSerialPacket, RIOBase` — ctor assigns as before, zero behavioral delta);
|
||
`L4CTRL.h` `rioPointer` retyped `RIOBase*`; gated `L4CONTROLS=PAD` factory arm. NEW gated TUs
|
||
`L4PADRIO.*` (XInput+keyboard → the surface; 3s hot-plug; focus-guarded keys; per-poll
|
||
AnalogEvent heartbeat time-gated so the manager's drain loop terminates; `lampState[]`;
|
||
static `SetScreenButton` — button addresses AND the keypad space 0x50-0x6F → KeyEvents) and
|
||
`L4PADBINDINGS.*` (`content\bindings.txt`, default written on first run; deflect/slew/set axis
|
||
model; addresses validated vs ButtonCount).
|
||
|
||
**2c (stand-downs, commit 9f35a80):** engine query `BTRIODevicePresent()` (ungated —
|
||
pod-correct); both dev bridges (`mech4.cpp` attr writes, `mechmppr.cpp` BT_KEY_BRIDGE) gate
|
||
their WRITES: unset=auto / "0"=off / else on; forced harness always rides. **Definitive live
|
||
test:** OS-injected LSHIFT hold slewed PadRIO throttle 0→0.33→1.0 with `pre==thr` every frame —
|
||
the value arrives via the ENGINE push through the streamed `.CTL` binding (bridge stood down),
|
||
`speedDemand=61.501` from authentic InterpretControls.
|
||
|
||
**2d/2e (displays + preset, commits db9ac94 + 418acf0):** the on-screen panel follows **vRIO**
|
||
(`C:\VWE\vrio` — `CockpitLayout.cs`/`PanelCanvas.cs`, per Cyd): five 4x2 MFD clusters
|
||
(descending addresses), Throttle/Secondary/Screen/Joystick 1x8 columns, two 4x4 hex keypads
|
||
(0x50 internal→pilot unit, 0x60 external→operator unit), physical names, red/yellow/blue
|
||
coloration, 500/250/125ms flash, right-click latch — 104 controls. `PlasmaWindow :
|
||
Video8BitBuffered` renders the 128x32 plasma via the SAME gauge-renderer path as serial
|
||
(buffer is TOP-DOWN — the flip rendered it upside down, user-reported + fixed). MFD surfaces =
|
||
the EXISTING dev-gauge modes (no new code, no L4VB16 exposure). `-platform glass` preset +
|
||
run.cmd token. **Button-trap guard (e2c21c4 pattern):** the authentic base
|
||
`ConfigureMappableMessageHandler` FAIL trap is `abort()` under DEBUGOFF — any aux/zoom button
|
||
without its reconstructed L4 override KILLED the game on press (panel makes all reachable).
|
||
Default now log+ignore; `BT_BUTTON_TRAP=1` restores the hard trap. Verified: synthetic panel
|
||
click → 2 `[FAIL]` (press+release through panel→PadRIO→ProcessRIOEvent→streamed mapping), game
|
||
survives. NOTE: each `[FAIL]` marks a missing L4 override — reconstruction backlog, see
|
||
[[open-questions]].
|
||
|
||
**Step-2 verification matrix (all passed 2026-07-17):**
|
||
1. Purity: defaults `build-pod/` compiles (known mech3 /FORCE externals only); seam audit —
|
||
`BT_GLASS`/`BT_STEAM` mentions ONLY in CMakeLists, btl4main.cpp preset region, L4CTRL.cpp
|
||
(include+PAD arm), L4GREND.cpp (include+SCREEN branch), gated TUs, run.cmd docs.
|
||
2. Pod solo forced-walk clean (§2a).
|
||
3. **2-node loopback MP on the pod build:** A `-egg MP.EGG -net 1501` + B `-net 1601` +
|
||
`btconsole.py` relay — both nodes launch into the running mission (77/76 ticks, mechs at
|
||
their drop zones, gait/target loops live).
|
||
4. Glass build: PadRIO drives through the authentic mapper path; panel click path live; plasma
|
||
window; all boot lines present.
|
||
Deferred to the user (visual-only): panel/plasma appearance vs vRIO on screen, XInput stick
|
||
feel, `L4PADFLIP` polarity.
|
||
|
||
## 2026-07-17 — Step 3: the miniconsole — DONE + verified (commit c4ca87b)
|
||
|
||
**Architecture: the console stays a wire peer.** `game/glass/btl4fe.*` (GDI catalog menu + the
|
||
console-shape egg writer) and `btl4console.*` (the LocalConsole marshal: a worker-thread console
|
||
CLIENT speaking the `tools/btconsole.py` protocol verbatim — 1040-byte chunked
|
||
`ReceiveEggFileMessage`, 20s settle, `RunMission`×2, stay connected, own the clock,
|
||
`StopMission`(id 6)@expiry; logs `content\marshal.log`). The engine runs the 100% stock console
|
||
ladder — ZERO engine hooks; the planned `L4APP.H` setters proved unnecessary (launches pass real
|
||
argv on relaunch). WinMain: one gated block — zero-mission-arg glass launch → menu → relaunch
|
||
self with mission argv (`BT_FE_EGG/PODS/SECS` arm the marshal; `BT_FE_LOOP` returns exits to the
|
||
menu; menu relaunches CLEAR the handoff — the leak re-armed a marshal in cycle test 1).
|
||
|
||
**Verified (synthetic menu drives, all autonomous):**
|
||
- Golden egg: `frontend.egg` section sequence + line offsets IDENTICAL to `MP.EGG`; ordinal
|
||
pages verbatim; GDI name-glyph pages in MP.EGG framing (32/8 rows, 16 bytes/line).
|
||
- Solo cycle: menu → egg → relaunch → marshal → stock ladder → mission RUNS (58+ ticks) → 60s
|
||
clock → stop → menu returns with clean env.
|
||
- **HOST LAN 2-node:** peer started manually (`-net 1601`); menu-hosted launch → marshal feeds
|
||
BOTH pods (2-pilot egg, 10 chunks each) → both in-mission (65/18 ticks) → stop both → host
|
||
returns to the menu, peer idles at console-wait.
|
||
|
||
## 2026-07-17 — Step 4 pre: Steamworks SDK 1.64 vendored (commit 23c0019)
|
||
|
||
Official Valve zip (provided by Cyd at the repo root), extracted to
|
||
`extern/steamworks_sdk_164/` and trimmed to `public/` + `redistributable_bin/` + Readme
|
||
(12.5 MB); zip removed. Consumed only under `-DBT_STEAM=ON`.
|
||
|
||
## 2026-07-17/18 — Step 4: the Steam wire — CODE COMPLETE (commits e79e8fa, 48ede2f, f703bb1)
|
||
|
||
**4a (e79e8fa) — the wire seam:** file-local `BTNetSend/Recv/Accept/Close` wrappers around the
|
||
9 raw Winsock sites in `L4NET.CPP`; OFF builds are pure code motion. Regression: 2-node loopback
|
||
MP through the seam — mesh forms, full 31/31 mission on both nodes.
|
||
|
||
**4b (48ede2f) — the transport:** gated TU `L4STEAMNET` — pseudo-SOCKETs (0x5EA0xxxx) behind the
|
||
seam; the engine's TCP byte stream rides reliable-NoNagle Steam messages re-assembled into
|
||
per-connection rings (recv semantics preserved: empty→WSAEWOULDBLOCK, closed→0); seam
|
||
completions: `CheckSocket` (peer = token address), `OpenConnection` TCP_OPEN delegate,
|
||
`GetMyAddress` self-token. Install on `BT_STEAM_NET=1`; graceful degrade to Winsock at every
|
||
step. BOTH pumps needed (`SteamAPI_RunCallbacks` + sockets `RunCallbacks`).
|
||
|
||
**4c (f703bb1) — the lobby + THE FAKEIP LESSON [T2]:** prior art assumed FakeIP survives the
|
||
per-mission process relaunch — DISPROVEN live (menu process got 169.254.124.110, the mission
|
||
process got 169.254.54.234; the pod timed out connecting to its own stale address). Redesign:
|
||
roster addresses are opaque ipv4-shaped TOKENS (`169.254.77.N`, ports 1501 console / 1502 game)
|
||
minted by the host at GO and mapped to Steam IDENTITIES; connections ride
|
||
`ConnectP2P(identity, channel)` (console=0, game=1); the map hands off via env
|
||
(`BT_FE_MYFAKE`/`BT_FE_STEAMMAP`). `btl4lobby`: ISteamMatchmaking room (`btl4=1` filter),
|
||
pilots publish name/mech/color, host GO mints tokens + writes `btl4map` + `btl4go`.
|
||
|
||
**Verified single-machine (ON/ON, live Steam client):** menu → HOST STEAM LOBBY → room → GO →
|
||
map `169.254.77.1=<steamID64>` → egg roster carries the token → mission process
|
||
`[steamnet] up ... 1 roster token(s) incl. self` → pod self-matches → stock console ladder →
|
||
mission RUNS (46 ticks). 3-config build matrix green throughout. **Remaining (user hardware):**
|
||
the live 2-account cross-machine session — procedure in `docs/STEAM_TEST.md`.
|
||
|
||
## 2026-07-18 — Merge origin/master (commit f889e24): btinput meets PadRIO
|
||
|
||
Master's 33-commit run landed (D1 relay/operator-console line, CONTROLS.MAP+XInput binding
|
||
engine `btinput.cpp`, camera seats, torso pitch, sign fixes, the 1995 manual, version stamping).
|
||
**The reconciliation:** btinput (ungated, dev/pod desktops) joined the stand-down convention —
|
||
`BTInputPoll` yields and `BTInputSuppressKey` claims nothing when an operational cockpit device
|
||
(serial RIO / PadRIO) owns the input path (`BTRIODevicePresent`; `BT_KEY_BRIDGE` override;
|
||
forced harness exempt). **One input system per mode:** `CONTROLS.MAP`/btinput on dev+pod
|
||
desktops, `bindings.txt`/PadRIO on glass. The D1 relay keeps its own raw sockets by design
|
||
(relay and Steam are alternative wires; the BTNet seam covers the arcade mesh both need).
|
||
Verified: 3-config build, glass stand-down live, pod forced-walk with btinput active, 2-node
|
||
loopback MP 31/31.
|
||
|
||
## 2026-07-19 — Mission-control options: the AUTHORITATIVE catalog (TeslaSuite console.ini)
|
||
|
||
Source: `C:\VWE\TeslaSuite\410console\4_10-console-extracted\Console.ini`, the Mac 4.10 operator
|
||
console's `BT::` adventure tree (the `tag=` values are the egg field values; display names are the
|
||
console's). Cross-checked vs `410console/battletech-port/BATTLETECH-PORT-SPEC.md` + `reference/
|
||
cavern.egg`. This is every option the original console exposed for a BattleTech mission.
|
||
|
||
**Mission-level** — `[mission]`: `adventure=BattleTech` (fixed), `temperature=27` (fixed),
|
||
`scenario=freeforall` (the tag is always freeforall; the RESPAWN choice is the ROLE):
|
||
- map (8): cavern, grass, rav, polar3, polar4, arena1, arena2, dbase
|
||
- time (4): morning, day, evening, night ← was day/night/morning/**dusk** (wrong: evening)
|
||
- weather (3): clear, fog (light), soup (heavy)
|
||
- role (2): Default (`model=dfltrole`, respawn) / NoReturn (`model=noreturn`, one life)
|
||
- length: seconds
|
||
|
||
**Per-pilot** — participant block:
|
||
- experience (4 [T1 game decode]): novice, standard, veteran, expert ← was hardcoded `expert`
|
||
- vehicle (18, friendly→tag): Black Hawk=blkhawk, Loki=loki, Nova=bhk1, Mad Cat=madcat, Thor=thor,
|
||
Owens=owens, Kotori=own1, Summoner=thr1, Hellbringer=lok1, Timber Wolf=mad1, Avatar=avatar,
|
||
Satyr=ava1, Sunder=sunder, Sunder V1=snd1, Vulture=vulture, Mad Dog=vul1, Mischief=lok2,
|
||
Zanin Neko=mad2 (the 18 ModelList names; matches the 2026-07-18 vehicle sweep)
|
||
- color/camo (7, display→tag): White=White, Black=Black, Brown=Brown, Red=**Crimson**, Green=Green,
|
||
Gray=**Grey**, Tan=Tan ← was White/Black/Crimson only
|
||
- badge/emblem (7, the `badge=` field): VGL, Davion, Kurita, Liao, Marik, None, Steiner ← was `VGL`
|
||
- patch/badge-color (8, the `patch=` field): Yellow, Blue, Grey, Green, Violet, Red, White, Black
|
||
← was `Yellow`
|
||
- dropzone (5, the `dropzone=` field): one, two, three, four, five ← was `one`
|
||
- advancedDamage (0/1): the advanced damage model toggle ← was hardcoded `1`
|
||
- vehicleValue: 1000 (const), loadzones=1 (const), bitmapindex (per-pilot ordinal)
|