From c007f5a9620fef1314c0b33282e4501d4cdf1d88 Mon Sep 17 00:00:00 2001 From: Erik Date: Fri, 12 Jun 2026 12:42:27 +0200 Subject: [PATCH] CLAUDE.md: adopt the condensed structure, updated to current truth (-503/+176) Reconciles the wip/main-local-claudemd-condensation distillation (made ~2026-06-03 by a parallel session) onto post-merge main: - Current state section: ONE status block (<=5 lines + pointers, by rule) + canonical reading order + the two digest entry points + the divergence register; replaces status sediment scattered through Goal/Roadmap sections. - Kept from the merged main (the condensation predated them): the memory/digest rule in How to operate, the divergence-register section + phase-checklist item, de-dated milestone rules. - Dropped: shipped-phase ship-notes, stale next-phase candidate lists, the superseded reference_render_pipeline_state pointer. - Also salvaged from the wip branch: .gitignore entries (.obsidian/, claude-memory junction) + pdb_extract.py __main__ guard. The wip's TextureDump edit predates main's args support (discarded) and its physics-probe edits were STRIP-marked leftovers (discarded). Co-Authored-By: Claude Fable 5 --- .gitignore | 6 + CLAUDE.md | 664 ++++++++----------------------- tools/pdb-extract/pdb_extract.py | 3 +- 3 files changed, 173 insertions(+), 500 deletions(-) diff --git a/.gitignore b/.gitignore index 4e268653..357fded9 100644 --- a/.gitignore +++ b/.gitignore @@ -81,3 +81,9 @@ substep_trace* sg_built.txt # Stray bash-mangled path artifacts from PowerShell-via-bash escaping C[€-￿]* + +# Obsidian vault config (personal, not project-wide) +.obsidian/ + +# Junction to Claude Code per-project memory (Obsidian vault visibility) +claude-memory diff --git a/CLAUDE.md b/CLAUDE.md index a7d83157..58354787 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,4 +1,4 @@ -# acdream — project instructions for Claude +# acdream — project instructions for Claude ## Goal @@ -25,119 +25,40 @@ single source of truth for how the client is structured. All work must align with this document. When the architecture doc and reality diverge, update one or the other — never leave them out of sync. -**WorldBuilder code lives in our tree as of Phase O (shipped 2026-05-21).** -Phase N.4 (2026-05-08) adopted WB's rendering + dat-handling base as a -project reference. Phase O (2026-05-21) extracted the ~33 files / ~7.7K LOC -we actually use into our own namespaces and dropped the two external project -references. `DatCollection` is now the **only** dat reader in process — -`DefaultDatReaderWriter` is gone. `references/WorldBuilder/` remains in-tree -as a read-reference (MIT-licensed; grep it freely), but nothing in -`src/AcDream.*` references it as a project dependency. +**WorldBuilder code lives in our tree.** Phase O extracted ~33 WB files +(~7.7K LOC) into our own namespaces and dropped the two external project +references. `DatCollection` is the **only** dat reader in process — +`DefaultDatReaderWriter` is gone. `references/WorldBuilder/` remains +in-tree as a read-reference (MIT-licensed; grep it freely), but nothing +in `src/AcDream.*` references it as a project dependency. **Where the extracted code lives (post-Phase O):** -- `src/AcDream.Core/Rendering/Wb/` — pure dat/mesh helpers (5 files, ~782 LOC): - `TerrainUtils`, `TerrainEntry`, `RegionInfo`, `SceneryHelpers`, - `TextureHelpers`. No GL dependency; safe to use from Core. -- `src/AcDream.App/Rendering/Wb/` — GL infrastructure + mesh pipeline (~27 files, - ~7K LOC): `ObjectMeshManager`, `WbMeshAdapter`, `WbDrawDispatcher`, - `LandblockSpawnAdapter`, `EntitySpawnAdapter`, `TextureCache`, - `GlobalMeshBuffer`, shader infrastructure, and the EnvCell/portal/scenery/ - terrain-blending pipeline classes. +- `src/AcDream.Core/Rendering/Wb/` — pure dat/mesh helpers (5 files, + ~782 LOC): `TerrainUtils`, `TerrainEntry`, `RegionInfo`, + `SceneryHelpers`, `TextureHelpers`. No GL dependency; safe to use + from Core. +- `src/AcDream.App/Rendering/Wb/` — GL infrastructure + mesh pipeline + (~27 files, ~7K LOC): `ObjectMeshManager`, `WbMeshAdapter`, + `WbDrawDispatcher`, `LandblockSpawnAdapter`, `EntitySpawnAdapter`, + `TextureCache`, `GlobalMeshBuffer`, shader infrastructure, and the + EnvCell/portal/scenery/terrain-blending pipeline classes. -Before re-implementing any AC-specific rendering or dat-handling algorithm, -**read `docs/architecture/worldbuilder-inventory.md` FIRST**. The inventory -describes what we extracted (now in our tree) and what we still write ourselves. -Re-porting from retail decomp when we already have a tested port is how subtle -bugs (the scenery edge-vertex bug, the triangle-Z bug) keep slipping in. Retail -decomp remains the oracle for network, physics, animation, movement, UI, plugin, -audio, chat — see the inventory doc's 🔴 list. +**Modern rendering path is MANDATORY** as of the N.5 ship amendment. +`WbFoundationFlag`, `InstancedMeshRenderer`, and `StaticMeshRenderer` +are deleted. Missing `GL_ARB_bindless_texture` or +`GL_ARB_shader_draw_parameters` throws `NotSupportedException` at +startup. There is no legacy fallback. Engineering cribs (WbMeshAdapter +seams, N.5 SSBO layout, translucency model, gotchas) live in +`memory/reference_modern_rendering_pipeline.md`. -**WB rendering cribs (all paths now in `src/AcDream.App/Rendering/Wb/`):** -- `WbMeshAdapter.cs` — single seam over `ObjectMeshManager`. Owns the mesh - pipeline, drains its staged-upload queue per frame via `Tick()`, populates - `AcSurfaceMetadataTable` with per-batch translucency / luminosity / fog - metadata. Consumes `DatCollection` via `DatCollectionAdapter` (O-D7 fallback - path; `ObjectMeshManager` has 26 internal `_dats.X` call sites that exceed - the inline-swap threshold — the adapter bridges our `IDatCollection` to the - `IDatReaderWriter` interface WB's internals expect). -- `WbDrawDispatcher.cs` — production draw path. Groups all visible (entity, - batch) pairs, single-uploads the matrix buffer, fires one - `glDrawElementsInstancedBaseVertexBaseInstance` per group with `BaseInstance` - pointing at the slice. Per-entity frustum cull, opaque front-to-back sort, - palette-hash memoization. -- `LandblockSpawnAdapter.cs` / `EntitySpawnAdapter.cs` — bridge spawn lifecycle - to ref-counts. Atlas tier (procedural) goes via Landblock; per-instance tier - (server-spawned, palette/texture overrides) goes via Entity. -- **Modern path is mandatory as of N.5 ship amendment (2026-05-08).** - `WbFoundationFlag`, `InstancedMeshRenderer`, and `StaticMeshRenderer` - are deleted. Missing `GL_ARB_bindless_texture` or - `GL_ARB_shader_draw_parameters` throws `NotSupportedException` at - startup. There is no legacy fallback. -- **The modern rendering path** (GL 4.3 + bindless) packs every mesh - into a single global VAO/VBO/IBO. Each batch references its slice - via `FirstIndex` (offset into IBO) + `BaseVertex` (offset into VBO). - Honor those offsets when issuing draws — `DrawElementsInstanced` - with `indices=0` will draw every entity's first triangle from the - global mesh, not the per-batch range. (This is exactly the - exploded-character bug we hit during Task 26.) -- **`ObjectRenderBatch.SurfaceId` is unset** — the actual surface - id lives in `batch.Key.SurfaceId` (the `TextureKey` struct). -- **`ObjectMeshManager.IncrementRefCount` only bumps a counter** — it - does NOT trigger mesh loading. You must explicitly call - `PrepareMeshDataAsync(id, isSetup)` to fire the background decode. - Result auto-enqueues to `_stagedMeshData` which `Tick()` drains. - `WbMeshAdapter` does this for you on first registration. -- **N.5 modern dispatch** (`docs/superpowers/specs/2026-05-08-phase-n5-modern-rendering-design.md`) - uses bindless textures + multi-draw indirect on top of N.4's grouped - pipeline. Per frame: three SSBO uploads (`_instanceSsbo` mat4 per - instance @ binding=0; `_batchSsbo` `(uvec2 textureHandle, uint layer, - uint flags)` per group @ binding=1; `_indirectBuffer` - `DrawElementsIndirectCommand[]` opaque-section + transparent-section). - Two `glMultiDrawElementsIndirect` calls per frame, one per pass. - Total ~12-15 GL calls per frame for entity rendering regardless of - scene complexity. -- **`TextureCache` requires `BindlessSupport`** for the modern path. - Three `Bindless`-suffixed `GetOrUpload*` methods return 64-bit handles - made resident at upload time, backed by parallel Texture2DArray uploads - (`UploadRgba8AsLayer1Array`). The legacy `uint`-returning methods stay - for Sky / Terrain / Debug / particle paths that still sample via - `sampler2D`. After N.6 retires legacy renderers, the legacy upload path - + caches can be deleted. -- **Translucency model is two-pass alpha-test** (matches original WB), not - per-blend-mode subpasses. Opaque pass discards `α<0.95`; transparent - pass discards `α≥0.95` AND `α<0.05`. Native `Additive` blend renders - as alpha-blend on GfxObj surfaces — falsifiable; if a magic-content - regression shows up, add a third indirect call with - `glBlendFunc(SrcAlpha, One)` per spec §6 fallback (~30 min change). -- **Per-instance highlight (selection blink) is reserved — open - backlog, no scheduled phase.** `mesh_modern.vert`'s `InstanceData` - struct has a documented hook for `vec4 highlightColor`. Whoever - eventually picks it up finds the hook there; the change is localized: - extend `InstanceData` stride 64→80 bytes, add the field, mix into - fragment color in `mesh_modern.frag`. ~30 min when the time comes. -- `src/AcDream.App/Rendering/TerrainModernRenderer.cs` — terrain dispatcher - on N.5's modern primitives. Mirrors the original WB `TerrainRenderManager` - pattern (single global VBO/EBO + slot allocator + `glMultiDrawElementsIndirect`) - but driven by acdream's `LandblockMesh.Build` so retail's `FSplitNESW` - formula is preserved (issue #51 resolved). Atlas handles bound via the - uvec2 + `sampler2DArray(handle)` constructor pattern (NOT the direct - `uniform sampler2DArray` + `glProgramUniformHandleARB` form, which - GL_INVALID_OPERATIONs on at least one driver). -- **Two-tier streaming architecture (Phase A.5, shipped 2026-05-10).** - `src/AcDream.App/Streaming/` owns the full streaming pipeline. Key types: - `StreamingRegion` (two-radius Chebyshev window: N₁=near, N₂=far; produces - `TwoTierDiff` with 5 transition lists per tick), `StreamingController` - (render-thread coordinator: routes `TwoTierDiff` to the worker queue and - drains completions up to `MaxCompletionsPerFrame` per frame), - `LandblockStreamer` (single background worker thread: `LoadFar` = heightmap - + mesh only, `LoadNear` = heightmap + `LandBlockInfo` + scenery + mesh, - `PromoteToNear` = `LandBlockInfo` + scenery only), - `GpuWorldState` (render-thread entity state: `AddEntitiesToExistingLandblock` - for promotions, `RemoveEntitiesFromLandblock` for demotions). - Default: N₁=4 (81 near LBs, full detail), N₂=12 (544 far LBs, terrain - only). Quality Preset system (`QualitySettings.From(preset)`) controls - both radii and MSAA/anisotropic/A2C/completions-per-frame as a unit. - Spec: `docs/superpowers/specs/2026-05-09-phase-a5-two-tier-streaming-design.md`. +Before re-implementing any AC-specific rendering or dat-handling +algorithm, **read `docs/architecture/worldbuilder-inventory.md` FIRST**. +The inventory describes what we extracted (now in our tree) and what we +still write ourselves. Re-porting from retail decomp when we already +have a tested port is how subtle bugs (the scenery edge-vertex bug, the +triangle-Z bug) keep slipping in. Retail decomp remains the oracle for +network, physics, animation, movement, UI, plugin, audio, chat — see +the inventory doc's 🔴 list. **Execution model:** the active source of truth is the **milestones doc** (`docs/plans/2026-05-12-milestones.md`) for "what are we building right @@ -150,26 +71,28 @@ live under `docs/superpowers/specs/`. The codebase is organized by layer (see architecture doc + the **Code Structure Rules** section below). Plans live in `docs/plans/`, -research in `docs/research/`, persistent project memory in `memory/`. +research in `docs/research/`, persistent project memory in `memory/` +and `~/.claude/projects/.../memory/` (the latter is browsable in +Obsidian via the `claude-memory/` junction in the repo root; see +`memory/reference_obsidian_vault.md`). **UI strategy:** three-layer split — swappable backend (ImGui.NET + `Silk.NET.OpenGL.Extensions.ImGui` for Phase D.2a, custom retail-look toolkit for D.2b later) / stable `AcDream.UI.Abstractions` layer (ViewModels + Commands + `IPanel` / `IPanelRenderer`) / unchanged game -state. **As of Phase I (2026-04-25), ImGui hosts every dev/debug -panel** — Vitals, Chat, Debug. The previous custom-StbTrueTypeSharp -`DebugOverlay` was deleted in I.2; `TextRenderer` + `BitmapFont` are -kept alive specifically for the future world-space HUD (D.6 — damage -floaters, name plates) where ImGui can't reach into the 3D scene. -D.2b remains the long-term retail-look path (panels reskinned one at a -time using dat assets); ImGui persists forever as the -`ACDREAM_DEVTOOLS=1` overlay. **All plugin-facing UI targets -`AcDream.UI.Abstractions` — never import a backend namespace from a -panel.** Full design: `docs/plans/2026-04-24-ui-framework.md`. +state. **As of Phase I, ImGui hosts every dev/debug panel** — Vitals, +Chat, Debug. The previous custom-StbTrueTypeSharp `DebugOverlay` was +deleted in I.2; `TextRenderer` + `BitmapFont` are kept alive +specifically for the future world-space HUD (D.6 — damage floaters, +name plates) where ImGui can't reach into the 3D scene. D.2b remains +the long-term retail-look path (panels reskinned one at a time using +dat assets); ImGui persists forever as the `ACDREAM_DEVTOOLS=1` +overlay. **All plugin-facing UI targets `AcDream.UI.Abstractions` — +never import a backend namespace from a panel.** Full design: +[`docs/plans/2026-04-24-ui-framework.md`](docs/plans/2026-04-24-ui-framework.md). Memory cribs: `memory/project_chat_pipeline.md` (chat pipeline as of Phase I), `memory/project_input_pipeline.md` (input pipeline as of -Phase K). UI architecture full design at -[`docs/plans/2026-04-24-ui-framework.md`](docs/plans/2026-04-24-ui-framework.md). +Phase K). **Input pipeline:** `src/AcDream.UI.Abstractions/Input/` (action enum, `KeyChord`, `KeyBindings`, multicast `InputDispatcher` with scope @@ -179,9 +102,43 @@ stack + modal capture for rebind UX) + `src/AcDream.App/Input/` `KeyBindings.RetailDefaults()` matching `docs/research/named-retail/retail-default.keymap.txt`). The Settings panel (F11 / View → Settings) lets users remap any action via -click-to-rebind. As of Phase K (2026-04-26), ALL keyboard / mouse -input flows through the dispatcher — no IsKeyPressed polling outside -the per-frame movement queries. +click-to-rebind. As of Phase K, ALL keyboard / mouse input flows +through the dispatcher — no IsKeyPressed polling outside the per-frame +movement queries. + +## Current state + +**Currently working toward: M1.5 — Indoor world feels right** +(M1 — Walkable + clickable world — landed 2026-05-16 via Phase B.6). +The holistic building-render port (Option A: ONE `DrawInside(viewer_cell)`, +no inside/outside branch; BR-2..BR-7/T1..T6) is SHIPPED and user-gated, +as are the 2026-06-12 closes: #119/#128 tower stairs, #112 cottage +transparency. Open render/physics ledger: #113 re-check, #124, #129, +#130, #108-residual, #116, #127 (leads in ISSUES.md). Keep this +paragraph ≤5 lines + pointers — detail lives in the docs below, NOT here. + +For canonical state, read in this order: +- [`docs/plans/2026-05-12-milestones.md`](docs/plans/2026-05-12-milestones.md) — milestone targets + freeze list per milestone +- [`docs/plans/2026-04-11-roadmap.md`](docs/plans/2026-04-11-roadmap.md) — what's shipped, what's in flight, what's next +- [`docs/ISSUES.md`](docs/ISSUES.md) — open + recently closed bugs (tactical) +- [`docs/architecture/retail-divergence-register.md`](docs/architecture/retail-divergence-register.md) — every known acdream-vs-retail deviation (see the register rules in the workflow section) + +**Domain entry points (START HERE before domain work):** +- `claude-memory/project_render_pipeline_digest.md` — indoor render / doorway-FLAP / portal flood SSOT, with the DO-NOT-RETRY table +- `claude-memory/project_physics_collision_digest.md` — physics / collision / cell-membership SSOT, with the DO-NOT-RETRY table + +For engineering reference (read on demand, not at session start): +- `memory/reference_modern_rendering_pipeline.md` — N.4/N.5 bindless+MDI dispatch, WbMeshAdapter/WbDrawDispatcher, translucency model +- `memory/reference_two_tier_streaming.md` — Phase A.5 streaming architecture +- `memory/reference_indoor_cell_tracking.md` — Phase 2 + A4 portal-based cell tracking + multi-cell BSP iteration +- `memory/reference_obsidian_vault.md` — Obsidian-as-memory-viewer setup + the memory-handling protocol +- `memory/reference_ghidra_projects.md` — Ghidra + GhidraMCP for acclient RE +- `memory/reference_repos.md` — what each `references/*` repo is for + +The memory dir also holds `feedback_*.md` lessons-learned (cross-cutting +patterns the project has agreed on) and `project_*.md` per-subsystem +cribs (chat pipeline, input pipeline, interaction pipeline, etc.). +See `memory/MEMORY.md` for the index. ## Code Structure Rules @@ -202,8 +159,8 @@ pursuing live in [`docs/architecture/code-structure.md`](docs/architecture/code- as part of the change. 2. **`AcDream.Core` must not depend on the window / GL / backend - projects, except via documented interop seams.** As of Phase O - (2026-05-21), the only allowed seams are the extracted helpers in + projects, except via documented interop seams.** As of Phase O, + the only allowed seams are the extracted helpers in `src/AcDream.Core/Rendering/Wb/` (`TerrainUtils`, `TerrainEntry`, `RegionInfo`, `SceneryHelpers`, `TextureHelpers` — stateless, no GL). The former `WorldBuilder.Shared` and `Chorizite.OpenGLSDLBackend.Lib` @@ -211,8 +168,7 @@ pursuing live in [`docs/architecture/code-structure.md`](docs/architecture/code- paths. New Core code that needs a GL surface must define an interface in Core and let `AcDream.App` implement it — never the reverse. If you need to add a project reference to Core, the change must come with an - inventory-doc - update explaining why. + inventory-doc update explaining why. 3. **UI panels target `AcDream.UI.Abstractions` only.** No panel may import `AcDream.UI.ImGui` or any backend namespace. ViewModels, @@ -253,14 +209,15 @@ pursuing live in [`docs/architecture/code-structure.md`](docs/architecture/code- lives in `claude-memory/` (the auto-loaded index is `MEMORY.md`). Before starting work in a domain that has a **digest**, read it first: `project_render_pipeline_digest.md` (indoor render / doorway FLAP) and `project_physics_collision_digest.md` -(physics / collision / #98 / membership). Each digest is current-truth-on-top -plus a DO-NOT-RETRY table — it supersedes the dated banners that used to sprawl -across this file. The memory-handling protocol (distill-don't-journal, the digest -pattern, tags, recall + capture) is in `reference_obsidian_vault.md`. **Do NOT add -new dated banners to this file — update the relevant digest instead.** Obsidian -(auto-started in the main repo via a SessionStart hook) is the live search / graph / -tag lens over `claude-memory/` through the `mcp__obsidian__*` tools when it's -running; the files read and write the same with it closed. +(physics / collision / membership). Each digest is current-truth-on-top +plus a DO-NOT-RETRY table — it supersedes dated banners. The memory-handling +protocol (distill-don't-journal, the digest pattern, tags, recall + capture) +is in `reference_obsidian_vault.md`. **Do NOT add new dated status banners to +this file — update the relevant digest (or the Current state pointer list) +instead.** Obsidian (auto-started in the main repo via a SessionStart hook) is +the live search / graph / tag lens over `claude-memory/` through the +`mcp__obsidian__*` tools when it's running; the files read and write the same +with it closed. **You are the lead engineer AND architect on this project at all times.** You own the architecture (`docs/architecture/acdream-architecture.md`), @@ -393,9 +350,9 @@ The triangle-boundary Z bug cost 5 failed fix attempts from guessing. The animation frame-swap bug cost 4 failed attempts. Every time we checked the decompiled code first, we got it right on the first try. **Now we have named retail symbols too — Step 0 cuts most lookups -from 30 minutes to 5 seconds. And as of 2026-04-30, when "what does -retail actually DO at runtime?" is the question and decomp alone -isn't enough, attach cdb to a live retail client (Step -1).** +from 30 minutes to 5 seconds. When "what does retail actually DO at +runtime?" is the question and decomp alone isn't enough, attach cdb +to a live retail client (Step -1).** ### For each new feature or bug fix: @@ -528,10 +485,10 @@ Before marking any phase as done: **When the question is "what does retail actually DO frame-by-frame?"** the decomp alone is often not enough — code paths interact with state (LastKnownContactPlane, transient flags, accumulated counters) in ways -that aren't obvious from reading. As of 2026-04-30 we have a working -toolchain to attach Windows' console debugger (cdb.exe) to a live -retail acclient.exe with full PDB symbols and capture state at any -breakpoint. **Use this when guessing has failed twice in a row.** +that aren't obvious from reading. We have a working toolchain to attach +Windows' console debugger (cdb.exe) to a live retail acclient.exe with +full PDB symbols and capture state at any breakpoint. **Use this when +guessing has failed twice in a row.** ### What we have @@ -636,11 +593,6 @@ breakpoint. **Use this when guessing has failed twice in a row.** - **Network protocol questions** — `holtburger` references + ACE source + Wireshark are the right tools, not cdb. -This toolchain was used to settle the L.5 steep-roof investigation: -30Hz physics tick (vs our 60Hz), `kill_velocity` gating, -`set_collide` rate per minute. See commit history around 2026-04-30 -for the trace data and the decisions it drove. - ## MCP servers (live tooling) Two MCP servers extend the static decomp + cdb workflow with live @@ -740,64 +692,9 @@ acdream operates at **two altitudes** above the daily commit: is where you orient when the project feels half-built and you're not sure what to work on. Phases are too granular to feel like progress; this doc is the multi-week target. -- **`docs/plans/2026-04-11-roadmap.md`** — the strategic roadmap (next - section). Phase-level index. This is where you orient when you know - the milestone and need the next concrete sub-phase. - -**M1 landed 2026-05-16** via Phase B.6 (`d640ed7`). L.2 collision + -B.4 interaction + B.5 pickup + B.6 server-driven auto-walk all -shipped. The four demo targets work end-to-end: walk Holtburg, open -inn door, click NPC, pick up item. Freeze list active — M1's phases -are off-limits until M7 polish. Writeup at top of M1 block in -`docs/plans/2026-05-12-milestones.md`. - -**Phase O — DatPath Unification — SHIPPED 2026-05-21.** ONE thing -touches the DATs. ~33 WB files (~7.7K LOC) extracted into -`src/AcDream.{Core,App}/Rendering/Wb/`. Project references to -`WorldBuilder.Shared` + `Chorizite.OpenGLSDLBackend` dropped. -`DefaultDatReaderWriter` eliminated; `DatCollection` is the only dat -reader. `WbMeshAdapter` consumes it via `DatCollectionAdapter` -(O-D7 fallback; 26 `_dats.*` call sites exceeded the inline-swap -threshold). `references/WorldBuilder/` stays in-tree as read-reference. -Visual side-by-side passed: Holtburg town, inn interior, dungeon all -render identically to pre-O. Spec: -[`docs/superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md`](docs/superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md). - -**Currently working toward: M1.5 — Indoor world feels right** (resumed -from 2026-05-20 baseline after Phase O ship). - -**Indoor render & the doorway "FLAP" — read the digest, not banners.** -The full current state, the root cause, the DO-NOT-RETRY list, the ⚠️ -`ACDREAM_PROBE_FLAP`→white-textures landmine, and the detail-doc pointers are -distilled in **`claude-memory/project_render_pipeline_digest.md`** (auto-loaded -via MEMORY.md). As of 2026-06-09 (HEAD `a1b12df`): governing direction is -**Option A — one `DrawInside(viewer_cell)`, NO inside/outside branch**; -R-A1/R-A2/R-A2b shipped (outside-looking-in flap GONE, seams GONE, portal-flood -churn KILLED); remaining = the visible indoor flap narrowed to §4 (edge-on -doorway grey + corner camera-seal). Render roots at the **VIEWER** cell, not the -player cell. Read the digest before any render/flap work — it supersedes the -dated render banners that used to live here. - -**Physics / collision / cell-membership — read the digest, not banners.** The #98 cellar-ascent saga, the A6.P* phase ledger, the door/step-up (P2) work, the phantom-collision fixes (#100/#101), P1 membership, the apparatus inventory, and the full 18-entry DO-NOT-RETRY list are distilled in **`claude-memory/project_physics_collision_digest.md`** (auto-loaded via MEMORY.md). Current state (2026-06-09): #98 CLOSED via the `b3ce505` stopgap (a WORKAROUND — it introduced #99 door run-through, OPEN HIGH); P2 cellar-lip FIXED (`cc4590f`, visual-gated); P1 membership matches retail (no port needed); #100/#101 CLOSED. The open debt is the per-cell shadow architecture (A6.P4) that closes #99. Read the digest before any collision/physics/membership work — it supersedes the dated A6 banners that used to live here. - -**Today's pre-M1.5 baseline (2026-05-20).** Five surgical fixes -shipped to close the user-reported "logged in inside the inn, ran -through walls" bug: A4 (multi-cell BSP iteration, `691493e`), -#89 (sphere-overlap in CheckBuildingTransit, `7ac8f54`), -#90 (sphere-overlap stickiness in ResolveCellId, `4ca3596` — WORKAROUND, -flagged for removal in A6.P4), #91 (indoor cell shadows in -FindObjCollisions, `c0d8405`), #92 (server cell id at player-mode -entry, `23ab173`). 1147 + 8 baseline maintained throughout. Walls -+ furniture block correctly at Holtburg inn and surrounding cottages -as of visual verification 2026-05-20. M1.5 starts from this baseline. - -**M2 ("Kill a drudge") — deferred.** Equip a sword, walk to a drudge, -swing, see damage in chat, watch the swing animation, drudge dies -and drops loot, pick up the loot, open inventory and see it. Phases -to ship after M1.5: F.2 (Inventory panel), F.3 (Combat math + damage -flow), F.5a (visible-at-login dev panels — Attributes / Skills / -Equipped / Inventory list, minimal ImGui), L.1c (combat animation -wiring), L.1b (command router prereq). ~6–10 weeks once M1.5 lands. +- **`docs/plans/2026-04-11-roadmap.md`** — the strategic roadmap. + Phase-level index. This is where you orient when you know the + milestone and need the next concrete sub-phase. **Work-order autonomy — the meta-rule.** You decide what to work on next, always. **The user does NOT pick between phases, milestones, or @@ -816,15 +713,16 @@ four below actually work. **The four motivation-keeping rules:** 1. **One active milestone at a time.** Work that isn't on the critical - path to M1 gets filed in `docs/ISSUES.md` with a `post-M1` tag and - muted. This is the single rule that kills the "jumping between - things" feeling. If a phase isn't part of the current milestone, it - doesn't get touched — even if it's tempting, even if it would be - "quick", even if it would be "while I'm here." + path to the current milestone gets filed in `docs/ISSUES.md` with + the appropriate `post-Mx` tag and muted. This is the single rule + that kills the "jumping between things" feeling. If a phase isn't + part of the current milestone, it doesn't get touched — even if + it's tempting, even if it would be "quick", even if it would be + "while I'm here." -2. **Frozen phases are off-limits.** M0's ~25 shipped phases are frozen - until M7's polish pass. Concretely: no rework on streaming, chat, - input, the WB rendering migration, sky/lighting, the particle +2. **Frozen phases are off-limits.** Shipped-milestone phases are + frozen until M7's polish pass. Concretely: no rework on streaming, + chat, input, the WB rendering migration, sky/lighting, the particle system, or the network handshake. Those are done. Don't revisit them — even if you see something that could be 10% better. Visual nice-to-haves and architecture second-guesses on frozen phases are @@ -835,16 +733,18 @@ four below actually work. When a milestone's demo scenario is functionally complete, update `2026-05-12-milestones.md` with a one-paragraph writeup describing what works end-to-end, flip the freeze list, and update the - "currently working toward" line in this CLAUDE.md to the next - milestone. Do NOT ask the user to record a demo video — they find - it pointless. The milestones doc + the CLAUDE.md flip ARE the - milestone artifact. Phases ship; milestones land. + "currently working toward" line in this CLAUDE.md's **Current + state** section to the next milestone. Do NOT ask the user to + record a demo video — they find it pointless. The milestones doc + + the CLAUDE.md flip ARE the milestone artifact. Phases ship; + milestones land. 4. **State both altitudes at session start.** First action of any - session: "Currently working toward M1 — Walkable + clickable world. - Current phase: L.2. Next concrete step: [whatever]." This keeps the - high-level orientation visible alongside the immediate task and - makes mid-session drift obvious. + session: "Currently working toward [milestone]. Current phase: + [phase]. Next concrete step: [whatever]." This keeps the high-level + orientation visible alongside the immediate task and makes + mid-session drift obvious. The **Current state** section at the + top of this file is the always-current snapshot. When reality and the milestones diverge — a phase grows beyond the milestone's scope, a demo scenario turns out to be unreachable without @@ -868,228 +768,6 @@ acdream's plan lives in two files committed to the repo: acceptance criteria. Do not drift from the spec without explicit user approval. -**Indoor walking Phase 2 — Portal-based cell tracking shipped -2026-05-19.** Six commits: -- `1969c55` — CellBSP + Portals wired into CellPhysics (`PortalInfo` struct, `VisibleCellIds`) -- `aad6976` — `CellTransit.FindCellList` + `FindTransitCellsSphere` + `AddAllOutsideCells`; `ResolveCellId` rename -- `069534a` — `BuildingPhysics` + `CheckBuildingTransit` for outdoor→indoor entry via `BldPortalInfo` -- `702b30a` — code-review polish (DRY cell-id derivation, `PortalFlags.ExactMatch` enum, docs) -- `3ffe1e4` — critical fix: pass foot-sphere center (`GlobalSphere[0].Origin`) not `CheckPos` to `ResolveCellId` -- `eb0f772` — `TryFindIndoorWalkablePlane` synthesizes indoor walkable plane from cell floor poly - -**#86** (click selection penetrates walls) — **CLOSED** (Phase 1 Cluster A). -**#84** (blocked by air indoors) — **FULLY CLOSED.** Spawn-in-building variant -closed by Phase 1 (Phase D AABB containment). Wall-block-from-inside variant -closed by Phase 2 (portal-graph traversal). -**#85** (pass through walls outside→in) — **CLOSED** by Phase 2. -`CheckBuildingTransit` promotes CellId via the building-shell portal graph -on outdoor→indoor entry; indoor-BSP collision fires from both sides. -**#87** (indoor portal-based cell tracking) — **CLOSED** by Phase 2. -**#88** (indoor static objects vibrate) — **FILED** (pre-existing, Medium). -**#89** (port `BSPQuery.SphereIntersectsCellBsp`) — **FILED** (Low, documented -approximation in `CheckBuildingTransit`). -Diagnostic infrastructure: `[indoor-bsp]`, `[cell-cache]`, `[cell-transit]`, -`[check-bldg]` probes all stay in place. -Handoff: [`docs/research/2026-05-19-indoor-walking-phase2-shipped-handoff.md`](docs/research/2026-05-19-indoor-walking-phase2-shipped-handoff.md). -Phase 1 handoff: [`docs/research/2026-05-19-cluster-a-shipped-handoff.md`](docs/research/2026-05-19-cluster-a-shipped-handoff.md). - -**Indoor walking Phase A4 — Multi-cell BSP iteration shipped 2026-05-20.** -Three commits land the slices (with one revert/reapply during visual -verification proving A4 wasn't the cause of the bug that surfaced): -- `e6369e2` — `CellTransit.FindCellSet` overload exposes the candidate set -- `493c5e5` — `Transition.CheckOtherCells` + `ApplyOtherCellResult` combine helper -- `691493e` — wire `CheckOtherCells` into `FindEnvCollisions` (orig `967d065`, revert `3add110`, reapply) - -Ports retail's `CTransition::check_other_cells` at -`acclient_2013_pseudo_c.txt:272717-272798`. After the primary cell's BSP -returns OK, every other cell the foot-sphere overlaps is queried. Halt -on first Collided/Adjusted/Slid; Slid clears the contact-plane fields. -10 new unit tests; 1139 + 8 baseline maintained. - -**Visual verification surfaced a separate, pre-existing M2 blocker**: -at the Holtburg inn doorway, the CellId ping-pongs between outdoor -`0xA9B40022` and indoor vestibule `0xA9B40164` every few ticks. Indoor -BSP DOES detect walls (Collided/Adjusted/Slid fire on push-back), but -the push-back exits the indoor CellBSP volume → ResolveCellId -reclassifies as outdoor → wall checks bypassed on outdoor ticks → net -appearance "walls walk through." Bug reproduces fully with A4 reverted -(see `launch-revert2.log`), confirming A4 is not the cause. A4 is -correct and tested but **dormant in practice** until the ping-pong is -fixed. Handoff: -[`docs/research/2026-05-20-phase-a4-shipped-cell-pingpong-finding.md`](docs/research/2026-05-20-phase-a4-shipped-cell-pingpong-finding.md). - -**Next: cell-tracking ping-pong fix.** Retail oracle: -`acclient_2013_pseudo_c.txt:308742-308783` (`CObjCell::find_cell_list` -Position-variant). Look for the cell-array hysteresis / stickiness -logic that prevents flipping CellId on a single push-back. Likely -modifies `PhysicsEngine.ResolveCellId` to prefer the previous indoor -classification when the sphere is close to the indoor CellBSP volume. - -**Next phase is Claude's choice** per work-order autonomy. Candidates: -M2 critical path (F.2 / F.3 / F.5a / L.1c / L.1b — kill-a-drudge demo); -or the pre-existing "next phase candidates" list below. - -**Previously in Phase L.2 (Movement & Collision Conformance).** L.2a slices -1+2+3 + L.2d slice 1+1.5 + L.2g slice 1 + L.2g slice 1b + L.2g slice 1c + -**Phase B.4b** + **Phase B.4c** all shipped and visual-verified 2026-05-13; -**Phase B.5** (ground-item pickup, F-key) shipped and visual-verified -2026-05-14. The M1 demo target *"pick up an item"* is met for the -close-range path — single-click a ground item to select, walk within -~0.6 m of it, press F, and the item is removed from the world and added -to the player's inventory. Wire chain: `InteractRequests.BuildPickUp` -sends `PutItemInContainer (0xF7B1/0x0019)`; ACE despawns the item with -`GameMessagePickupEvent (0xF74A)` (NOT `0xF747 DeleteObject` — the -distinction surfaced during visual testing and is fixed by the new -`PickupEvent.cs` parser routed through the shared `EntityDeleted` -event). The M1 demo target *"open the inn door"* remains met from B.4b -+ B.4c. Issue #57 (B.4 handler gap) is closed. Issue #58 (door swing -animation) is closed by B.4c. Issues #61 (link→cycle boundary flash), -#62 (PARTSDIAG null-guard), **#63 (server-initiated MoveToObject -auto-walk not honored — blocks out-of-range pickup / Use)**, and **#64 -(local-player pickup animation does not render)** are filed as -M1-deferred follow-up. - -**B.5 ship handoff:** [`docs/research/2026-05-14-b5-shipped-handoff.md`](docs/research/2026-05-14-b5-shipped-handoff.md) -— full evidence for the 5 commits across InteractRequests / GameWindow / WorldSession + the bonus `PickupEvent (0xF74A)` wire-handler fix that closes the despawn gap. -**B.4c ship handoff:** [`docs/research/2026-05-13-b4c-shipped-handoff.md`](docs/research/2026-05-13-b4c-shipped-handoff.md) -— full evidence for the 4 commits + 2 bonus discoveries (stance-value wrong -`0x01` vs `0x3D` causing underground doors; link→cycle boundary flash). -**B.4b ship handoff:** [`docs/research/2026-05-13-b4b-shipped-handoff.md`](docs/research/2026-05-13-b4b-shipped-handoff.md) -— full evidence for the 9 commits + 4 bonus discoveries (double-click dead -code, DoubleClick gate, CollisionExemption, ServerGuid→Id translation). -**L.2g slice 1 ship handoff:** [`docs/research/2026-05-12-l2g-slice1-shipped-handoff.md`](docs/research/2026-05-12-l2g-slice1-shipped-handoff.md). -**L.2d ship handoff:** [`docs/research/2026-05-13-l2d-slice1-shipped-handoff.md`](docs/research/2026-05-13-l2d-slice1-shipped-handoff.md). - -**Phase L.2a (Truth & Diagnostics) slices 1-3 shipped 2026-05-12.** -Three commits land the L.2 "make every bad movement outcome explainable" -diagnostic foundation. Slice 1 (`ebef820`) adds runtime-toggleable -`ACDREAM_PROBE_RESOLVE` (one `[resolve]` line per -`PhysicsEngine.ResolveWithTransition` call) + `ACDREAM_PROBE_CELL` (one -`[cell-transit]` line per `PlayerMovementController.CellId` change), -both backed by a new `AcDream.Core.Physics.PhysicsDiagnostics` static -class and mirrored as DebugPanel checkboxes. Slice 2 (`e0c08bc`) extends -the `[resolve]` line with `obj=0x...` attribution. Slice 3 (`a068292`) -populates the previously-stub `CollisionInfo.CollideObjectGuids` / -`LastCollidedObjectGuid` (declared in `TransitionTypes.cs` but never -written anywhere) at the per-object iteration in `FindObjCollisions`, -so the slice-2 promise is now actually delivered. Visual-verified at -the Holtburg Town doorway: probes captured 140 wall hits attributed to -`obj=0xA9B47900` (landblock-baked static = the building itself, -**NOT** a door entity), confirming L.2d sub-direction as **port -`CBuildingObj` collision + per-cell walkability** rather than door- -state-toggle. Plus a definitive L.2e finding: player `CellId` tracked -as bare low byte (`0x00000029`) with no landblock prefix. - -**Phase C.1.5b (per-part PES transforms + dat-hydrated entity DefaultScript) -shipped 2026-05-12.** Closes issue #56. `SetupPartTransforms.Compute(setup)` -walks `PlacementFrames[Resting]` → `[Default]` → first-available and -returns one `Matrix4x4` per Setup part; `ParticleHookSink.SpawnFromHook` -now transforms each `CreateParticleHook.Offset` through -`partTransforms[PartIndex]` before applying entity rotation, so -multi-emitter scripts distribute across mesh parts instead of collapsing -to entity root. The `EntityScriptActivator.OnCreate` `ServerGuid==0` -guard was relaxed: it now keys by `entity.ServerGuid` when non-zero, else -`entity.Id` (the `0x40xxxxxx` interior-entity range is collision-free -with server guids, so no synthetic-ID scheme is needed). `GpuWorldState` -fires the activator from 4 new sites — `AddLandblock` + -`AddEntitiesToExistingLandblock` (Far→Near promotion) for OnCreate, -`RemoveLandblock` + `RemoveEntitiesFromLandblock` (Near→Far demotion) -for OnRemove — so dat-hydrated EnvCell statics (inn fireplaces, building -decorations) and exterior stabs (cottage chimneys) now activate their -`Setup.DefaultScript` automatically. **Reality discovery during design -(folded into spec §3):** EnvCell `StaticObjects` are already hydrated as -`WorldEntity` instances by `GameWindow.BuildInteriorEntitiesForStreaming` -with stable `entity.Id` in `0x40xxxxxx` — the handoff's §4 Q1/Q2 -(synthetic ID scheme, separate walker class) were mooted by this. -**Visual-verified 2026-05-12** at Holtburg Town network portal (no -ground-burial, distributed swirl), Inn fireplace flames, cottage -chimney smoke, and a spell cast on `+Acdream`. Plan archived at -[`docs/superpowers/plans/2026-05-13-phase-c1.5b.md`](docs/superpowers/plans/2026-05-13-phase-c1.5b.md). - -**Phase C.1.5a (portal PES wiring) shipped 2026-05-11** (merge `88bda12`). -Server-spawned `WorldEntity` entities fire their `Setup.DefaultScript` -through `PhysicsScriptRunner` on enter-world via the -`EntityScriptActivator` ([src/AcDream.App/Rendering/Vfx/EntityScriptActivator.cs](src/AcDream.App/Rendering/Vfx/EntityScriptActivator.cs)). -Visual-verified at the Holtburg Town network portal: 10-hook portal -script fires end-to-end with correct color, persistence, orientation, -multi-emitter dispatch. Filed #56 for per-part transform handling -(resolved in C.1.5b above). Plan archived at -[`docs/superpowers/plans/2026-05-12-phase-c1.5a-portals.md`](docs/superpowers/plans/2026-05-12-phase-c1.5a-portals.md). - -**Phase N.6 slice 1 (gpu_us fix + radius=12 perf baseline) shipped -2026-05-11** (merge `9b447d4`). Fixed `gpu_us` double-buffering in -`WbDrawDispatcher` (ring-of-3 query slots, read-before-overwrite, -vendor-neutral). Captured authoritative perf baseline at Holtburg radii -4 / 8 / 12. **Conclusion: CPU dominates GPU by 30–50× at every radius**; -GPU sits at 3.6% of frame budget; per-LB walk is the next bottleneck. -Baseline-doc recommendation: do C.1.5 next, then a reduced-scope slice 2 -(atlas + persistent-mapped buffers dropped from slice-2 scope). Baseline -at [`docs/plans/2026-05-11-phase-n6-perf-baseline.md`](docs/plans/2026-05-11-phase-n6-perf-baseline.md). -Plan archived at [`docs/superpowers/plans/2026-05-11-phase-n6-slice1.md`](docs/superpowers/plans/2026-05-11-phase-n6-slice1.md). -Issue #55 filed (static-entity slow path reports ~1.45M `meshMissing` -per 5s at r4 standstill — diagnostic, not a visible regression). - -**Post-A.5 polish phase complete 2026-05-11.** All three post-A.5 -issues closed: #52 (lifestone, `e40159f`), #54 (JobKind, `bf31e59`), -#53 (Tier 1 entity cache, `f928e66`). Phase A.5 + post-A.5 polish -together comprise the streaming + rendering perf foundation for the -project. - -**Next phase candidates (in rough preference order):** -- **"Click an NPC" verification spike (M1 critical path).** B.4b's - `WorldPicker` + `BuildUse` is already wired. The question is whether ACE - NPCs respond to a Use message from our testaccount and what they broadcast - back (TalkDirect? MoveToObject?). Spike: stand near a Holtburg NPC, - double-click, read what ACE sends back. If ACE responds with recognizable - packets, wire the handlers; if it is silent, investigate ACE's NPC handler - configuration. ~30 min spike, outcome determines whether NPC interaction - needs a full phase or is a one-commit fix. -- **Phase B.6 — Client-side MoveToObject auto-walk handling (closes #63).** - ACE auto-walks the player to out-of-range Use / Pickup targets via - `CreateMoveToChain` + `EnqueueBroadcastMotion(MoveToObject)`, but our client - doesn't honor the inbound motion broadcast — character drifts toward the - target and snaps back, ACE's chain times out. Reference implementation - exists in `references/holtburger/crates/holtburger-core/src/client/simulation.rs` - (the `approximate_move_to_object_projection_target` + `MoveToObject` case). - Unlocks double-click pickup, F-key pickup from any distance, Use on - out-of-range NPCs / corpses. Probably 1-2 commits + visual verification. -- **Triage the chronic open-issue list** in `docs/ISSUES.md` — #2 (lightning), - #4 (sky horizon-glow), #28 (aurora), #29 (cloud thinness), #37 (humanoid - coat), #41 (remote-motion blips) have been open since April/early-May and - keep getting deferred. Either link each to a future phase or downgrade. - ~1 hour, surfaces what's chronic vs. linked-to-a-phase. -- **More Phase C visual-fidelity work** (C.2 dynamic point lights, C.3 - palette tuning, C.4 double-sided translucent polys) closing the - "world reads as old / broken vs. retail" backlog. -- **N.6 slice 2** at reduced scope (atlas opportunities only — persistent- - mapped buffers and other slice-2 items dropped per slice-1 baseline doc). -- **Perf tiers 2/3** (`docs/plans/2026-05-10-perf-tiers-2-3-roadmap.md`) - only if user wants sustained 500+ FPS. With Tier 1 dispatcher at ~1.2 ms - the project comfortably hits 200-400 FPS at radius=12 standstill; - escalation is optional from here. -- **Issue #61 — AnimationSequencer link→cycle boundary flash** (M1-deferred - polish). Brief flap at end of door-swing animations. Low severity; does - not block M1 demo. Address before milestone demo record if distracting. -- **Issue #62 — PARTSDIAG null-guard** (trivial latent fix). One-line - null-coalescing guard in `GameWindow.TickAnimations`. Address any time a - diagnostic-related PR is open nearby. - -**Earlier rendering + streaming arc (2026-05-08 → 2026-05-10).** -Phases **N.4 → N.5 → N.5b → A.5** shipped the modern rendering -pipeline + two-tier streaming foundation: WB `ObjectMeshManager` as -production mesh path (N.4); bindless + `glMultiDrawElementsIndirect` -for entities (N.5, ~12-15 GL calls/frame) and terrain via Path C -preserving retail's `FSplitNESW` formula (N.5b, closes #51); two-tier -streaming N₁=4 / N₂=12 + QualityPreset system (A.5). Modern path is -mandatory as of N.5 ship amendment — `InstancedMeshRenderer`, -`StaticMeshRenderer`, `WbFoundationFlag` all deleted; missing bindless -throws at startup. Detail + decomp anchors + plan archives in roadmap -shipped-table rows 63–66 at `docs/plans/2026-04-11-roadmap.md`. -Engineering gotchas (bindless Dispose order, texture target lock-in, -`uvec2` sampler-handle pattern, WB-vs-retail formula divergence) -documented inline at the relevant call sites and in -`feedback_wb_migration_*.md` memory entries. - **Rules:** 1. Before starting a new phase or sub-piece, re-read the roadmap and the @@ -1248,44 +926,39 @@ via `PlayerMovementController.ApplyServerRunRate`) or from diagnostics (`[UM_RAW]`, `[SCFAST]`, `[SCFULL]`, `[SETCYCLE]`, `[FWD_WIRE]`, `[OMEGA_DIAG]`, `[SEQSTATE]`, `[PARTSDIAG]`, `[VEL_DIAG]`, `[UPCYCLE]`). Heavy. -- `ACDREAM_PROBE_RESOLVE=1` — L.2a slice 1+2+3 (2026-05-12). One - `[resolve]` line per `PhysicsEngine.ResolveWithTransition` call: - input + target + output position/cell, ok-vs-partial, grounded-in, - contact-plane status, wall normal if hit, **responsible entity - guid** (post-slice-3 attribution plumbing), env flag, walkable - polygon valid. Heavy (~30 Hz × every entity). Runtime-toggleable - via the DebugPanel "Diagnostics" section if `ACDREAM_DEVTOOLS=1`. -- `ACDREAM_PROBE_CELL=1` — L.2a slice 1 (2026-05-12). One - `[cell-transit]` line per `PlayerMovementController.CellId` - change: old → new cell, world position, reason tag - (`resolver` / `teleport`). Low volume — only fires on actual cell - crossings. Runtime-toggleable via the same DebugPanel section. -- `ACDREAM_PROBE_PUSH_BACK=1` — A6.P1 cdb probe spike (2026-05-21). - Emits three line types per physics tick: `[push-back]` (per - `BSPQuery.AdjustSphereToPlane` call), `[push-back-disp]` (per - `BSPQuery.FindCollisions` dispatch), `[push-back-cell]` (per - `Transition.CheckOtherCells` off-cell hit). Heavy under motion - (~100–500 lines/sec). Pair with retail's cdb breakpoint set at - `tools/cdb/a6-probe.cdb` for the A6.P1 capture protocol. - Runtime-toggleable via the DebugPanel "Diagnostics" section. -- `ACDREAM_CAPTURE_RESOLVE=` — A6.P3 #98 live capture of every - player-side `PhysicsEngine.ResolveWithTransition` call (2026-05-23 PM - apparatus). Each call appends one JSON Lines record with full inputs, - PhysicsBody snapshot before AND after, plus the `ResolveResult`. - Filtered to `IsPlayer` mover flag — NPC / remote DR calls don't - pollute. Pairs with the trajectory replay harness comparison test - (`CellarUpTrajectoryReplayTests.Capture_*`) to diff captured vs harness - state per field — the first divergence pinpoints missing apparatus - state. Capture is OFF when the env var is unset (one null-check - cost per call). -- *(retired 2026-05-05 by L.3 M2/M3)* `ACDREAM_INTERP_MANAGER` was an - env-var gate on an experimental per-tick remote motion path. L.3 M2 - (commit 40d88b9) replaced both gates (`OnLivePositionUpdated` + - `TickAnimations`) with `IsPlayerGuid(...)` so player remotes use the - retail-faithful queue routing (InterpolationManager queue catch-up + - PositionManager combiner) unconditionally. NPCs and airborne player - remotes still flow through the legacy `apply_current_movement` + - `ResolveWithTransition` path. The env-var no longer toggles anything. +- `ACDREAM_PROBE_RESOLVE=1` — one `[resolve]` line per + `PhysicsEngine.ResolveWithTransition` call: input + target + output + position/cell, ok-vs-partial, grounded-in, contact-plane status, + wall normal if hit, **responsible entity guid**, env flag, walkable + polygon valid. Heavy (~30 Hz × every entity). Runtime-toggleable via + the DebugPanel "Diagnostics" section if `ACDREAM_DEVTOOLS=1`. +- `ACDREAM_PROBE_CELL=1` — one `[cell-transit]` line per + `PlayerMovementController.CellId` change: old → new cell, world + position, reason tag (`resolver` / `teleport`). Low volume — only + fires on actual cell crossings. Runtime-toggleable via the same + DebugPanel section. +- `ACDREAM_PROBE_PUSH_BACK=1` — emits three line types per physics + tick: `[push-back]` (per `BSPQuery.AdjustSphereToPlane` call), + `[push-back-disp]` (per `BSPQuery.FindCollisions` dispatch), + `[push-back-cell]` (per `Transition.CheckOtherCells` off-cell hit). + Heavy under motion (~100–500 lines/sec). Pair with retail's cdb + breakpoint set at `tools/cdb/a6-probe.cdb` for the A6.P1 capture + protocol. Runtime-toggleable via the DebugPanel. +- `ACDREAM_PROBE_FLAP=1` — capture probe for indoor visibility + decisions at frame boundaries. Used to converge the U.4c flap fix + (root indoor visibility at player's cell, not eye). +- `ACDREAM_CAPTURE_RESOLVE=` — live capture of every player-side + `PhysicsEngine.ResolveWithTransition` call. Each call appends one + JSON Lines record with full inputs, PhysicsBody snapshot before AND + after, plus the `ResolveResult`. Filtered to `IsPlayer` mover flag + — NPC / remote DR calls don't pollute. Pairs with the trajectory + replay harness comparison tests to diff captured vs harness state + per field — the first divergence pinpoints missing apparatus state. + Capture is OFF when the env var is unset (one null-check cost per + call). +- `ACDREAM_DUMP_CELLS=` / `ACDREAM_DUMP_GFXOBJS=` — dump + resolved cell/GfxObj polygon tables as JSON when ids cache. Used + for harness fixture extraction. ### Outbound motion wire format (acdream → ACE) @@ -1304,8 +977,8 @@ when relaying to remote observers. So our INBOUND parser sees When the local player toggles Shift while keeping W held (Run↔Walk demote/promote), acdream sends a fresh `MoveToState` with the new HoldKey + ForwardSpeed. Retail's outbound likely does the same, but -ACE's behavior on relay is uncertain — see `#L.X` in ISSUES.md for -the open Run↔Walk cycle bug on observed retail-driven remotes. +ACE's behavior on relay is uncertain — see open issues in `docs/ISSUES.md` +for the Run↔Walk cycle bug on observed retail-driven remotes. ### Visual verification workflow @@ -1358,12 +1031,12 @@ The six references: `ACViewer/Render/TextureCache.cs::IndexToColor` for the canonical subpalette overlay algorithm. - **`references/WorldBuilder/`** — **acdream's rendering + dat-handling - base.** WorldBuilder is not just a reference: as of Phase N.4 (shipped - 2026-05-08), `ObjectMeshManager` is the production mesh pipeline, - `WbMeshAdapter` is the seam, and `WbDrawDispatcher` is the production - draw path. The modern path (`N.5`) is **mandatory** — missing bindless - throws at startup, there is no legacy fallback. **Before re-porting - any rendering or dat-handling algorithm from retail decomp, read + base.** WorldBuilder is not just a reference: `ObjectMeshManager` is + the production mesh pipeline, `WbMeshAdapter` is the seam, and + `WbDrawDispatcher` is the production draw path. The modern path is + **mandatory** — missing bindless throws at startup, there is no + legacy fallback. **Before re-porting any rendering or dat-handling + algorithm from retail decomp, read `docs/architecture/worldbuilder-inventory.md` first.** The inventory tells you what WB covers (terrain, scenery, static objects, EnvCells, portals, sky, particles, texture decoding, mesh extraction, @@ -1406,13 +1079,6 @@ The six references: and uses the server's authoritative Z. See `docs/research/2026-04-12-movement-deep-dive.md` for the full analysis. -Pattern: when you encounter an unknown behavior, grep all four for the -relevant term, read each hit, and compose a multi-source understanding -BEFORE writing acdream code. A single reference can be misleading; the -intersection of all four is almost always the truth. The user has -repeatedly had to remind me about this when I narrowly searched one ref -and missed obvious answers in another. - ### Reference hierarchy by domain **NEVER GUESS an algorithm, formula, constant, wire format, or coordinate diff --git a/tools/pdb-extract/pdb_extract.py b/tools/pdb-extract/pdb_extract.py index 571f4f40..17cc8ea8 100644 --- a/tools/pdb-extract/pdb_extract.py +++ b/tools/pdb-extract/pdb_extract.py @@ -455,4 +455,5 @@ def _main(): print(f"\nspot check: {target} NOT FOUND in symbols (PDB lookup mismatch?)") -_main() +if __name__ == "__main__": + _main()