L.2d as scoped is essentially closed at the Holtburg site. The slice-1.5 trace settled the question: the "I can't walk through doorways" symptom is a closed Door entity (Setup 0x020019FF named "Door") at the building threshold, not a building-BSP-collision issue. Building BSP is healthy. The two prior framings turned out wrong: - L.2a handoff (2026-05-12): "per-cell walkability missing" — based on hit attribution pointing at the building, missed the Door cylinder also colliding per tick. - L.2d slice 1 spec (2026-05-13 morning): "BSP shape fidelity, three hypotheses X/Y/Z" — ruled out by the trace once the probe labeling bug was fixed in slice 1.5. Handoff doc captures full evidence, side findings (building double- registration latent bug, missing PhysicsState in entity-source log), and a candidates list for the next-session ordering discussion. Plan-of-record L.2d sub-direction paragraph updated to match: "watch- and-wait" mode, no more slices until a new shape-fidelity bug is observed at a different site. Door-state handling becomes its own sub-phase, scope deferred to project-ordering discussion. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
10 KiB
L.2d slice 1 + 1.5 shipped — handoff
Date: 2026-05-13 evening, immediately after slice 1.5 + Holtburg verification.
Branch: claude/sharp-chatelet-023dda (ready to merge to main).
Predecessor: 2026-05-12-l2a-shipped-l2d-handoff.md.
TL;DR
The "I can't walk through Holtburg doorways" symptom is a closed Door entity blocking the threshold, not a building-collision-mesh bug. Building BSP collision is healthy. The L.2a handoff's framing ("per-cell walkability missing") was wrong, the L.2d-slice-1 spec's reframe ("BSP shape fidelity, three hypotheses X/Y/Z") was also wrong, and the actual answer fell out of one capture once the probe labeling was fixed (slice 1.5). L.2d as scoped is essentially closed. The remaining work is door-state handling — a different sub-phase entirely.
What shipped on this branch
| Commit | What |
|---|---|
92cd723 |
docs(phys L.2d): design spec for slice 1 BSP-hit diagnostic + L.2d reframe |
66dc23e |
feat(phys L.2d slice 1): BSP-hit diagnostic probe + plan-of-record correction |
8bacef0 |
fix(phys L.2d slice 1.5): probe captures hit poly under StepSphereUp recursion |
What slice 1 + 1.5 give the next agent:
ACDREAM_PROBE_BUILDING=1env var + DebugPanel checkbox: one multi-line[resolve-bldg]entry per attributed BSP shadow-entry hit (partIdx, hasPhys, bspR vs vAabbR, world-space entOrigin_lb, actual hit polygon vertices in both local and world coords). Reliable underStepSphereUprecursion after the slice 1.5 fix.[entity-source]one-time log line perShadowObjects.Registercall, gated on the same flag. MakesentityId=0xA9B479in a probe line greppable to its WorldEntity source.PhysicsDiagnostics.LastBspHitPoly— diagnostic side-channel for any future "what poly did BSPQuery hit" question.- The two synthetic tests in PhysicsDiagnosticsTests.cs pin the side-channel API contract.
What the trace actually showed
After slice 1.5, walking acdream into a Holtburg town doorway captured 242 real BSP hit polys + 122 cylinder n/a. Definitive finding:
live: spawn guid=0x7A9B4015 name="Door" setup=0x020019FF
pos=(132.6,17.1,94.1)@0xA9B40029 itemType=0x00000080
[entity-source] id=0x000F4244 entityId=0x000F4244 src=0x020019FF
gfxObj=0x020019FF lb=0xA9B40029 type=Cylinder note=server-spawn-root
The blocker is a Door entity — Setup 0x020019FF named "Door" —
server-spawned by ACE at the threshold of each Holtburg town building.
Five Doors appear across Holtburg (landblock cells 0xA9B40029,
0xA9B40154, 0xA9B40155); same Setup DID reused. ItemType
0x00000080 = Misc category in AC's ItemType flags.
Each Door's Cylinder collision blocks the player. The building BSP
also fires (the L.2a evidence the original handoff pointed at), but
the BSP hits were the player already pushed back by the Door
cylinder then grazing the doorframe — they look like wall collision
but are a side effect of the Door cylinder push. Slice 1.5's per-tick
multi-entity probe revealed this by showing nObj=3 on every hit
resolve: one Door + two sphere checks against the building BSP.
The L.2a slice 2 handoff's expectation that doors would be in the
0xCC0Cxxxx range was wrong; doors are in 0x000Fxxxx (server-
spawn-root range) because they're hydrated through the live
CreateObject stream like NPCs, not the static landblock pipeline.
What this means for L.2d
L.2d as originally scoped ("Shape Fidelity: Sphere / CylSphere / Building Objects") is essentially closed at this site:
- Building BSP is loaded, parsed, queried correctly.
bspR=13.99mfor GfxObj0x01000A2B, real triangles in real positions. Setup.CylSpheresfor Door (0x020019FF) is also loaded correctly — the cylinder is firing the cylinder collision path with sensible world-space radius.- No actual shape-fidelity bug observed at this test site.
The remaining work is door state handling, which is a different class of problem entirely — it touches network (CreateObject PhysicsState bits), interaction (Use action on door entity), animation (door open/close animation state), and collision-state-toggle (ETHEREAL during open animation). That doesn't fit under L.2d's shape-fidelity umbrella.
Recommend reframing L.2d as "watch-and-wait": keep the probes for future shape-fidelity work at other sites (dungeon walls, stairs, roofs), but don't plan more slices until a NEW shape-fidelity bug is observed with the probe-armed client.
Side findings (latent bugs to file, not block this slice)
1. Building double-registration
The trace shows the same WorldEntity registered TWICE in ShadowObjectRegistry:
[entity-source] id=0xA9B47900 entityId=0xC0A9B479 ... type=BSP note=partIdx=0 hasPhys=true
[entity-source] id=0xC0A9B479 entityId=0xC0A9B479 ... type=Cylinder note=mesh-aabb-fallback
GameWindow.cs:5625
gates the mesh-AABB-fallback on entityBsp == 0, but the BSP
registration at line 5530
DOES increment entityBsp. So the fallback shouldn't fire when BSP
parts exist. Either entityBsp isn't being checked in the right
scope, or there's a second mesh-AABB-fallback site that doesn't gate
on entityBsp. Worth a short investigation + one-line fix.
Filing as ISSUE candidate. Doesn't break anything observable yet (cylinder is too far from player to fire at this Holtburg site), but will cause confusion in any future "why does entity X have two ShadowEntries" trace.
2. PhysicsState / EntityCollisionFlags not in entity-source log
The slice 1 [entity-source] log captures id, entityId, src, gfxObj, lb, type, note, hasPhys but not state (PhysicsState
bits) or flags (EntityCollisionFlags). For any future
ethereal-handling / IGNORE_COLLISIONS work — including the door
state handling above — these would be required.
Tiny slice 1.6 if the next agent needs them: add state=0x{...:X8} flags={...} to the format string. ~5 LOC, gated on the same
ProbeBuilding flag.
What the next session probably should NOT do
- Re-investigate Holtburg doorways with the same setup. The evidence is conclusive; we're not going to find new information by re-running the probe at the same site.
- Port
CBuildingObjor per-cell walkability infrastructure. That was based on the original (wrong) hypothesis. ACE'sfind_building_collisionsis six lines and doesn't use per-cell walkability; our equivalent is already in place implicitly. - Start L.2d slice 2 as scoped in the design spec. Hypotheses X / Y / Z don't apply — the trace ruled them all out. Update or close the spec.
What the next session COULD do (in rough preference order)
These are NOT prescribed; they're candidates for the project-level ordering discussion the user wants to have.
-
Door state handling sub-phase. New phase (call it L.2g or nest under B.4). Touches: Use action → server door toggle, PhysicsState ETHEREAL bit honor, door open/close animation, collision-shape suppression during open animation. Probably 2-3 commits.
-
Fix the building double-registration latent bug (side finding #1). One-liner, no real impact today but cleaner trace later.
-
Capture slice 1.6 (state + flags in entity-source log) if any future ethereal-related work is on the immediate horizon. Otherwise defer.
-
Move to a different L.2 sub-phase entirely — L.2e (cell ownership /
find_cell_list/ outdoor seam updates) or L.2f (real-DAT + retail-observer conformance). Both are scoped in the L.2 plan-of-record. -
Triage the 8 pre-existing test failures that have shadowed the last few sessions. Some are in physics modules that L.2d slice 2 (if it ever happens) would touch — fixing them first gives a cleaner baseline.
-
Pick from CLAUDE.md's "Next phase candidates" list — non-L.2 work like Phase C visual fidelity, N.6 slice 2, or perf tiers 2/3. The session-level "I don't know what to do" feeling is often easier to resolve by shipping something in a different area for a session.
Reproducibility
Same recipe as L.2a + L.2d slice 1:
$env:ACDREAM_DAT_DIR = "$env:USERPROFILE\Documents\Asheron's Call"
$env:ACDREAM_LIVE = "1"
$env:ACDREAM_TEST_HOST = "127.0.0.1"
$env:ACDREAM_TEST_PORT = "9000"
$env:ACDREAM_TEST_USER = "testaccount"
$env:ACDREAM_TEST_PASS = "testpassword"
$env:ACDREAM_DEVTOOLS = "1"
$env:ACDREAM_PROBE_CELL = "1"
$env:ACDREAM_PROBE_RESOLVE = "1"
$env:ACDREAM_PROBE_BUILDING = "1"
dotnet run --project src\AcDream.App\AcDream.App.csproj --no-build -c Debug 2>&1 |
Tee-Object -FilePath "launch-l2d.log"
Walk acdream toward any Holtburg building threshold. Hit Ctrl+F2 to
toggle collision wireframes — you'll see the Door cylinder right at
the threshold. The name="Door" line appears in the log at startup
during the CreateObject stream replay.
Open questions / unresolved
- What
PhysicsStatebits is ACE sending for the Door entity? Not captured in current logs. Slice 1.6 would answer this. - Are these doors supposed to be open by default in retail? If yes, ACE config issue. If no, retail clients see the same blocker and players had to open them manually.
- What does ACE's door-state state machine look like? Probably
documented in
references/ACE/Source/ACE.Server/Entity/Door.csor similar.
These are doors-and-ACE-side questions; defer to the door-state sub-phase when (if) it gets scoped.
Worktree state at handoff
- All three slice 1 / 1.5 commits ready to merge to main.
- WorldBuilder submodule initialized + 6 directory junctions in place for the gitignored peer reference dirs (created during slice 1 prep). Worktree builds clean.
- Three test artifacts (
launch-l2d-slice1.log,launch-l2d-slice1b.log,launch-l2d-slice1c.log) are in working tree but not committed — they're large and ephemeral. Delete or preserve at the merge author's discretion.