probe(physics): ACDREAM_PROBE_STEP_HEIGHTS — three readings along #338's chain
Some checks are pending
Headless portability / portable-headless (ubuntu-latest) (push) Waiting to run
Headless portability / portable-headless (windows-latest) (push) Waiting to run
Headless portability / linux-graphical (push) Waiting to run
Headless portability / linux-vulkan (push) Waiting to run

The live reading of 0.400 says the controller held its default at that
instant, not why. Two very different causes produce it: the
Setup-derived prepare/publish path never runs for the local player, or
it runs and a later writer clobbers the result. Fixing without knowing
which is a coin flip.

One reading at each hop — prepare (Setup value computed and scaled),
publish (assigned to the controller), resolve (what the resolver is
actually handed) — with a decision table on the flag mapping each
pattern to its cause, including the 0.000 case that would mean a null
Setup took the retail dummy path.

Edge-triggered per site, so the per-tick resolve site prints once per
distinct pair and cannot drown the two one-shot sites it exists to be
compared against. Prepare prints the raw authored pair beside the
scaled one, so a surprise separates wrong-Setup from wrong-scale
without a second run.

Lives in PhysicsDiagnostics per code-structure rule 5 rather than as
per-call-site env reads. Zero cost when off.

Suite 11,231 passed / 4 skipped / 0 failed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-06 22:50:42 +02:00
parent d5dd0b554b
commit 45d7154712
4 changed files with 120 additions and 0 deletions

View file

@ -2625,6 +2625,15 @@ public sealed class PlayerMovementController
// or it re-zeros the gravity velocity and the body re-wedges instead of falling off.
bool candidateMoved = postIntegratePos != preIntegratePos;
// #338 (TEMPORARY): third and last reading along the chain — what
// the resolver is ACTUALLY handed, per ordinary movement tick.
// Edge-triggered inside the probe, so this per-tick site prints
// once per distinct pair and cannot drown the two one-shot sites
// it exists to be compared against.
PhysicsDiagnostics.LogStepHeights(
"resolve", StepUpHeight, StepDownHeight,
$"onWalkable={_body.OnWalkable} cell=0x{CellId:X8}");
// ── 3. Collision resolution via CTransition sphere-sweep ─────────────
// The Transition system subdivides the movement from pre→post into
// sphere-radius steps, testing terrain collision at each step.

View file

@ -214,6 +214,15 @@ internal sealed class RuntimeLocalPlayerPhysicsPublicationState : IDisposable
controller.LocalEntityId = record.Key!.Value.LocalEntityId;
controller.StepUpHeight = command.Physics.StepUpHeight;
controller.StepDownHeight = command.Physics.StepDownHeight;
// #338 (TEMPORARY): second of three readings. If `prepare` printed the
// authored pair and this line does not appear, the command is built
// and never consumed on the local player's path.
PhysicsDiagnostics.LogStepHeights(
"publish",
command.Physics.StepUpHeight,
command.Physics.StepDownHeight,
$"localEntityId={controller.LocalEntityId} scale={command.Physics.Scale:F3}");
controller.SphereList = command.Physics.Spheres;
controller.ObjectScale = command.Physics.Scale;
controller.PreparePositionForCommit(

View file

@ -180,6 +180,15 @@ internal static class RuntimeSetPositionMoverPreparer
float stepUp = setup is not null ? setup.StepUpHeight * scale : 0f;
float stepDown = setup is not null ? setup.StepDownHeight * scale : 0f;
// #338 (TEMPORARY): first of three readings along the chain. Prints
// the raw authored pair alongside the scaled one, so a surprise here
// separates "wrong Setup" from "wrong scale" without a second run.
PhysicsDiagnostics.LogStepHeights(
"prepare", stepUp, stepDown,
setup is not null
? $"authored=({setup.StepUpHeight:F3},{setup.StepDownHeight:F3}) scale={scale:F3}"
: "setup=NULL (retail dummy path, exact zero steps)");
EntityCollisionFlags collisionFlags =
EntityCollisionFlagsExt.FromPwdBitfield(
record.Snapshot.ObjectDescriptionFlags ?? 0u);