diff --git a/docs/dev/GE-ENV-PROBES.md b/docs/dev/GE-ENV-PROBES.md index fe9b1f5b..880ed6af 100644 --- a/docs/dev/GE-ENV-PROBES.md +++ b/docs/dev/GE-ENV-PROBES.md @@ -43,6 +43,9 @@ Two classes: | `GE_WRAPFIX` | `port/fast3d/gfx_pc.cpp:3166` (`gfx_set_wrap_fix`) | RC3 test override for the `Video.WrapFix` D74/D167 wrap-block path (`atoi != 0` wins over the ini). | **live** (RC3 not finished) | | `GE_SAVELOG` | `src/game/file.c:46`, `src/game/file2.c:16` (`SAVELOG` macro), `src/boss.c:749`, `src/game/objective_status.c:297,343` | Traces the campaign save/unlock chain (objective completion → `end_of_mission_briefing` → `fileWriteSave` → EEPROM). Used to confirm D157. `g_savelogObjOnce` gates the per-criterion dump to the `bossReturnTitleStage` call. | **live** (strip once a few more level boundaries are playtested) | | `GE_D160` | `src/boss.c:744`, `src/game/bondview2.c:791`, `src/aicommands.def:10055,10181,10236,10242,10247` (all `#ifdef PORT`) | Dam exit-cutscene (D148/D160) diagnostic: trace points in `bossReturnTitleStage`, `bondviewSetCameraMode`, and AI cmds `EndLevel`/`exit_level`/`CameraLookAtBondFromPad`/`CameraSwitch`. **Investigation in progress** — user owes a `GE_D160=1` Dam run. | **live** | +| `GE_D243` | `src/game/front.c` (`menu_init`, cached `s_d243`) | Per-menu-transition trace with a local per-tick frame counter (D243 stale-DL-burst screen identification). Counter is NOT numerically identical to the port's internal frame count — cross-reference by elapsed offset. | **live** (D243) | +| `GE_D243CAM` | `src/game/bondview2.c` (`bondviewFrozenCameraTick`, three `CAMERAMODE_POSEND` branches) | Dumps eye/look-at positions (+theta/verta on the `gBondViewCutscene` branch, +dt/angle/pad on the orbit branch) per call. **Found M-143**: the `gBondViewCutscene` branch fires and its look-at (`field_3C4/8/C`) sweeps through a filter catch-up after the cutscene warp — the shake mechanism. | **live** (D243) | +| `GE_D243M` | `src/game/bondview2.c` (`solo_char_load`, `bondviewRemovePlayerBody`, `playerTick`; cached `d243mEnabled`) | M-151 model-lifecycle probe for the Dam-abseil "two Bonds + shake": `D243M: CREATE/REMOVE` at both `bodyModel` sites, `D243M: tick` (idx/model/cam modes/`prop->pos`/`field_488.pos`/`field_3B8`/`field_3C4-8-C`) while the model exists or POSEND/INTRO is active, `D243M: draw` per on-screen render submission (two lines same frame = second Bond instance). Note: `field_3B8` x/z read at 10× world units in the smoke capture — not plain world position. | **live** (D243) | | `GE_D193` | `src/game/frametiming.c` (`waitForNextFrame`, `#ifdef PORT`) | Per-wall-second line: `render=` rendered frames, `sim=` sim ticks delivered (Σ of the `deltaFrames`/`g_ClockTimer` fed downstream), `clamped=` frames whose catch-up hit `FRAMETIMING_PORT_MAX_CATCHUP`, `maxraw=` largest pre-clamp delta. Healthy steady state = `render=30 sim=60 clamped=0 maxraw=2` (measured M-80, all levels idle). Use to check whether the wall-clock timer under-advances under real AI load (D193). Cheap; safe for a full playtest. | **live** (D193) | | `GE_D193A` | `src/game/chr.c` (`chrTick` → `d193aSample`, plus `d193aMoved` in `chrUpdateAnim`; `#ifdef PORT`) | Per-wall-second per-ticked-chr locomotion telemetry (D193/D209): `chr=` chrnum, `act=` ACT_TYPE, `speed=` world-units travelled/s, `gtd=` g_GlobalTimerDelta, `tier=` `act_gopos.unk59` SPEED tier (0 walk / 1 run / 2 sprint; `-1` when not ACT_GOPOS), `gspd=` `act_gopos.speed` (**a turn rate, not a travel rate** — 0 means running straight), `typ=` PROP_TYPE (3=CHR, 6=VIEWER), `tick=`/`move=`/`frz=` chrTick entries vs `chrUpdateAnim` calls vs freeze-gated (move « tick = the chr is being visibility-culled out of its own locomotion; `move=0` at high `speed` = WAYMODE_MAGIC), `hid=` CHRFLAG_HIDDEN, then the root-motion scalars `scale` / `xlscale` / `animrate` / `playspeed` / `mspeed` (model->speed) / `endframe` / `anim` ptr / `aidx=` that pointer reverse-resolved to its `animation_table_ptrs1` index (40 walking, 41 sprinting, 42 running, 107 walking_unarmed — map via `src/game/initanitable.c`). `GE_D193A_CHR=` restricts to one chrnum. **This probe found D209**: Cradle showed `tier=1` (RUN) with `aidx=40/107` (walk anims) on every sample. | **live** (D193/D209) | | `GE_D193B` | `src/game/model.c` (`sub_GAME_7F06D3F4`, `#ifdef PORT`) | Rate-limited (1/97) raw anim root-motion decode trace (D193): `joint=` / `flip=` / `base=` mtx channel index / `frame=` / `tmp=` decoded s16 translation triple from the anim bitstream / `angle=` / `anim` ptr / `bd=`+`bs=` the D32 u32 bitDescriptors/bitStream addresses / `njoints`. M-80 Facility: `base=0`, `angle=0` **always** (root-motion facing delta never non-zero — top lead), `tmp` ≈ (±small, ~1086 hip-height, ±small). Compare `tmp`/`angle` magnitudes against a clean N64 decomp build of the same anim+frame. | **live** (D193) | diff --git a/docs/dev/findings.md b/docs/dev/findings.md index 5a98ec4f..4b1f0bc0 100644 --- a/docs/dev/findings.md +++ b/docs/dev/findings.md @@ -546,7 +546,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121). | D240 | **Player gunshot SFX timing/cadence reportedly does not match the N64 original's rate (M-106, user QA report).** — full `## D240` entry at file tail | **OPEN — not root-caused; static call-site tracing only, no runtime verification possible in this environment.** Confidence LOW-MEDIUM that this is a *new* bug distinct from the D155/D186/D193/D204/D209 timing-keystone family (all of which gate the identical `deltaFrames→speedgraphframes→g_ClockTimer→g_GlobalTimer` chain this SFX trigger consumes).… | | D241 | **Player gunshot SFX goes silent under some conditions — reported on Bunker with the alarm sounding, "or some other factor" — but only for PP7 and AK47, Klobb…** — full `## D241` entry at file tail | **OPEN — not newly root-caused; high confidence this is D207 reproduced, not an independent bug.** Recommend folding this repro into D207 (or closing D241 as a duplicate once D207 is reopened) rather than tracking separately.… | | D242 | **Player-death sequence UX regression vs N64 original: PC requires an LMB click to proceed to the mission-failed/file-select screen; user recalls N64 auto-plays…** — full `## D242` entry at file tail | **NOT A PORT-LOGIC BUG as far as static analysis can tell.** Confidence HIGH that the LMB-click skip path is faithful original behavior (Z_TRIG-mapped, exists unmodified in `bondview2.c`), not a defect to remove.… | -| D243 | **Dam end-of-level cutscene (the abseil/bungee sequence, D148/D160's subject): the camera violently shakes/"spazzes" during each of the scripted camera-angle…** — full `## D243` entry at file tail | OPEN — M-144: CONFIRMED PC-only (user, direct N64 comparison) — real N64 does not show this shake. Mechanism (M-143): the `gBondViewCutscene` look-at branch reads `field_3C4/8/C`, a leaky-integrator low-pass filter of Bond's real position that isn't reset when the scripted cutscene warps him, producing a ~1s look-at snap. Filter math is decomp-exact and `g_ClockTimer`-scaled, so the bug must be upstream: either the position warp itself is wrong/bigger on PC, or a call-ordering desync around the `CAMERAMODE_INTRO` transition. Next: probe raw `field_488.pos`/`field_3B8` around the warp tick. D146 still fully decoupled (zero hits, 3 runs). | +| D243 | **Dam end-of-level cutscene (the abseil/bungee sequence, D148/D160's subject): the camera violently shakes/"spazzes" during each of the scripted camera-angle…** — full `## D243` entry at file tail | OPEN — M-144: CONFIRMED PC-only (user, direct N64 comparison) — real N64 does not show this shake. Mechanism (M-143): the `gBondViewCutscene` look-at branch reads `field_3C4/8/C`, a leaky-integrator low-pass filter of Bond's real position that isn't reset when the scripted cutscene warps him, producing a ~1s look-at snap. Filter math is decomp-exact and `g_ClockTimer`-scaled, so the bug must be upstream: either the position warp itself is wrong/bigger on PC, or a call-ordering desync around the `CAMERAMODE_INTRO` transition. M-151: that probe pass is LANDED (`GE_D243M=1`, `bondview2.c` — bodyModel CREATE/REMOVE + per-tick state + draw count), build-verified; next: the user's interactive Dam capture with `GE_D243M=1 GE_D160=1`. D146 still fully decoupled (zero hits, 3 runs). | | D245 | **IsWater water: two competing patterns with a visible seam that moves as the player looks around (mouse/aim) — "looks almost exactly like skybox" (M-116, user…** — full `## D245` entry at file tail | OPEN — REOPENED, M-142 headless probe found the tc-shift `k` measurably flips during ordinary view rotation (new root-cause evidence); an opt-in `GE_D245_FIXEDSHIFT` candidate is build-verified but needs a live A/B, not shipped as default.… | | D246 | **Tiny pixel strips at the left and right edges of the screen, "like overscan" — look like raw pixels, not 3D graphics (M-116, user QA report).** — full `## D246` entry at file tail | **OPEN — one candidate ruled out (M-116 follow-up), root cause not found.** Step (1) from the original plan is done… | | D247 | **Black bars top and bottom of screen — possible aspect-ratio defect (M-116, user QA report).** — full `## D247` entry at file tail | **NOT A BUG — confirmed faithful, unmodified N64 decomp behaviour, no code change made.** `GE_PCDUMP` captures (both against the pre-existing save and a fresh-EEPROM run, to rule out a stale `Screen` option from earlier test sessions) show the exact same inset on Frigate… | @@ -10711,6 +10711,8 @@ crash class C2 (Facility `-level_34`, Runway `-level_35`): `import_texture_i8` A **M-145: independent review pass on the user's new "two Bonds" observation — the user's third-person-world-model hypothesis is confirmed architecturally; the camera bug and the model lifecycle are one mechanism.** User reports that during the Dam abseil, **two instances of Bond are visible at once — one standing on the dam, one floating in the air playing a jump/fall animation; neither animates correctly** — and suspects all the cutscene bugs (D148 umbrella) relate to a bug with Bond's third-person (world) model. Static trace this pass (no build): **(1) The exit script runs on an invisible, model-less "false GUARD" BG chr.** `alloc_false_GUARDdata_to_exec_global_action()` (`src/game/deb_loadallmodels.c:38`, called unconditionally from level setup at `prop.c:2054`; file IS compiled — `SRC_GAME` glob) binds every ailist with ID ≥ 0x1000 to a **zeroed** `ChrRecord` (chrnum 0xFE, model/prop NULL) ticked by `chrlvActionTick` (`chraction.c:9516`). Dam's exit chain — ai_24 (ID 0x1004: Bond auto-walks off the edge via `bond_set_locked_velocity`, falls until `if_bond_y_pos_less_than(0x98bd)` ≈ s16 −24771, fade, `chr_hide_all`, `camera_switch`) → ai_17 (ID 0x0412: the abseil) — runs on such a driver. Consequence: ai_17's `guard_play_animation(0xb100…)` rappel commands act on the **empty** driver record — `check_if_able_to_then_perform_animation` (`chraction.c:10263`) has no model to animate (D122's C1 NULL-deref crash class hit exactly this call on Dam). **(2) The visible Bond is the player's own third-person model, moved by `chr_try_teleporting_to_pad(0xFD, 0x4b01)`** — 0xFD = `CHR_BOND_CINEMA` (`bondconstants.h:4604`, comment: *"only works when bond has a third person model (intro/exit cutscene)"*), resolved by `chrResolveId` (`chraction.c:9792`) to `g_CurrentPlayer->prop->chr`. The teleport handler (`chrai.c:4064`) sets `prop->pos`, `setsuboffset(model, &pos)`, and `field_488.collision_position`/`current_tile_ptr` — but **not** `field_488.pos` and **not** the filter `field_3B8`. **(3) This reconciles M-143's sweep direction.** For `pos2.y` to rise from −22832 (the void, where ai_24's fall ends) up to +691 (dam top), the filter's input `field_488.pos` must be re-seeded to the post-teleport position — but under `CAMERAMODE_POSEND`, `bondviewUpdatePlayerCollisionPositionFields` (`bondview2.c:~4720`) **skips** the `pos ← collision_position` copy *unless `bodyModel == 0`*. The observed sweep therefore implies a window where Bond's third-person model was **removed** (bodyModel==0) after the teleport — tying the camera shake directly to the model lifecycle, exactly the user's intuition. **(4) Nothing in the script hides Bond:** `chr_hide_all` (`chrai.c:4164`) iterates guard slots only. The double image is most plausibly two live Bond models (stale pre-cutscene model still drawn at its fall position playing the fall anim + teleported cinema model standing on the dam with no rappel anim to play) or a stale draw of a removed model (D75 family) — not yet distinguishable statically. **Confidence: MEDIUM-HIGH** that D243's shake and the double image share the third-person-model-lifecycle root (arithmetic consistency with M-143's log + code trace; the bodyModel==0 window is inferred, not directly observed). **Next (supersedes M-144's single step):** one `#ifdef PORT` probe pass logging per tick around cutscene entry: `field_488.pos`, `field_3B8`, `prop->pos`, `bodyModel` (NULL/non-NULL), `g_CameraMode`, `dword_CODE_bss_80079A18`; plus call-trace on `bondviewRemovePlayerBody` and any `bodyModel` reassignment; plus a per-frame count of Bond-model draws during the cutscene to identify the second instance. That single capture discriminates (a) the re-seed window timing, (b) the double-draw source, (c) whether `guard_play_animation` is a PC no-op. No game-logic changes yet (rule #2). Cross-ref D173/D292: all three are now the same "third-person world model transform/lifecycle" family. +**M-151 (2026-09-16): the M-145 probe pass is LANDED and build-verified — `GE_D243M=1`, all in `src/game/bondview2.c` (`#ifdef PORT`, D250-cached getenv, zero behaviour change when unset).** Three probes: (a) `D243M: CREATE`/`REMOVE` at the file's only two `bodyModel` assignment sites (`solo_char_load` / `bondviewRemovePlayerBody`), with `g_CameraMode` + `dword_CODE_bss_80079A18` at the moment of the transition; (b) `D243M: tick` once per `playerTick()` call while the third-person model exists OR a cutscene camera mode (`CAMERAMODE_POSEND`/`INTRO`) is active — idx, model pointer, both camera modes, `prop->pos`, `field_488.pos`, and the filter fields `field_3B8` + `field_3C4/8/C`; (c) `D243M: draw` once per on-screen `render_pos` submission — a second Bond instance shows up as two lines (different idx or model pointer) at the same frame number. Frame counter is local to `playerTick()` calls (solo = per-frame), cross-referenced by elapsed offset, same convention as front.c's `GE_D243` trace. Smoke-tested on a bare `-level_33` boot (50 s, 2900+ frames, no crash): the level *intro* cutscene exercises the full lifecycle cleanly — frames 1-533 `cam=1` (INTRO) model=NULL, `CREATE frame=534 cam=3` (SWIRL), one tick+draw pair per frame while the model exists, `REMOVE frame=1127 had_model=1 cam=0` (NONE); FPS play afterwards is silent. Two incidental data points from that capture: during SWIRL `f488pos` tracks `prop` exactly and `f3B8`'s x/z read at exactly 10× the prop values (y differs) — i.e. `field_3B8` is not in the same units as world position, treat its magnitude accordingly when reading the live capture. **Next: the user's interactive Dam run with `GE_D243M=1` (plus `GE_D160=1`, optionally `GE_D243CAM=1`) to the abseil — single capture should discriminate (a) re-seed window timing, (b) double-draw source, (c) whether the model-remove/recreate window M-145 suspects actually occurs on PC.** + **Status (from §F table):** **OPEN — M-109 re-analysis of the same session's raw logs corrects the timing/pattern claims above; the D134/pendingGfx race hypothesis is likely wrong.** Confidence MEDIUM on the correction, LOW-MEDIUM on the new lead. Re-reading `data/playtest_d160.log`/`_run2.log` at the `g_framesRendered` level (not just grepping for `D146`) shows the D146 burst fires **~1600-3900 frames (tens of real seconds) *after*** `bossReturnTitleStage`/the `camera_switch(7)` calls, not during them, in **both** runs — "right in the camera-cut window" and "run 1 only" are both wrong as stated above. This also weakens the D134/`pendingGfx` citation: `osSpTaskStartGo` runs `gfx_run()` synchronously and inline (`port/src/libultra.c:1232-1258`), so nothing from the cutscene's own DL submission is still in flight by the time any teardown code runs — the port doesn't have the async-RSP race the hypothesis above assumes. **New lead:** whatever screen renders in that multi-thousand-frame gap after mission-complete is the more likely site — probably `MENU_MISSION_COMPLETE` (`front.c:7267`, `constructor_menu0D_missioncomplete`) or an adjacent briefing/file-select screen in that chain, same general class as D75 (front-end model absence) / D141 (menu bad-pointer bug), not a cutscene-render race. Of the 4 repeating fault addresses, 3 read as literal zero words and only 1 matches the `0xfafbfbfc…` pattern, arguing against a deliberate poison-fill (none found anywhere in `port/`) and for either genuinely-unwritten memory or a decode landing inside unrelated valid data. Next: an env-gated `front.c` menu-transition trace (`MENU_*` id + `g_framesRendered` on every transition after `bossReturnTitleStage`) plus a `GE_PCDUMP` window over frames ~4500-8000 (not the camera-cut window) to name the exact screen/segment. Cross-ref D160 (this supersedes its stale "cutscene skipped" framing with a "cutscene renders corrupted" one, now further corrected to "corruption isn't in the cutscene render at all") and D173 (Cradle's Bond-floating/spinning cutscene bug — same subsystem family, unconfirmed shared root cause). **M-120: the `GE_D243` trace above was BUILT (statically, i.e. written) on branch `investigate/d243-cutscene-race` (`764a1f26`, pushed, not merged) — `src/game/front.c` only, `#ifdef PORT`, logs the target `MENU_*` name + a local per-tick counter on every `menu_init()` state transition. NOT build-verified: two independent sessions (the dispatched subagent, then this session re-verifying in a fresh worktree) both hit the same reproducible failure — `cmake`'s C-compiler ABI check fails with "Cannot create temporary file in C:\Windows\: Permission denied" when gcc is invoked *through* CMake/ninja, even though a bare `gcc -c` invocation from the same shell, same session, succeeds and correctly resolves its temp dir to `%TEMP%`. This is a genuine, reproducible environment issue distinct from the diff's content (static review of the diff found nothing wrong: enum cases all verified to exist in `bondconstants.h`, brace/`#ifdef` balance checked, follows the existing `GE_D160`/`GE_D178` env-gated trace idiom used elsewhere in the same file) — worth a session with a working toolchain investigating the CMake/ninja-vs-bare-gcc environment discrepancy itself as its own item, separate from D243. Next: build `investigate/d243-cutscene-race` on a session where `./build-pc.sh` actually completes, then run the `-level_33` Dam repro with `GE_D243=1` (and the existing `GE_D160`/`GE_PCDUMP` mechanisms) to correlate the trace against the D146 burst's frame range. **M-141: the M-120 toolchain blocker is EXPLAINED and worked around, GE_D243 is now build-verified.** The "Cannot create temporary file in C:\Windows\" failure isn't specific to CMake/ninja vs. bare `gcc -c` — it's this Claude Code session's `Bash` tool shell (Git-for-Windows Bash, not a real MSYS2 MINGW64 window) not exposing a usable `TMP`/`TEMP` to child processes the way an interactive MSYS2 shell does; a bare `gcc -c` from the same shell happened to not hit the temp-file path M-120 tested, which is why it looked clean while the full build wasn't. Fix: explicitly `export TMP=... TEMP=...` (Windows-style path) alongside the already-known `PATH=/c/msys64/mingw64/bin:/c/msys64/usr/bin:$PATH` fix, both before invoking `./build-pc.sh`/`ninja`/`cmake` — now also recorded in `porting-notes.md` §E as a general session-start gotcha, not just a D243-specific note. Cherry-picked the `GE_D243` trace commit (`764a1f26`) cleanly onto `release/v0.3.0` (branch `investigate/d243-fresh`), fixed one thing the original diff got wrong per this project's own D250 rule (`menu_init()` runs ~once/frame during front-end states — the diff's three `getenv("GE_D243")` calls were uncached; now a single cached `static int s_d243` check), and confirmed it builds clean. **Smoke-tested, not fully exercised:** a bare `-level_33` boot runs 1100+ frames with zero crashes and (correctly) zero `D243:` log lines — bare `-level_XX` skips the front end entirely (per this doc's own M-24-era note), so `menu_init()` never runs; this only proves the probe doesn't regress normal boot, not that it captures anything useful yet. **Still blocked on the same thing M-107/M-109/M-120 all were: an actual interactive playthrough** (main menu → Dam → abseil cutscene → mission-complete → whatever screen the D146 burst fires on, ~1600-3900 frames later) with both `GE_D243=1` and `GE_D160=1` set, which needs a human with input, not available in this session. **Possible connection to D173 worth checking in that same playthrough** (per user, M-141): D173 (floating/spinning Bond puppet in cutscenes, e.g. Cradle/Trevelyan) and D243 (stale-memory DL read some seconds after a cutscene-driven `bossReturnTitleStage`) both sit in the scripted-cutscene/level-transition neighborhood and both ultimately touch "Bond's (or a puppet's) in-world model" — no code-level link found this session (D173's own M-106 pass already ruled out the two most likely struct-aliasing sites, and D243's mechanism looks like a stale/freed buffer read in a *different* subsystem, front-end/menu memory rather than a chr/puppet transform), so treat this as an unconfirmed hypothesis to keep in mind while capturing the next live trace, not an established shared root cause. **Steam Deck playtest confirmation, same M-141 session** — user independently reported "cutscene at end of dam level is badly glitched" without prompting, matching this row's own "camera violently shakes" symptom exactly. Second independent confirmation this is real and reproducible, not a one-off; still needs the interactive `GE_D243=1`+`GE_D160=1` capture this row already calls for. ## D244 — Self-hosted CI runner (`corpyvt6agent01`) link failure: `undefined reference to 'objectiveGetStatus_WEAK'` (2026-09-12, first-ever build attempt on the…