docs(phys L.2d): slice 1 + 1.5 shipped handoff + 3rd plan-of-record reframe

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>
This commit is contained in:
Erik 2026-05-12 19:46:45 +02:00
parent 8bacef0598
commit 34b7f1faa1
2 changed files with 283 additions and 22 deletions

View file

@ -169,30 +169,40 @@ fallback.
- Audit `Setup.Radius` and cylinder fallback behavior against retail before
relying on them for conformance.
Current sub-direction (revised 2026-05-13 in slice 1 design spec):
The "I can't walk through doorways" symptom at Holtburg is **NOT a door-
state-toggle issue** — the `[resolve]` probe captured 140 hit=yes lines
at the doorway with `obj=0xA9B47900` (126 hits), one specific BSP shadow
entry. The 2026-05-12 handoff initially proposed porting `CBuildingObj` +
**per-cell walkability** as the fix, but reading
[ACE BuildingObj.cs:39-52](../../references/ACE/Source/ACE.Server/Physics/Common/BuildingObj.cs)
and named-retail
[acclient_2013_pseudo_c.txt:701260](../research/named-retail/acclient_2013_pseudo_c.txt)
shows that's **not how retail solves doorways**. `find_building_collisions`
is just one BSP test on `PartArray.Parts[0]`. The doorway gap lives
inside that part's physics BSP itself. Per-cell walkability
(`CCellStruct::point_in_cell`, `sphere_intersects_cell`,
`box_intersects_cell`, `CObjCell::find_cell_list`) is how the resolver
selects **which cells** to iterate, not how it decides whether a wall
has a hole — that work belongs to **L.2e**, not L.2d.
Current sub-direction (revised 2026-05-13 evening after slice 1 + 1.5
shipped and Holtburg-doorway capture analyzed — third reframe):
L.2d as scoped ("shape fidelity: Sphere / CylSphere / Building Objects")
is **essentially closed at the Holtburg site that motivated this phase**.
Building BSP collision works correctly — the slice-1.5 probe captured
real triangles in plausible world positions for `gfxObj=0x01000A2B` with
`bspR=13.99m`. The 121 wall hits the L.2a probe attributed to
`obj=0xA9B47900` were **side effects of the player already being pushed
back by a separate Door cylinder entity** at the same doorway threshold.
L.2d slice 1 is therefore a **read-only BSP-hit diagnostic** that
captures full collision evidence per `[resolve]` `hit=yes` line.
Distinguishes three hypotheses (wrong BSP loaded / over-registered
parts / BSPQuery flaw) from a single Holtburg-doorway capture; slice
2 is the right-sized fix scoped from slice 1's evidence. Design spec:
The actual blocker is a server-spawned **Door** entity — Setup
`0x020019FF` named `"Door"` — that ACE places at each Holtburg-town
building threshold (five doors total observed across `0xA9B40029`,
`0xA9B40154`, `0xA9B40155`). It registers as a Cylinder shadow entry
via the server-spawn path; its Cylinder collision blocks the player
walking into the doorway. That's **door-state handling**, a different
class of problem from L.2d's shape-fidelity scope — it touches network
(`CreateObject` PhysicsState bits), interaction (Use action on door
entity), animation (door open/close), and collision-state-toggle.
Recommend: **leave L.2d in "watch-and-wait" mode** with slice 1's probe
infrastructure in place. No more L.2d slices until a NEW shape-fidelity
bug is observed at a different site (dungeon walls, stairs, roofs) with
the probe-armed client. The door-state work becomes its own sub-phase
(probably nested under B.4 interaction or filed as a new L.2 sub-phase
like L.2g) scoped separately.
Full slice 1 + 1.5 handoff:
[docs/research/2026-05-13-l2d-slice1-shipped-handoff.md](../research/2026-05-13-l2d-slice1-shipped-handoff.md).
Design spec (now mostly historical, framing was wrong but probe
infrastructure shipped from it):
[docs/superpowers/specs/2026-05-13-l2d-cbuildingobj-collision-design.md](../superpowers/specs/2026-05-13-l2d-cbuildingobj-collision-design.md).
Handoff: [docs/research/2026-05-12-l2a-shipped-l2d-handoff.md](../research/2026-05-12-l2a-shipped-l2d-handoff.md).
Predecessor L.2a handoff:
[docs/research/2026-05-12-l2a-shipped-l2d-handoff.md](../research/2026-05-12-l2a-shipped-l2d-handoff.md).
### L.2e - Cell Ownership: Outdoor Seams, CELLARRAY, cell_bsp

View file

@ -0,0 +1,251 @@
# 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](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=1`** env 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
under `StepSphereUp` recursion after the slice 1.5 fix.
- **`[entity-source]`** one-time log line per `ShadowObjects.Register`
call, gated on the same flag. Makes `entityId=0xA9B479` in 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](../../tests/AcDream.Core.Tests/Physics/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.99m` for
GfxObj `0x01000A2B`, real triangles in real positions.
- `Setup.CylSpheres` for 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](../../src/AcDream.App/Rendering/GameWindow.cs:5625)
gates the mesh-AABB-fallback on `entityBsp == 0`, but the BSP
registration at [line 5530](../../src/AcDream.App/Rendering/GameWindow.cs: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 `CBuildingObj` or per-cell walkability infrastructure.**
That was based on the original (wrong) hypothesis. ACE's
`find_building_collisions` is 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.
1. **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.
2. **Fix the building double-registration latent bug** (side
finding #1). One-liner, no real impact today but cleaner trace
later.
3. **Capture slice 1.6** (state + flags in entity-source log) if
any future ethereal-related work is on the immediate horizon.
Otherwise defer.
4. **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](../plans/2026-04-29-movement-collision-conformance.md).
5. **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.
6. **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:
```powershell
$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 `PhysicsState` bits 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.cs`
or 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.