acdream/tests/AcDream.Runtime.Tests/Physics/RuntimeSetPositionStateTests.cs

2449 lines
103 KiB
C#

using System.Collections.Immutable;
using System.Numerics;
using AcDream.Core.Net;
using AcDream.Core.Net.Messages;
using AcDream.Core.Physics;
using AcDream.Core.Physics.Motion;
using AcDream.Core.World.Cells;
using AcDream.Runtime.Entities;
using AcDream.Runtime.Physics;
using DatReaderWriter.Enums;
using DatReaderWriter.Types;
namespace AcDream.Runtime.Tests.Physics;
public sealed class RuntimeSetPositionStateTests
{
private const uint SourceLandblock = 0xA9B40000u;
private const uint SourceCell = SourceLandblock | 0x0001u;
private const uint DestinationLandblock = 0xAAB40000u;
private const uint DestinationCell = DestinationLandblock | 0x0001u;
private const uint DestinationIndoorCell = DestinationLandblock | 0x0100u;
[Fact]
public void AcceptedTokenExistsBeforeHostPreparationAndRejectsInvalidPortalAuthority()
{
using var lifetime = new RuntimeEntityObjectLifetime();
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001001u, 1);
RuntimeEntityPlacementToken token = lifetime.Physics.SetPosition
.BeginAcceptedPlacement(
record,
record.PositionAuthorityVersion,
RuntimeSetPositionOperationKind.RemoteAuthoritative);
Assert.True(token.IsValid);
Assert.Equal(record.Key, token.Entity);
Assert.Equal(1, lifetime.Physics.CaptureOwnership()
.AwaitingSetPositionPreparationCount);
var invalidPortal = new RuntimePortalPlacementAuthority(
Present: true,
RevealGeneration: 7,
TeleportSequence: 0,
new RuntimeWorldHostProjectionToken(7, DestinationCell));
Assert.False(lifetime.Physics.SetPosition.BeginAcceptedPlacement(
record,
record.PositionAuthorityVersion,
RuntimeSetPositionOperationKind.RemoteAuthoritative,
invalidPortal).IsValid);
// 4B1 validates the immutable authority shape. The authoritative
// RuntimeWorldTransitState membership check is intentionally the
// tested 4B2 routing seam.
RuntimeEntityPlacementToken portalToken = lifetime.Physics.SetPosition
.BeginAcceptedPlacement(
record,
record.PositionAuthorityVersion,
RuntimeSetPositionOperationKind.LocalAuthoritative,
invalidPortal);
Assert.True(portalToken.IsValid);
}
[Fact]
public void ImmediateCommitIsCanonicalBeforeProjectionAndExactTokenRetriesUntilAck()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001002u, 1);
PhysicsBody body = AttachBody(lifetime, record, SourceCell);
var observer = new PlacementObserver(delta =>
{
Assert.Equal(RuntimePlacementProjectionKind.Place,
delta.Placement.Kind);
Assert.Same(body, record.PhysicsBody);
Assert.Equal(SourceCell, record.FullCellId);
Assert.Equal(new Vector3(12f, 18f, 7f), body.Position);
Assert.True(body.InWorld);
Assert.True(lifetime.Physics.IsSpatialRoot(record));
});
using IDisposable subscription = lifetime.Events.SubscribePlacement(observer);
RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition.Apply(
record,
record.PositionAuthorityVersion,
Command(Request(SourceCell, new Vector3(12f, 18f, 7f))));
Assert.Equal(
RuntimeSetPositionStatus.CommittedHostAcknowledgementPending,
outcome.Status);
Assert.Single(observer.Deltas);
RuntimePlacementProjectionToken token = outcome.Projection;
lifetime.Physics.SetPosition.RetryPendingProjections();
Assert.Equal(2, observer.Deltas.Count);
Assert.Equal(token, observer.Deltas[0].Placement.Token);
Assert.Equal(token, observer.Deltas[1].Placement.Token);
Assert.True(observer.Deltas[1].Stamp.Sequence
> observer.Deltas[0].Stamp.Sequence);
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(token));
RuntimeSetPositionOwnershipSnapshot ownership =
lifetime.Physics.SetPosition.CaptureOwnership();
Assert.Equal(0, ownership.ActiveOperationCount);
Assert.Equal(0, ownership.PendingProjectionAcknowledgementCount);
Assert.Equal(1, ownership.PreparedMoverCount);
}
[Fact]
public void InWorldSetPositionPreservesObjectClockEpochPendingTimeAndActiveState()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001110u, 1);
PhysicsBody body = AttachBody(lifetime, record, SourceCell);
body.LastUpdateTime = 3d;
_ = record.ObjectClock.Advance(0.01d);
ulong epoch = record.ObjectClockEpoch;
RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition.Apply(
record,
record.PositionAuthorityVersion,
Command(Request(SourceCell, new Vector3(11f, 18f, 7f))));
Assert.Equal(
RuntimeSetPositionStatus.CommittedHostAcknowledgementPending,
outcome.Status);
Assert.Equal(epoch, record.ObjectClockEpoch);
Assert.Equal(3d, body.LastUpdateTime);
Assert.Equal(0.01d, record.ObjectClock.PendingSeconds, precision: 12);
Assert.True(record.ObjectClock.IsActive);
Assert.True(body.TransientState.HasFlag(TransientStateFlags.Active));
}
[Fact]
public void InWorldSetPositionDoesNotWakeAnInactiveBody()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001111u, 1);
PhysicsBody body = AttachBody(lifetime, record, SourceCell);
body.LastUpdateTime = 4d;
record.ObjectClock.Deactivate();
body.TransientState &= ~TransientStateFlags.Active;
ulong epoch = record.ObjectClockEpoch;
RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition.Apply(
record,
record.PositionAuthorityVersion,
Command(Request(SourceCell, new Vector3(11f, 19f, 7f))));
Assert.Equal(
RuntimeSetPositionStatus.CommittedHostAcknowledgementPending,
outcome.Status);
Assert.Equal(epoch, record.ObjectClockEpoch);
Assert.Equal(4d, body.LastUpdateTime);
Assert.False(record.ObjectClock.IsActive);
Assert.False(body.TransientState.HasFlag(TransientStateFlags.Active));
}
[Fact]
public void WarmedImmediateCommitAllocationIsMeasuredBeforeProductionCutover()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x7000111Cu, 1);
_ = AttachBody(lifetime, record, SourceCell);
RuntimeSetPositionCommand command = Command(Request(
SourceCell,
new Vector3(11f, 18f, 7f)));
for (int warm = 0; warm < 64; warm++)
{
RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition.Apply(
record,
record.PositionAuthorityVersion,
command);
if (!lifetime.Physics.SetPosition.AcknowledgeProjection(
outcome.Projection))
{
throw new InvalidOperationException("Warmup acknowledgement failed.");
}
}
const int iterations = 1_000;
long before = GC.GetAllocatedBytesForCurrentThread();
for (int iteration = 0; iteration < iterations; iteration++)
{
RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition.Apply(
record,
record.PositionAuthorityVersion,
command);
if (!lifetime.Physics.SetPosition.AcknowledgeProjection(
outcome.Projection))
{
throw new InvalidOperationException("Measured acknowledgement failed.");
}
}
long allocated = GC.GetAllocatedBytesForCurrentThread() - before;
// The dormant 4B1 owner still allocates its operation/projection
// envelope. Pin the measured Release ceiling so 4B2 cannot activate
// the route without making this cost explicit or reducing it.
Assert.InRange(allocated / iterations, 1L, 2_048L);
}
[Fact]
public void MissingCrossLandblockCellParksExactBodyThenMatchingCellGenerationWakesIt()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
var gameClock = new GameRuntimeClock();
_ = gameClock.Advance(35d);
var time = new ManualTimeProvider(
DateTimeOffset.UnixEpoch.AddSeconds(100d));
using var lifetime = new RuntimeEntityObjectLifetime(
engine,
timeProvider: time,
gameClock: gameClock);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001003u, 1);
PhysicsBody body = AttachBody(lifetime, record, SourceCell);
var remote = new RemoteMotion(body)
{
HasServerVelocity = true,
ServerVelocity = Vector3.UnitX,
};
lifetime.Physics.SetRemoteMotion(record, remote);
body.TransientState = TransientStateFlags.Active
| TransientStateFlags.Contact
| TransientStateFlags.OnWalkable
| TransientStateFlags.WaterContact
| TransientStateFlags.Sliding;
body.ContactPlaneValid = true;
body.ContactPlaneIsWater = true;
body.SlidingNormal = Vector3.UnitX;
body.set_velocity(new Vector3(3f, 4f, 5f));
engine.ShadowObjects.Register(
record.Key!.Value.LocalEntityId,
0x01000001u,
body.Position,
body.Orientation,
0.48f,
0f,
0f,
SourceLandblock,
seedCellId: SourceCell,
isStatic: false);
var observer = new PlacementObserver();
using IDisposable subscription = lifetime.Events.SubscribePlacement(observer);
RuntimeSetPositionOutcome deferred = lifetime.Physics.SetPosition.Apply(
record,
record.PositionAuthorityVersion,
Command(CrossLandblockRequest()));
Assert.Equal(RuntimeSetPositionStatus.DeferredCell, deferred.Status);
Assert.Equal(DestinationCell, deferred.ExactCellId);
Assert.Same(body, record.PhysicsBody);
Assert.Equal(DestinationCell, body.CellPosition.ObjCellId);
Assert.Equal(0u, record.FullCellId);
Assert.False(body.InWorld);
Assert.Equal(1d, body.LastUpdateTime);
Assert.False(body.TransientState.HasFlag(TransientStateFlags.Active));
Assert.True(body.InContact);
Assert.True(body.OnWalkable);
Assert.True(body.ContactPlaneIsWater);
Assert.Equal(Vector3.UnitX, body.SlidingNormal);
Assert.Equal(new Vector3(3f, 4f, 5f), body.Velocity);
Assert.False(lifetime.Physics.IsSpatialRoot(record));
Assert.Equal(1, engine.ShadowObjects.SuspendedRegistrationCount);
Assert.Equal(RuntimePlacementProjectionKind.Withdraw,
Assert.Single(observer.Deltas).Placement.Kind);
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
deferred.Projection));
ulong suspendedEpoch = record.ObjectClockEpoch;
Assert.False(record.ObjectClock.IsActive);
lifetime.Physics.SetPosition.CommitCollisionGeneration(
DestinationLandblock,
generation: 2,
ready: true);
Assert.Single(observer.Deltas);
AddFlatLandblock(engine, DestinationLandblock, 192f);
_ = gameClock.Advance(5d);
lifetime.Physics.SetPosition.CommitCollisionGeneration(
DestinationLandblock,
generation: 1,
ready: true);
Assert.Equal(2, observer.Deltas.Count);
RuntimePlacementProjectionSnapshot placed = observer.Deltas[1].Placement;
Assert.Equal(RuntimePlacementProjectionKind.Place, placed.Kind);
Assert.Equal(DestinationCell, placed.Token.ExactCellId);
Assert.Same(body, record.PhysicsBody);
Assert.Equal(DestinationCell, record.FullCellId);
Assert.True(body.InWorld);
Assert.True(double.IsFinite(body.LastUpdateTime));
Assert.Equal(40d, body.LastUpdateTime);
Assert.Equal(suspendedEpoch + 1UL, record.ObjectClockEpoch);
Assert.Equal(0d, record.ObjectClock.PendingSeconds);
Assert.True(record.ObjectClock.IsActive);
Assert.True(body.TransientState.HasFlag(TransientStateFlags.Active));
Assert.True(lifetime.Physics.IsSpatialRoot(record));
Assert.Equal(100d, remote.LastServerPosTime);
var remoteUpdater = new RuntimeRemotePhysicsUpdater(
lifetime.Physics);
Assert.True(remoteUpdater.Tick(
record,
remote,
objectScale: 1f,
sequencer: null,
dt: 0.01f,
objectClockEpoch: record.ObjectClockEpoch,
new MotionDeltaFrame
{
Orientation = Quaternion.Identity,
},
radius: 0.48f,
height: 1.835f,
liveCenterX: 1,
liveCenterY: 1));
Assert.True(remote.HasServerVelocity);
Assert.Equal(Vector3.UnitX, remote.ServerVelocity);
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
placed.Token));
Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership()
.ActiveOperationCount);
}
[Fact]
public void FailedDeferredWakeRetainsLostOwnerDeadlineAndRetries()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001121u, 1);
PhysicsBody body = AttachBody(lifetime, record, SourceCell);
var observer = new PlacementObserver();
using IDisposable subscription = lifetime.Events.SubscribePlacement(observer);
RuntimeSetPositionOutcome deferred = lifetime.Physics.SetPosition.Apply(
record,
record.PositionAuthorityVersion,
Command(CrossLandblockRequest()));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
deferred.Projection));
AddFlatLandblock(engine, DestinationLandblock, 192f);
engine.TransitionCellCollisionTestHook =
static (_, _, _, _) => TransitionState.Collided;
lifetime.Physics.SetPosition.CommitCollisionGeneration(
DestinationLandblock,
generation: 1,
ready: true);
RuntimeSetPositionOwnershipSnapshot failed = lifetime.Physics
.SetPosition.CaptureOwnership();
Assert.Equal(1, failed.ActiveOperationCount);
Assert.Equal(1, failed.DeferredCellCount);
Assert.Equal(1, failed.LostDeadlineCount);
Assert.Equal(1, failed.DeferredBucketCount);
Assert.True(failed.IndexesConsistent);
Assert.Equal(0u, record.FullCellId);
Assert.False(body.InWorld);
Assert.Single(observer.Deltas);
engine.TransitionCellCollisionTestHook = null;
lifetime.Physics.SetPosition.CommitCollisionGeneration(
DestinationLandblock,
generation: 1,
ready: true);
RuntimePlacementProjectionSnapshot placed = observer.Deltas[^1].Placement;
Assert.Equal(RuntimePlacementProjectionKind.Place, placed.Kind);
Assert.Equal(record.Key, placed.Token.Entity);
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
placed.Token));
Assert.Equal(DestinationCell, record.FullCellId);
Assert.True(body.InWorld);
}
[Fact]
public void StandaloneDeferredWakeRetainsAcceptedSimulationTimeFallback()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001126u, 1);
PhysicsBody body = AttachBody(lifetime, record, SourceCell);
RuntimeSetPositionOutcome deferred = lifetime.Physics.SetPosition.Apply(
record,
record.PositionAuthorityVersion,
Command(CrossLandblockRequest()));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
deferred.Projection));
AddFlatLandblock(engine, DestinationLandblock, 192f);
lifetime.Physics.SetPosition.CommitCollisionGeneration(
DestinationLandblock,
generation: 1,
ready: true);
Assert.Equal(10d, body.LastUpdateTime);
Assert.True(lifetime.Physics.SetPosition.TryPeekProjection(
out RuntimePlacementProjectionSnapshot placed));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
placed.Token));
}
[Fact]
public void RejectedPreparationKeepsValidatedMoverAndExactTokenRetryable()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001122u, 1);
_ = AttachBody(lifetime, record, SourceCell);
ImmutableArray<FlatCollisionSphere> validated =
[
new FlatCollisionSphere(new Vector3(0.25f, 0f, 0f), 0.3f),
new FlatCollisionSphere(new Vector3(-0.2f, 0f, 0.4f), 0.2f),
];
RuntimeSetPositionOutcome seeded = lifetime.Physics.SetPosition.Apply(
record,
record.PositionAuthorityVersion,
Command(Request(
SourceCell,
new Vector3(12f, 18f, 7f)) with
{
Spheres = validated,
}));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
seeded.Projection));
Assert.True(lifetime.Physics.SetPosition.TryGetPreparedMoverSphereCount(
record,
out int sphereCount));
Assert.Equal(2, sphereCount);
RuntimeEntityPlacementToken token = lifetime.Physics.SetPosition
.BeginAcceptedPlacement(
record,
record.PositionAuthorityVersion,
RuntimeSetPositionOperationKind.RemoteAuthoritative);
RuntimeSetPositionCommand replacement = Command(Request(
SourceCell,
new Vector3(13f, 18f, 7f)) with
{
Spheres =
[
new FlatCollisionSphere(Vector3.Zero, 0.4f),
],
});
engine.TransitionCellCollisionTestHook =
static (_, _, _, _) => TransitionState.Collided;
RuntimeSetPositionOutcome rejected = lifetime.Physics.SetPosition
.SubmitPreparedPlacement(token, replacement);
Assert.Equal(RuntimeSetPositionStatus.Rejected, rejected.Status);
Assert.True(lifetime.Physics.SetPosition.TryGetPreparedMoverSphereCount(
record,
out sphereCount));
Assert.Equal(2, sphereCount);
RuntimeSetPositionOutcome malformed = lifetime.Physics.SetPosition
.SubmitPreparedPlacement(
token,
replacement with { GameTime = double.NaN });
Assert.Equal(PhysicsSetPositionError.InvalidArguments, malformed.Error);
Assert.True(lifetime.Physics.SetPosition.TryGetAwaitingPreparationToken(
record,
out RuntimeEntityPlacementToken retryToken));
Assert.Equal(token, retryToken);
engine.TransitionCellCollisionTestHook = null;
RuntimeSetPositionOutcome retried = lifetime.Physics.SetPosition
.SubmitPreparedPlacement(retryToken, replacement);
Assert.Equal(
RuntimeSetPositionStatus.CommittedHostAcknowledgementPending,
retried.Status);
Assert.True(lifetime.Physics.SetPosition.TryGetPreparedMoverSphereCount(
record,
out sphereCount));
Assert.Equal(1, sphereCount);
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
retried.Projection));
}
[Theory]
[InlineData(0u)]
[InlineData(SourceLandblock | 0x0041u)]
public void InvalidRetailCellFrameRejectsBeforeCoreAndPreparedCache(
uint invalidCellId)
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001128u, 1);
_ = AttachBody(lifetime, record, SourceCell);
RuntimeEntityPlacementToken token = lifetime.Physics.SetPosition
.BeginAcceptedPlacement(
record,
record.PositionAuthorityVersion,
RuntimeSetPositionOperationKind.RemoteAuthoritative);
int collisionCalls = 0;
engine.TransitionCellCollisionTestHook = (_, _, _, _) =>
{
collisionCalls++;
return TransitionState.OK;
};
RuntimeSetPositionOutcome rejected = lifetime.Physics.SetPosition
.SubmitPreparedPlacement(
token,
Command(Request(
invalidCellId,
new Vector3(12f, 18f, 7f))));
Assert.Equal(RuntimeSetPositionStatus.Rejected, rejected.Status);
Assert.Equal(PhysicsSetPositionError.InvalidArguments, rejected.Error);
Assert.Equal(0, collisionCalls);
Assert.False(lifetime.Physics.SetPosition
.TryGetPreparedMoverSphereCount(record, out _));
Assert.True(lifetime.Physics.SetPosition.TryGetAwaitingPreparationToken(
record,
out RuntimeEntityPlacementToken retry));
Assert.Equal(token, retry);
}
[Fact]
public void InvalidRetailQuaternionRejectsBeforeCoreAndPreparedCache()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001129u, 1);
_ = AttachBody(lifetime, record, SourceCell);
RuntimeEntityPlacementToken token = lifetime.Physics.SetPosition
.BeginAcceptedPlacement(
record,
record.PositionAuthorityVersion,
RuntimeSetPositionOperationKind.RemoteAuthoritative);
int collisionCalls = 0;
engine.TransitionCellCollisionTestHook = (_, _, _, _) =>
{
collisionCalls++;
return TransitionState.OK;
};
Quaternion[] invalid =
[
Quaternion.Zero,
new Quaternion(0f, 0f, 0f, 0.9f),
];
foreach (Quaternion orientation in invalid)
{
RuntimeSetPositionOutcome rejected = lifetime.Physics.SetPosition
.SubmitPreparedPlacement(
token,
Command(Request(
SourceCell,
new Vector3(12f, 18f, 7f)) with
{
Orientation = orientation,
}));
Assert.Equal(RuntimeSetPositionStatus.Rejected, rejected.Status);
Assert.Equal(
PhysicsSetPositionError.InvalidArguments,
rejected.Error);
}
Assert.Equal(0, collisionCalls);
Assert.False(lifetime.Physics.SetPosition
.TryGetPreparedMoverSphereCount(record, out _));
Assert.True(lifetime.Physics.SetPosition.TryGetAwaitingPreparationToken(
record,
out RuntimeEntityPlacementToken retry));
Assert.Equal(token, retry);
}
[Fact]
public void ExtremeScatterAttemptsRejectWithoutEnteringSynchronousLoop()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x7000112Au, 1);
_ = AttachBody(lifetime, record, SourceCell);
RuntimeEntityPlacementToken token = lifetime.Physics.SetPosition
.BeginAcceptedPlacement(
record,
record.PositionAuthorityVersion,
RuntimeSetPositionOperationKind.RemoteAuthoritative);
int randomDraws = 0;
engine.SetPositionRandomUnit = () =>
{
randomDraws++;
return 0.5d;
};
RuntimeSetPositionOutcome rejected = lifetime.Physics.SetPosition
.SubmitPreparedPlacement(
token,
Command(Request(
SourceCell,
new Vector3(12f, 18f, 7f)) with
{
Flags = PhysicsSetPositionFlags.RandomScatter,
ScatterRadiusX = 1f,
ScatterRadiusY = 1f,
ScatterAttempts = uint.MaxValue,
}));
Assert.Equal(RuntimeSetPositionStatus.Rejected, rejected.Status);
Assert.Equal(PhysicsSetPositionError.InvalidArguments, rejected.Error);
Assert.Equal(0, randomDraws);
Assert.False(lifetime.Physics.SetPosition
.TryGetPreparedMoverSphereCount(record, out _));
Assert.True(lifetime.Physics.SetPosition.TryGetAwaitingPreparationToken(
record,
out RuntimeEntityPlacementToken retry));
Assert.Equal(token, retry);
}
[Fact]
public void BodylessWithdrawalCancellationDoesNotInventBodyOrProjection()
{
using var lifetime = new RuntimeEntityObjectLifetime();
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001123u, 1);
RuntimeEntityPlacementToken token = lifetime.Physics.SetPosition
.BeginAcceptedPlacement(
record,
record.PositionAuthorityVersion,
RuntimeSetPositionOperationKind.RemoteAuthoritative);
Assert.True(token.IsValid);
Assert.True(lifetime.Physics.SetPosition.Cancel(
record,
publishWithdrawal: true));
Assert.Null(record.PhysicsBody);
Assert.False(lifetime.Physics.SetPosition.TryPeekProjection(out _));
RuntimeSetPositionOwnershipSnapshot ownership = lifetime.Physics
.SetPosition.CaptureOwnership();
Assert.Equal(0, ownership.ActiveOperationCount);
Assert.Equal(0, ownership.PendingProjectionAcknowledgementCount);
Assert.True(ownership.IndexesConsistent);
}
[Fact]
public void ZeroExpectedVelocityPreservesBeginVersionAcrossInterveningVector()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001124u, 1);
PhysicsBody body = AttachBody(lifetime, record, SourceCell);
RuntimeEntityPlacementToken token = lifetime.Physics.SetPosition
.BeginAcceptedPlacement(
record,
record.PositionAuthorityVersion,
RuntimeSetPositionOperationKind.RemoteAuthoritative);
lifetime.Entities.AdvanceVectorAuthority(record);
body.set_velocity(new Vector3(-2f, 0f, 0f));
engine.TransitionCellCollisionTestHook =
(transition, phase, _, observed) =>
{
if (phase is TransitionCellCollisionPhase.Environment)
transition.CollisionInfo.SetCollisionNormal(Vector3.UnitX);
return observed;
};
RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition
.SubmitPreparedPlacement(
token,
Command(Request(
SourceCell,
new Vector3(14f, 18f, 7f))));
Assert.Equal(
RuntimeSetPositionStatus.CommittedHostAcknowledgementPending,
outcome.Status);
Assert.Equal(new Vector3(-2f, 0f, 0f), body.Velocity);
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
outcome.Projection));
}
[Fact]
public void CanonicalCommitReplacesDerivedBitsAndPublishesSlopeNormal()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001125u, 1);
PhysicsBody body = AttachBody(lifetime, record, SourceCell);
body.TransientState |= TransientStateFlags.Sliding
| TransientStateFlags.StationaryStop
| TransientStateFlags.StationaryStuck
| TransientStateFlags.WaterContact;
body.SlidingNormal = Vector3.UnitY;
Vector3 slope = Vector3.Normalize(new Vector3(0.2f, 0f, 1f));
engine.TransitionCellCollisionTestHook =
(transition, phase, _, observed) =>
{
if (phase is TransitionCellCollisionPhase.Environment)
{
transition.CollisionInfo.SetContactPlane(
new Plane(slope, 0f),
SourceCell);
transition.CollisionInfo.FramesStationaryFall = 1;
}
return observed;
};
RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition.Apply(
record,
record.PositionAuthorityVersion,
Command(Request(
SourceCell,
new Vector3(15f, 18f, 7f))));
Assert.Equal(
RuntimeSetPositionStatus.CommittedHostAcknowledgementPending,
outcome.Status);
Assert.Equal(slope, body.GroundNormal);
Assert.False(body.TransientState.HasFlag(TransientStateFlags.Sliding));
Assert.Equal(Vector3.Zero, body.SlidingNormal);
Assert.True(body.TransientState.HasFlag(
TransientStateFlags.StationaryFall));
Assert.False(body.TransientState.HasFlag(
TransientStateFlags.StationaryStop));
Assert.False(body.TransientState.HasFlag(
TransientStateFlags.StationaryStuck));
Assert.False(body.TransientState.HasFlag(
TransientStateFlags.WaterContact));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
outcome.Projection));
}
[Fact]
public void ReentrantNewPlacementConvertsPublishedTokenToOrderedDiscard()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001004u, 1);
_ = AttachBody(lifetime, record, SourceCell);
RuntimeEntityPlacementToken replacement = default;
var observer = new PlacementObserver(delta =>
{
if (delta.Placement.Kind is RuntimePlacementProjectionKind.Place)
{
replacement = lifetime.Physics.SetPosition
.BeginAcceptedPlacement(
record,
record.PositionAuthorityVersion,
RuntimeSetPositionOperationKind.RemoteAuthoritative);
}
});
using IDisposable subscription = lifetime.Events.SubscribePlacement(observer);
RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition.Apply(
record,
record.PositionAuthorityVersion,
Command(Request(SourceCell, new Vector3(13f, 18f, 7f))));
Assert.True(replacement.IsValid);
Assert.Equal(2, observer.Deltas.Count);
Assert.Equal(RuntimePlacementProjectionKind.Place,
observer.Deltas[0].Placement.Kind);
Assert.Equal(RuntimePlacementProjectionKind.Discard,
observer.Deltas[1].Placement.Kind);
Assert.Equal(outcome.Projection.TokenSequence(),
observer.Deltas[1].Placement.Token.TokenSequence());
Assert.False(lifetime.Physics.SetPosition.AcknowledgeProjection(
outcome.Projection));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
observer.Deltas[1].Placement.Token));
lifetime.Physics.SetPosition.Forget(
record,
releasePreparedMover: true);
Assert.True(lifetime.Physics.SetPosition.CaptureOwnership().IsConverged);
}
[Fact]
public void ReentrantGroundEdgePlacementCannotBeCancelledByDisplacedOperation()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
engine.TransitionCellCollisionTestHook =
(transition, phase, _, observed) =>
{
if (phase == TransitionCellCollisionPhase.Environment)
{
transition.CollisionInfo.SetContactPlane(
new Plane(Vector3.UnitZ, 0f),
SourceCell);
}
return observed;
};
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001042u, 1);
PhysicsBody body = AttachBody(lifetime, record, SourceCell);
engine.ShadowObjects.Register(
record.Key!.Value.LocalEntityId,
0x01000001u,
body.Position,
body.Orientation,
0.48f,
0f,
0f,
SourceLandblock,
seedCellId: SourceCell,
isStatic: false);
RuntimeEntityPlacementToken replacement = default;
var remote = new ReentrantRemotePlacement(body)
{
CellId = SourceCell,
OnHitGround = () =>
{
replacement = lifetime.Physics.SetPosition
.BeginAcceptedPlacement(
record,
record.PositionAuthorityVersion,
RuntimeSetPositionOperationKind.RemoteAuthoritative);
},
};
lifetime.Entities.SetRemoteMotion(record, remote);
RuntimeSetPositionOutcome displaced = lifetime.Physics.SetPosition.Apply(
record,
record.PositionAuthorityVersion,
Command(Request(SourceCell, new Vector3(13f, 18f, 7f))));
Assert.Equal(RuntimeSetPositionStatus.Cancelled, displaced.Status);
Assert.True(replacement.IsValid);
RuntimeSetPositionOwnershipSnapshot ownership =
lifetime.Physics.SetPosition.CaptureOwnership();
Assert.Equal(1, ownership.ActiveOperationCount);
Assert.Equal(1, ownership.AwaitingPreparationCount);
Assert.Equal(13f, body.Position.X);
Assert.Equal(18f, body.Position.Y);
Assert.Equal(SourceCell, record.FullCellId);
Assert.True(body.InContact);
Assert.True(body.OnWalkable);
Assert.True(lifetime.Physics.IsSpatialRoot(record));
ShadowEntry shadow = Assert.Single(
engine.ShadowObjects.AllEntriesForDebug());
Assert.Equal(body.Position, shadow.Position);
RuntimeSetPositionOutcome retained = lifetime.Physics.SetPosition
.SubmitPreparedPlacement(
replacement,
Command(Request(
SourceCell,
new Vector3(14f, 18f, 7f))));
Assert.Equal(
RuntimeSetPositionStatus.CommittedHostAcknowledgementPending,
retained.Status);
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
retained.Projection));
lifetime.Physics.SetPosition.Forget(
record,
releasePreparedMover: true);
Assert.True(lifetime.Physics.SetPosition.CaptureOwnership().IsConverged);
}
[Fact]
public void RetrySnapshotPreservesOrderWhenObserverAcknowledgesTwoPendingTokens()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord first = CreateRecord(lifetime, 0x70001040u, 1);
RuntimeEntityRecord second = CreateRecord(lifetime, 0x70001041u, 1);
_ = AttachBody(lifetime, first, SourceCell);
_ = AttachBody(lifetime, second, SourceCell);
bool acknowledgeRetries = false;
var retryOrder = new List<ulong>();
var observer = new PlacementObserver(delta =>
{
if (!acknowledgeRetries)
return;
retryOrder.Add(delta.Placement.Token.Sequence);
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
delta.Placement.Token));
});
using IDisposable subscription = lifetime.Events.SubscribePlacement(observer);
RuntimeSetPositionOutcome firstOutcome = lifetime.Physics.SetPosition.Apply(
first,
first.PositionAuthorityVersion,
Command(Request(SourceCell, new Vector3(14f, 18f, 7f))));
RuntimeSetPositionOutcome secondOutcome = lifetime.Physics.SetPosition.Apply(
second,
second.PositionAuthorityVersion,
Command(Request(SourceCell, new Vector3(16f, 18f, 7f))));
acknowledgeRetries = true;
lifetime.Physics.SetPosition.RetryPendingProjections();
Assert.Equal(
[firstOutcome.Projection.Sequence, secondOutcome.Projection.Sequence],
retryOrder);
Assert.Equal(0, lifetime.Physics.CaptureOwnership()
.PendingSetPositionHostAcknowledgementCount);
}
[Fact]
public void ThrowingObserverDoesNotRollBackCanonicalCommitAndRetryCanAck()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001043u, 1);
PhysicsBody body = AttachBody(lifetime, record, SourceCell);
using IDisposable throwing = lifetime.Events.SubscribePlacement(
new ThrowingPlacementObserver());
RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition.Apply(
record,
record.PositionAuthorityVersion,
Command(Request(SourceCell, new Vector3(17f, 18f, 7f))));
Assert.Equal(
RuntimeSetPositionStatus.CommittedHostAcknowledgementPending,
outcome.Status);
Assert.Equal(new Vector3(17f, 18f, 7f), body.Position);
Assert.Equal(1, lifetime.Events.DispatchFailureCount);
Assert.Equal(1, lifetime.Physics.CaptureOwnership()
.PendingSetPositionHostAcknowledgementCount);
throwing.Dispose();
var healthy = new PlacementObserver();
using IDisposable subscription = lifetime.Events.SubscribePlacement(healthy);
lifetime.Physics.SetPosition.RetryPendingProjections();
Assert.Equal(outcome.Projection,
Assert.Single(healthy.Deltas).Placement.Token);
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
outcome.Projection));
}
[Fact]
public void ForgetRevisesPendingTokenAndRetryPublishesDiscardBeforeAck()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001117u, 1);
_ = AttachBody(lifetime, record, SourceCell);
var observer = new PlacementObserver();
using IDisposable subscription = lifetime.Events.SubscribePlacement(observer);
RuntimeSetPositionOutcome pending = lifetime.Physics.SetPosition.Apply(
record,
record.PositionAuthorityVersion,
Command(Request(SourceCell, new Vector3(17f, 17f, 7f))));
RuntimePlacementCancellationReceipt cancellation =
lifetime.Physics.SetPosition.Forget(record);
Assert.True(cancellation.IsValid);
Assert.Single(observer.Deltas);
Assert.False(lifetime.Physics.SetPosition.AcknowledgeProjection(
pending.Projection));
lifetime.Physics.SetPosition.RetryPendingProjections();
RuntimePlacementProjectionSnapshot discard = observer.Deltas[^1].Placement;
Assert.Equal(RuntimePlacementProjectionKind.Discard, discard.Kind);
Assert.Equal(pending.Projection.Sequence, discard.Token.Sequence);
Assert.True(discard.Token.Revision > pending.Projection.Revision);
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
discard.Token));
}
[Fact]
public void ReplacementRetainsUnacknowledgedLostWithdrawalUntilPreparedCommandCanPlace()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x7000111Au, 1);
_ = AttachBody(lifetime, record, SourceCell);
using IDisposable throwing = lifetime.Events.SubscribePlacement(
new ThrowingPlacementObserver());
RuntimeSetPositionOutcome original = lifetime.Physics.SetPosition.Apply(
record,
record.PositionAuthorityVersion,
Command(CrossLandblockRequest()));
Assert.Equal(RuntimeSetPositionStatus.DeferredCell, original.Status);
RuntimeEntityPlacementToken replacement = lifetime.Physics.SetPosition
.BeginAcceptedPlacement(
record,
record.PositionAuthorityVersion,
RuntimeSetPositionOperationKind.RemoteAuthoritative);
Assert.True(replacement.IsValid);
RuntimeSetPositionOutcome waiting = lifetime.Physics.SetPosition
.SubmitPreparedPlacement(
replacement,
Command(Request(SourceCell, new Vector3(18f, 18f, 7f))));
Assert.Equal(RuntimeSetPositionStatus.DeferredCell, waiting.Status);
Assert.Equal(original.Projection, waiting.Projection);
Assert.Equal(0u, record.FullCellId);
throwing.Dispose();
var healthy = new PlacementObserver();
using IDisposable subscription = lifetime.Events.SubscribePlacement(healthy);
lifetime.Physics.SetPosition.RetryPendingProjections();
RuntimePlacementProjectionSnapshot withdrawal =
Assert.Single(healthy.Deltas).Placement;
Assert.Equal(RuntimePlacementProjectionKind.Withdraw, withdrawal.Kind);
Assert.Equal(original.Projection, withdrawal.Token);
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
withdrawal.Token));
Assert.Equal(2, healthy.Deltas.Count);
RuntimePlacementProjectionSnapshot placed = healthy.Deltas[1].Placement;
Assert.Equal(RuntimePlacementProjectionKind.Place, placed.Kind);
Assert.Equal(SourceCell, record.FullCellId);
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
placed.Token));
}
[Fact]
public void ReentrantNewerPositionDuringPickupDiscardSuppressesStalePickupDelta()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001118u, 1);
_ = AttachBody(lifetime, record, SourceCell);
RuntimeSetPositionOutcome pending = lifetime.Physics.SetPosition.Apply(
record,
record.PositionAuthorityVersion,
Command(Request(SourceCell, new Vector3(17f, 16f, 7f))));
var entities = new EntityObserver();
using IDisposable entitySubscription = lifetime.Events.Subscribe(entities);
bool reentered = false;
RuntimeEventStamp discardStamp = default;
var placements = new PlacementObserver(delta =>
{
if (reentered
|| delta.Placement.Kind
is not RuntimePlacementProjectionKind.Discard)
{
return;
}
reentered = true;
discardStamp = delta.Stamp;
Assert.True(lifetime.TryApplyPosition(
new WorldSession.EntityPositionUpdate(
record.ServerGuid,
new CreateObject.ServerPosition(
SourceCell, 12f, 20f, 7f, 1f, 0f, 0f, 0f),
Velocity: null,
PlacementId: null,
IsGrounded: true,
InstanceSequence: record.Incarnation,
PositionSequence: 3,
TeleportSequence: 0,
ForcePositionSequence: 0),
isLocalPlayer: false,
forcePositionRotation: null,
currentLocalVelocity: null,
projectionRequiresTeleportHook: false,
acknowledgeProjection: null,
out _,
out _,
out _));
});
using IDisposable placementSubscription =
lifetime.Events.SubscribePlacement(placements);
Assert.False(lifetime.TryApplyPickup(
new PickupEvent.Parsed(record.ServerGuid, 1, 2),
acknowledgeProjection: null,
out _));
Assert.True(reentered);
RuntimeEntityDelta only = Assert.Single(entities.Deltas);
Assert.Equal(RuntimeEntityChange.Rebucketed, only.Change);
Assert.True(only.Stamp.Sequence > discardStamp.Sequence);
Assert.False(lifetime.Physics.SetPosition.AcknowledgeProjection(
pending.Projection));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
Assert.Single(placements.Deltas).Placement.Token));
}
[Fact]
public void SyntheticWithdrawalAcknowledgementTerminatesOperation()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001044u, 1);
_ = AttachBody(lifetime, record, SourceCell);
var observer = new PlacementObserver();
using IDisposable subscription = lifetime.Events.SubscribePlacement(observer);
Assert.True(lifetime.Physics.SetPosition.Cancel(
record,
publishWithdrawal: true));
RuntimePlacementProjectionToken token = Assert.Single(observer.Deltas)
.Placement.Token;
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(token));
Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership()
.ActiveOperationCount);
}
[Fact]
public void ReentrantBeginDuringDiscardCannotBeOverwrittenByOuterBegin()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001045u, 1);
_ = AttachBody(lifetime, record, SourceCell);
RuntimeSetPositionOutcome pending = lifetime.Physics.SetPosition.Apply(
record,
record.PositionAuthorityVersion,
Command(Request(SourceCell, new Vector3(18f, 18f, 7f))));
RuntimeEntityPlacementToken inner = default;
var observer = new PlacementObserver(delta =>
{
if (delta.Placement.Kind is RuntimePlacementProjectionKind.Discard)
{
inner = lifetime.Physics.SetPosition.BeginAcceptedPlacement(
record,
record.PositionAuthorityVersion,
RuntimeSetPositionOperationKind.RemoteAuthoritative);
}
});
using IDisposable subscription = lifetime.Events.SubscribePlacement(observer);
RuntimeEntityPlacementToken outer = lifetime.Physics.SetPosition
.BeginAcceptedPlacement(
record,
record.PositionAuthorityVersion,
RuntimeSetPositionOperationKind.RemoteAuthoritative);
Assert.False(outer.IsValid);
Assert.True(inner.IsValid);
Assert.False(lifetime.Physics.SetPosition.AcknowledgeProjection(
pending.Projection));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
Assert.Single(observer.Deltas).Placement.Token));
RuntimeSetPositionOutcome committed = lifetime.Physics.SetPosition
.SubmitPreparedPlacement(
inner,
Command(Request(SourceCell, new Vector3(19f, 18f, 7f))));
Assert.Equal(
RuntimeSetPositionStatus.CommittedHostAcknowledgementPending,
committed.Status);
}
[Fact]
public void CollisionRetirementRejectsActivePlacementBeforeResidentMutation()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001046u, 1);
_ = AttachBody(lifetime, record, SourceCell);
RuntimeEntityPlacementToken token = lifetime.Physics.SetPosition
.BeginAcceptedPlacement(
record,
record.PositionAuthorityVersion,
RuntimeSetPositionOperationKind.RemoteAuthoritative);
Assert.Throws<InvalidOperationException>(() =>
lifetime.Physics.SetPosition.ParkCollisionResidents(
SourceLandblock,
includeOutdoorCells: true));
Assert.Equal(SourceCell, record.FullCellId);
Assert.True(lifetime.Physics.IsSpatialRoot(record));
RuntimeSetPositionOutcome submitted = lifetime.Physics.SetPosition
.SubmitPreparedPlacement(
token,
Command(Request(SourceCell, new Vector3(20f, 18f, 7f))));
Assert.Equal(
RuntimeSetPositionStatus.CommittedHostAcknowledgementPending,
submitted.Status);
}
[Fact]
public void CollisionRetirementRejectsHostAckPendingPlacementBeforeResidentMutation()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x7000111Bu, 1);
_ = AttachBody(lifetime, record, SourceCell);
RuntimeSetPositionOutcome pending = lifetime.Physics.SetPosition.Apply(
record,
record.PositionAuthorityVersion,
Command(Request(SourceCell, new Vector3(20f, 19f, 7f))));
Assert.Throws<InvalidOperationException>(() =>
lifetime.Physics.SetPosition.ParkCollisionResidents(
SourceLandblock,
includeOutdoorCells: true));
Assert.Equal(SourceCell, record.FullCellId);
Assert.True(lifetime.Physics.IsSpatialRoot(record));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
pending.Projection));
}
[Fact]
public void CollisionRetirementInstallsEveryRootBeforeWithdrawObserverReentry()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord first = CreateRecord(lifetime, 0x7000111Du, 1);
RuntimeEntityRecord second = CreateRecord(lifetime, 0x7000111Eu, 1);
_ = AttachBody(lifetime, first, SourceCell);
_ = AttachBody(lifetime, second, SourceCell);
RuntimeEntityPlacementToken replacement = default;
RuntimeSetPositionOutcome submitted = default;
var observer = new PlacementObserver(delta =>
{
if (delta.Placement.Kind is RuntimePlacementProjectionKind.Withdraw
&& delta.Placement.Token.Entity == first.Key)
{
replacement = lifetime.Physics.SetPosition
.BeginAcceptedPlacement(
second,
second.PositionAuthorityVersion,
RuntimeSetPositionOperationKind.RemoteAuthoritative);
submitted = lifetime.Physics.SetPosition
.SubmitPreparedPlacement(
replacement,
Command(Request(
SourceCell,
new Vector3(21f, 19f, 7f))));
}
});
using IDisposable subscription = lifetime.Events.SubscribePlacement(observer);
lifetime.Physics.SetPosition.ParkCollisionResidents(
SourceLandblock,
includeOutdoorCells: true);
Assert.True(replacement.IsValid);
Assert.Equal(RuntimeSetPositionStatus.DeferredCell, submitted.Status);
Assert.Equal(2, observer.Deltas.Count);
Assert.All(observer.Deltas, delta => Assert.Equal(
RuntimePlacementProjectionKind.Withdraw,
delta.Placement.Kind));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
observer.Deltas[0].Placement.Token));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
observer.Deltas[1].Placement.Token));
RuntimePlacementProjectionSnapshot placed = observer.Deltas[^1].Placement;
Assert.Equal(RuntimePlacementProjectionKind.Place, placed.Kind);
Assert.Equal(second.Key, placed.Token.Entity);
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
placed.Token));
}
[Fact]
public void RootAndCurrentDirectChildOwnIndependentExactLostDeadlines()
{
var time = new ManualTimeProvider(DateTimeOffset.UnixEpoch);
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(
engine,
timeProvider: time);
RuntimeEntityRecord root = CreateRecord(lifetime, 0x70001005u, 3);
RuntimeEntityRecord child = CreateRecord(lifetime, 0x70001006u, 9);
_ = AttachBody(lifetime, root, SourceCell);
var relation = new ParentAttachmentRelation(
root.ServerGuid,
child.ServerGuid,
ParentLocation: 1,
PlacementId: 2,
ParentInstanceSequence: root.Incarnation,
ChildPositionSequence: 1);
lifetime.Entities.ParentAttachments.AcceptCreateObjectRelation(relation);
Assert.True(lifetime.Entities.ParentAttachments.CommitProjection(relation));
RuntimeSetPositionOutcome deferred = lifetime.Physics.SetPosition.Apply(
root,
root.PositionAuthorityVersion,
Command(CrossLandblockRequest()));
Assert.Equal(2, lifetime.Physics.CaptureOwnership().LostCellDeadlineCount);
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
deferred.Projection));
time.Advance(TimeSpan.FromSeconds(26));
lifetime.Physics.SetPosition.TickLostCellDeadlines();
Assert.Equal(2, lifetime.Physics.CaptureOwnership()
.ExpiredLostCellCount);
var expired = new HashSet<RuntimeEntityKey>();
while (lifetime.Physics.SetPosition.TryDequeueExpiredLostCell(out var key))
expired.Add(key);
Assert.Equal(2, expired.Count);
Assert.Contains(root.Key!.Value, expired);
Assert.Contains(child.Key!.Value, expired);
Assert.Equal(0, lifetime.Physics.CaptureOwnership().LostCellDeadlineCount);
Assert.Equal(0, lifetime.Physics.CaptureOwnership()
.ExpiredLostCellCount);
}
[Fact]
public void ExpiredRootDeletionDoesNotConsumeIndependentChildExpiry()
{
var time = new ManualTimeProvider(DateTimeOffset.UnixEpoch);
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine, timeProvider: time);
RuntimeEntityRecord root = CreateRecord(lifetime, 0x70001047u, 1);
RuntimeEntityRecord child = CreateRecord(lifetime, 0x70001048u, 1);
_ = AttachBody(lifetime, root, SourceCell);
CommitParent(lifetime, root, child);
RuntimeSetPositionOutcome deferred = lifetime.Physics.SetPosition.Apply(
root,
root.PositionAuthorityVersion,
Command(CrossLandblockRequest()));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
deferred.Projection));
time.Advance(TimeSpan.FromSeconds(26));
lifetime.Physics.SetPosition.TickLostCellDeadlines();
Assert.True(lifetime.Physics.SetPosition.TryDequeueExpiredLostCell(
out RuntimeEntityKey expiredRoot));
Assert.Equal(root.Key, expiredRoot);
lifetime.Physics.SetPosition.Forget(root);
Assert.True(lifetime.Physics.SetPosition.TryDequeueExpiredLostCell(
out RuntimeEntityKey expiredChild));
Assert.Equal(child.Key, expiredChild);
}
[Fact]
public void ChildPickupCancelsOnlyItsCapturedLostDeadline()
{
var time = new ManualTimeProvider(DateTimeOffset.UnixEpoch);
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine, timeProvider: time);
RuntimeEntityRecord root = CreateRecord(lifetime, 0x70001049u, 1);
RuntimeEntityRecord child = CreateRecord(lifetime, 0x7000104Au, 1);
_ = AttachBody(lifetime, root, SourceCell);
CommitParent(lifetime, root, child);
RuntimeSetPositionOutcome deferred = lifetime.Physics.SetPosition.Apply(
root,
root.PositionAuthorityVersion,
Command(CrossLandblockRequest()));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
deferred.Projection));
Assert.True(lifetime.TryApplyPickup(
new PickupEvent.Parsed(child.ServerGuid, 1, 2),
acknowledgeProjection: null,
out _));
time.Advance(TimeSpan.FromSeconds(26));
lifetime.Physics.SetPosition.TickLostCellDeadlines();
Assert.True(lifetime.Physics.SetPosition.TryDequeueExpiredLostCell(
out RuntimeEntityKey only));
Assert.Equal(root.Key, only);
Assert.False(lifetime.Physics.SetPosition.TryDequeueExpiredLostCell(out _));
}
[Fact]
public void LostDeadlineUsesMonotonicTimeAndResetClearsExpiredOwnership()
{
var time = new ManualTimeProvider(DateTimeOffset.UnixEpoch);
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine, timeProvider: time);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x7000104Bu, 1);
_ = AttachBody(lifetime, record, SourceCell);
RuntimeSetPositionOutcome deferred = lifetime.Physics.SetPosition.Apply(
record,
record.PositionAuthorityVersion,
Command(CrossLandblockRequest()));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
deferred.Projection));
time.JumpUtc(TimeSpan.FromDays(30));
lifetime.Physics.SetPosition.TickLostCellDeadlines();
Assert.Equal(0, lifetime.Physics.CaptureOwnership()
.ExpiredLostCellCount);
time.Advance(TimeSpan.FromSeconds(26));
lifetime.Physics.SetPosition.TickLostCellDeadlines();
Assert.Equal(1, lifetime.Physics.CaptureOwnership()
.ExpiredLostCellCount);
_ = lifetime.BeginSessionClear();
Assert.Equal(0, lifetime.Physics.CaptureOwnership()
.ExpiredLostCellCount);
Assert.False(lifetime.Physics.SetPosition.TryDequeueExpiredLostCell(out _));
}
[Fact]
public void LostDeadlineTickIsZeroAllocationWhenEmptyAndExpiresByDeadlinePriority()
{
var time = new ManualTimeProvider(DateTimeOffset.UnixEpoch);
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(
engine,
timeProvider: time);
lifetime.Physics.SetPosition.TickLostCellDeadlines();
long before = GC.GetAllocatedBytesForCurrentThread();
for (int index = 0; index < 1_000; index++)
lifetime.Physics.SetPosition.TickLostCellDeadlines();
Assert.Equal(0, GC.GetAllocatedBytesForCurrentThread() - before);
RuntimeEntityRecord first = CreateRecord(lifetime, 0x70001101u, 1);
RuntimeEntityRecord middle = CreateRecord(lifetime, 0x70001102u, 1);
RuntimeEntityRecord last = CreateRecord(lifetime, 0x70001103u, 1);
foreach (RuntimeEntityRecord record in new[] { first, middle, last })
{
_ = AttachBody(lifetime, record, SourceCell);
RuntimeSetPositionOutcome deferred = lifetime.Physics.SetPosition.Apply(
record,
record.PositionAuthorityVersion,
Command(CrossLandblockRequest()));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
deferred.Projection));
time.Advance(TimeSpan.FromSeconds(1));
}
lifetime.Physics.SetPosition.LeaveWorld(middle);
time.Advance(TimeSpan.FromSeconds(24));
lifetime.Physics.SetPosition.TickLostCellDeadlines();
Assert.True(lifetime.Physics.SetPosition.TryDequeueExpiredLostCell(
out RuntimeEntityKey firstExpired));
Assert.True(lifetime.Physics.SetPosition.TryDequeueExpiredLostCell(
out RuntimeEntityKey secondExpired));
Assert.Equal(first.Key, firstExpired);
Assert.Equal(last.Key, secondExpired);
Assert.False(lifetime.Physics.SetPosition.TryDequeueExpiredLostCell(out _));
}
[Fact]
public void RearmedLostDeadlineSupersedesItsStalePriorityEntry()
{
var time = new ManualTimeProvider(DateTimeOffset.UnixEpoch);
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(
engine,
timeProvider: time);
RuntimeEntityRecord first = CreateRecord(lifetime, 0x70001112u, 1);
RuntimeEntityRecord second = CreateRecord(lifetime, 0x70001113u, 1);
_ = AttachBody(lifetime, first, SourceCell);
_ = AttachBody(lifetime, second, SourceCell);
RuntimeSetPositionOutcome firstLost = lifetime.Physics.SetPosition.Apply(
first,
first.PositionAuthorityVersion,
Command(CrossLandblockRequest()));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
firstLost.Projection));
time.Advance(TimeSpan.FromSeconds(1));
RuntimeSetPositionOutcome secondLost = lifetime.Physics.SetPosition.Apply(
second,
second.PositionAuthorityVersion,
Command(CrossLandblockRequest()));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
secondLost.Projection));
time.Advance(TimeSpan.FromSeconds(1));
RuntimeEntityPlacementToken replacement = lifetime.Physics.SetPosition
.BeginAcceptedPlacement(
first,
first.PositionAuthorityVersion,
RuntimeSetPositionOperationKind.RemoteAuthoritative);
RuntimeSetPositionOutcome rearmed = lifetime.Physics.SetPosition
.SubmitPreparedPlacement(replacement, Command(CrossLandblockRequest()));
Assert.Equal(RuntimeSetPositionStatus.DeferredCell, rearmed.Status);
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
rearmed.Projection));
time.Advance(TimeSpan.FromSeconds(24.5));
lifetime.Physics.SetPosition.TickLostCellDeadlines();
Assert.True(lifetime.Physics.SetPosition.TryDequeueExpiredLostCell(
out RuntimeEntityKey expired));
Assert.Equal(second.Key, expired);
Assert.False(lifetime.Physics.SetPosition.TryDequeueExpiredLostCell(out _));
time.Advance(TimeSpan.FromSeconds(1));
lifetime.Physics.SetPosition.TickLostCellDeadlines();
Assert.True(lifetime.Physics.SetPosition.TryDequeueExpiredLostCell(
out expired));
Assert.Equal(first.Key, expired);
}
[Fact]
public void RepeatedLostDeadlineRearmAndCancelKeepsExactHeapBounded()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001119u, 1);
_ = AttachBody(lifetime, record, SourceCell);
RuntimeSetPositionOutcome deferred = lifetime.Physics.SetPosition.Apply(
record,
record.PositionAuthorityVersion,
Command(CrossLandblockRequest()));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
deferred.Projection));
for (int iteration = 0; iteration < 100; iteration++)
{
RuntimeEntityPlacementToken token = lifetime.Physics.SetPosition
.BeginAcceptedPlacement(
record,
record.PositionAuthorityVersion,
RuntimeSetPositionOperationKind.RemoteAuthoritative);
deferred = lifetime.Physics.SetPosition.SubmitPreparedPlacement(
token,
Command(CrossLandblockRequest()));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
deferred.Projection));
RuntimeSetPositionOwnershipSnapshot snapshot = lifetime.Physics
.SetPosition.CaptureOwnership();
Assert.True(snapshot.IndexesConsistent);
Assert.Equal(1, snapshot.LostDeadlineCount);
Assert.Equal(1, snapshot.LostDeadlineNodeCount);
Assert.Equal(1, snapshot.LostDeadlineIndexCount);
}
lifetime.Physics.SetPosition.LeaveWorld(record);
RuntimeSetPositionOwnershipSnapshot final = lifetime.Physics
.SetPosition.CaptureOwnership();
Assert.True(final.IndexesConsistent);
Assert.Equal(0, final.LostDeadlineCount);
Assert.Equal(0, final.LostDeadlineNodeCount);
Assert.Equal(0, final.LostDeadlineIndexCount);
}
[Fact]
public void CommittedGenerationWithoutExactIndoorCellRebindsAndWakesOnNextGeneration()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001114u, 1);
_ = AttachBody(lifetime, record, SourceCell);
var observer = new PlacementObserver();
using IDisposable subscription = lifetime.Events.SubscribePlacement(observer);
RuntimeSetPositionOutcome deferred = lifetime.Physics.SetPosition.Apply(
record,
record.PositionAuthorityVersion,
Command(Request(
DestinationIndoorCell,
new Vector3(10f, 12f, 7f))));
Assert.Equal(RuntimeSetPositionStatus.DeferredCell, deferred.Status);
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
deferred.Projection));
RuntimeCollisionAdmission missing = lifetime.Physics
.BeginCollisionAdmission(DestinationLandblock);
using (PreparedLandblockCollisionGeneration prepared =
lifetime.Physics.PrepareCollisionGeneration(missing))
{
lifetime.Physics.StageCollisionAssets(
missing,
prepared,
CollisionAssets(DestinationLandblock));
Assert.True(CommitPrepared(
lifetime.Physics,
missing,
prepared).Committed);
}
Assert.Single(observer.Deltas);
RuntimePhysicsOwnershipSnapshot unbound = lifetime.Physics
.CaptureOwnership();
Assert.Equal(1, unbound.UnboundDeferredSetPositionCellCount);
Assert.Equal(1, unbound.UnboundDeferredSetPositionCellOrderCount);
RuntimeCollisionAdmission resident = lifetime.Physics
.BeginCollisionAdmission(DestinationLandblock);
using PreparedLandblockCollisionGeneration preparedResident =
lifetime.Physics.PrepareCollisionGeneration(resident);
lifetime.Physics.StageCollisionAssets(
resident,
preparedResident,
CollisionAssets(DestinationLandblock));
AddSyntheticCell(preparedResident.DataCache, DestinationIndoorCell);
Assert.True(CommitPrepared(
lifetime.Physics,
resident,
preparedResident).Committed);
RuntimePlacementProjectionSnapshot placed = observer.Deltas[^1].Placement;
Assert.Equal(RuntimePlacementProjectionKind.Place, placed.Kind);
Assert.Equal(DestinationIndoorCell, placed.Token.ExactCellId);
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
placed.Token));
RuntimePhysicsOwnershipSnapshot final = lifetime.Physics.CaptureOwnership();
Assert.Equal(0, final.DeferredSetPositionBucketCount);
Assert.Equal(0, final.UnboundDeferredSetPositionCellCount);
}
[Fact]
public void CollisionAdmissionSupersessionAndInvalidationPreserveDeferredSurvivorOrder()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord first = CreateRecord(lifetime, 0x70001115u, 1);
RuntimeEntityRecord second = CreateRecord(lifetime, 0x70001116u, 1);
_ = AttachBody(lifetime, first, SourceCell);
_ = AttachBody(lifetime, second, SourceCell);
var observer = new PlacementObserver();
using IDisposable subscription = lifetime.Events.SubscribePlacement(observer);
foreach (RuntimeEntityRecord record in new[] { first, second })
{
RuntimeSetPositionOutcome deferred = lifetime.Physics.SetPosition.Apply(
record,
record.PositionAuthorityVersion,
Command(Request(
DestinationIndoorCell,
new Vector3(10f, 12f, 7f))));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
deferred.Projection));
}
observer.Deltas.Clear();
RuntimeCollisionAdmission superseded = lifetime.Physics
.BeginCollisionAdmission(DestinationLandblock);
RuntimeCollisionAdmission invalidated = lifetime.Physics
.BeginCollisionAdmission(DestinationLandblock);
Assert.Throws<InvalidOperationException>(() =>
lifetime.Physics.PrepareCollisionGeneration(superseded));
lifetime.Physics.CancelCollisionGeneration(invalidated);
RuntimeCollisionAdmission current = lifetime.Physics
.BeginCollisionAdmission(DestinationLandblock);
using PreparedLandblockCollisionGeneration prepared = lifetime.Physics
.PrepareCollisionGeneration(current);
lifetime.Physics.StageCollisionAssets(
current,
prepared,
CollisionAssets(DestinationLandblock));
AddSyntheticCell(prepared.DataCache, DestinationIndoorCell);
Assert.True(CommitPrepared(
lifetime.Physics,
current,
prepared).Committed);
Assert.Equal(2, observer.Deltas.Count);
Assert.Equal(first.Key, observer.Deltas[0].Placement.Token.Entity);
Assert.Equal(second.Key, observer.Deltas[1].Placement.Token.Entity);
Assert.All(observer.Deltas, delta => Assert.Equal(
RuntimePlacementProjectionKind.Place,
delta.Placement.Kind));
Assert.True(lifetime.Physics.CaptureOwnership()
.DeferredSetPositionBucketCount == 0);
}
[Fact]
public void NextAdmissionMergesOlderUnboundAndNewerFutureBoundSurvivors()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord older = CreateRecord(lifetime, 0x7000111Fu, 1);
RuntimeEntityRecord newer = CreateRecord(lifetime, 0x70001120u, 1);
_ = AttachBody(lifetime, older, SourceCell);
_ = AttachBody(lifetime, newer, SourceCell);
var observer = new PlacementObserver();
using IDisposable subscription = lifetime.Events.SubscribePlacement(observer);
RuntimeSetPositionOutcome olderLost = lifetime.Physics.SetPosition.Apply(
older,
older.PositionAuthorityVersion,
Command(Request(
DestinationIndoorCell,
new Vector3(10f, 12f, 7f))));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
olderLost.Projection));
RuntimeCollisionAdmission cancelled = lifetime.Physics
.BeginCollisionAdmission(DestinationLandblock);
lifetime.Physics.CancelCollisionGeneration(cancelled);
RuntimeSetPositionOutcome newerLost = lifetime.Physics.SetPosition.Apply(
newer,
newer.PositionAuthorityVersion,
Command(Request(
DestinationIndoorCell,
new Vector3(11f, 12f, 7f))));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
newerLost.Projection));
RuntimePhysicsOwnershipSnapshot split = lifetime.Physics.CaptureOwnership();
Assert.Equal(1, split.UnboundDeferredSetPositionCellCount);
Assert.Equal(1, split.DeferredSetPositionBucketCount);
observer.Deltas.Clear();
RuntimeCollisionAdmission current = lifetime.Physics
.BeginCollisionAdmission(DestinationLandblock);
RuntimePhysicsOwnershipSnapshot merged = lifetime.Physics.CaptureOwnership();
Assert.Equal(0, merged.UnboundDeferredSetPositionCellCount);
Assert.Equal(1, merged.DeferredSetPositionBucketCount);
using PreparedLandblockCollisionGeneration prepared = lifetime.Physics
.PrepareCollisionGeneration(current);
lifetime.Physics.StageCollisionAssets(
current,
prepared,
CollisionAssets(DestinationLandblock));
AddSyntheticCell(prepared.DataCache, DestinationIndoorCell);
Assert.True(CommitPrepared(
lifetime.Physics,
current,
prepared).Committed);
Assert.Equal(2, observer.Deltas.Count);
Assert.Equal(older.Key, observer.Deltas[0].Placement.Token.Entity);
Assert.Equal(newer.Key, observer.Deltas[1].Placement.Token.Entity);
Assert.All(observer.Deltas, delta => Assert.Equal(
RuntimePlacementProjectionKind.Place,
delta.Placement.Kind));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
observer.Deltas[0].Placement.Token));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
observer.Deltas[1].Placement.Token));
}
[Theory]
[InlineData(false, 0x0101u)]
[InlineData(true, 0x0001u)]
public void CollisionRetirementParksOnlyAffectedDynamicParentlessRoots(
bool fullWithdrawal,
uint lowCell)
{
const uint landblock = 0x01010000u;
PhysicsEngine engine = FlatEngine(landblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord dynamicRoot = CreateRecord(lifetime, 0x70001007u, 1);
uint cell = landblock | lowCell;
PhysicsBody dynamicBody = AttachBody(
lifetime,
dynamicRoot,
landblock | 0x0001u);
ImmutableArray<FlatCollisionSphere> authoredSpheres =
[
new FlatCollisionSphere(new Vector3(0.25f, 0f, 0f), 0.3f),
new FlatCollisionSphere(new Vector3(-0.2f, 0.1f, 0.4f), 0.2f),
];
RuntimeSetPositionOutcome prepared = lifetime.Physics.SetPosition.Apply(
dynamicRoot,
dynamicRoot.PositionAuthorityVersion,
Command(Request(
landblock | 0x0001u,
new Vector3(12f, 18f, 7f)) with
{
Spheres = authoredSpheres,
}));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
prepared.Projection));
Assert.True(lifetime.Physics.SetPosition.TryGetPreparedMoverSphereCount(
dynamicRoot,
out int sphereCount));
Assert.Equal(2, sphereCount);
lifetime.Entities.SetFullCell(
dynamicRoot,
cell,
landblock | 0xFFFFu);
dynamicBody.SnapToCell(cell, dynamicBody.Position, dynamicBody.Position);
RuntimeEntityRecord staticRoot = CreateRecord(lifetime, 0x70001008u, 1);
_ = AttachBody(lifetime, staticRoot, cell, PhysicsStateFlags.Static);
RuntimeEntityRecord parent = CreateRecord(lifetime, 0x7000104Cu, 1);
_ = AttachBody(lifetime, parent, SourceCell);
RuntimeEntityRecord attached = CreateRecord(lifetime, 0x7000104Du, 1);
_ = AttachBody(lifetime, attached, cell);
CommitParent(lifetime, parent, attached);
lifetime.Physics.SetPosition.ParkCollisionResidents(
landblock,
includeOutdoorCells: fullWithdrawal);
Assert.True(lifetime.Physics.SetPosition.IsDeferred(dynamicRoot));
Assert.Equal(0, lifetime.Physics.CaptureOwnership()
.AwaitingSetPositionPreparationCount);
Assert.Equal(0u, dynamicRoot.FullCellId);
Assert.False(lifetime.Physics.IsSpatialRoot(dynamicRoot));
Assert.False(lifetime.Physics.SetPosition.IsDeferred(staticRoot));
Assert.Equal(cell, staticRoot.FullCellId);
Assert.True(lifetime.Physics.IsSpatialRoot(staticRoot));
Assert.False(lifetime.Physics.SetPosition.IsDeferred(attached));
Assert.Equal(cell, attached.FullCellId);
lifetime.Entities.ParentAttachments.EndChildProjection(
attached.ServerGuid);
lifetime.Physics.SetPosition.ParkCollisionResidents(
landblock,
includeOutdoorCells: fullWithdrawal);
Assert.True(lifetime.Physics.SetPosition.IsDeferred(attached));
}
[Fact]
public void PickupStyleLeaveWorldCancelsLostWakeAndTerminalDisposeConverges()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001009u, 1);
PhysicsBody body = AttachBody(lifetime, record, SourceCell);
RuntimeSetPositionOutcome deferred = lifetime.Physics.SetPosition.Apply(
record,
record.PositionAuthorityVersion,
Command(CrossLandblockRequest()));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
deferred.Projection));
lifetime.Physics.SetPosition.LeaveWorld(record);
AddFlatLandblock(engine, DestinationLandblock, 192f);
lifetime.Physics.SetPosition.CommitCollisionGeneration(
DestinationLandblock,
generation: 1,
ready: true);
Assert.Equal(0u, record.FullCellId);
Assert.Equal(0u, body.CellPosition.ObjCellId);
Assert.False(body.InWorld);
Assert.False(lifetime.Physics.SetPosition.IsDeferred(record));
lifetime.Dispose();
Assert.True(lifetime.Physics.CaptureOwnership().IsConverged);
}
[Fact]
public void CommittedParentDoesNotLeakAcrossChildGuidReuse()
{
const uint childGuid = 0x7000104Eu;
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord parent = CreateRecord(lifetime, 0x7000104Fu, 1);
RuntimeEntityRecord retired = CreateRecord(lifetime, childGuid, 1);
_ = AttachBody(lifetime, parent, SourceCell);
_ = AttachBody(lifetime, retired, SourceCell);
CommitParent(lifetime, parent, retired);
Assert.True(lifetime.TryAcceptDelete(
new DeleteObject.Parsed(childGuid, 1),
isLocalPlayer: false,
removeRetainedObject: false,
out RuntimeEntityDeleteAcceptance acceptance));
lifetime.CompleteAcceptedDelete(acceptance);
Assert.Null(lifetime.RetireCanonicalOnly(retired));
RuntimeEntityRecord replacement = CreateRecord(lifetime, childGuid, 2);
_ = AttachBody(lifetime, replacement, SourceCell);
lifetime.Physics.SetPosition.ParkCollisionResidents(
SourceLandblock,
includeOutdoorCells: true);
Assert.True(lifetime.Physics.SetPosition.IsDeferred(replacement));
}
[Fact]
public void NewerPositionPickupAndParentEachCancelExactLostOperation()
{
VerifyPositionChannelCancellation(CancellationChannel.Position);
VerifyPositionChannelCancellation(CancellationChannel.Pickup);
VerifyPositionChannelCancellation(CancellationChannel.Parent);
}
[Fact]
public void DeleteGuidReuseAndSessionResetCannotWakeStaleIncarnation()
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
const uint guid = 0x7000100Du;
RuntimeEntityRecord retired = CreateRecord(lifetime, guid, 1);
_ = AttachBody(lifetime, retired, SourceCell);
RuntimeSetPositionOutcome deferred = lifetime.Physics.SetPosition.Apply(
retired,
retired.PositionAuthorityVersion,
Command(CrossLandblockRequest()));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
deferred.Projection));
Assert.True(lifetime.TryAcceptDelete(
new DeleteObject.Parsed(guid, 1),
isLocalPlayer: false,
removeRetainedObject: false,
out RuntimeEntityDeleteAcceptance acceptance));
lifetime.CompleteAcceptedDelete(acceptance);
Assert.Null(lifetime.RetireCanonicalOnly(retired));
RuntimeEntityRecord replacement = CreateRecord(lifetime, guid, 2);
PhysicsBody replacementBody = AttachBody(
lifetime,
replacement,
SourceCell);
AddFlatLandblock(engine, DestinationLandblock, 192f);
lifetime.Physics.SetPosition.CommitCollisionGeneration(
DestinationLandblock,
generation: 1,
ready: true);
Assert.Equal(SourceCell, replacement.FullCellId);
Assert.Equal(SourceCell, replacementBody.CellPosition.ObjCellId);
Assert.False(lifetime.Physics.SetPosition.IsDeferred(replacement));
RuntimeSetPositionOutcome pending = lifetime.Physics.SetPosition.Apply(
replacement,
replacement.PositionAuthorityVersion,
Command(Request(SourceCell, new Vector3(15f, 18f, 7f))));
Assert.True(pending.Projection.IsValid);
IReadOnlyList<RuntimeEntityRecord> retiring = lifetime.BeginSessionClear();
Assert.Single(retiring);
Assert.Equal(0, lifetime.Physics.CaptureOwnership()
.SetPositionOperationCount);
Assert.Equal(0, lifetime.Physics.CaptureOwnership()
.PendingSetPositionHostAcknowledgementCount);
}
[Fact]
public void ColdCollisionRetirementWaitsForExactAuthoredMoverPreparation()
{
const uint landblock = 0x01010000u;
PhysicsEngine engine = FlatEngine(landblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x7000100Eu, 1);
_ = AttachBody(lifetime, record, landblock | 0x0101u);
lifetime.Physics.SetPosition.ParkCollisionResidents(
landblock,
includeOutdoorCells: false);
Assert.True(lifetime.Physics.SetPosition.IsDeferred(record));
Assert.Equal(1, lifetime.Physics.CaptureOwnership()
.AwaitingSetPositionPreparationCount);
Assert.True(lifetime.Physics.SetPosition.TryGetAwaitingPreparationToken(
record,
out RuntimeEntityPlacementToken token));
Assert.True(lifetime.Physics.SetPosition.TryPeekProjection(
out RuntimePlacementProjectionSnapshot withdrawal));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
withdrawal.Token));
ImmutableArray<FlatCollisionSphere> authored =
[
new FlatCollisionSphere(new Vector3(0.4f, 0f, 0f), 0.35f),
new FlatCollisionSphere(new Vector3(-0.3f, 0f, 0.5f), 0.2f),
];
RuntimeSetPositionOutcome supplied = lifetime.Physics.SetPosition
.SubmitPreparedPlacement(
token,
Command(Request(
landblock | 0x0101u,
new Vector3(999f, 999f, 999f)) with
{
Spheres = authored,
}));
Assert.Equal(RuntimeSetPositionStatus.DeferredCell, supplied.Status);
Assert.Equal(0, lifetime.Physics.CaptureOwnership()
.AwaitingSetPositionPreparationCount);
Assert.False(lifetime.Physics.SetPosition.TryGetPreparedMoverSphereCount(
record,
out _));
AddSyntheticCell(engine.DataCache!, landblock | 0x0101u);
lifetime.Physics.SetPosition.CommitCollisionGeneration(
landblock,
generation: 1,
ready: true);
Assert.True(lifetime.Physics.SetPosition.TryGetPreparedMoverSphereCount(
record,
out int sphereCount));
Assert.Equal(2, sphereCount);
Assert.Equal(landblock | 0x0101u,
record.PhysicsBody!.CellPosition.ObjCellId);
Assert.True(lifetime.Physics.SetPosition.TryPeekProjection(
out RuntimePlacementProjectionSnapshot placed));
Assert.Equal(RuntimePlacementProjectionKind.Place, placed.Kind);
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
placed.Token));
}
[Fact]
public void ColdMalformedWakeReindexesUntilCorrectedPreparationAndNextAdmission()
{
const uint landblock = 0x01010000u;
uint exactCell = landblock | 0x0101u;
PhysicsEngine engine = FlatEngine(landblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
RuntimeEntityRecord record = CreateRecord(lifetime, 0x70001127u, 1);
_ = AttachBody(lifetime, record, exactCell);
var observer = new PlacementObserver();
using IDisposable subscription = lifetime.Events.SubscribePlacement(observer);
lifetime.Physics.SetPosition.ParkCollisionResidents(
landblock,
includeOutdoorCells: false);
Assert.True(lifetime.Physics.SetPosition.TryGetAwaitingPreparationToken(
record,
out RuntimeEntityPlacementToken token));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
Assert.Single(observer.Deltas).Placement.Token));
observer.Deltas.Clear();
RuntimeSetPositionCommand malformed = Command(Request(
exactCell,
new Vector3(10f, 18f, 7f)) with
{
Spheres =
[
new FlatCollisionSphere(
new Vector3(float.NaN, 0f, 0f),
0.4f),
],
});
RuntimeSetPositionOutcome acceptedMalformed = lifetime.Physics
.SetPosition.SubmitPreparedPlacement(token, malformed);
Assert.Equal(RuntimeSetPositionStatus.DeferredCell,
acceptedMalformed.Status);
RuntimeCollisionAdmission first = lifetime.Physics
.BeginCollisionAdmission(landblock);
using (PreparedLandblockCollisionGeneration prepared = lifetime.Physics
.PrepareCollisionGeneration(first))
{
lifetime.Physics.StageCollisionAssets(
first,
prepared,
CollisionAssets(landblock));
AddSyntheticCell(prepared.DataCache, exactCell);
Assert.True(CommitPrepared(
lifetime.Physics,
first,
prepared).Committed);
}
RuntimeSetPositionOwnershipSnapshot invalidWake = lifetime.Physics
.SetPosition.CaptureOwnership();
Assert.Equal(1, invalidWake.AwaitingPreparationCount);
Assert.Equal(1, invalidWake.DeferredBucketCount);
Assert.Equal(1, invalidWake.LostDeadlineCount);
Assert.Empty(observer.Deltas);
Assert.False(lifetime.Physics.SetPosition.TryGetPreparedMoverSphereCount(
record,
out _));
RuntimeSetPositionCommand corrected = Command(Request(
exactCell,
new Vector3(10f, 18f, 7f)) with
{
Spheres =
[
new FlatCollisionSphere(Vector3.Zero, 0.4f),
],
});
RuntimeSetPositionOutcome correctedPending = lifetime.Physics
.SetPosition.SubmitPreparedPlacement(token, corrected);
Assert.Equal(RuntimeSetPositionStatus.DeferredCell,
correctedPending.Status);
Assert.Equal(PhysicsResidenceDisposition.DeferredCell,
correctedPending.Residence);
Assert.Equal(exactCell, correctedPending.ExactCellId);
RuntimeCollisionAdmission second = lifetime.Physics
.BeginCollisionAdmission(landblock);
using PreparedLandblockCollisionGeneration preparedSecond = lifetime
.Physics.PrepareCollisionGeneration(second);
lifetime.Physics.StageCollisionAssets(
second,
preparedSecond,
CollisionAssets(landblock));
AddSyntheticCell(preparedSecond.DataCache, exactCell);
Assert.True(CommitPrepared(
lifetime.Physics,
second,
preparedSecond).Committed);
RuntimePlacementProjectionSnapshot placed = Assert.Single(observer.Deltas)
.Placement;
Assert.Equal(RuntimePlacementProjectionKind.Place, placed.Kind);
Assert.True(lifetime.Physics.SetPosition.TryGetPreparedMoverSphereCount(
record,
out int sphereCount));
Assert.Equal(1, sphereCount);
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
placed.Token));
}
private static void VerifyPositionChannelCancellation(
CancellationChannel channel)
{
PhysicsEngine engine = FlatEngine(SourceLandblock, 0f);
using var lifetime = new RuntimeEntityObjectLifetime(engine);
uint guid = 0x7000100Au + (uint)channel;
RuntimeEntityRecord record = CreateRecord(lifetime, guid, 1);
_ = AttachBody(lifetime, record, SourceCell);
RuntimeSetPositionOutcome deferred = lifetime.Physics.SetPosition.Apply(
record,
record.PositionAuthorityVersion,
Command(CrossLandblockRequest()));
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
deferred.Projection));
switch (channel)
{
case CancellationChannel.Position:
Assert.True(lifetime.TryApplyPosition(
new WorldSession.EntityPositionUpdate(
guid,
new CreateObject.ServerPosition(
SourceCell, 11f, 20f, 7f, 1f, 0f, 0f, 0f),
Velocity: null,
PlacementId: null,
IsGrounded: true,
InstanceSequence: 1,
PositionSequence: 2,
TeleportSequence: 0,
ForcePositionSequence: 0),
isLocalPlayer: false,
forcePositionRotation: null,
currentLocalVelocity: null,
projectionRequiresTeleportHook: false,
acknowledgeProjection: null,
out _,
out _,
out _));
break;
case CancellationChannel.Pickup:
Assert.True(lifetime.TryApplyPickup(
new PickupEvent.Parsed(guid, 1, 2),
acknowledgeProjection: null,
out _));
break;
case CancellationChannel.Parent:
RuntimeEntityRecord parent = CreateRecord(
lifetime,
guid + 0x100u,
4);
Assert.True(lifetime.TryApplyParent(
new ParentEvent.Parsed(
parent.ServerGuid,
guid,
ParentLocation: 1,
PlacementId: 2,
ParentInstanceSequence: parent.Incarnation,
ChildPositionSequence: 2),
acknowledgeProjection: null,
out _));
break;
default:
throw new ArgumentOutOfRangeException(nameof(channel));
}
Assert.False(lifetime.Physics.SetPosition.IsDeferred(record));
Assert.Equal(0, lifetime.Physics.CaptureOwnership()
.DeferredSetPositionCount);
Assert.Equal(0, lifetime.Physics.CaptureOwnership()
.LostCellDeadlineCount);
}
private static void CommitParent(
RuntimeEntityObjectLifetime lifetime,
RuntimeEntityRecord parent,
RuntimeEntityRecord child)
{
var relation = new ParentAttachmentRelation(
parent.ServerGuid,
child.ServerGuid,
ParentLocation: 1,
PlacementId: 2,
ParentInstanceSequence: parent.Incarnation,
ChildPositionSequence: 1);
lifetime.Entities.ParentAttachments.AcceptCreateObjectRelation(relation);
Assert.True(lifetime.Entities.ParentAttachments.CommitProjection(relation));
}
private static RuntimeSetPositionCommand Command(
PhysicsSetPositionRequest request) => new(
request,
RuntimeSetPositionOperationKind.RemoteAuthoritative,
GameTime: 10d,
ExpectedVelocityAuthorityVersion: 0UL,
ShadowWorldOffsetX: request.CellId == SourceCell ? 0f : 192f,
ShadowWorldOffsetY: 0f);
private static PhysicsSetPositionRequest CrossLandblockRequest() =>
Request(
SourceCell,
new Vector3(193f, 12f, 7f),
new Vector3(193f, 12f, 7f));
private static PhysicsSetPositionRequest Request(
uint cellId,
Vector3 position,
Vector3? cellLocal = null) => new(
position,
Quaternion.Identity,
cellId,
cellLocal ?? position,
ImmutableArray<FlatCollisionSphere>.Empty,
Scale: 1f,
StepUpHeight: 0.4f,
StepDownHeight: 0.4f,
Flags: PhysicsSetPositionFlags.Placement
| PhysicsSetPositionFlags.Slide);
private static RuntimeEntityRecord CreateRecord(
RuntimeEntityObjectLifetime lifetime,
uint guid,
ushort incarnation)
{
RuntimeEntityRecord record = lifetime.RegisterEntity(
Spawn(guid, incarnation)).Canonical!;
lifetime.Entities.SetFinalPhysicsState(record, PhysicsStateFlags.Gravity);
return record;
}
private static PhysicsBody AttachBody(
RuntimeEntityObjectLifetime lifetime,
RuntimeEntityRecord record,
uint cellId,
PhysicsStateFlags state = PhysicsStateFlags.Gravity)
{
lifetime.Entities.SetFullCell(
record,
cellId,
(cellId & 0xFFFF0000u) | 0xFFFFu);
lifetime.Entities.SetFinalPhysicsState(record, state);
var body = new PhysicsBody
{
Position = new Vector3(10f, 20f, 7f),
Orientation = Quaternion.Identity,
LastUpdateTime = 1d,
State = state,
TransientState = TransientStateFlags.Active,
};
body.SnapToCell(cellId, body.Position, body.Position);
lifetime.Entities.SetPhysicsBody(record, body);
record.ObjectClock.Activate();
lifetime.Physics.AcknowledgeSpatialProjection(record, spatial: true);
return body;
}
private static PhysicsEngine FlatEngine(uint landblock, float worldOffsetX)
{
var engine = new PhysicsEngine
{
DataCache = new PhysicsDataCache(),
};
AddFlatLandblock(engine, landblock, worldOffsetX);
return engine;
}
private static RuntimeLandblockCollisionAssets CollisionAssets(
uint landblockId) => new(
landblockId,
new TerrainSurface(new byte[81], new float[256]),
Array.Empty<CellSurface>(),
Array.Empty<PortalPlane>(),
WorldOffsetX: 192f,
WorldOffsetY: 0f,
CurrentCellId: 0u);
private static RuntimeCollisionGenerationCommit CommitPrepared(
RuntimePhysicsState physics,
RuntimeCollisionAdmission admission,
PreparedLandblockCollisionGeneration prepared)
{
while (true)
{
while (!physics.AdvanceCollisionRetainedOwnerCapture(
admission,
prepared).Completed)
{
}
foreach (uint ownerId in prepared.RetainedOwnerIds)
{
physics.RefreshCollisionRetainedOwner(
admission,
prepared,
ownerId);
}
RuntimeCollisionSealStep seal;
do
{
seal = physics.AdvanceCollisionGenerationSeal(
admission,
prepared);
}
while (!seal.Completed && !seal.Restarted);
if (seal.Completed)
break;
}
return physics.CommitCollisionGeneration(admission, prepared);
}
private static void AddSyntheticCell(
PhysicsDataCache cache,
uint cellId)
{
cache.RegisterCellStructForTest(
cellId,
new CellPhysics
{
WorldTransform = Matrix4x4.Identity,
InverseWorldTransform = Matrix4x4.Identity,
Resolved = new Dictionary<ushort, ResolvedPolygon>(),
Portals = [new PortalInfo(0, 0, 0)],
CellBSP = new CellBSPTree
{
Root = new CellBSPNode { Type = BSPNodeType.Leaf },
},
});
cache.CellGraph.Add(new EnvCell(
cellId,
Matrix4x4.Identity,
Matrix4x4.Identity,
Vector3.Zero,
Vector3.One,
Array.Empty<AcDream.Core.World.Cells.CellPortal>(),
Array.Empty<uint>(),
seenOutside: false,
containmentBsp: null));
}
private static void AddFlatLandblock(
PhysicsEngine engine,
uint landblock,
float worldOffsetX)
{
engine.AddLandblock(
landblock,
new TerrainSurface(new byte[81], new float[256]),
Array.Empty<CellSurface>(),
Array.Empty<PortalPlane>(),
worldOffsetX,
worldOffsetY: 0f);
}
private static WorldSession.EntitySpawn Spawn(uint guid, ushort instance)
{
var position = new CreateObject.ServerPosition(
SourceCell,
10f,
20f,
7f,
1f,
0f,
0f,
0f);
var timestamps = new PhysicsTimestamps(
Position: 1,
Movement: 1,
State: 1,
Vector: 1,
Teleport: 0,
ServerControlledMove: 1,
ForcePosition: 0,
ObjDesc: 1,
Instance: instance);
var physics = new PhysicsSpawnData(
RawState: (uint)PhysicsStateFlags.Gravity,
Position: position,
Movement: null,
AnimationFrame: null,
SetupTableId: 0x02000001u,
MotionTableId: 0x09000001u,
SoundTableId: null,
PhysicsScriptTableId: null,
Parent: null,
Children: null,
Scale: null,
Friction: null,
Elasticity: null,
Translucency: null,
Velocity: null,
Acceleration: null,
AngularVelocity: null,
DefaultScriptType: null,
DefaultScriptIntensity: null,
Timestamps: timestamps);
return new WorldSession.EntitySpawn(
guid,
position,
0x02000001u,
Array.Empty<CreateObject.AnimPartChange>(),
Array.Empty<CreateObject.TextureChange>(),
Array.Empty<CreateObject.SubPaletteSwap>(),
null,
null,
"set-position-fixture",
null,
null,
0x09000001u,
PhysicsState: (uint)PhysicsStateFlags.Gravity,
InstanceSequence: instance,
MovementSequence: 1,
ServerControlSequence: 1,
PositionSequence: 1,
Physics: physics);
}
private sealed class PlacementObserver(
Action<RuntimePlacementDelta>? onPlacement = null)
: IRuntimePlacementObserver
{
internal List<RuntimePlacementDelta> Deltas { get; } = [];
public void OnPlacement(in RuntimePlacementDelta delta)
{
Deltas.Add(delta);
onPlacement?.Invoke(delta);
}
}
private sealed class EntityObserver : IRuntimeEntityObjectObserver
{
internal List<RuntimeEntityDelta> Deltas { get; } = [];
public void OnEntity(in RuntimeEntityDelta delta) => Deltas.Add(delta);
public void OnInventory(in RuntimeInventoryDelta delta)
{
}
}
private sealed class ThrowingPlacementObserver : IRuntimePlacementObserver
{
public void OnPlacement(in RuntimePlacementDelta delta) =>
throw new InvalidOperationException("fixture observer failure");
}
private sealed class ReentrantRemotePlacement(PhysicsBody body)
: IRuntimeRemotePlacement
{
private Func<uint>? _readCell;
private Action<uint>? _writeCell;
private uint _cellId;
public PhysicsBody Body { get; } = body;
public uint CellId
{
get => _readCell?.Invoke() ?? _cellId;
set
{
_cellId = value;
_writeCell?.Invoke(value);
}
}
public bool Airborne { get; set; }
public Vector3 LastServerPosition { get; set; }
public double LastServerPositionTime { get; set; }
public Vector3 LastShadowSyncPosition { get; set; }
public Quaternion LastShadowSyncOrientation { get; set; }
internal Action? OnHitGround { get; init; }
internal Action? OnLeaveGround { get; init; }
public void BindCanonicalCell(Func<uint> read, Action<uint> write)
{
_readCell = read;
_writeCell = write;
}
public void HitGround() => OnHitGround?.Invoke();
public void LeaveGround() => OnLeaveGround?.Invoke();
}
private sealed class ManualTimeProvider(DateTimeOffset utcNow)
: TimeProvider
{
private DateTimeOffset _utcNow = utcNow;
private long _timestamp;
public override DateTimeOffset GetUtcNow() => _utcNow;
public override long TimestampFrequency => TimeSpan.TicksPerSecond;
public override long GetTimestamp() => _timestamp;
internal void Advance(TimeSpan duration)
{
_utcNow += duration;
_timestamp += duration.Ticks;
}
internal void JumpUtc(TimeSpan duration) => _utcNow += duration;
}
private enum CancellationChannel : uint
{
Position,
Pickup,
Parent,
}
}
internal static class RuntimePlacementProjectionTokenTestExtensions
{
internal static ulong TokenSequence(
this RuntimePlacementProjectionToken token) => token.Sequence;
}