Visual verification of Task 7 ship: doors block at dead-center (the small Cylinder catches) but the BSP slab doesn't catch off-center or inside-walking-out approaches. Probe-instrumented live capture proves multi-part registration is correct — every door spawns with shapes=cyl1+bsp1, and the BSP part is visited 135 times for a single door at player approaches as close as 0.42 m, with cacheHit=True. But zero [resolve-bldg] attributions for the BSP shape. Three artifacts added: 1. TransitionTypes.cs — new [bsp-test] probe in the BSP collision dispatch, fires BEFORE the cache lookup. Mirrors [cyl-test] on the Cylinder branch. Distinguishes "cache miss → silent skip" from "queried but no hit" (the latter doesn't show up in [resolve-bldg] which only fires on attributed hits). 2. DoorCollisionApparatusTests.cs — new grounded test (Apparatus_Grounded_50cmOffCenter_*) attempts to reproduce the production bug via a seeded PhysicsBody (Contact + OnWalkable + ContactPlane + WalkablePolygon). Currently doesn't reproduce because the apparatus's stub-terrain + synthetic-floor setup diverges from production's real Holtburg geometry. Captured as "documents-the-bug" — flip the assertion shape when the fix lands. 3. docs/research/2026-05-24-door-collision-task7-shipped-but-bug-remains.md — full session handoff. Identifies the remaining bug as a Path 5 (Contact branch + StepSphereUp) misbehavior at thin tall obstacles, not in the multi-part registration we just shipped. Leading hypothesis: DoStepUp's downward probe finds the same flat floor on the OTHER side of the door (Holtburg cottages have no Z change between exterior and interior floor), declares step-up success, BSP collision returns OK, sphere walks through. Recommended next move: relaunch with ACDREAM_DUMP_STEPUP=1 to verify the hypothesis. What this commit DOES NOT do: fix the remaining step-up bug. The A6.P4 multi-part registration foundation is correct and stays. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
10 KiB
Door collision — Task 7 shipped, partial fix, deeper bug remains
Date: 2026-05-24 (evening, continuation of door collision investigation)
Branch: claude/strange-albattani-3fc83c
Status: A6.P4 architecture is correct. Multi-part registration works.
The Holtburg door bug PARTIALLY fixed — center blocks, but off-center
and inside-out still walk through. Root cause is downstream in the
engine's grounded BSP collision path (Path 5 + step-up), NOT in the
multi-part registration we just shipped.
TL;DR
Three commits shipped (composable foundation):
| SHA | Title | What it does |
|---|---|---|
e1d94d7 |
dat-inspection test | Confirmed door part 0x010044B5 has full 1.9×0.26×2.5 m BSP slab (6 Landblock polys). Hypothesis A from prior handoff was wrong. |
3b7dc46 |
GetNearbyObjects dedup fix |
Changed HashSet<uint> (entityId) → HashSet<ShadowEntry>. Multi-part shapes no longer silently dropped. |
ca9341c |
Task 7 live wiring | RegisterLiveEntityCollision uses ShadowShapeBuilder.FromSetup + RegisterMultiPart. Doors now register cyl+bsp instead of just cyl. |
Live verification (visual user test):
| Scenario | Result |
|---|---|
| Dead center, walk into closed door (outside) | ✅ Blocks |
| 50 cm off-center, walk into closed door (outside) | ❌ Walks through |
| Inside walking out (closed door) | ❌ Walks through |
| Use door → swing → walk through | ✅ Works (ETHEREAL flip path) |
Probe-instrumented live capture confirms multi-part registration works:
- Every door spawn shows
[entity-source] shapes=cyl1+bsp1— both shapes register. - BSP part
0x010044B5is visited 135 times for a single door at player approaches as close asdistXY=0.415 m. cacheHit=Truefor every visit — the cache is populated.- BUT: zero
[resolve-bldg]attributions for the BSP shape (all 19 attributed hits showgfxObj=0x00000000= the Cylinder shape).
So the BSP is being QUERIED but never produces an attributed hit. The sphere walks through despite the BSP geometry being present and visited.
What's in the tree right now
$ git log --oneline -8
ca9341c feat(phys): A6.P4 Task 7 — RegisterLiveEntityCollision uses ShadowShapeBuilder + RegisterMultiPart
3b7dc46 fix(phys): GetNearbyObjects dedup-by-entityId silently drops multi-part shadows
e1d94d7 test(phys): door setup + GfxObj dat-inspection — Hypothesis A falsified
c89df8e docs(handoff): door collision per-part BSP session handoff (2026-05-24)
1498697 diag(phys): [cyl-test] probe — log every Cylinder shadow collision test
3e5dc8c test(phys): Task 6 regression — Deregister clears _entityShapes cache
d5ffb03 feat(phys): UpdatePosition handles multi-part entities
fca0a13 feat(phys): ShadowObjectRegistry.RegisterMultiPart
Uncommitted (to commit next):
src/AcDream.Core/Physics/TransitionTypes.cs— new[bsp-test]probe in the BSP collision dispatch, mirrors[cyl-test]. Fires when a BSP entry is visited, BEFORE the cache lookup. Distinguishes "cache miss → silent skip" from "queried but no hit." Gated onACDREAM_PROBE_BUILDING=1.tests/AcDream.Core.Tests/Physics/DoorCollisionApparatusTests.cs— new testApparatus_Grounded_50cmOffCenter_FrontApproach_DocumentsBugthat attempts to reproduce the production bug with a grounded body + seeded ContactPlane. Currently fails because the apparatus's behavior diverges from production (apparatus blocks immediately at tick 0 with a Z+ normal from the synthetic floor; production walks through).
Path 5 vs Path 6 — the divergence
BSPQuery.FindCollisions dispatches to 6 paths based on ObjectInfo
state. The crucial difference:
-
Path 6 (Default) — fires when
obj.Statehas noContactflag. CallsSphereIntersectsPolyInternalandSetCollideon hit. Apparatus tests use this path (no body,isOnGround=false). They all PASS — the door's BSP blocks the sphere correctly. -
Path 5 (Contact branch) — fires when
obj.State.HasFlag(Contact). CallsSphereIntersectsPolyInternal; on hit, callsStepSphereUp → DoStepUp → DoStepDownto attempt climbing over the obstacle. Returns OK if step succeeds, Slid if step fails. Production uses this path (player grounded →isOnGround=true→ engine setsContactflag atPhysicsEngine.cs:631). Production WALKS THROUGH.
So the bug is somewhere in Path 5's step-up logic. The leading hypothesis (not yet proven):
When the player is standing on flat ground in front of the door, step-up's
DoStepDownprobes 0.6 m downward from the sphere's current position. It finds the SAME flat ground extending to the OTHER SIDE of the door (Holtburg cottages have no Z change between exterior and interior floor — both at Z=94).find_walkabledeclares step-up SUCCESS, the BSP collision returnsOK, and the sphere walks through the door.The fix probably involves: step-up should reject if a forward probe at the lifted height STILL hits the same obstacle. The current DoStepDown probes only DOWNWARD; it doesn't verify that the forward motion at the lifted height is clear.
This is speculation — needs apparatus verification.
Why the apparatus didn't reproduce the bug
The grounded apparatus test (Apparatus_Grounded_50cmOffCenter_*) was
supposed to fail in the same way as production (walk through). Instead
it BLOCKED at tick 0 with normal=(0,0,1) — sphere position unchanged.
Diagnostic output:
[bsp-test] obj=0x000F424F gfx=0x010044B5 ... pos=(11.99,12.12,1.27)
distXY=1.234 cacheHit=True
[resolve] in=(12.500,11.000,0.480) tgt=(12.500,11.100,0.480)
out=(12.500,11.000,0.480) ok=True hit=yes n=(0,0,1) walkable=True
ACDREAM_DUMP_STEPUP=1 produced no stepup: ENTER lines, so
DoStepUp was NOT called. The hit normal (0,0,1) came from
somewhere else (likely the seeded walkable polygon or the synthetic
floor interaction with the engine's terrain step-down).
The apparatus's stub terrain (Z=-1000) + synthetic walkable poly at Z=0 may be causing the engine to take a different code path than production's real Holtburg terrain. Reproducing production fully would require:
- Real terrain heightmap covering the test landblock at Z=94
- EnvCell or stab geometry near the test door
- Proper cottage/cell setup so portal-reachable cells include the door's outdoor cell when player is indoor
This is significant apparatus investment. Worth it IF the bug requires multi-tick simulation in real geometry to surface. For now, the apparatus shows the broad shape: with proper grounded state + seeded body, the engine doesn't take the same path as the airborne (Path 6) test.
Recommended next steps (ranked)
Option A — Live diagnostic with ACDREAM_DUMP_STEPUP=1 (cheapest)
Relaunch with ACDREAM_PROBE_BUILDING=1 + ACDREAM_DUMP_STEPUP=1.
Walk into a closed door off-center. The step-up dump will show:
- Whether
DoStepUpfires at all when the BSP hits - If so, what the input normal is
- Whether
stepDownsucceeds or fails
If stepDown succeeds (i.e., step-up climbs over the door), we've
confirmed the hypothesis above and can target the fix.
Option B — Build a richer apparatus
Replace the stub terrain with a real heightmap-like surface at Z=94 spanning the test landblock. Replace the synthetic walkable poly with a proper terrain polygon at the door's world XY. This should let Path 5 run the SAME way as production. Then iterate on the fix locally in <500 ms.
Estimated effort: 1-2 hours of apparatus work.
Option C — Direct retail cdb trace
Attach cdb to a running retail client at a Holtburg cottage doorway,
break on CTransition::step_up or CTransition::step_down, and
observe how retail handles step-up against a door. Compare against
acdream's behavior.
Estimated effort: 30 min - 2 hours depending on what we find.
Option D — Pivot to fix-and-verify
Hypothesis-based fix: in DoStepUp, reject step-up if the input
collision normal is mostly horizontal AND the obstacle's bounding
sphere height range significantly exceeds the step-up height. The
door has BS radius 1.975 m centered at Z=1.275 → top of BS at Z=3.25,
way above step-up=0.6. If we detect "this obstacle is too tall to
step over," fall back to wall-slide.
Risk: might break stairs / ramps. Need apparatus to verify.
Recommendation
Option A first (~5 min, no code changes needed). If hypothesis confirmed, then Option D (with apparatus from Option B for regression testing).
Files touched this session (cumulative)
Committed:
src/AcDream.Core/Physics/ShadowObjectRegistry.cs(dedup fix)src/AcDream.App/Rendering/GameWindow.cs(Task 7 wiring)tests/AcDream.Core.Tests/Physics/DoorSetupGfxObjInspectionTests.cs(NEW)tests/AcDream.Core.Tests/Physics/DoorCollisionApparatusTests.cs(NEW)docs/research/2026-05-24-door-dat-inspection-findings.md(NEW)
Uncommitted (this doc + 2 file changes):
src/AcDream.Core/Physics/TransitionTypes.cs(added[bsp-test]probe)tests/AcDream.Core.Tests/Physics/DoorCollisionApparatusTests.cs(added grounded test scenario — fails for unrelated apparatus reasons but the probe wiring is sound)
Memory updated: feedback_dedup_keys_after_cardinality_change.md
Pickup prompt for next session
A6.P4 Task 7 shipped (RegisterLiveEntityCollision uses
ShadowShapeBuilder + RegisterMultiPart) and the foundation fix
(GetNearbyObjects dedup on full ShadowEntry instead of entityId).
Production verification: center blocks, but off-center + inside-out
still walk through closed doors. The multi-part registration is
correct (verified by live probes); the remaining bug is downstream
in BSPQuery Path 5's step-up logic.
Read docs/research/2026-05-24-door-collision-task7-shipped-but-bug-remains.md
State both altitudes:
Currently working toward: M1.5 — Indoor world feels right
Current phase: A6.P4 door collision — step-up misbehavior
investigation. Multi-part registration shipped;
step-up at thin tall obstacles is the remaining bug.
Recommended first move: Option A from the findings doc — relaunch
with ACDREAM_PROBE_BUILDING=1 + ACDREAM_DUMP_STEPUP=1, walk into
a Holtburg cottage door off-center. The step-up dump will reveal
whether DoStepUp is incorrectly succeeding for the door's BSP slab
hit (the leading hypothesis: DoStepDown finds the same flat floor
on the other side of the door, declaring step-up success).
DO NOT re-investigate the multi-part registration or GetNearbyObjects
dedup — both are confirmed working. Focus on the step-up path 5
behavior for thin tall obstacles.