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
|
sg_built.txt
|
||||||
# Stray bash-mangled path artifacts from PowerShell-via-bash escaping
|
# Stray bash-mangled path artifacts from PowerShell-via-bash escaping
|
||||||
C[-]*
|
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
|
## 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,
|
align with this document. When the architecture doc and reality diverge,
|
||||||
update one or the other — never leave them out of sync.
|
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).**
|
**WorldBuilder code lives in our tree.** Phase O extracted ~33 WB files
|
||||||
Phase N.4 (2026-05-08) adopted WB's rendering + dat-handling base as a
|
(~7.7K LOC) into our own namespaces and dropped the two external project
|
||||||
project reference. Phase O (2026-05-21) extracted the ~33 files / ~7.7K LOC
|
references. `DatCollection` is the **only** dat reader in process —
|
||||||
we actually use into our own namespaces and dropped the two external project
|
`DefaultDatReaderWriter` is gone. `references/WorldBuilder/` remains
|
||||||
references. `DatCollection` is now the **only** dat reader in process —
|
in-tree as a read-reference (MIT-licensed; grep it freely), but nothing
|
||||||
`DefaultDatReaderWriter` is gone. `references/WorldBuilder/` remains in-tree
|
in `src/AcDream.*` references it as a project dependency.
|
||||||
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):**
|
**Where the extracted code lives (post-Phase O):**
|
||||||
- `src/AcDream.Core/Rendering/Wb/` — pure dat/mesh helpers (5 files, ~782 LOC):
|
- `src/AcDream.Core/Rendering/Wb/` — pure dat/mesh helpers (5 files,
|
||||||
`TerrainUtils`, `TerrainEntry`, `RegionInfo`, `SceneryHelpers`,
|
~782 LOC): `TerrainUtils`, `TerrainEntry`, `RegionInfo`,
|
||||||
`TextureHelpers`. No GL dependency; safe to use from Core.
|
`SceneryHelpers`, `TextureHelpers`. No GL dependency; safe to use
|
||||||
- `src/AcDream.App/Rendering/Wb/` — GL infrastructure + mesh pipeline (~27 files,
|
from Core.
|
||||||
~7K LOC): `ObjectMeshManager`, `WbMeshAdapter`, `WbDrawDispatcher`,
|
- `src/AcDream.App/Rendering/Wb/` — GL infrastructure + mesh pipeline
|
||||||
`LandblockSpawnAdapter`, `EntitySpawnAdapter`, `TextureCache`,
|
(~27 files, ~7K LOC): `ObjectMeshManager`, `WbMeshAdapter`,
|
||||||
`GlobalMeshBuffer`, shader infrastructure, and the EnvCell/portal/scenery/
|
`WbDrawDispatcher`, `LandblockSpawnAdapter`, `EntitySpawnAdapter`,
|
||||||
terrain-blending pipeline classes.
|
`TextureCache`, `GlobalMeshBuffer`, shader infrastructure, and the
|
||||||
|
EnvCell/portal/scenery/terrain-blending pipeline classes.
|
||||||
|
|
||||||
Before re-implementing any AC-specific rendering or dat-handling algorithm,
|
**Modern rendering path is MANDATORY** as of the N.5 ship amendment.
|
||||||
**read `docs/architecture/worldbuilder-inventory.md` FIRST**. The inventory
|
`WbFoundationFlag`, `InstancedMeshRenderer`, and `StaticMeshRenderer`
|
||||||
describes what we extracted (now in our tree) and what we still write ourselves.
|
are deleted. Missing `GL_ARB_bindless_texture` or
|
||||||
Re-porting from retail decomp when we already have a tested port is how subtle
|
`GL_ARB_shader_draw_parameters` throws `NotSupportedException` at
|
||||||
bugs (the scenery edge-vertex bug, the triangle-Z bug) keep slipping in. Retail
|
startup. There is no legacy fallback. Engineering cribs (WbMeshAdapter
|
||||||
decomp remains the oracle for network, physics, animation, movement, UI, plugin,
|
seams, N.5 SSBO layout, translucency model, gotchas) live in
|
||||||
audio, chat — see the inventory doc's 🔴 list.
|
`memory/reference_modern_rendering_pipeline.md`.
|
||||||
|
|
||||||
**WB rendering cribs (all paths now in `src/AcDream.App/Rendering/Wb/`):**
|
Before re-implementing any AC-specific rendering or dat-handling
|
||||||
- `WbMeshAdapter.cs` — single seam over `ObjectMeshManager`. Owns the mesh
|
algorithm, **read `docs/architecture/worldbuilder-inventory.md` FIRST**.
|
||||||
pipeline, drains its staged-upload queue per frame via `Tick()`, populates
|
The inventory describes what we extracted (now in our tree) and what we
|
||||||
`AcSurfaceMetadataTable` with per-batch translucency / luminosity / fog
|
still write ourselves. Re-porting from retail decomp when we already
|
||||||
metadata. Consumes `DatCollection` via `DatCollectionAdapter` (O-D7 fallback
|
have a tested port is how subtle bugs (the scenery edge-vertex bug, the
|
||||||
path; `ObjectMeshManager` has 26 internal `_dats.X` call sites that exceed
|
triangle-Z bug) keep slipping in. Retail decomp remains the oracle for
|
||||||
the inline-swap threshold — the adapter bridges our `IDatCollection` to the
|
network, physics, animation, movement, UI, plugin, audio, chat — see
|
||||||
`IDatReaderWriter` interface WB's internals expect).
|
the inventory doc's 🔴 list.
|
||||||
- `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`.
|
|
||||||
|
|
||||||
**Execution model:** the active source of truth is the **milestones doc**
|
**Execution model:** the active source of truth is the **milestones doc**
|
||||||
(`docs/plans/2026-05-12-milestones.md`) for "what are we building right
|
(`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
|
The codebase is organized by layer (see architecture doc + the **Code
|
||||||
Structure Rules** section below). Plans live in `docs/plans/`,
|
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 +
|
**UI strategy:** three-layer split — swappable backend (ImGui.NET +
|
||||||
`Silk.NET.OpenGL.Extensions.ImGui` for Phase D.2a, custom retail-look
|
`Silk.NET.OpenGL.Extensions.ImGui` for Phase D.2a, custom retail-look
|
||||||
toolkit for D.2b later) / stable `AcDream.UI.Abstractions` layer
|
toolkit for D.2b later) / stable `AcDream.UI.Abstractions` layer
|
||||||
(ViewModels + Commands + `IPanel` / `IPanelRenderer`) / unchanged game
|
(ViewModels + Commands + `IPanel` / `IPanelRenderer`) / unchanged game
|
||||||
state. **As of Phase I (2026-04-25), ImGui hosts every dev/debug
|
state. **As of Phase I, ImGui hosts every dev/debug panel** — Vitals,
|
||||||
panel** — Vitals, Chat, Debug. The previous custom-StbTrueTypeSharp
|
Chat, Debug. The previous custom-StbTrueTypeSharp `DebugOverlay` was
|
||||||
`DebugOverlay` was deleted in I.2; `TextRenderer` + `BitmapFont` are
|
deleted in I.2; `TextRenderer` + `BitmapFont` are kept alive
|
||||||
kept alive specifically for the future world-space HUD (D.6 — damage
|
specifically for the future world-space HUD (D.6 — damage floaters,
|
||||||
floaters, name plates) where ImGui can't reach into the 3D scene.
|
name plates) where ImGui can't reach into the 3D scene. D.2b remains
|
||||||
D.2b remains the long-term retail-look path (panels reskinned one at a
|
the long-term retail-look path (panels reskinned one at a time using
|
||||||
time using dat assets); ImGui persists forever as the
|
dat assets); ImGui persists forever as the `ACDREAM_DEVTOOLS=1`
|
||||||
`ACDREAM_DEVTOOLS=1` overlay. **All plugin-facing UI targets
|
overlay. **All plugin-facing UI targets `AcDream.UI.Abstractions` —
|
||||||
`AcDream.UI.Abstractions` — never import a backend namespace from a
|
never import a backend namespace from a panel.** Full design:
|
||||||
panel.** Full design: `docs/plans/2026-04-24-ui-framework.md`.
|
[`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
|
Memory cribs: `memory/project_chat_pipeline.md` (chat pipeline as of
|
||||||
Phase I), `memory/project_input_pipeline.md` (input pipeline as of
|
Phase I), `memory/project_input_pipeline.md` (input pipeline as of
|
||||||
Phase K). UI architecture full design at
|
Phase K).
|
||||||
[`docs/plans/2026-04-24-ui-framework.md`](docs/plans/2026-04-24-ui-framework.md).
|
|
||||||
|
|
||||||
**Input pipeline:** `src/AcDream.UI.Abstractions/Input/` (action enum,
|
**Input pipeline:** `src/AcDream.UI.Abstractions/Input/` (action enum,
|
||||||
`KeyChord`, `KeyBindings`, multicast `InputDispatcher` with scope
|
`KeyChord`, `KeyBindings`, multicast `InputDispatcher` with scope
|
||||||
|
|
@ -179,9 +102,43 @@ stack + modal capture for rebind UX) + `src/AcDream.App/Input/`
|
||||||
`KeyBindings.RetailDefaults()` matching
|
`KeyBindings.RetailDefaults()` matching
|
||||||
`docs/research/named-retail/retail-default.keymap.txt`). The Settings
|
`docs/research/named-retail/retail-default.keymap.txt`). The Settings
|
||||||
panel (F11 / View → Settings) lets users remap any action via
|
panel (F11 / View → Settings) lets users remap any action via
|
||||||
click-to-rebind. As of Phase K (2026-04-26), ALL keyboard / mouse
|
click-to-rebind. As of Phase K, ALL keyboard / mouse input flows
|
||||||
input flows through the dispatcher — no IsKeyPressed polling outside
|
through the dispatcher — no IsKeyPressed polling outside the per-frame
|
||||||
the per-frame movement queries.
|
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
|
## Code Structure Rules
|
||||||
|
|
||||||
|
|
@ -202,8 +159,8 @@ pursuing live in [`docs/architecture/code-structure.md`](docs/architecture/code-
|
||||||
as part of the change.
|
as part of the change.
|
||||||
|
|
||||||
2. **`AcDream.Core` must not depend on the window / GL / backend
|
2. **`AcDream.Core` must not depend on the window / GL / backend
|
||||||
projects, except via documented interop seams.** As of Phase O
|
projects, except via documented interop seams.** As of Phase O,
|
||||||
(2026-05-21), the only allowed seams are the extracted helpers in
|
the only allowed seams are the extracted helpers in
|
||||||
`src/AcDream.Core/Rendering/Wb/` (`TerrainUtils`, `TerrainEntry`,
|
`src/AcDream.Core/Rendering/Wb/` (`TerrainUtils`, `TerrainEntry`,
|
||||||
`RegionInfo`, `SceneryHelpers`, `TextureHelpers` — stateless, no GL).
|
`RegionInfo`, `SceneryHelpers`, `TextureHelpers` — stateless, no GL).
|
||||||
The former `WorldBuilder.Shared` and `Chorizite.OpenGLSDLBackend.Lib`
|
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
|
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
|
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
|
need to add a project reference to Core, the change must come with an
|
||||||
inventory-doc
|
inventory-doc update explaining why.
|
||||||
update explaining why.
|
|
||||||
|
|
||||||
3. **UI panels target `AcDream.UI.Abstractions` only.** No panel may
|
3. **UI panels target `AcDream.UI.Abstractions` only.** No panel may
|
||||||
import `AcDream.UI.ImGui` or any backend namespace. ViewModels,
|
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
|
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`
|
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`
|
(indoor render / doorway FLAP) and `project_physics_collision_digest.md`
|
||||||
(physics / collision / #98 / membership). Each digest is current-truth-on-top
|
(physics / collision / membership). Each digest is current-truth-on-top
|
||||||
plus a DO-NOT-RETRY table — it supersedes the dated banners that used to sprawl
|
plus a DO-NOT-RETRY table — it supersedes dated banners. The memory-handling
|
||||||
across this file. The memory-handling protocol (distill-don't-journal, the digest
|
protocol (distill-don't-journal, the digest pattern, tags, recall + capture)
|
||||||
pattern, tags, recall + capture) is in `reference_obsidian_vault.md`. **Do NOT add
|
is in `reference_obsidian_vault.md`. **Do NOT add new dated status banners to
|
||||||
new dated banners to this file — update the relevant digest instead.** Obsidian
|
this file — update the relevant digest (or the Current state pointer list)
|
||||||
(auto-started in the main repo via a SessionStart hook) is the live search / graph /
|
instead.** Obsidian (auto-started in the main repo via a SessionStart hook) is
|
||||||
tag lens over `claude-memory/` through the `mcp__obsidian__*` tools when it's
|
the live search / graph / tag lens over `claude-memory/` through the
|
||||||
running; the files read and write the same with it closed.
|
`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 are the lead engineer AND architect on this project at all times.**
|
||||||
You own the architecture (`docs/architecture/acdream-architecture.md`),
|
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
|
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.
|
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
|
**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
|
from 30 minutes to 5 seconds. When "what does retail actually DO at
|
||||||
retail actually DO at runtime?" is the question and decomp alone
|
runtime?" is the question and decomp alone isn't enough, attach cdb
|
||||||
isn't enough, attach cdb to a live retail client (Step -1).**
|
to a live retail client (Step -1).**
|
||||||
|
|
||||||
### For each new feature or bug fix:
|
### 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?"**
|
**When the question is "what does retail actually DO frame-by-frame?"**
|
||||||
the decomp alone is often not enough — code paths interact with state
|
the decomp alone is often not enough — code paths interact with state
|
||||||
(LastKnownContactPlane, transient flags, accumulated counters) in ways
|
(LastKnownContactPlane, transient flags, accumulated counters) in ways
|
||||||
that aren't obvious from reading. As of 2026-04-30 we have a working
|
that aren't obvious from reading. We have a working toolchain to attach
|
||||||
toolchain to attach Windows' console debugger (cdb.exe) to a live
|
Windows' console debugger (cdb.exe) to a live retail acclient.exe with
|
||||||
retail acclient.exe with full PDB symbols and capture state at any
|
full PDB symbols and capture state at any breakpoint. **Use this when
|
||||||
breakpoint. **Use this when guessing has failed twice in a row.**
|
guessing has failed twice in a row.**
|
||||||
|
|
||||||
### What we have
|
### 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
|
- **Network protocol questions** — `holtburger` references + ACE source
|
||||||
+ Wireshark are the right tools, not cdb.
|
+ 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)
|
## MCP servers (live tooling)
|
||||||
|
|
||||||
Two MCP servers extend the static decomp + cdb workflow with live
|
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
|
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;
|
sure what to work on. Phases are too granular to feel like progress;
|
||||||
this doc is the multi-week target.
|
this doc is the multi-week target.
|
||||||
- **`docs/plans/2026-04-11-roadmap.md`** — the strategic roadmap (next
|
- **`docs/plans/2026-04-11-roadmap.md`** — the strategic roadmap.
|
||||||
section). Phase-level index. This is where you orient when you know
|
Phase-level index. This is where you orient when you know the
|
||||||
the milestone and need the next concrete sub-phase.
|
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.
|
|
||||||
|
|
||||||
**Work-order autonomy — the meta-rule.** You decide what to work on
|
**Work-order autonomy — the meta-rule.** You decide what to work on
|
||||||
next, always. **The user does NOT pick between phases, milestones, or
|
next, always. **The user does NOT pick between phases, milestones, or
|
||||||
|
|
@ -816,15 +713,16 @@ four below actually work.
|
||||||
**The four motivation-keeping rules:**
|
**The four motivation-keeping rules:**
|
||||||
|
|
||||||
1. **One active milestone at a time.** Work that isn't on the critical
|
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
|
path to the current milestone gets filed in `docs/ISSUES.md` with
|
||||||
muted. This is the single rule that kills the "jumping between
|
the appropriate `post-Mx` tag and muted. This is the single rule
|
||||||
things" feeling. If a phase isn't part of the current milestone, it
|
that kills the "jumping between things" feeling. If a phase isn't
|
||||||
doesn't get touched — even if it's tempting, even if it would be
|
part of the current milestone, it doesn't get touched — even if
|
||||||
"quick", even if it would be "while I'm here."
|
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
|
2. **Frozen phases are off-limits.** Shipped-milestone phases are
|
||||||
until M7's polish pass. Concretely: no rework on streaming, chat,
|
frozen until M7's polish pass. Concretely: no rework on streaming,
|
||||||
input, the WB rendering migration, sky/lighting, the particle
|
chat, input, the WB rendering migration, sky/lighting, the particle
|
||||||
system, or the network handshake. Those are done. Don't revisit them
|
system, or the network handshake. Those are done. Don't revisit them
|
||||||
— even if you see something that could be 10% better. Visual
|
— even if you see something that could be 10% better. Visual
|
||||||
nice-to-haves and architecture second-guesses on frozen phases are
|
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
|
When a milestone's demo scenario is functionally complete, update
|
||||||
`2026-05-12-milestones.md` with a one-paragraph writeup describing
|
`2026-05-12-milestones.md` with a one-paragraph writeup describing
|
||||||
what works end-to-end, flip the freeze list, and update the
|
what works end-to-end, flip the freeze list, and update the
|
||||||
"currently working toward" line in this CLAUDE.md to the next
|
"currently working toward" line in this CLAUDE.md's **Current
|
||||||
milestone. Do NOT ask the user to record a demo video — they find
|
state** section to the next milestone. Do NOT ask the user to
|
||||||
it pointless. The milestones doc + the CLAUDE.md flip ARE the
|
record a demo video — they find it pointless. The milestones doc
|
||||||
milestone artifact. Phases ship; milestones land.
|
+ the CLAUDE.md flip ARE the milestone artifact. Phases ship;
|
||||||
|
milestones land.
|
||||||
|
|
||||||
4. **State both altitudes at session start.** First action of any
|
4. **State both altitudes at session start.** First action of any
|
||||||
session: "Currently working toward M1 — Walkable + clickable world.
|
session: "Currently working toward [milestone]. Current phase:
|
||||||
Current phase: L.2. Next concrete step: [whatever]." This keeps the
|
[phase]. Next concrete step: [whatever]." This keeps the high-level
|
||||||
high-level orientation visible alongside the immediate task and
|
orientation visible alongside the immediate task and makes
|
||||||
makes mid-session drift obvious.
|
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
|
When reality and the milestones diverge — a phase grows beyond the
|
||||||
milestone's scope, a demo scenario turns out to be unreachable without
|
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
|
acceptance criteria. Do not drift from the spec without explicit user
|
||||||
approval.
|
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:**
|
**Rules:**
|
||||||
|
|
||||||
1. Before starting a new phase or sub-piece, re-read the roadmap and the
|
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]`,
|
diagnostics (`[UM_RAW]`, `[SCFAST]`, `[SCFULL]`, `[SETCYCLE]`,
|
||||||
`[FWD_WIRE]`, `[OMEGA_DIAG]`, `[SEQSTATE]`, `[PARTSDIAG]`,
|
`[FWD_WIRE]`, `[OMEGA_DIAG]`, `[SEQSTATE]`, `[PARTSDIAG]`,
|
||||||
`[VEL_DIAG]`, `[UPCYCLE]`). Heavy.
|
`[VEL_DIAG]`, `[UPCYCLE]`). Heavy.
|
||||||
- `ACDREAM_PROBE_RESOLVE=1` — L.2a slice 1+2+3 (2026-05-12). One
|
- `ACDREAM_PROBE_RESOLVE=1` — one `[resolve]` line per
|
||||||
`[resolve]` line per `PhysicsEngine.ResolveWithTransition` call:
|
`PhysicsEngine.ResolveWithTransition` call: input + target + output
|
||||||
input + target + output position/cell, ok-vs-partial, grounded-in,
|
position/cell, ok-vs-partial, grounded-in, contact-plane status,
|
||||||
contact-plane status, wall normal if hit, **responsible entity
|
wall normal if hit, **responsible entity guid**, env flag, walkable
|
||||||
guid** (post-slice-3 attribution plumbing), env flag, walkable
|
polygon valid. Heavy (~30 Hz × every entity). Runtime-toggleable via
|
||||||
polygon valid. Heavy (~30 Hz × every entity). Runtime-toggleable
|
the DebugPanel "Diagnostics" section if `ACDREAM_DEVTOOLS=1`.
|
||||||
via the DebugPanel "Diagnostics" section if `ACDREAM_DEVTOOLS=1`.
|
- `ACDREAM_PROBE_CELL=1` — one `[cell-transit]` line per
|
||||||
- `ACDREAM_PROBE_CELL=1` — L.2a slice 1 (2026-05-12). One
|
`PlayerMovementController.CellId` change: old → new cell, world
|
||||||
`[cell-transit]` line per `PlayerMovementController.CellId`
|
position, reason tag (`resolver` / `teleport`). Low volume — only
|
||||||
change: old → new cell, world position, reason tag
|
fires on actual cell crossings. Runtime-toggleable via the same
|
||||||
(`resolver` / `teleport`). Low volume — only fires on actual cell
|
DebugPanel section.
|
||||||
crossings. Runtime-toggleable via the same DebugPanel section.
|
- `ACDREAM_PROBE_PUSH_BACK=1` — emits three line types per physics
|
||||||
- `ACDREAM_PROBE_PUSH_BACK=1` — A6.P1 cdb probe spike (2026-05-21).
|
tick: `[push-back]` (per `BSPQuery.AdjustSphereToPlane` call),
|
||||||
Emits three line types per physics tick: `[push-back]` (per
|
`[push-back-disp]` (per `BSPQuery.FindCollisions` dispatch),
|
||||||
`BSPQuery.AdjustSphereToPlane` call), `[push-back-disp]` (per
|
`[push-back-cell]` (per `Transition.CheckOtherCells` off-cell hit).
|
||||||
`BSPQuery.FindCollisions` dispatch), `[push-back-cell]` (per
|
Heavy under motion (~100–500 lines/sec). Pair with retail's cdb
|
||||||
`Transition.CheckOtherCells` off-cell hit). Heavy under motion
|
breakpoint set at `tools/cdb/a6-probe.cdb` for the A6.P1 capture
|
||||||
(~100–500 lines/sec). Pair with retail's cdb breakpoint set at
|
protocol. Runtime-toggleable via the DebugPanel.
|
||||||
`tools/cdb/a6-probe.cdb` for the A6.P1 capture protocol.
|
- `ACDREAM_PROBE_FLAP=1` — capture probe for indoor visibility
|
||||||
Runtime-toggleable via the DebugPanel "Diagnostics" section.
|
decisions at frame boundaries. Used to converge the U.4c flap fix
|
||||||
- `ACDREAM_CAPTURE_RESOLVE=<path>` — A6.P3 #98 live capture of every
|
(root indoor visibility at player's cell, not eye).
|
||||||
player-side `PhysicsEngine.ResolveWithTransition` call (2026-05-23 PM
|
- `ACDREAM_CAPTURE_RESOLVE=<path>` — live capture of every player-side
|
||||||
apparatus). Each call appends one JSON Lines record with full inputs,
|
`PhysicsEngine.ResolveWithTransition` call. Each call appends one
|
||||||
PhysicsBody snapshot before AND after, plus the `ResolveResult`.
|
JSON Lines record with full inputs, PhysicsBody snapshot before AND
|
||||||
Filtered to `IsPlayer` mover flag — NPC / remote DR calls don't
|
after, plus the `ResolveResult`. Filtered to `IsPlayer` mover flag
|
||||||
pollute. Pairs with the trajectory replay harness comparison test
|
— NPC / remote DR calls don't pollute. Pairs with the trajectory
|
||||||
(`CellarUpTrajectoryReplayTests.Capture_*`) to diff captured vs harness
|
replay harness comparison tests to diff captured vs harness state
|
||||||
state per field — the first divergence pinpoints missing apparatus
|
per field — the first divergence pinpoints missing apparatus state.
|
||||||
state. Capture is OFF when the env var is unset (one null-check
|
Capture is OFF when the env var is unset (one null-check cost per
|
||||||
cost per call).
|
call).
|
||||||
- *(retired 2026-05-05 by L.3 M2/M3)* `ACDREAM_INTERP_MANAGER` was an
|
- `ACDREAM_DUMP_CELLS=<path>` / `ACDREAM_DUMP_GFXOBJS=<path>` — dump
|
||||||
env-var gate on an experimental per-tick remote motion path. L.3 M2
|
resolved cell/GfxObj polygon tables as JSON when ids cache. Used
|
||||||
(commit 40d88b9) replaced both gates (`OnLivePositionUpdated` +
|
for harness fixture extraction.
|
||||||
`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.
|
|
||||||
|
|
||||||
### Outbound motion wire format (acdream → ACE)
|
### 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
|
When the local player toggles Shift while keeping W held (Run↔Walk
|
||||||
demote/promote), acdream sends a fresh `MoveToState` with the new
|
demote/promote), acdream sends a fresh `MoveToState` with the new
|
||||||
HoldKey + ForwardSpeed. Retail's outbound likely does the same, but
|
HoldKey + ForwardSpeed. Retail's outbound likely does the same, but
|
||||||
ACE's behavior on relay is uncertain — see `#L.X` in ISSUES.md for
|
ACE's behavior on relay is uncertain — see open issues in `docs/ISSUES.md`
|
||||||
the open Run↔Walk cycle bug on observed retail-driven remotes.
|
for the Run↔Walk cycle bug on observed retail-driven remotes.
|
||||||
|
|
||||||
### Visual verification workflow
|
### Visual verification workflow
|
||||||
|
|
||||||
|
|
@ -1358,12 +1031,12 @@ The six references:
|
||||||
`ACViewer/Render/TextureCache.cs::IndexToColor` for the canonical
|
`ACViewer/Render/TextureCache.cs::IndexToColor` for the canonical
|
||||||
subpalette overlay algorithm.
|
subpalette overlay algorithm.
|
||||||
- **`references/WorldBuilder/`** — **acdream's rendering + dat-handling
|
- **`references/WorldBuilder/`** — **acdream's rendering + dat-handling
|
||||||
base.** WorldBuilder is not just a reference: as of Phase N.4 (shipped
|
base.** WorldBuilder is not just a reference: `ObjectMeshManager` is
|
||||||
2026-05-08), `ObjectMeshManager` is the production mesh pipeline,
|
the production mesh pipeline, `WbMeshAdapter` is the seam, and
|
||||||
`WbMeshAdapter` is the seam, and `WbDrawDispatcher` is the production
|
`WbDrawDispatcher` is the production draw path. The modern path is
|
||||||
draw path. The modern path (`N.5`) is **mandatory** — missing bindless
|
**mandatory** — missing bindless throws at startup, there is no
|
||||||
throws at startup, there is no legacy fallback. **Before re-porting
|
legacy fallback. **Before re-porting any rendering or dat-handling
|
||||||
any rendering or dat-handling algorithm from retail decomp, read
|
algorithm from retail decomp, read
|
||||||
`docs/architecture/worldbuilder-inventory.md` first.** The inventory
|
`docs/architecture/worldbuilder-inventory.md` first.** The inventory
|
||||||
tells you what WB covers (terrain, scenery, static objects, EnvCells,
|
tells you what WB covers (terrain, scenery, static objects, EnvCells,
|
||||||
portals, sky, particles, texture decoding, mesh extraction,
|
portals, sky, particles, texture decoding, mesh extraction,
|
||||||
|
|
@ -1406,13 +1079,6 @@ The six references:
|
||||||
and uses the server's authoritative Z. See
|
and uses the server's authoritative Z. See
|
||||||
`docs/research/2026-04-12-movement-deep-dive.md` for the full analysis.
|
`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
|
### Reference hierarchy by domain
|
||||||
|
|
||||||
**NEVER GUESS an algorithm, formula, constant, wire format, or coordinate
|
**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?)")
|
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