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 <noreply@anthropic.com>
This commit is contained in:
parent
3c3293aebb
commit
c007f5a962
3 changed files with 173 additions and 500 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -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
|
||||
|
|
|
|||
664
CLAUDE.md
664
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=<path>` — 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=<path>` — 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=<path>` / `ACDREAM_DUMP_GFXOBJS=<path>` — 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
|
||||
|
|
|
|||
|
|
@ -455,4 +455,5 @@ def _main():
|
|||
print(f"\nspot check: {target} NOT FOUND in symbols (PDB lookup mismatch?)")
|
||||
|
||||
|
||||
_main()
|
||||
if __name__ == "__main__":
|
||||
_main()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue