diag(physics): probe whether Runtime's world frame and App's origin ever disagree
#283 step 1: prove or disprove reachability before restructuring ownership.
Runtime rebases its world frame the instant an accepted Position carries
TeleportAdvanced (RuntimePhysicsState.ObserveLocalWorldFrame). App's
LiveWorldOriginState rebases only once StreamingOriginRecenterCoordinator
.Advance observes IsOriginRecenterRetirementComplete - many frames later,
after the old window has fully retired. Between those two edges the owners can
disagree by the source-to-destination landblock delta, and anything converted
in the gap lands a multiple of 192 m from the geometry App is building. Same
failure family as the zero-offset bug 670f307c fixed, with a wrong origin
rather than a missing one.
Reasoning has already closed most of the window: the recenter detaches EVERY
resident landblock before adopting the new origin, so old-origin collision is
retired first. What remains is the narrow gap between Runtime's flip and App's
BeginOriginRecenter, while old-origin geometry is still resident. Whether that
is ever actually hit is an empirical question, and the campaign rule is that a
restructure needs evidence, not a hypothesis.
ACDREAM_PROBE_WORLD_FRAME=1 emits one [world-frame] line per DISAGREEMENT at
DatLiveEntityProjectionMaterializer's landblock->world conversion - the exact
App-side counterpart of Runtime's TryGetWorldFrameOffset, and the site that
already holds both owners, so no new dependency is introduced. Silence across
a portal run is the evidence that #283 is unreachable and can close as a
permanent invariant instead of an ownership move.
Measurement only: the probe never gates placement, and the flag lives in
PhysicsDiagnostics with the rest of the ACDREAM_PROBE_* family per the
diagnostic-owner rule rather than as a scattered env read.
Complete Release solution: 10,836 passed / 4 skipped / 0 failed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
0c14c4029c
commit
898ff18b26
3 changed files with 67 additions and 0 deletions
|
|
@ -163,6 +163,8 @@ internal sealed class DatLiveEntityProjectionMaterializer
|
|||
CreateObject.ServerPosition position = canonicalSpawn.Position.Value;
|
||||
int lbX = (int)((position.LandblockId >> 24) & 0xFFu);
|
||||
int lbY = (int)((position.LandblockId >> 16) & 0xFFu);
|
||||
if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeWorldFrameEnabled)
|
||||
ProbeWorldFrameAgreement(position.LandblockId);
|
||||
var worldOrigin = new Vector3(
|
||||
(lbX - _origin.CenterX) * 192f,
|
||||
(lbY - _origin.CenterY) * 192f,
|
||||
|
|
@ -1185,4 +1187,38 @@ internal sealed class DatLiveEntityProjectionMaterializer
|
|||
}
|
||||
return nullSlots >= 8;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// #283 reachability probe. This method is the App-side counterpart of
|
||||
/// Runtime's <c>TryGetWorldFrameOffset</c>: both convert a
|
||||
/// landblock-local network origin into the streamed world frame, and both
|
||||
/// must use the SAME centre landblock or the entity lands a multiple of
|
||||
/// 192 m from the geometry around it.
|
||||
///
|
||||
/// Runtime rebases on the accepted teleport Position; App's
|
||||
/// <c>LiveWorldOriginState</c> rebases only after old-window retirement
|
||||
/// completes. Emits one line per DISAGREEMENT — silence across a portal
|
||||
/// run is the evidence that the window is unreachable in practice, and
|
||||
/// that #283 can be closed as a permanent invariant rather than an
|
||||
/// ownership restructure. Measurement only; it never gates placement.
|
||||
/// </summary>
|
||||
private void ProbeWorldFrameAgreement(uint landblockId)
|
||||
{
|
||||
uint runtimeCenter = _runtime.Physics.WorldFrameCenterLandblockId;
|
||||
if (runtimeCenter == 0u || !_origin.IsKnown)
|
||||
return;
|
||||
|
||||
int runtimeCenterX = (int)((runtimeCenter >> 24) & 0xFFu);
|
||||
int runtimeCenterY = (int)((runtimeCenter >> 16) & 0xFFu);
|
||||
if (runtimeCenterX == _origin.CenterX
|
||||
&& runtimeCenterY == _origin.CenterY)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float deltaX = (runtimeCenterX - _origin.CenterX) * 192f;
|
||||
float deltaY = (runtimeCenterY - _origin.CenterY) * 192f;
|
||||
Console.WriteLine(System.FormattableString.Invariant(
|
||||
$"[world-frame] DISAGREE runtime=({runtimeCenterX},{runtimeCenterY}) app=({_origin.CenterX},{_origin.CenterY}) offsetDelta=({deltaX:F0}m,{deltaY:F0}m) projecting=0x{landblockId:X8}"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,6 +61,27 @@ public static class PhysicsDiagnostics
|
|||
public static bool ProbeCellEnabled { get; set; } =
|
||||
Environment.GetEnvironmentVariable("ACDREAM_PROBE_CELL") == "1";
|
||||
|
||||
/// <summary>
|
||||
/// #283 reachability probe (2026-08-03). Runtime rebases its world frame
|
||||
/// the instant an accepted Position carries TeleportAdvanced, while App's
|
||||
/// <c>LiveWorldOriginState</c> rebases only once
|
||||
/// <c>StreamingOriginRecenterCoordinator</c> observes old-window
|
||||
/// retirement completion — many frames later. Between those edges the two
|
||||
/// owners can disagree by the source-to-destination landblock delta, and
|
||||
/// anything converted in that gap lands a multiple of 192 m from the
|
||||
/// geometry App is building.
|
||||
///
|
||||
/// <para>When true, every projected placement compares Runtime's frame
|
||||
/// centre against App's origin centre and emits one
|
||||
/// <c>[world-frame]</c> line per DISAGREEMENT only — silence means the
|
||||
/// window is never observed. This exists to prove or disprove
|
||||
/// reachability before any ownership is restructured; it is a
|
||||
/// measurement, never a gate. Initial state from
|
||||
/// <c>ACDREAM_PROBE_WORLD_FRAME=1</c>.</para>
|
||||
/// </summary>
|
||||
public static bool ProbeWorldFrameEnabled { get; set; } =
|
||||
Environment.GetEnvironmentVariable("ACDREAM_PROBE_WORLD_FRAME") == "1";
|
||||
|
||||
/// <summary>
|
||||
/// Stuck-cast/missing-attack investigation (2026-07-30). When true,
|
||||
/// every REMOTE ground-contact edge (HitGround / LeaveGround — each of
|
||||
|
|
|
|||
|
|
@ -546,6 +546,16 @@ public sealed class RuntimePhysicsState : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// #283: the landblock Runtime currently treats as world-frame (0,0), or
|
||||
/// 0 before the local-player Create publishes one. Read-only and
|
||||
/// diagnostic — App compares it against its own
|
||||
/// <c>LiveWorldOriginState</c> centre to detect the teleport-window
|
||||
/// disagreement described on
|
||||
/// <c>PhysicsDiagnostics.ProbeWorldFrameEnabled</c>.
|
||||
/// </summary>
|
||||
internal uint WorldFrameCenterLandblockId => _worldFrameCenterLandblockId;
|
||||
|
||||
/// <summary>
|
||||
/// #284: records that this session's local-player Create was accepted,
|
||||
/// independently of whether it carried a usable landblock. The frame is
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue