sweep the knowledge base up to this session: log day, crash symbolization, a misnamed accessor
Audit of what actually landed in context/ versus what only ever got said in
chat. Five gaps and one stale claim repeated in four files.
New env gates were the biggest hole -- BT_TORSO_LOG, BT_MYOMERS_LOG,
BT_MYOMERS_REPAIR_TEST, BT_SELF_DAMAGE and BT_POWER_DETACH_TEST were all in the
code and none of them in decomp-reference §6, which is supposed to be the hub.
Added with the reasoning that makes them usable: why the myomers probe samples
every frame while unpowered (the NoVoltage window is about a second and a 1 Hz
probe steps straight over it), why the repair test also forces Manual (or
AutoConnect restores power a frame later), why SELF_DAMAGE latches off at the
first death, and why DETACH_TEST taking a NAME is what proves failover rather
than a same-generator re-attach.
The 6am log day was in the code and the player bats but not the KB. Now in
build-and-run with the stem table, the unconditional append, the 8 MB part
roll-over and the BT_LOG-truncates trap, plus the operator-facing note in
operator-console and OPERATOR_GUIDE that you ask for the NEWEST log, never
"today's".
Recorded the crash-symbolization gap, which is the one with teeth. BTCrashFilter
writes a stack into the day log and its own comment claims "we hold the PDB". We
do not: no Release PDB is produced at all, and /O2 implies /Oy so the EBP walk is
unreliable anyway. When the Owens crash finally lands we get an address we cannot
resolve. Written down with the fix (/Oy-, /Zi + /DEBUG, archive the PDB per
build) rather than left as something I mentioned once.
New gotcha 22: HeatModelOff() reads simulationState == 1, i.e. "am I destroyed",
and has nothing to do with the heat model. Behaviour at the call sites is right,
the name is not, and while chasing #70 it reads as "novice pilots cannot twist"
and sends you hunting an experience-level bug that does not exist. Same misnomer
in torso/gyro/sensor headers.
Also documented the dist flavor trap, having nearly shipped it myself: mkdist
reads build/CMakeCache.txt, so a tree configured BT_STEAM=OFF silently produces
a -nosteam zip with no play_steam.bat and no steam_api.dll. OFF is the CMake
default; ON is the documented dev state.
Swept the stale `btl4.log` filename out of reconstruction-gotchas,
reconstruction-method, build-and-run and CLAUDE.md itself -- the log has been
<stem>_YYYYMMDD.log since 1777d5a and every "read btl4.log" instruction was
pointing at a file that is no longer written.
checkctx CLEAN.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
5fc969ae95
commit
bc27313f20
@@ -47,7 +47,7 @@ precise than anything you can infer.
|
||||
fields, resource mismatch), linkage (`/FORCE`), dtor-epilogue, databinding trap. Most bugs are here.
|
||||
3. **RULE: no stand-ins.** The full logic IS in the pseudocode; a "gap" is an unfilled stub, not a
|
||||
hole. Never write placeholder logic — read the decomp. Bring-up scaffolding is marked + temporary.
|
||||
4. **Verify honestly.** `static_assert`-lock layouts; run env-gated; read `btl4.log`; cdb on crashes.
|
||||
4. **Verify honestly.** `static_assert`-lock layouts; run env-gated; read `content\<stem>_YYYYMMDD.log`; cdb on crashes.
|
||||
Tag claims with the evidence tier; flag T3/T4.
|
||||
5. **Persist insights.** A genuinely new finding → add it to the right `context/*.md` (+ the `docs/`
|
||||
ledger for detail), with an evidence tier. Keep the knowledge base current — this is a mandate,
|
||||
|
||||
@@ -38,13 +38,23 @@ run\run.cmd [EGG] # default DEV.EGG; cd's to content\ and runs btl4.exe -eg
|
||||
```
|
||||
- **cwd MUST be `content\`** — the engine resolves `BTL4.RES`, `VIDEO\`, `BTDPL.INI`, eggs relative
|
||||
to cwd (the `loadTables` gotcha: `L4VIDEO.cpp:849` `fopen("VIDEO\\REPLACEMATS.tbl")` is relative +
|
||||
unchecked → fread on NULL if cwd is wrong). Logs to `btl4.log` in `content\` (or `BT_LOG=<file>`). [T2]
|
||||
unchecked → fread on NULL if cwd is wrong). Logs to `content\<stem>_YYYYMMDD.log` (or `BT_LOG=<file>`). [T2]
|
||||
**Log naming (2026-07-28):** stem = the launcher (`solo`/`steam`/`join`/`joyconfig`, else `btl4`),
|
||||
chosen from `BT_FE_SOLO`/`BT_STEAM_NET`/`BT_FE_JOIN`/`BT_RELAY`/`BT_JOYCONFIG`. A self-named day
|
||||
file **always appends** (the default stream mode truncates, so a second launch would erase the
|
||||
morning — that is how the 2026-07-27 Owens crash stacks were lost under the old rotation). Nothing
|
||||
is ever deleted; past 8 MB the NEXT launch opens `<stem>_YYYYMMDD.1.log` (checked only at open, so
|
||||
a live session is never split). ⚠ **The log DAY runs 06:00 → 06:00 local, not midnight** — a late
|
||||
session stays filed under the evening it started, so one playtest night is one file. Only the
|
||||
FILENAME is shifted; the session header and `lastrun_<stem>.txt` each call `GetLocalTime`
|
||||
separately and carry true local time. ⚠ `BT_LOG=<file>` is used VERBATIM and **truncates** unless
|
||||
`BT_LOG_APPEND=1` — the player bats clear it for exactly this reason.
|
||||
**Since 2026-07-26 the exe enforces this itself** (`BTEnsureContentDirectory`, btl4main.cpp): if
|
||||
cwd has no `BTL4.RES` it probes from the exe's own directory (`..\..\content` for the shipped
|
||||
layout, plus flattened / in-content cases) and `SetCurrentDirectory`s there, before the log file
|
||||
opens. The boot line says so when it had to look. ⚠ **The landmine it defuses** (field report):
|
||||
a bare `btl4.exe` launch from `build\Release` found no resources ("Resource file btl4.res
|
||||
v1.0.0.0 is obsolete!"), wrote a stray `bindings.txt`/`environ.ini`/`btl4.log` NEXT TO THE EXE —
|
||||
v1.0.0.0 is obsolete!"), wrote a stray `bindings.txt`/`environ.ini`/`btl4_*.log` NEXT TO THE EXE —
|
||||
so the player's real ones in `content\` looked like they were never created — and killed the
|
||||
mission generation the menu launched (the child inherits the parent's cwd). Invisible for years
|
||||
because every launcher `cd`s to `content\` first; it became reachable once glass became the
|
||||
@@ -84,6 +94,20 @@ heap **0xCDCDCDCD** (uninit) + freed **0xFEEEFEEE** — invaluable for "was this
|
||||
`BT_HEAPCHECK=1` = whole-heap validation every alloc/free (O(n²) at mission load — SLOW). To attach
|
||||
to a frozen abort dialog: `cdb -p <pid> -c ".lines;~*kp 30;q"`. [T2]
|
||||
|
||||
⚠ **A FIELD crash stack is currently NOT symbolizable — two gaps, 2026-07-28** [T1].
|
||||
`BTCrashFilter` (btl4main.cpp:193) catches unhandled exceptions and writes
|
||||
`[crash] UNHANDLED EXCEPTION code=… addr=… (btl4+0xNNNN)` + an EBP-chain walk straight into the day
|
||||
log — no minidump, no separate file. Its comment claims "we hold the PDB". **We do not:**
|
||||
1. **No Release PDB is produced.** Only `build/Debug/btl4.pdb` exists and the shipped exe embeds no
|
||||
PDB path, so `btl4+0xNNNN` from a tester cannot be resolved to a function.
|
||||
2. **Frame pointers are omitted.** Release flags are `/O2 /Ob2 /DNDEBUG` with no `/Oy-` anywhere, and
|
||||
`/O2` implies `/Oy` on x86 — so the EBP walk is unreliable and the `[crash] stack:` chain is often
|
||||
shallow or wrong. The **first** line is still exact (`addr` comes from the exception record, not
|
||||
the walk).
|
||||
FIX when field stacks are wanted: add `/Oy-` to the Release flags and `/Zi` + linker `/DEBUG` so a PDB
|
||||
is generated, then ARCHIVE that PDB per build (never ship it in the zip). The session header already
|
||||
stamps `build=4.11.<n> (<hash>)`, so an archived PDB matches a tester's log exactly.
|
||||
|
||||
## Repo layout (bt411)
|
||||
- **`engine/MUNGA/` + `engine/MUNGA_L4/`** — the shared 2007 MUNGA engine + Win32/D3D9 HAL (carries
|
||||
our BT render/loader work: bgfload/L4D3D/L4VIDEO + the image codec). `engine/shim/` (ATL),
|
||||
@@ -101,8 +125,18 @@ to a frozen abort dialog: `cdb -p <pid> -c ".lines;~*kp 30;q"`. [T2]
|
||||
`4.10` = the 1995 arcade release; `4.11` = this win32 reconstruction; dev builds =
|
||||
**`4.11.<git commit count>` + short hash**, `+` suffix = built from an uncommitted tree
|
||||
(e.g. `4.11.311 (980c9cd+)`). Stamped every build by `tools/btversion.cmake` →
|
||||
`build/btversion.h` (`BT_VERSION_*` macros); shown in the boot banner (btl4.log head) and
|
||||
the window title. Ask a tester for their title bar / btl4.log head to identify a build.
|
||||
`build/btversion.h` (`BT_VERSION_*` macros); shown in the boot banner (day-log head) and
|
||||
the window title. Ask a tester for their title bar, or read the `===== BT411 SESSION … build=… =====`
|
||||
header that starts every session inside `content\<stem>_YYYYMMDD.log`, to identify a build.
|
||||
|
||||
**Cutting a tester zip:** `python tools/mkdist.py` → `dist/BT411_4.11.<n>.zip` (name from
|
||||
`build/btversion.h`; ships git-TRACKED `content/` only, so working-tree junk stays out).
|
||||
⚠ **It reads `build/CMakeCache.txt` for the flavor, so configure BEFORE building:** with
|
||||
`BT_STEAM=OFF` the zip is renamed `-nosteam` and carries neither `play_steam.bat` nor
|
||||
`steam_api.dll` — useless for a Steam playtest, and easy to ship by accident since OFF is the CMake
|
||||
default (`-DBT_STEAM=ON` is the documented dev-checkout state). `BT_EXPIRE=ON` (default) gives the
|
||||
14-day tester window; an expire-OFF zip is renamed `-noexpire` and warns. Verify by extracting the
|
||||
zip somewhere clean and booting it with no repo present — that is what catches a missing runtime DLL.
|
||||
|
||||
## Key Relationships
|
||||
- Base: [[wintesla-port]] (the engine build recipe).
|
||||
|
||||
@@ -467,6 +467,12 @@ default-ON (`'0'` disables).
|
||||
| `BT_CTRLMAP_LOG` | streamed `.CTL` record audit (L4CTRL.cpp CreateStreamedMappings): Direct records with the resolved member offset + EVENT records (button→subsystem message; found the pod's secondary-panel Cycle buttons 0x15/0x18) |
|
||||
| `BT_CAM_LOG` | camera-seat/broadcast trace: streamed camera network count, director pick + Players-group census, ship follow state, PlayerLink dispatch/receive, ranking-window render ([[multiplayer]] camera seat) |
|
||||
| `BT_SPEC_LOG` | manual spec-audit dump at subsystem ctor: torso speed/limits, heat-sink count, reservoir capacity, per-subsystem coolant loop ([[pod-hardware]] §Spec cross-check) |
|
||||
| `BT_TORSO_LOG` | `[torso]` gate probe — why `effectiveTwistRate` is zero: `hmOff/elec/heatState/rate/base/limits/axis/twist/wIdx/wElec/wSrc/gOut/gRated/minV` (torso.cpp ~592). The `elec` field is the #70 tell: `4`=Ready, `1`=NoVoltage |
|
||||
| `BT_MYOMERS_LOG` | `[myo]` per-subsystem line — `elec / outV / speed / dmg / gear`. Samples EVERY frame while the electrical state ≠ Ready (the NoVoltage window is ~1s and a 1 Hz probe steps right over it), else ~1 Hz |
|
||||
| `BT_MYOMERS_REPAIR_TEST=<lvl>` | ⚠ bring-up scaffolding. At frame 300 seeds this myomer's OWN zone damage to `<lvl>`, detaches its voltage source and forces `ManualConnect` so the AutoConnect hunt cannot restore power a frame later. The only way to hold the un-powered window open long enough to watch `@004b8bb9`. (That branch is inert — see [[combat-damage]].) |
|
||||
| `BT_SELF_DAMAGE=<dps>` | dispatch an unaimed `TakeDamage` at your OWN mech once a second, through the real `Entity::Dispatch` path, so the whole RESPAWN family is bench-testable solo (nothing else can kill the local pilot: `BT_MP_FORCE_DMG` only targets replicants). **Latches off at first death** so everything after the respawn is the respawn's doing, not the harness still shooting you |
|
||||
| `BT_POWER_DETACH_TEST=<name\|1>` | drop a subsystem's voltage link + force Auto, so the auto-hunt must recover it. `1` = first powered subsystem to tick; a NAME (`PPC_1`, `Myomers`) targets one, which is what proves FAILOVER to a different generator rather than a same-generator re-attach |
|
||||
| `BT_AUDIO_SOURCES=<n>` | request `n` OpenAL mono sources instead of the driver default (~256). **Opt-in on purpose** — the cap doubles as a governor, and with EFX reverb live a higher ceiling means more simultaneous voices mixing during heavy combat. Measure frame time. See [[wintesla-port]] |
|
||||
|
||||
Full render/locomotion gates (BT_RAMP, BT_MATPRI, BT_CULL, BT_SHADOW_*, BT_LODSEL, BT_ADDLOD,
|
||||
BT_PUNCH, …) are catalogued in [[rendering]]. Warp visuals: [[translocation-warp]].
|
||||
|
||||
@@ -381,7 +381,7 @@ operator control link over a private tunnel (mesh VPN).
|
||||
|---|---|
|
||||
| `content/operator_relay.log` | the relay's own stdout, teed by the GUI. **Truncated at each session start** — so a Start Session destroys the evidence of the wedge it is being used to clear. Copy it first when diagnosing. |
|
||||
| `matchlogs/<peer>_<name>` | per-pod match reports, auto-uploaded on route -9 at each clean round end (relay modes only; a mid-round crash loses it) |
|
||||
| `content/join_YYYYMMDD.log` | the pod's own log — ONE file per day, appended, every session headed by `===== BT411 SESSION … =====`; **never** uploaded, only on the player's machine. Rotation was removed 2026-07-28: two generations used to be deleted by the player's own relaunches, which is how the 2026-07-27 Owens crash stacks were lost. |
|
||||
| `content/join_YYYYMMDD.log` | the pod's own log — ONE file per day, appended, every session headed by `===== BT411 SESSION … =====`; **never** uploaded, only on the player's machine. Rotation was removed 2026-07-28: two generations used to be deleted by the player's own relaunches, which is how the 2026-07-27 Owens crash stacks were lost. ⚠ **The log day runs 06:00 → 06:00 local**, so a session played after midnight stays filed under the evening it started (one playtest night = one file). Ask for the NEWEST `<stem>_*.log`, not "today's date". Stems: `solo`/`steam`/`join`/`joyconfig`. |
|
||||
| `content/lastrun_<mode>.txt` (mode = solo/join/steam/joyconfig) | the launch record (exe's first-breath block + the bat's exit line). Its ABSENCE after a run means the exe never reached WinMain — loader failure or antivirus (Gitea #41). Replaced `launch_report.txt`. |
|
||||
|
||||
Every relay log line is wall-clock stamped (`_StampedOut`) so post-mortems can measure delays.
|
||||
|
||||
@@ -528,7 +528,8 @@ segment tables) — the same class of latent overflow.
|
||||
2. Map `FUN_`/`DAT_`/`this+0xNN` to engine symbols via BT headers + WinTesla MUNGA source +
|
||||
`CLASSMAP.md` + RP's parallel code.
|
||||
3. Write the REAL reconstruction; `static_assert`-lock the layout.
|
||||
4. Build; run env-gated; read `btl4.log`; cdb on any crash (0xCDCDCDCD=uninit, 0xFEEEFEEE=freed).
|
||||
4. Build; run env-gated; read `content\<stem>_YYYYMMDD.log`; cdb on any crash (0xCDCDCDCD=uninit,
|
||||
0xFEEEFEEE=freed).
|
||||
5. For exhaustive multi-function analysis: a read-only Workflow (understand), then implement hands-on.
|
||||
|
||||
## Key Relationships
|
||||
@@ -600,3 +601,25 @@ body reads `in_ST0`) is a **carve artifact** — raw-disassemble the call site
|
||||
export blind spots: mid-function iterator carves at odd addresses (FUN_004acfa9-style thunks),
|
||||
truncated vtables.tsv rows (dump exe bytes at vtable+slot*4), and whole-function gaps (#60 —
|
||||
raw-disasm recovered @004b838c and @004bb9b8).
|
||||
|
||||
## 22. A port accessor NAMED for one thing that reads another (`HeatModelOff`) — 2026-07-28
|
||||
|
||||
`torso.hpp:176`, `gyro.hpp:176` and `sensor.hpp:132` each define:
|
||||
|
||||
```cpp
|
||||
Logical HeatModelOff() const { return simulationState == 1; /* Destroyed */ }
|
||||
```
|
||||
|
||||
It has **nothing to do with the heat model**. `simulationState` is the MechSubsystem state
|
||||
(`Default=0 / Destroyed=1 / Exploding=2`), so this is "am I destroyed?". The behaviour at the call
|
||||
sites is CORRECT — `torso.cpp:569` zeroing `effectiveTwistRate` when the torso is destroyed is
|
||||
right — but the name reads as an experience-level heat gate, and the ACTUAL heat-model gate is a
|
||||
different thing entirely: `OwnerAdvancedDamage()` → `BTPlayerExperienceHeatModelOn()` → the player's
|
||||
`+0x260` flag (off below veteran). Two unrelated concepts, one misleading name.
|
||||
|
||||
**Why it matters:** while chasing #70 this reads as "novice pilots cannot twist their torso", which
|
||||
sends you hunting an experience-level bug that does not exist. Verify what an accessor READS before
|
||||
trusting what it is called — the reconstruction's names are reconstructions too.
|
||||
|
||||
**Sweep note:** the same misnomer sits in all three headers; renaming is safe (no binary meaning
|
||||
attaches to the port's accessor name) but touches three size-locked classes, so do it deliberately.
|
||||
|
||||
@@ -17,7 +17,7 @@ Full detail: `docs/PROGRESS_LOG.md §10b`; the systemic bug classes are [[recons
|
||||
2. Map `FUN_`/`DAT_`/`this+0xNN` to engine symbols using: the BT headers + the WinTesla MUNGA
|
||||
source + `game/reconstructed/CLASSMAP.md` + **RP's parallel code** (VTV≈mech, WEAPSYS≈weapons).
|
||||
3. Write the **real** reconstruction into `game/reconstructed/*.cpp`.
|
||||
4. Build; run env-gated; read `btl4.log` (grep `[anim]/[drive]/[target]/[fire]/[damage]` markers).
|
||||
4. Build; run env-gated; read `content\<stem>_YYYYMMDD.log` (grep `[anim]/[drive]/[target]/[fire]/[damage]` markers).
|
||||
5. cdb on any crash. `static_assert`-lock the layout against the binary's offsets. [T2]
|
||||
|
||||
## RULE: no stand-ins
|
||||
|
||||
@@ -243,7 +243,10 @@ Exceptions worth knowing: a client that **crashes mid-round** loses its report,
|
||||
**solo** or **Steam** have no relay to upload through. In those cases ask them for
|
||||
`content\matchlog_*.txt` directly. Their crash log — `content\join_YYYYMMDD.log`, one file per day
|
||||
holding every session (plus the tiny `content\lastrun_<mode>.txt`) — is **never** uploaded — ask
|
||||
for it explicitly when something went wrong on their end.
|
||||
for it explicitly when something went wrong on their end. Ask for the **newest** `<stem>_*.log`
|
||||
rather than "today's": the log day runs **06:00 → 06:00**, so a session after midnight is still
|
||||
filed under the evening it started. Always ask for `lastrun_<mode>.txt` too, **even when they say
|
||||
there is no log** — that combination is how a launch blocked before `WinMain` is identified.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user