probe(physics): ACDREAM_PROBE_SUPPORT + ACDREAM_WIRE_MESH — separate #337's three candidates
The user is wedged at the top of Neftet rock plateaus, jumps sink into the mesh, and a corpse falls straight through. ACDREAM_PROBE_REACH already ruled out its own domain: blocked=0, every candidate tested-ok. Three candidates remain — terrain support, a collision mesh not where its visual is, or the transition wedging on an unobstructed path. ACDREAM_PROBE_RESOLVE alone cannot separate them. It prints a three-value contact-plane token, no plane normal, no plane height, no terrain sample and no plane provenance, so all three produce the same line. Two additions: [support] — one line per resolve for EVERY body, not just the player. A corpse is a plain physics body with no player-specific logic, so its fall-through is the cheapest available control on "movement code vs geometry data", and it is invisible to any player-filtered probe. The line samples the outdoor terrain INDEPENDENTLY at the body's own out-XY and prints the contact plane's own height at that same XY. Two heights at one point make support=terrain / object / none a measurement rather than an inference, and cpSrc= names the site that asserted the plane so provenance and classification cross-check. [geom] — once per GfxObj that comes near a mover: the object's physics-BSP vertex cloud against its visual mesh AABB in the same local frame, through the same prepared accessors the resolver queries. verdict=coincident REFUTES the working hypothesis for that object outright; no-physics-bsp / empty-physics-bsp / displaced / extent-mismatch each name a specific data defect. Built to refute, not to confirm — two diagnoses on this defect's lineage have already been refuted by measurement. ACDREAM_WIRE_MESH upgrades the existing F2 overlay, which drew a broadphase proxy cylinder for BSP objects and so could not answer the question at all, to the real physics-BSP polygon edges (cyan) beside the visual mesh box (magenta) and the terrain surface (yellow). Own class per code-structure rule 1. The provenance latch lives on PhysicsDiagnostics, not on CollisionInfo. Two fields there first — the obvious home — broke the flat/graph differential referee and the scratch-reset poison test, both of which compare CollisionInfo member-for-member. Teaching either to skip a member is a one-line green fix that puts a permanent hole in a referee whose whole job is comparing everything. Captured as feedback_probe_state_off_compared_types. Seven tests cover the support classifier's boundaries: a wrong classifier does not fail to answer, it answers confidently wrong. Gates: Release build 0 errors; complete suite 11,225 passed / 4 skipped / 0 failed from a cleaned tree — baseline 11,218/4/0 plus exactly the seven new tests, skips unchanged. Issue #337 filed with the symptom set, what is ruled out, and a table of what each possible output means. All of this is TEMPORARY and recorded for stripping with the physics-probe family. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
13fcf38138
commit
49a7e90652
9 changed files with 1376 additions and 1 deletions
|
|
@ -1913,6 +1913,15 @@ public sealed class PhysicsEngine
|
|||
? PhysicsResolveCapture.Snapshot(body)
|
||||
: null;
|
||||
|
||||
// #337 (2026-08-06 — TEMPORARY): arm the [support] probe's
|
||||
// contact-plane provenance latch for this resolve, ahead of everything
|
||||
// including the carried-plane seed below. The seed is itself one of
|
||||
// the ten sites that assert a plane, so it stamps its own name and a
|
||||
// capture can read `cpSrc=ResolveWithTransition:<line>` as "carried
|
||||
// from the body, nothing re-derived it this resolve" without needing a
|
||||
// sentinel value for that case. No-op when the probe is off.
|
||||
PhysicsDiagnostics.BeginContactPlaneAttribution();
|
||||
|
||||
var transition = RentTransition();
|
||||
try
|
||||
{
|
||||
|
|
@ -2299,6 +2308,67 @@ public sealed class PhysicsEngine
|
|||
$"[resolve] ent=0x{movingEntityId:X8} in=({currentPos.X:F3},{currentPos.Y:F3},{currentPos.Z:F3}) cell=0x{cellId:X8} tgt=({targetPos.X:F3},{targetPos.Y:F3},{targetPos.Z:F3}) out=({probePost.X:F3},{probePost.Y:F3},{probePost.Z:F3}) cell=0x{sp.CheckCellId:X8} ok={ok} groundedIn={isOnGround} cp={probeCp} hit={probeHit} walkable={sp.HasLastWalkablePolygon}"));
|
||||
}
|
||||
|
||||
// #337 [support] probe (2026-08-06 — TEMPORARY, strip with the
|
||||
// physics-probe family). Runs for EVERY body, not just the player:
|
||||
// a corpse sinking through geometry is a plain physics body with
|
||||
// no player-specific logic, so it is the cheapest possible control
|
||||
// on whether the movement code or the geometry is at fault, and it
|
||||
// is invisible to any player-filtered probe.
|
||||
//
|
||||
// The terrain sample below is INDEPENDENT of whatever the sweep
|
||||
// decided — it asks the landblock directly what the ground height
|
||||
// is under the body's own out-XY. Pairing that with the contact
|
||||
// plane's height at the same XY is what separates "terrain is
|
||||
// holding this body up" from "some object surface is". Read-only:
|
||||
// SampleTerrainWalkable takes no locks, mutates nothing, and is
|
||||
// not on the resolve's committed path.
|
||||
if (PhysicsDiagnostics.ProbeSupportEnabled)
|
||||
{
|
||||
Vector3 outPos = sp.CheckPos;
|
||||
TerrainWalkableSample? terrain =
|
||||
SampleTerrainWalkable(outPos.X, outPos.Y);
|
||||
|
||||
bool terrainSampled = terrain.HasValue
|
||||
&& PhysicsDiagnostics.TryPlaneZAt(
|
||||
terrain.Value.Plane, outPos.X, outPos.Y, out _);
|
||||
float terrainZ = float.NaN;
|
||||
if (terrainSampled)
|
||||
{
|
||||
PhysicsDiagnostics.TryPlaneZAt(
|
||||
terrain!.Value.Plane, outPos.X, outPos.Y, out terrainZ);
|
||||
}
|
||||
|
||||
PhysicsDiagnostics.LogSupport(
|
||||
moverId: movingEntityId,
|
||||
isPlayer: (moverFlags & ObjectInfoState.IsPlayer) != 0,
|
||||
inPos: currentPos,
|
||||
inCell: cellId,
|
||||
targetPos: targetPos,
|
||||
outPos: outPos,
|
||||
outCell: sp.CheckCellId,
|
||||
ok: ok,
|
||||
groundedIn: isOnGround,
|
||||
contact: transition.ObjectInfo.Contact,
|
||||
onWalkable: transition.ObjectInfo.OnWalkable,
|
||||
contactPlaneValid: ci.ContactPlaneValid,
|
||||
contactPlane: ci.ContactPlane,
|
||||
contactPlaneCellId: ci.ContactPlaneCellId,
|
||||
contactPlaneIsWater: ci.ContactPlaneIsWater,
|
||||
contactPlaneSource: PhysicsDiagnostics.ContactPlaneSource,
|
||||
lastKnownValid: ci.LastKnownContactPlaneValid,
|
||||
lastKnownPlane: ci.LastKnownContactPlane,
|
||||
terrainSampled: terrainSampled,
|
||||
terrainZ: terrainZ,
|
||||
terrainNormal: terrain?.Plane.Normal ?? Vector3.Zero,
|
||||
terrainCellId: terrain?.CellId ?? 0u,
|
||||
terrainIsWater: terrain?.IsWater ?? false,
|
||||
walkablePolygon: sp.HasWalkablePolygon,
|
||||
lastWalkablePolygon: sp.HasLastWalkablePolygon,
|
||||
stepUpHeight: stepUpHeight,
|
||||
stepDownHeight: stepDownHeight,
|
||||
velocity: body?.Velocity ?? Vector3.Zero);
|
||||
}
|
||||
|
||||
// Phase W Stage 0 (2026-06-02): [cell-swept] probe — swept cell vs static-derived cell.
|
||||
// Emits before the ResolveResult is built so it shows what BOTH paths would return.
|
||||
// No ResolveCellId call here (it has a CellGraph.CurrCell side effect). No behavior change.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue