acdream/tests/AcDream.Core.Tests
Erik 5a1eeace73 docs(physics): #337 diagnosed — it is #333's query-site broadphase, not the mesh
Report-only. No production code changed.

The collision mesh is present, correctly shaped, correctly placed in the
world, and the BSP traversal reaches every part of it. The mover never gets
as far as the query. FindObjCollisionsInCell's per-object broadphase measures
the mover's distance to the shadow entry's Position — the part ORIGIN — and
compares it against obj.Radius, which is the physics-BSP ROOT BOUNDING
SPHERE's radius. For 0xC8766009 those two points are 23.556 m apart, so a
mover standing on its plateau is inside the real bounding sphere by ~20 m of
margin and is still rejected. Same defect AP-156 fixed in the flood and #334
fixed in the registration extent walk, left in place at the query site.

Measured, not inferred. An offline replay against the installed DAT
reconstructs all eleven landblock-0x8766 owners and matches the live [geom]
placement exactly (0xC8766002 at (84.699,100.082,13.000) yaw -45.00 vs the
log's objPos + bspCentreOffset). At the position the client fell through, the
production swept query returns a hit on poly 31 at 0.037-0.366 m while the
filter rejects the candidate: distToOrigin=60.434 > maxReach=59.697, distance
to the bounding-sphere CENTRE 37.083 m against a 56.909 m radius. The live
capture recorded that rejection 7,225 times with the probe's own
wouldAcceptAtCenter=True on every one.

Bounded because the dead zone is the shell between maxReach and the true
sphere, up to ~23.5 m thick on the far side. movement.Length() is a budget
term: a 0.25 m walking step gives shortfall +0.60, a 0.72 m step +0.14, and
~0.86 m passes — which is exactly why jumping over the spot works, walking
into it does not, and a corpse falls through.

Three hypotheses refuted by measurement, not by argument:

- "the rock's own mesh never collides" — true of 0xC8766002 and it is
  INNOCENT; its geometry is 22.8 m from the wedge and it has zero brute-force
  hits over a 12,493-point lattice covering the plateau. It is a candidate
  only because it is a 130x147 m owner. The rock actually walked on is
  0xC8766009.
- wrong world transform — the offline placement reproduces the runtime
  exactly, and a uniform displacement cannot produce a bounded pocket.
- BSP traversal hole — a referee ran the production walk against brute force
  at 7,770 on-surface probes across all eleven owners plus 137,423 lattice
  points. Mismatch 0 everywhere. A 0.5 m hole map also shows continuous
  upward-facing coverage across the whole wedge region.

[geom]'s verdict=coincident was never able to decide this: LogGeometry
compares the physics box against the visual box in the object's OWN LOCAL
FRAME, so it proves shape agreement and says nothing about world placement.
Recorded in the doc so the next reader does not re-trust it.

Retail has no per-object distance filter on the BSP branch. Verified
instruction-by-instruction with cdb against the PDB-paired v11.4186 binary:
CPartArray::FindObjCollisions @0x00518180 is 14 instructions of bare
do/while over parts[i]; CPhysicsPart::find_obj_collisions @0x0050d8d0 is 17
instructions of two null checks plus the call to CGfxObj::find_obj_collisions
@0x00534700. No compare, no float math in either. The in-tree comment calling
the filter a retail analog and response-neutral is wrong on both counts.

The support=object cpNz=1.0000 readings inside the rock are not the rock:
ValidateTransition:6076 is retail's stationary-fall failsafe manufacturing a
flat plane through the sphere bottom, and :5997 is the LastKnownContactPlane
restore holding a stale plane. Both are retail-correct responses to a stuck
body, and they are why the client believes it is standing while ACE rejects
the position.

Preferred fix is to delete the pre-check for BSP entries and correct the
comment; fallback is to measure to the bounding-sphere centre, which also
needs BoundsCenter carried on ShadowEntry. Neither is landed.

The reproducer was confirmed to FAIL when un-skipped, with the numbers above
— this campaign has caught eleven green tests covering nothing, so a fixture
that cannot distinguish the bug is worse than none.

Gates: bin/obj deleted, Release build 0 errors, Core suite 4,287 passed /
2 skipped / 0 failed (baseline 4,286/1 plus three new dumps and the one
deliberately skipped reproducer).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-06 21:16:31 +02:00
..
Allegiance feat(allegiance): Phase H.2 AllegianceRequests + AllegianceTree model 2026-04-18 17:17:45 +02:00
Audio feat(core): adopt retail's SoundType catalog; retire AC2D 2026-07-29 07:38:56 +02:00
Chat fix(chat): display retail's text for WeenieError 0x0504 and three PK siblings 2026-08-03 22:20:31 +02:00
Combat fix(combat): #298 — admit player targets to melee/missile attack and the camera 2026-08-03 21:29:15 +02:00
Conformance refactor(pipeline): MP1a cleanup - narrow public surface, csproj parity, move residue 2026-07-05 20:52:01 +02:00
Fixtures fix(physics): enforce retail step-down support radius (#273) 2026-07-31 12:10:03 +02:00
Input refactor(physics): delete legacy PhysicsEngine.Resolve/ResolvePlacement/HasCellSurface (C5a, AP-1/AD-1) 2026-08-05 14:11:31 +02:00
Items fix(physics): #297 — keep the PWD bitfield live so PK status reaches the client 2026-08-03 20:59:01 +02:00
Lighting perf(lighting): bound global light selection 2026-07-25 05:40:32 +02:00
Meshing feat(vfx): bind effects to live animated poses 2026-07-14 10:56:01 +02:00
Physics docs(physics): #337 diagnosed — it is #333's query-site broadphase, not the mesh 2026-08-06 21:16:31 +02:00
Player feat(player): port retail augmentation stat chain 2026-07-31 08:08:23 +02:00
Plugins fix(rendering): bound portal resource lifetime 2026-07-18 21:35:16 +02:00
Properties feat(core): adopt retail's full WeenieError code table 2026-07-29 07:33:27 +02:00
Rendering fix(physics): validate retail cell containment roots 2026-07-31 14:48:26 +02:00
Selection refactor(net): converge live session state reset 2026-07-21 12:00:48 +02:00
Social refactor(net): converge live session state reset 2026-07-21 12:00:48 +02:00
Spells merge: #267 vitae character-panel display (attributes vitae-immune per retail; skill dual parentheticals) 2026-07-30 18:14:48 +02:00
Streaming fix(test): stop the streaming priority-apply tests reading a warm JIT 2026-07-28 18:09:08 +02:00
Terrain fix #108-residual (root cause): terrain drew DOUBLE-SIDED - port retail landPolysDraw eye-side gate as terrain backface cull 2026-06-12 22:05:31 +02:00
Textures fix(render): keep authored surface translucency on composite textures 2026-07-29 21:40:26 +02:00
Ui feat(chat): port retail client command families 2026-07-13 14:50:15 +02:00
Vfx fix(vfx): classify hardwareless particle emitters once 2026-07-27 00:03:44 +02:00
World fix(physics): validate retail cell containment roots 2026-07-31 14:48:26 +02:00
AcDream.Core.Tests.csproj test: fix PhysicsResolveCapture/PhysicsDiagnostics static-leak isolation 2026-06-02 15:20:24 +02:00
GlobalUsings.cs refactor(runtime): own local movement and outbound cadence 2026-07-26 12:33:53 +02:00
SmokeTest.cs chore: phase 1 — add Core, Abstractions, App, Tests projects 2026-04-10 09:22:33 +02:00
xunit.runner.json test: fix PhysicsResolveCapture/PhysicsDiagnostics static-leak isolation 2026-06-02 15:20:24 +02:00