diag(physics): remote landing-edge probe; record the two live jump defects
The user live-tested route 4a and reported two defects on player remotes: a
remote holds the falling animation after landing before finally landing, and a
remote jumping onto a house plants on the roof where retail slides off, then
blips to the slid-down position.
Neither is a route 4a regression. Do NOT revert 44830a0e — reverting would
restore the per-packet render slam 4a removed without touching either defect.
Bug B's root cause is identified and already covered by open issue #32, whose
text names both symptoms in one sentence. Both landing sites assert
TransientState |= Contact | OnWalkable unconditionally, where retail derives it
from the contact plane — CPhysicsObj::SetPositionInternal @0x00515330
(`if (contact_plane.N.z < floor_z) set_on_walkable(0) else set_on_walkable(1)`).
A steep roof is contact but NOT on_walkable; asserting both suppresses the slide
response, so the body sits until the server's positions walk 4 m away and
AP-87's threshold snaps it. That is the blip. Verified byte-identical pre-4a via
`git show 19d95094:`.
Bug B's *visible shape* IS 4a's: pre-4a every packet slammed the render entity
to the wire pose, so a stuck body flickered toward the true sliding position
5-10x per second — jitter rather than a clean hold.
Bug A stops at the goal's stop-condition rather than getting a speculative fix.
Three hypotheses with non-overlapping fixes; picking wrong means changing a
retail-ported gate on a guess. Retail's mechanism is already fully decoded, so
what is missing is OUR runtime state — no cdb trace against retail is needed.
Adds ACDREAM_PROBE_REMOTE_LANDING (PhysicsDiagnostics, read once at startup per
the diagnostic-owner rule, one bool check when off). It logs both landing sites
immediately before HitGround, and — the most diagnostic signal — emits a
separate line when a site is reached but the gravity gate is about to no-op,
which is hypothesis 1 (a wholesale Body.State write wiping the transient Gravity
bit mid-air, exactly AP-81's stated risk). Temporary instrumentation, marked for
stripping once the evidence is in.
Evidence recorded rather than new bugs filed: #32 gains the observation, the
root cause and the #173/AD-10 dependency caveat; AP-87 gains a live instance of
its stated risk; AD-10's stale file:line is corrected to RemoteMotionCombiner
with a note that its terrain-only normal cannot see a house roof at all.
Also files #308 — a SECOND flaky test, distinct from #302, which was twice
misattributed to it before being written down. #302 is a GC-allocation assertion
in App.Tests; #308 is a wall-clock deadline loop in Core.Net.Tests that fails
only under full-suite CPU contention (0 failures in 4 isolated runs). Conflating
them hides one, and an agent told to "ignore the known flake" would wave through
a real transport regression.
Gates: complete Release solution 10,938 passed / 4 skipped / 0 failed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
dda1e2a03a
commit
eeec4fb42a
6 changed files with 366 additions and 2 deletions
|
|
@ -1860,6 +1860,34 @@ internal sealed class LiveEntityNetworkUpdateController
|
|||
{
|
||||
_motionRuntime.EnsureRemoteMotionBindings(rmState, aeForLand, update.Guid);
|
||||
}
|
||||
|
||||
// Bug A investigation (2026-08-04, docs/ISSUES.md #32):
|
||||
// capture the exact state HitGround is about to act on —
|
||||
// see PhysicsDiagnostics.LogRemoteLanding for the field
|
||||
// list and PhysicsDiagnostics.ProbeRemoteLandingEnabled
|
||||
// for the discriminator table. TEMPORARY — strip once
|
||||
// the live-test run has landed.
|
||||
if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeRemoteLandingEnabled)
|
||||
{
|
||||
bool gravitySetForProbe = rmState.Body.HasGravity;
|
||||
AcDream.Core.Physics.PhysicsDiagnostics.LogRemoteLanding(
|
||||
site: "controller",
|
||||
guid: update.Guid,
|
||||
airborneBefore: true,
|
||||
gravitySet: gravitySetForProbe,
|
||||
contact: rmState.Body.InContact,
|
||||
onWalkable: rmState.Body.OnWalkable,
|
||||
hasDefaultSink: rmState.Motion.DefaultSink is not null,
|
||||
resolveIsOnGround: null,
|
||||
sequencerStyle: aeForLand?.Sequencer?.CurrentStyle ?? 0,
|
||||
sequencerMotion: aeForLand?.Sequencer?.CurrentMotion ?? 0);
|
||||
if (!gravitySetForProbe)
|
||||
{
|
||||
AcDream.Core.Physics.PhysicsDiagnostics.LogRemoteLandingGateNoOp(
|
||||
"controller", update.Guid);
|
||||
}
|
||||
}
|
||||
|
||||
ulong landingStateAuthorityVersion =
|
||||
positionRecord.StateAuthorityVersion;
|
||||
rmState.Movement.HitGround();
|
||||
|
|
|
|||
|
|
@ -155,6 +155,92 @@ public static class PhysicsDiagnostics
|
|||
public static bool ProbeStickyEnabled { get; set; }
|
||||
= Environment.GetEnvironmentVariable("ACDREAM_PROBE_STICKY") == "1";
|
||||
|
||||
/// <summary>
|
||||
/// Bug A investigation (2026-08-04, live route 4a test — see
|
||||
/// <c>docs/ISSUES.md</c> #32 and
|
||||
/// <c>docs/research/2026-08-04-remote-landing-investigation.md</c>): a
|
||||
/// PLAYER remote sometimes stays in the falling animation after landing,
|
||||
/// then snaps to the grounded pose after a delay. Three hypotheses were
|
||||
/// identified, with non-overlapping fixes, so this probe captures the
|
||||
/// state needed to discriminate them at BOTH remote landing-detection
|
||||
/// sites: the UpdatePosition landing block in
|
||||
/// <c>LiveEntityNetworkUpdateController</c> (site=<c>controller</c>) and
|
||||
/// the per-tick VectorUpdate landing branch in
|
||||
/// <c>RuntimeRemotePhysicsUpdater</c> (site=<c>per-tick</c>).
|
||||
///
|
||||
/// <para>
|
||||
/// When true, emits one <c>[remote-landing]</c> line per landing edge via
|
||||
/// <see cref="LogRemoteLanding"/>, capturing: the airborne flag on entry,
|
||||
/// whether the Gravity state bit is still set (the
|
||||
/// <c>MotionInterpreter.HitGround</c> gate at
|
||||
/// <c>MotionInterpreter.cs</c>:~2435 no-ops silently when it is NOT —
|
||||
/// hypothesis 1), the Contact/OnWalkable transient bits, whether a
|
||||
/// <c>DefaultSink</c> is bound (hypothesis 2 — nothing to dispatch
|
||||
/// through), the per-tick site's <c>resolveResult.IsOnGround</c> (n/a at
|
||||
/// the controller site, which has no resolver call), and the
|
||||
/// sequencer's current style/motion id (hypothesis 3 — the sequencer
|
||||
/// disagrees with what the re-apply should produce). A companion
|
||||
/// <c>[remote-landing-gate]</c> line fires via
|
||||
/// <see cref="LogRemoteLandingGateNoOp"/> whenever a landing site is
|
||||
/// reached but Gravity is already clear, so the HitGround call about to
|
||||
/// happen is a silent no-op — the single most valuable signal for
|
||||
/// hypothesis 1.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Initial state from <c>ACDREAM_PROBE_REMOTE_LANDING=1</c>. TEMPORARY —
|
||||
/// strip once the discriminating live-test capture has landed.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public static bool ProbeRemoteLandingEnabled { get; set; } =
|
||||
Environment.GetEnvironmentVariable("ACDREAM_PROBE_REMOTE_LANDING") == "1";
|
||||
|
||||
/// <summary>
|
||||
/// Emit one <c>[remote-landing]</c> line for a remote landing-detection
|
||||
/// edge. Caller MUST guard with
|
||||
/// <c>if (!ProbeRemoteLandingEnabled) return;</c> before calling.
|
||||
/// <paramref name="resolveIsOnGround"/> is <see langword="null"/> at the
|
||||
/// controller site (no per-frame resolver call at that edge).
|
||||
/// </summary>
|
||||
public static void LogRemoteLanding(
|
||||
string site,
|
||||
uint guid,
|
||||
bool airborneBefore,
|
||||
bool gravitySet,
|
||||
bool contact,
|
||||
bool onWalkable,
|
||||
bool hasDefaultSink,
|
||||
bool? resolveIsOnGround,
|
||||
uint sequencerStyle,
|
||||
uint sequencerMotion)
|
||||
{
|
||||
var ci = System.Globalization.CultureInfo.InvariantCulture;
|
||||
string onGroundText = resolveIsOnGround.HasValue
|
||||
? resolveIsOnGround.Value.ToString()
|
||||
: "n/a";
|
||||
Console.WriteLine(string.Format(ci,
|
||||
"[remote-landing] site={0} guid=0x{1:X8} t={2} airborneBefore={3} " +
|
||||
"gravitySet={4} contact={5} onWalkable={6} hasDefaultSink={7} " +
|
||||
"resolveIsOnGround={8} seqStyle=0x{9:X8} seqMotion=0x{10:X8}",
|
||||
site, guid, Environment.TickCount64, airborneBefore,
|
||||
gravitySet, contact, onWalkable, hasDefaultSink, onGroundText,
|
||||
sequencerStyle, sequencerMotion));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit one <c>[remote-landing-gate]</c> line when a landing edge is
|
||||
/// reached but the Gravity state bit is already clear, so the imminent
|
||||
/// <c>MotionInterpreter.HitGround</c> call will silently no-op (the
|
||||
/// gate at <c>MotionInterpreter.cs</c>:~2435) — hypothesis 1 for Bug A.
|
||||
/// Caller MUST guard with
|
||||
/// <c>if (!ProbeRemoteLandingEnabled) return;</c> before calling.
|
||||
/// </summary>
|
||||
public static void LogRemoteLandingGateNoOp(string site, uint guid)
|
||||
{
|
||||
Console.WriteLine(System.FormattableString.Invariant(
|
||||
$"[remote-landing-gate] site={site} guid=0x{guid:X8} t={Environment.TickCount64} NOOP gravityAlreadyClear=true"));
|
||||
}
|
||||
|
||||
public static void LogCellSetBuild(
|
||||
uint seedCellId,
|
||||
System.Numerics.Vector3 sphereCenter,
|
||||
|
|
@ -651,6 +737,7 @@ public static class PhysicsDiagnostics
|
|||
ProbeSweptEnabled = false;
|
||||
ProbeStepWalkEnabled = false;
|
||||
ProbeTeleportEnabled = false;
|
||||
ProbeRemoteLandingEnabled = false;
|
||||
|
||||
// Side-channel fields
|
||||
LastBspHitPoly = null;
|
||||
|
|
|
|||
|
|
@ -528,6 +528,34 @@ internal sealed class RuntimeRemotePhysicsUpdater
|
|||
// ~1 Hz re-emit.
|
||||
ulong landingStateAuthorityVersion =
|
||||
record.StateAuthorityVersion;
|
||||
|
||||
// Bug A investigation (2026-08-04, docs/ISSUES.md #32):
|
||||
// capture the exact state HitGround is about to act on —
|
||||
// see PhysicsDiagnostics.LogRemoteLanding for the field
|
||||
// list and PhysicsDiagnostics.ProbeRemoteLandingEnabled
|
||||
// for the discriminator table. TEMPORARY — strip once
|
||||
// the live-test run has landed.
|
||||
if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeRemoteLandingEnabled)
|
||||
{
|
||||
bool gravitySetForProbe = rm.Body.HasGravity;
|
||||
AcDream.Core.Physics.PhysicsDiagnostics.LogRemoteLanding(
|
||||
site: "per-tick",
|
||||
guid: serverGuid,
|
||||
airborneBefore: true,
|
||||
gravitySet: gravitySetForProbe,
|
||||
contact: rm.Body.InContact,
|
||||
onWalkable: rm.Body.OnWalkable,
|
||||
hasDefaultSink: rm.Motion.DefaultSink is not null,
|
||||
resolveIsOnGround: resolveResult.IsOnGround,
|
||||
sequencerStyle: sequencer?.CurrentStyle ?? 0,
|
||||
sequencerMotion: sequencer?.CurrentMotion ?? 0);
|
||||
if (!gravitySetForProbe)
|
||||
{
|
||||
AcDream.Core.Physics.PhysicsDiagnostics.LogRemoteLandingGateNoOp(
|
||||
"per-tick", serverGuid);
|
||||
}
|
||||
}
|
||||
|
||||
rm.Movement.HitGround();
|
||||
if (!IsCurrentOwner(
|
||||
record,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue