fix: complete retail parity stability pass
This commit is contained in:
parent
d3df4cb20a
commit
f7aa8e0eb7
131 changed files with 7765 additions and 1190 deletions
|
|
@ -275,18 +275,24 @@ public sealed class RuntimeAuthoritativePositionRouteClassifierTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void ForcePosition_IsRejectedForRemoteOrUnequalTeleport()
|
||||
public void ForcePosition_AcceptsLocalNonRegressedTeleportAndRejectsRemoteOrRegressed()
|
||||
{
|
||||
RuntimeAuthoritativePositionAuthority force = Authority(
|
||||
10, 10, PositionTimestampDisposition.ForcePosition);
|
||||
RuntimeAuthoritativePositionRoute remote = ClassifyRemote(authority: force);
|
||||
RuntimeAuthoritativePositionRoute unequal = ClassifyLocal(
|
||||
RuntimeAuthoritativePositionRoute newer = ClassifyLocal(
|
||||
Authority(10, 11, PositionTimestampDisposition.ForcePosition));
|
||||
RuntimeAuthoritativePositionRoute regressed = ClassifyLocal(
|
||||
Authority(11, 10, PositionTimestampDisposition.ForcePosition));
|
||||
|
||||
Assert.Equal(RuntimeAuthoritativePositionDisposition.RejectedAuthority,
|
||||
remote.Disposition);
|
||||
Assert.Equal(RuntimeAuthoritativePositionDisposition.SetPositionSimple,
|
||||
newer.Disposition);
|
||||
Assert.False(newer.ZeroVelocity);
|
||||
Assert.Equal(RuntimeTeleportHookPhase.None, newer.TeleportHookPhase);
|
||||
Assert.Equal(RuntimeAuthoritativePositionDisposition.RejectedAuthority,
|
||||
unequal.Disposition);
|
||||
regressed.Disposition);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -330,23 +330,18 @@ public sealed partial class RuntimeCollisionPrefixQuiescenceTests
|
|||
/// root holds an operation. (Additionally, a parked record has
|
||||
/// <c>FullCellId == 0</c>, so it is not an affected resident at all.)
|
||||
///
|
||||
/// <para><b>The real hazard is not a throw, it is an unbounded streaming
|
||||
/// stall — and this test PINS it as a stall, not as a pass.</b> A retained
|
||||
/// preparation retry keeps its landblock prefix in placement debt, so the
|
||||
/// retirement is refused on EVERY poll and the landblock never retires.
|
||||
/// There is no bound: the retirement coordinator simply retries, and
|
||||
/// <c>TickLostCellDeadlines</c> — the only expiry that could break the
|
||||
/// cycle — has NO production caller, so its deadline never fires. The only
|
||||
/// thing that clears it is an inbound packet for that same entity, which
|
||||
/// is exactly what a <c>RetrySetupUnavailable</c> on an asset that never
|
||||
/// loads does not produce.</para>
|
||||
/// <para>#310: collision retirement now supersedes an authored mover that
|
||||
/// is still waiting for its first preparation. The exact unprepared
|
||||
/// operation is cancelled before ordinary placement-debt evaluation, so
|
||||
/// the resident enters the normal retirement park and the prefix can
|
||||
/// converge without an inbound packet or asset-readiness edge.</para>
|
||||
///
|
||||
/// <para>This is a pre-existing hazard independent of route 4b-1, filed as
|
||||
/// its own issue. 4b-1 does NOT bound it; it only avoids widening it, by
|
||||
/// declining to retain operations for destinations it cannot service.</para>
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void RetainedPreparationRetryStallsPrefixRetirementIndefinitely()
|
||||
public void RetainedPreparationRetryIsCancelledByPrefixRetirement()
|
||||
{
|
||||
using var fixture = new Fixture();
|
||||
RuntimeEntityRecord record = fixture.Add(
|
||||
|
|
@ -358,7 +353,7 @@ public sealed partial class RuntimeCollisionPrefixQuiescenceTests
|
|||
// A retained preparation retry: begun, never prepared — the shape a
|
||||
// RetrySetupUnavailable on an asset that never resolves leaves behind.
|
||||
RuntimeEntityPlacementToken retained = fixture.Lifetime.Physics
|
||||
.SetPosition.BeginAcceptedPlacement(
|
||||
.SetPosition.BeginAuthoredPlacement(
|
||||
record,
|
||||
record.PositionAuthorityVersion,
|
||||
RuntimeSetPositionOperationKind.RemoteAuthoritative);
|
||||
|
|
@ -366,29 +361,14 @@ public sealed partial class RuntimeCollisionPrefixQuiescenceTests
|
|||
|
||||
RuntimeCollisionPrefixQuiescenceToken token = fixture.Begin(2UL);
|
||||
|
||||
// The production retirement path, polled hard. Permission is refused
|
||||
// every single time; nothing in the system advances it.
|
||||
for (int poll = 0; poll < 1_000; poll++)
|
||||
{
|
||||
Assert.False(
|
||||
fixture.TryAcquire(token, out _),
|
||||
$"retirement unexpectedly acquired permission on poll {poll}; "
|
||||
+ "if this now succeeds the stall has been bounded and "
|
||||
+ "this test's pinned decision must be revisited");
|
||||
}
|
||||
// The first production poll cancels the unprepared operation and
|
||||
// enters the ordinary two-phase resident-withdrawal handshake.
|
||||
Assert.False(fixture.TryAcquire(token, out _));
|
||||
Assert.False(fixture.Lifetime.Physics.SetPosition
|
||||
.IsPlacementCurrent(retained));
|
||||
Assert.Equal(1, fixture.Lifetime.Physics.CaptureOwnership()
|
||||
.SetPositionOperationCount); // the replacement retirement park
|
||||
|
||||
// ParkCollisionResidents was never entered, so its overlap throw could
|
||||
// not fire — the contract's item 6, proven structurally.
|
||||
Assert.True(fixture.Lifetime.Physics.IsSpatialRoot(record));
|
||||
Assert.False(fixture.Lifetime.Physics.SetPosition.TryPeekProjection(
|
||||
out _));
|
||||
|
||||
// And the discriminator: retiring the retained operation is what
|
||||
// releases the prefix. Once the debt is gone the ordinary two-phase
|
||||
// handshake proceeds — ParkCollisionResidents withdraws the residents
|
||||
// and permission follows the withdrawal acknowledgements — so drain
|
||||
// those exactly as the production host does.
|
||||
_ = fixture.Lifetime.Physics.SetPosition.ForgetExactPlacement(retained);
|
||||
bool acquired = false;
|
||||
for (int poll = 0; poll < 32 && !acquired; poll++)
|
||||
{
|
||||
|
|
@ -404,8 +384,8 @@ public sealed partial class RuntimeCollisionPrefixQuiescenceTests
|
|||
}
|
||||
Assert.True(
|
||||
acquired,
|
||||
"clearing the retained preparation retry must let the prefix "
|
||||
+ "retire; if it does not, the stall has a second cause");
|
||||
"prefix retirement must converge after it supersedes an "
|
||||
+ "unprepared mover operation");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -116,6 +116,73 @@ public sealed class RuntimeSetPositionStateTests
|
|||
Assert.Equal(1, ownership.PreparedMoverCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WarmedPendingProjectionRetryDoesNotAllocateSnapshotArray()
|
||||
{
|
||||
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
|
||||
using var lifetime = new RuntimeEntityObjectLifetime(engine);
|
||||
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001042u, 1);
|
||||
_ = AttachBody(lifetime, record, SourceCell);
|
||||
var observer = new CountingPlacementObserver();
|
||||
using IDisposable subscription = lifetime.Events.SubscribePlacement(observer);
|
||||
RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition.Apply(
|
||||
record,
|
||||
record.PositionAuthorityVersion,
|
||||
Command(Request(SourceCell, new Vector3(13f, 18f, 7f))));
|
||||
|
||||
// Prime both retained retry scratch and the event stream's dispatch
|
||||
// storage before measuring the normal N>0 per-tick path.
|
||||
lifetime.Physics.SetPosition.RetryPendingProjections();
|
||||
const int iterations = 256;
|
||||
_ = GC.GetAllocatedBytesForCurrentThread();
|
||||
long before = GC.GetAllocatedBytesForCurrentThread();
|
||||
for (int iteration = 0; iteration < iterations; iteration++)
|
||||
lifetime.Physics.SetPosition.RetryPendingProjections();
|
||||
long allocated = GC.GetAllocatedBytesForCurrentThread() - before;
|
||||
|
||||
// The event stream itself currently costs 72 B/publication. The old
|
||||
// Values.ToArray snapshot raised this exact fixture to 424 B/retry;
|
||||
// keep enough runtime variance for the dispatch floor while making a
|
||||
// fresh projection array (or equivalent regression) fail loudly.
|
||||
Assert.InRange(allocated / iterations, 0L, 128L);
|
||||
Assert.Equal(iterations + 2, observer.Count);
|
||||
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
|
||||
outcome.Projection));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PendingProjectionRetryKeepsIndependentSnapshotWhenReentered()
|
||||
{
|
||||
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
|
||||
using var lifetime = new RuntimeEntityObjectLifetime(engine);
|
||||
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001044u, 1);
|
||||
_ = AttachBody(lifetime, record, SourceCell);
|
||||
bool retrying = false;
|
||||
bool nested = false;
|
||||
var observer = new PlacementObserver(_ =>
|
||||
{
|
||||
if (!retrying || nested)
|
||||
return;
|
||||
nested = true;
|
||||
lifetime.Physics.SetPosition.RetryPendingProjections();
|
||||
});
|
||||
using IDisposable subscription = lifetime.Events.SubscribePlacement(observer);
|
||||
RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition.Apply(
|
||||
record,
|
||||
record.PositionAuthorityVersion,
|
||||
Command(Request(SourceCell, new Vector3(13f, 19f, 7f))));
|
||||
|
||||
retrying = true;
|
||||
lifetime.Physics.SetPosition.RetryPendingProjections();
|
||||
|
||||
Assert.True(nested);
|
||||
Assert.Equal(3, observer.Deltas.Count);
|
||||
Assert.All(observer.Deltas,
|
||||
delta => Assert.Equal(outcome.Projection, delta.Placement.Token));
|
||||
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
|
||||
outcome.Projection));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(PhysicsStateFlags.Hidden)]
|
||||
[InlineData(PhysicsStateFlags.Hidden | PhysicsStateFlags.NoDraw)]
|
||||
|
|
@ -3648,6 +3715,13 @@ public sealed class RuntimeSetPositionStateTests
|
|||
}
|
||||
}
|
||||
|
||||
private sealed class CountingPlacementObserver : IRuntimePlacementObserver
|
||||
{
|
||||
internal int Count { get; private set; }
|
||||
|
||||
public void OnPlacement(in RuntimePlacementDelta delta) => Count++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// C0-3 test double: a minimal <see cref="IPreparedCollisionSource"/>
|
||||
/// serving exactly one Setup id (matching <c>CanonicalSetupTableId</c>'s
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue