diff --git a/src/AcDream.Core/Physics/PhysicsTimestampGate.cs b/src/AcDream.Core/Physics/PhysicsTimestampGate.cs
index baae0594..0b9e5ad2 100644
--- a/src/AcDream.Core/Physics/PhysicsTimestampGate.cs
+++ b/src/AcDream.Core/Physics/PhysicsTimestampGate.cs
@@ -102,6 +102,25 @@ public sealed class PhysicsTimestampGate
return CreateObjectTimestampDisposition.ExistingGeneration;
}
+ ///
+ /// Classifies an incoming CreateObject generation without consuming any
+ /// channel timestamp. Runtime uses this to preflight only admissions which
+ /// can create or replace a canonical incarnation; equal/stale packets must
+ /// still reach the normal per-channel retail gates unchanged.
+ ///
+ public CreateObjectTimestampDisposition PreviewCreateObject(
+ ushort instance)
+ {
+ if (!_seeded)
+ return CreateObjectTimestampDisposition.InitialGeneration;
+ ushort currentInstance = _timestamps[Instance];
+ if (IsNewer(currentInstance, instance))
+ return CreateObjectTimestampDisposition.NewGeneration;
+ if (IsNewer(instance, currentInstance))
+ return CreateObjectTimestampDisposition.StaleGeneration;
+ return CreateObjectTimestampDisposition.ExistingGeneration;
+ }
+
public bool TryAcceptMovementEvent(
ushort instance,
ushort movement,
diff --git a/src/AcDream.Runtime/Entities/InboundPhysicsStateController.cs b/src/AcDream.Runtime/Entities/InboundPhysicsStateController.cs
index 2b949929..a88a9058 100644
--- a/src/AcDream.Runtime/Entities/InboundPhysicsStateController.cs
+++ b/src/AcDream.Runtime/Entities/InboundPhysicsStateController.cs
@@ -20,6 +20,12 @@ public sealed class InboundPhysicsStateController
public bool TryGetSnapshot(uint guid, out WorldSession.EntitySpawn spawn) =>
_snapshots.TryGetValue(guid, out spawn);
+ public CreateObjectTimestampDisposition PreviewCreateDisposition(
+ WorldSession.EntitySpawn incoming) =>
+ _gates.TryGetValue(incoming.Guid, out PhysicsTimestampGate? gate)
+ ? gate.PreviewCreateObject(incoming.InstanceSequence)
+ : CreateObjectTimestampDisposition.InitialGeneration;
+
public InboundCreateResult AcceptCreate(WorldSession.EntitySpawn incoming)
{
if (!_gates.TryGetValue(incoming.Guid, out PhysicsTimestampGate? gate))
diff --git a/src/AcDream.Runtime/Entities/RuntimeEntityDirectory.cs b/src/AcDream.Runtime/Entities/RuntimeEntityDirectory.cs
index e9b028ae..293f34db 100644
--- a/src/AcDream.Runtime/Entities/RuntimeEntityDirectory.cs
+++ b/src/AcDream.Runtime/Entities/RuntimeEntityDirectory.cs
@@ -47,6 +47,10 @@ public sealed class RuntimeEntityDirectory
public InboundCreateResult AcceptCreate(WorldSession.EntitySpawn incoming) =>
_inbound.AcceptCreate(incoming);
+ public CreateObjectTimestampDisposition PreviewCreateDisposition(
+ WorldSession.EntitySpawn incoming) =>
+ _inbound.PreviewCreateDisposition(incoming);
+
public bool TryDelete(
AcDream.Core.Net.Messages.DeleteObject.Parsed delete,
bool isLocalPlayer) =>
diff --git a/src/AcDream.Runtime/Entities/RuntimeEntityObjectLifetime.cs b/src/AcDream.Runtime/Entities/RuntimeEntityObjectLifetime.cs
index 03dd3cc1..3c377d6c 100644
--- a/src/AcDream.Runtime/Entities/RuntimeEntityObjectLifetime.cs
+++ b/src/AcDream.Runtime/Entities/RuntimeEntityObjectLifetime.cs
@@ -27,6 +27,7 @@ public readonly record struct RuntimeEntityObjectOwnershipSnapshot(
int ContainerProjectionCount,
int EquipmentOwnerCount,
int PendingMoveCount,
+ int InitialCreateResidenceLeaseCount,
int StreamSubscriberCount,
int PlacementStreamSubscriberCount,
long StreamDispatchFailureCount,
@@ -51,6 +52,7 @@ public readonly record struct RuntimeEntityObjectOwnershipSnapshot(
&& ContainerProjectionCount == 0
&& EquipmentOwnerCount == 0
&& PendingMoveCount == 0
+ && InitialCreateResidenceLeaseCount == 0
&& StreamSubscriberCount == 0
&& PlacementStreamSubscriberCount == 0
&& PendingDispatchCount == 0
@@ -118,6 +120,9 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
InventoryView = views.Inventory;
Events = new RuntimeEntityObjectEventStream(Entities, Objects);
Physics.SetPosition.BindEventStream(Events);
+ InitialCreateResidences = new RuntimeInitialCreateResidenceState(
+ Entities,
+ Physics.SetPosition);
Placements = new RuntimePlacementProjectionChannel(
Events,
Physics.SetPosition);
@@ -143,6 +148,9 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
InventoryView = views.Inventory;
Events = new RuntimeEntityObjectEventStream(Entities, Objects);
Physics.SetPosition.BindEventStream(Events);
+ InitialCreateResidences = new RuntimeInitialCreateResidenceState(
+ Entities,
+ Physics.SetPosition);
Placements = new RuntimePlacementProjectionChannel(
Events,
Physics.SetPosition);
@@ -168,6 +176,9 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
InventoryView = views.Inventory;
Events = new RuntimeEntityObjectEventStream(Entities, Objects);
Physics.SetPosition.BindEventStream(Events);
+ InitialCreateResidences = new RuntimeInitialCreateResidenceState(
+ Entities,
+ Physics.SetPosition);
Placements = new RuntimePlacementProjectionChannel(
Events,
Physics.SetPosition);
@@ -180,10 +191,14 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
public IRuntimeInventoryView InventoryView { get; }
public RuntimeEntityObjectEventStream Events { get; }
public RuntimePlacementProjectionChannel Placements { get; }
+ internal RuntimeInitialCreateResidenceState InitialCreateResidences
+ { get; }
public RuntimeEntityObjectOwnershipSnapshot CaptureOwnership()
{
ParentAttachmentState parents = Entities.ParentAttachments;
+ RuntimeInitialCreateResidenceOwnershipSnapshot initialResidence =
+ InitialCreateResidences.CaptureOwnership();
return new RuntimeEntityObjectOwnershipSnapshot(
Entities.Count,
Entities.PendingTeardownCount,
@@ -198,6 +213,8 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
Objects.ContainerProjectionCount,
Objects.EquipmentOwnerCount,
Objects.PendingMoveCount,
+ initialResidence.ActiveLeaseCount
+ + initialResidence.PendingAdoptionCount,
Events.SubscriberCount,
Events.PlacementSubscriberCount,
Events.DispatchFailureCount,
@@ -215,6 +232,7 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
EnsureNotDisposed();
Events.BindContext(generation, frameNumber);
Placements.BindGeneration(generation);
+ InitialCreateResidences.BindGeneration(generation);
}
///
@@ -225,7 +243,28 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
///
public RuntimeEntityRegistrationResult RegisterEntity(
WorldSession.EntitySpawn incoming,
- Func? retirePriorProjection = null)
+ Func? retirePriorProjection = null) =>
+ RegisterEntityCore(
+ incoming,
+ beginInitialResidence: false,
+ isLocalPlayer: false,
+ retirePriorProjection);
+
+ internal RuntimeEntityRegistrationResult RegisterEntityWithInitialResidence(
+ WorldSession.EntitySpawn incoming,
+ bool isLocalPlayer,
+ Func? retirePriorProjection = null) =>
+ RegisterEntityCore(
+ incoming,
+ beginInitialResidence: true,
+ isLocalPlayer,
+ retirePriorProjection);
+
+ private RuntimeEntityRegistrationResult RegisterEntityCore(
+ WorldSession.EntitySpawn incoming,
+ bool beginInitialResidence,
+ bool isLocalPlayer,
+ Func? retirePriorProjection)
{
EnsureNotDisposed();
if (_sessionClearInProgress)
@@ -233,6 +272,18 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
throw new InvalidOperationException(
"A Runtime entity cannot register while its session lifetime is clearing.");
}
+ CreateObjectTimestampDisposition preview =
+ Entities.PreviewCreateDisposition(incoming);
+ bool requiresFreshResidenceAdmission = preview is
+ CreateObjectTimestampDisposition.InitialGeneration
+ or CreateObjectTimestampDisposition.NewGeneration;
+ if (beginInitialResidence
+ && requiresFreshResidenceAdmission
+ && !InitialCreateResidences.CanAcceptCreate(incoming))
+ {
+ throw new InvalidOperationException(
+ $"CreateObject 0x{incoming.Guid:X8} cannot acquire a structurally valid initial residence lease.");
+ }
InboundCreateResult result = Entities.AcceptCreate(incoming);
if (result.Disposition
@@ -256,11 +307,16 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
incoming.Guid,
out RuntimeEntityRecord retained))
{
+ // Existing-generation CreateObject contributes untimestamped
+ // description fields here. Position/Parent/Pickup/State/etc.
+ // remain separate freshness-gated events and must not churn a
+ // pending initial placement or re-preclaim its wire cell.
Entities.RefreshSnapshot(
retained,
result.Snapshot,
- refreshPosition: true);
- Entities.AdvanceCreateAuthority(retained);
+ refreshPosition: !beginInitialResidence);
+ if (!beginInitialResidence)
+ Entities.AdvanceCreateAuthority(retained);
PublishEntity(RuntimeEntityChange.Updated, retained);
if (!IsCurrentOperation(
incoming.Guid,
@@ -291,7 +347,24 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
ReplacedExistingGeneration: false);
}
+ if (beginInitialResidence
+ && !InitialCreateResidences.CanAcceptCreate(result.Snapshot))
+ {
+ throw new InvalidOperationException(
+ $"Recovered CreateObject 0x{incoming.Guid:X8} cannot acquire a structurally valid initial residence lease.");
+ }
+
RuntimeEntityRecord recovered = Entities.AddActive(result.Snapshot);
+ if (!InitializeAcceptedCreateResidence(
+ recovered,
+ result,
+ beginInitialResidence,
+ isLocalPlayer))
+ {
+ throw FailInitialResidenceRegistration(
+ recovered,
+ publishDeleted: false);
+ }
PublishEntity(RuntimeEntityChange.Registered, recovered);
if (!IsCurrentOperation(
incoming.Guid,
@@ -366,6 +439,16 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
}
RuntimeEntityRecord canonical = Entities.AddActive(result.Snapshot);
+ if (!InitializeAcceptedCreateResidence(
+ canonical,
+ result,
+ beginInitialResidence,
+ isLocalPlayer))
+ {
+ throw FailInitialResidenceRegistration(
+ canonical,
+ publishDeleted: false);
+ }
try
{
PublishEntity(RuntimeEntityChange.Registered, canonical);
@@ -446,10 +529,14 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
{
EnsureNotDisposed();
ArgumentNullException.ThrowIfNull(canonical);
- RuntimePlacementCancellationReceipt cancellation =
+ RuntimePlacementCancellationReceipt initialCancellation =
+ ForgetInitialCreateResidence(canonical);
+ RuntimePlacementCancellationReceipt ordinaryCancellation =
Physics.SetPosition.Forget(
canonical,
releasePreparedMover: true);
+ RuntimePlacementCancellationReceipt cancellation =
+ PreferCancellation(initialCancellation, ordinaryCancellation);
Physics.CollisionReports.Forget(canonical);
Physics.RemoveSpatialProjection(canonical);
Entities.SetRemoteMotion(canonical, null);
@@ -540,10 +627,14 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
}
Entities.RefreshSnapshot(canonical, accepted);
+ RuntimePlacementCancellationReceipt initialCancellation =
+ ForgetInitialCreateResidence(canonical);
Entities.AdvancePositionAuthority(canonical);
Physics.CollisionReports.LeaveWorld(canonical);
- RuntimePlacementCancellationReceipt cancellation =
+ RuntimePlacementCancellationReceipt ordinaryCancellation =
Physics.SetPosition.Forget(canonical);
+ RuntimePlacementCancellationReceipt cancellation =
+ PreferCancellation(initialCancellation, ordinaryCancellation);
Entities.SuspendObjectClock(canonical);
Entities.SetFullCell(canonical, 0u, 0u);
Entities.ParentAttachments.EndChildProjection(update.Guid);
@@ -631,8 +722,12 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
return false;
}
- RuntimePlacementCancellationReceipt cancellation =
+ RuntimePlacementCancellationReceipt initialCancellation =
+ ForgetInitialCreateResidence(canonical);
+ RuntimePlacementCancellationReceipt ordinaryCancellation =
Physics.SetPosition.Forget(canonical);
+ RuntimePlacementCancellationReceipt cancellation =
+ PreferCancellation(initialCancellation, ordinaryCancellation);
Physics.CollisionReports.LeaveWorld(canonical);
Entities.SuspendObjectClock(canonical);
Entities.SetFullCell(canonical, 0u, 0u);
@@ -788,6 +883,25 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
out AcceptedPhysicsTimestamps timestamps)
{
EnsureNotDisposed();
+ RuntimeInitialCreateResidenceLease priorInitialResidence = default;
+ bool hadPendingInitialResidence =
+ Entities.TryGetActive(
+ update.Guid,
+ out RuntimeEntityRecord pendingCanonical)
+ && InitialCreateResidences.TryGetTransaction(
+ pendingCanonical,
+ out priorInitialResidence);
+ if (hadPendingInitialResidence
+ && !InitialCreateResidences.CanEnqueueAcceptedPosition(
+ pendingCanonical,
+ priorInitialResidence,
+ update))
+ {
+ disposition = PositionTimestampDisposition.Rejected;
+ accepted = default;
+ timestamps = default;
+ return false;
+ }
bool hadCanonical = Entities.TryGetActive(
update.Guid,
out RuntimeEntityRecord beforeCanonical);
@@ -825,9 +939,47 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
};
}
- RuntimePlacementCancellationReceipt cancellation = acceptedPosition
- ? Physics.SetPosition.Forget(canonical)
- : default;
+
+ // Initial CreateObject residence is one immutable transaction. A
+ // Position packet accepted before its 0x11 placement completes is
+ // retained in the transaction's ordered continuation batch; it must
+ // not replace the initial mover operation, mutate the Runtime record
+ // used to prepare that operation, or escape through a host callback.
+ if (hadPendingInitialResidence)
+ {
+ if (!acceptedPosition)
+ return true;
+ if (!ReferenceEquals(canonical, pendingCanonical))
+ throw FailInitialResidenceRegistration(
+ canonical,
+ publishDeleted: true);
+
+ RuntimeInitialCreateResidenceLease retained =
+ InitialCreateResidences.EnqueueAcceptedPosition(
+ canonical,
+ priorInitialResidence,
+ update,
+ accepted,
+ disposition,
+ timestamps,
+ isLocalPlayer,
+ forcePositionRotation,
+ currentLocalVelocity,
+ projectionRequiresTeleportHook);
+ if (!retained.IsValid)
+ {
+ throw FailInitialResidenceRegistration(
+ canonical,
+ publishDeleted: true);
+ }
+ return true;
+ }
+
+ RuntimePlacementCancellationReceipt cancellation = default;
+ if (acceptedPosition)
+ {
+ cancellation = Physics.SetPosition.Forget(canonical);
+ }
Entities.RefreshSnapshot(
canonical,
snapshot,
@@ -903,6 +1055,8 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
if (!Entities.IsCurrent(canonical))
return false;
+ RuntimePlacementCancellationReceipt cancellation =
+ ForgetInitialCreateResidence(canonical);
Physics.CollisionReports.LeaveWorld(canonical);
Entities.SuspendObjectClock(canonical);
Entities.SetFullCell(canonical, 0u, 0u);
@@ -911,7 +1065,8 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
canonical,
() => acknowledgeProjection?.Invoke(canonical),
RuntimeEntityChange.Withdrawn,
- () => canonical.SpatialAuthorityVersion == spatialVersion);
+ () => canonical.SpatialAuthorityVersion == spatialVersion,
+ cancellation);
}
public bool CommitChildNoDraw(
@@ -981,11 +1136,17 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
&& active.Incarnation == delete.InstanceSequence
&& Entities.RemoveActive(active))
{
+ RuntimePlacementCancellationReceipt initialCancellation =
+ ForgetInitialCreateResidence(active);
Physics.CollisionReports.Forget(active);
- RuntimePlacementCancellationReceipt cancellation =
+ RuntimePlacementCancellationReceipt ordinaryCancellation =
Physics.SetPosition.Forget(
active,
releasePreparedMover: true);
+ RuntimePlacementCancellationReceipt cancellation =
+ PreferCancellation(
+ initialCancellation,
+ ordinaryCancellation);
retiredCanonical = active;
Entities.RetainTeardown(active);
Physics.SetPosition.PublishCancellation(cancellation);
@@ -1058,6 +1219,7 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
_sessionClearInProgress = true;
RuntimeEntityRecord[] active = Entities.ActiveRecords.ToArray();
+ InitialCreateResidences.Clear();
Physics.CollisionReports.LeaveWorldBatch(active);
Physics.ResetSessionPhysics();
Entities.BeginSessionClear();
@@ -1193,10 +1355,14 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
}
Entities.RefreshSnapshot(canonical, accepted);
+ RuntimePlacementCancellationReceipt initialCancellation =
+ ForgetInitialCreateResidence(canonical);
Entities.AdvancePositionAuthority(canonical);
Physics.CollisionReports.LeaveWorld(canonical);
- RuntimePlacementCancellationReceipt cancellation =
+ RuntimePlacementCancellationReceipt ordinaryCancellation =
Physics.SetPosition.Forget(canonical);
+ RuntimePlacementCancellationReceipt cancellation =
+ PreferCancellation(initialCancellation, ordinaryCancellation);
ulong positionVersion = canonical.PositionAuthorityVersion;
ulong spatialVersion = canonical.SpatialAuthorityVersion;
return AcknowledgeProjectionAndPublish(
@@ -1251,6 +1417,108 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
private void EnsureNotDisposed() =>
ObjectDisposedException.ThrowIf(_disposed, this);
+ internal bool TryGetInitialCreateResidence(
+ RuntimeEntityRecord canonical,
+ out RuntimeInitialCreateResidenceLease lease)
+ {
+ EnsureNotDisposed();
+ return InitialCreateResidences.TryGetCurrent(canonical, out lease);
+ }
+
+ internal RuntimeInitialCreateResidenceCompletionStatus
+ CompleteInitialCreateResidence(
+ RuntimeEntityRecord canonical,
+ in RuntimeInitialCreateResidenceToken token,
+ out RuntimeInitialCreateResidenceReceipt receipt)
+ {
+ EnsureNotDisposed();
+ return InitialCreateResidences.Complete(
+ canonical,
+ token,
+ out receipt);
+ }
+
+ internal bool AcknowledgeInitialCreateResidenceAdoption(
+ RuntimeEntityRecord canonical,
+ in RuntimeInitialCreateResidenceAdoptionToken token)
+ {
+ EnsureNotDisposed();
+ return InitialCreateResidences.AcknowledgeAdoption(
+ canonical,
+ token);
+ }
+
+ private RuntimePlacementCancellationReceipt ForgetInitialCreateResidence(
+ RuntimeEntityRecord canonical)
+ {
+ return InitialCreateResidences.Forget(
+ canonical,
+ out _,
+ out RuntimePlacementCancellationReceipt cancellation)
+ ? cancellation
+ : default;
+ }
+
+ private static RuntimePlacementCancellationReceipt PreferCancellation(
+ in RuntimePlacementCancellationReceipt initial,
+ in RuntimePlacementCancellationReceipt ordinary) =>
+ initial.IsValid ? initial : ordinary;
+
+ private bool InitializeAcceptedCreateResidence(
+ RuntimeEntityRecord canonical,
+ in InboundCreateResult accepted,
+ bool beginInitialResidence,
+ bool isLocalPlayer)
+ {
+ if (!beginInitialResidence)
+ return true;
+
+ // The explicit cutover path separates accepted wire authority from
+ // committed residence before any observer can hydrate it. Failure to
+ // own the exact route is fail-closed; the caller removes the canonical
+ // record before publishing Registered/Updated.
+ if (canonical.PositionAuthorityVersion == 0UL)
+ Entities.AdvancePositionAuthority(canonical);
+ if (canonical.FullCellId != 0u)
+ Entities.SetFullCell(canonical, 0u, 0u);
+ RuntimeInitialCreateResidenceLease lease =
+ InitialCreateResidences.Begin(
+ canonical,
+ accepted,
+ isLocalPlayer);
+ return lease.IsValid;
+ }
+
+ private Exception FailInitialResidenceRegistration(
+ RuntimeEntityRecord canonical,
+ bool publishDeleted)
+ {
+ if (!Entities.RemoveActive(canonical))
+ {
+ return new InvalidOperationException(
+ $"Initial residence for 0x{canonical.ServerGuid:X8} failed after its canonical incarnation was superseded.");
+ }
+
+ Exception? failure = null;
+ if (publishDeleted)
+ {
+ try
+ {
+ PublishEntity(RuntimeEntityChange.Deleted, canonical);
+ }
+ catch (Exception error)
+ {
+ failure = error;
+ }
+ }
+ failure = Combine(failure, RetireCanonicalOnly(canonical));
+ var cause = new InvalidOperationException(
+ $"Initial residence for 0x{canonical.ServerGuid:X8} could not acquire its exact Runtime placement lease.");
+ return failure is null
+ ? cause
+ : new AggregateException(cause, failure);
+ }
+
private static Exception? Combine(
Exception? first,
Exception? second) =>
diff --git a/src/AcDream.Runtime/Entities/RuntimeInitialCreateResidenceState.cs b/src/AcDream.Runtime/Entities/RuntimeInitialCreateResidenceState.cs
new file mode 100644
index 00000000..f35fa9f5
--- /dev/null
+++ b/src/AcDream.Runtime/Entities/RuntimeInitialCreateResidenceState.cs
@@ -0,0 +1,743 @@
+using System.Collections.Immutable;
+using System.Numerics;
+using AcDream.Core.Net;
+using AcDream.Core.Net.Messages;
+using AcDream.Core.Physics;
+using AcDream.Runtime.Physics;
+
+namespace AcDream.Runtime.Entities;
+
+internal readonly record struct RuntimeInitialCreateResidenceToken(
+ RuntimeEntityKey Entity,
+ ulong LeaseId,
+ ulong SessionLifetimeVersion,
+ ulong PositionAuthorityVersion,
+ ulong CreateIntegrationVersion,
+ ulong SourcePlacementCommitVersion)
+{
+ internal bool IsValid => Entity.LocalEntityId != 0u
+ && LeaseId != 0UL
+ && PositionAuthorityVersion != 0UL
+ && CreateIntegrationVersion != 0UL;
+}
+
+///
+/// Exact, presentation-free initial CreateObject residence authority. The
+/// accepted wire frame remains on the canonical record while FullCell stays
+/// zero until the authored Runtime SetPosition operation commits.
+///
+internal readonly record struct RuntimeInitialCreateResidenceLease(
+ RuntimeInitialCreateResidenceToken Token,
+ RuntimeAuthoritativePositionRoute Route,
+ RuntimeEntityPlacementToken Placement,
+ ImmutableArray Continuations)
+{
+ internal bool IsValid => Token.IsValid
+ && Route.Accepted
+ && Route.Authority.Entity == Token.Entity
+ && !Continuations.IsDefault
+ && HasValidContinuationChain()
+ && (!Route.PerformsSetPosition
+ || Placement.IsValid
+ && Placement.Entity == Token.Entity);
+
+ private bool HasValidContinuationChain()
+ {
+ ushort previousTeleport = Route.Authority.AcceptedTeleportSequence;
+ for (int index = 0; index < Continuations.Length; index++)
+ {
+ RuntimeInitialCreateResidenceContinuation continuation =
+ Continuations[index];
+ if (!continuation.IsValid
+ || continuation.Sequence != (ulong)index + 1UL
+ || continuation.InstanceSequence != Token.Entity.Incarnation
+ || continuation.PositionAuthorityVersion
+ != Token.PositionAuthorityVersion
+ || continuation.PreviousTeleportSequence
+ != previousTeleport)
+ {
+ return false;
+ }
+ previousTeleport = continuation.AcceptedTeleportSequence;
+ }
+ return true;
+ }
+}
+
+///
+/// One accepted Position packet which arrived before the immutable initial
+/// CreateObject admission completed. These records remain dormant until a
+/// host adopts the completed batch; accepting them never replaces or mutates
+/// the initial 0x11 SetPosition operation.
+///
+internal readonly record struct RuntimeInitialCreateResidenceContinuation(
+ ulong Sequence,
+ RuntimePositionEntityKind EntityKind,
+ ushort InstanceSequence,
+ ushort PositionSequence,
+ ushort PreviousTeleportSequence,
+ ushort TeleportSequence,
+ ushort ForcePositionSequence,
+ ushort AcceptedTeleportSequence,
+ ushort AcceptedForcePositionSequence,
+ PositionTimestampDisposition TimestampDisposition,
+ CreateObject.ServerPosition AcceptedWirePosition,
+ Vector3? PositionPackVelocity,
+ Vector3? AcceptedVelocity,
+ Quaternion? ForcePositionRotation,
+ Vector3? CurrentLocalVelocity,
+ bool ProjectionRequiresTeleportHook,
+ uint PlacementFrame,
+ ulong PositionAuthorityVersion)
+{
+ internal bool IsValid => Sequence != 0UL
+ && EntityKind is RuntimePositionEntityKind.LocalPlayer
+ or RuntimePositionEntityKind.Remote
+ or RuntimePositionEntityKind.Projectile
+ && PositionAuthorityVersion != 0UL
+ && TimestampDisposition is PositionTimestampDisposition.Apply
+ or PositionTimestampDisposition.ForcePosition;
+}
+
+internal readonly record struct RuntimeInitialCreateResidenceAdoptionToken(
+ RuntimeEntityKey Entity,
+ ulong LeaseId,
+ ulong AdoptionId,
+ ulong SessionLifetimeVersion,
+ ulong Revision)
+{
+ internal bool IsValid => Entity.LocalEntityId != 0u
+ && LeaseId != 0UL
+ && AdoptionId != 0UL
+ && Revision != 0UL;
+}
+
+internal enum RuntimeInitialCreateResidenceCompletionStatus : byte
+{
+ Completed,
+ PendingPlacement,
+ RejectedToken,
+ RejectedAuthority,
+}
+
+///
+/// Exact post-residence receipt. A local graphical or no-window host may run
+/// the retail after-enter teleport suffix only when this receipt carries
+/// .
+///
+internal readonly record struct RuntimeInitialCreateResidenceReceipt(
+ RuntimeInitialCreateResidenceToken Token,
+ RuntimeTeleportHookPhase TeleportHookPhase,
+ RuntimePlacementProjectionToken Projection,
+ uint FullCellId,
+ ulong PlacementCommitVersion,
+ RuntimeInitialCreateResidenceAdoptionToken Adoption,
+ ImmutableArray Continuations);
+
+internal readonly record struct RuntimeInitialCreateResidenceOwnershipSnapshot(
+ int ActiveLeaseCount,
+ int PendingAdoptionCount,
+ ulong LastLeaseId)
+{
+ internal bool IsConverged => ActiveLeaseCount == 0
+ && PendingAdoptionCount == 0;
+}
+
+///
+/// Owns only initial CreateObject residence leases. DAT lookup, body creation,
+/// and presentation stay outside this owner; their immutable preparation is
+/// submitted through the lease's canonical Runtime SetPosition token.
+///
+internal sealed class RuntimeInitialCreateResidenceState
+{
+ private sealed class Entry
+ {
+ internal required RuntimeEntityRecord Record { get; init; }
+ internal required RuntimeInitialCreateResidenceLease Lease { get; set; }
+ }
+
+ private sealed class CompletedEntry
+ {
+ internal required RuntimeEntityRecord Record { get; init; }
+ internal required RuntimeInitialCreateResidenceLease Lease { get; set; }
+ internal required RuntimeInitialCreateResidenceReceipt Receipt { get; set; }
+ }
+
+ private readonly RuntimeEntityDirectory _entities;
+ private readonly RuntimeSetPositionState _setPosition;
+ private readonly Dictionary _entries = [];
+ private readonly Dictionary _completed = [];
+ private Func? _generation;
+ private ulong _nextLeaseId;
+
+ internal RuntimeInitialCreateResidenceState(
+ RuntimeEntityDirectory entities,
+ RuntimeSetPositionState setPosition)
+ {
+ _entities = entities ?? throw new ArgumentNullException(nameof(entities));
+ _setPosition = setPosition
+ ?? throw new ArgumentNullException(nameof(setPosition));
+ }
+
+ internal void BindGeneration(Func generation)
+ {
+ ArgumentNullException.ThrowIfNull(generation);
+ if (_generation is not null)
+ {
+ throw new InvalidOperationException(
+ "The initial Create residence generation source is already bound.");
+ }
+ _generation = generation;
+ }
+
+ internal bool CanAcceptCreate(WorldSession.EntitySpawn incoming)
+ {
+ bool parented = (incoming.ParentGuid
+ ?? incoming.Physics?.Parent?.Guid)
+ is not null and not 0u;
+ bool topLevel = !parented
+ && incoming.Position is { LandblockId: not 0u };
+ return CurrentGeneration().Value != 0UL
+ && _nextLeaseId != ulong.MaxValue
+ && (!topLevel
+ || _setPosition.CanBeginAuthoredPlacementSequence
+ && RuntimeAuthoritativePositionRouteClassifier
+ .IsValidCreateWirePosition(
+ incoming.Position!.Value));
+ }
+
+ internal RuntimeInitialCreateResidenceLease Begin(
+ RuntimeEntityRecord record,
+ in InboundCreateResult accepted,
+ bool isLocalPlayer)
+ {
+ ArgumentNullException.ThrowIfNull(record);
+ if (!_entities.IsCurrent(record)
+ || record.Key is not { } key
+ || record.FullCellId != 0u
+ || accepted.Snapshot.Guid != record.ServerGuid
+ || accepted.Snapshot.InstanceSequence != record.Incarnation)
+ {
+ return default;
+ }
+
+ RuntimeGenerationToken generation = CurrentGeneration();
+ RuntimePositionEntityKind entityKind = isLocalPlayer
+ ? RuntimePositionEntityKind.LocalPlayer
+ : (record.FinalPhysicsState & PhysicsStateFlags.Missile) != 0
+ ? RuntimePositionEntityKind.Projectile
+ : RuntimePositionEntityKind.Remote;
+ WorldSession.EntitySpawn snapshot = accepted.Snapshot;
+ // PhysicsDesc parent ownership precedes its optional position frame in
+ // retail set_description. A parented Create is not a top-level world
+ // admission merely because ACE also supplied a position payload.
+ RuntimeCreateResidenceKind residence =
+ (snapshot.ParentGuid ?? snapshot.Physics?.Parent?.Guid)
+ is not null and not 0u
+ ? RuntimeCreateResidenceKind.Parented
+ : snapshot.Position is { LandblockId: not 0u }
+ ? RuntimeCreateResidenceKind.TopLevel
+ : RuntimeCreateResidenceKind.PickedUp;
+ var authority = new RuntimeAuthoritativePositionAuthority(
+ generation,
+ key,
+ record.PositionAuthorityVersion,
+ snapshot.PositionSequence,
+ accepted.Timestamps.Teleport,
+ accepted.Timestamps.Teleport,
+ PositionTimestampDisposition.Apply);
+ RuntimeAuthoritativePositionRoute route =
+ RuntimeAuthoritativePositionRouteClassifier.ClassifyCreate(
+ new RuntimeCreatePositionRouteRequest(
+ authority,
+ entityKind,
+ residence,
+ snapshot.Position,
+ new RuntimePositionPlacementFacts(
+ record.FinalPhysicsState,
+ HasAuthoredMoverShape: snapshot.SetupTableId is not null)));
+ return Own(record, route);
+ }
+
+ internal RuntimeInitialCreateResidenceLease EnqueueAcceptedPosition(
+ RuntimeEntityRecord record,
+ in RuntimeInitialCreateResidenceLease prior,
+ WorldSession.EntityPositionUpdate update,
+ in WorldSession.EntitySpawn accepted,
+ PositionTimestampDisposition disposition,
+ in AcceptedPhysicsTimestamps timestamps,
+ bool isLocalPlayer,
+ Quaternion? forcePositionRotation,
+ Vector3? currentLocalVelocity,
+ bool projectionRequiresTeleportHook)
+ {
+ ArgumentNullException.ThrowIfNull(record);
+ if (!_entities.IsCurrent(record)
+ || record.Key is not { } key
+ || update.Guid != record.ServerGuid
+ || accepted.Guid != record.ServerGuid
+ || disposition is PositionTimestampDisposition.Rejected)
+ {
+ return default;
+ }
+
+ RuntimeInitialCreateResidenceLease current;
+ Entry? active = null;
+ CompletedEntry? completed = null;
+ if (_entries.TryGetValue(key, out active)
+ && ReferenceEquals(active.Record, record)
+ && active.Lease.Token == prior.Token
+ && IsCurrent(active))
+ {
+ current = active.Lease;
+ }
+ else if (_completed.TryGetValue(key, out completed)
+ && ReferenceEquals(completed.Record, record)
+ && completed.Lease.Token == prior.Token
+ && IsCompletedCurrent(completed))
+ {
+ current = completed.Lease;
+ }
+ else
+ {
+ return default;
+ }
+ if (current.Continuations.Length == int.MaxValue
+ || completed is not null
+ && completed.Receipt.Adoption.Revision == ulong.MaxValue)
+ return default;
+
+ RuntimePositionEntityKind entityKind = isLocalPlayer
+ ? RuntimePositionEntityKind.LocalPlayer
+ : (record.FinalPhysicsState & PhysicsStateFlags.Missile) != 0
+ ? RuntimePositionEntityKind.Projectile
+ : RuntimePositionEntityKind.Remote;
+ CreateObject.ServerPosition acceptedPosition =
+ accepted.Position ?? update.Position;
+ ushort previousTeleport = current.Continuations.IsEmpty
+ ? current.Route.Authority.AcceptedTeleportSequence
+ : current.Continuations[^1].AcceptedTeleportSequence;
+ ulong sequence = (ulong)current.Continuations.Length + 1UL;
+ var continuation = new RuntimeInitialCreateResidenceContinuation(
+ sequence,
+ entityKind,
+ update.InstanceSequence,
+ update.PositionSequence,
+ previousTeleport,
+ update.TeleportSequence,
+ update.ForcePositionSequence,
+ timestamps.Teleport,
+ timestamps.ForcePosition,
+ disposition,
+ acceptedPosition,
+ update.Velocity,
+ accepted.Physics?.Velocity,
+ forcePositionRotation,
+ currentLocalVelocity,
+ projectionRequiresTeleportHook,
+ accepted.PlacementId ?? 0u,
+ record.PositionAuthorityVersion);
+ if (!continuation.IsValid)
+ return default;
+
+ RuntimeInitialCreateResidenceLease revised = current with
+ {
+ Continuations = current.Continuations.Add(continuation),
+ };
+ if (active is not null)
+ {
+ active.Lease = revised;
+ }
+ else
+ {
+ completed!.Lease = revised;
+ RuntimeInitialCreateResidenceAdoptionToken revisedAdoption =
+ completed.Receipt.Adoption with
+ {
+ Revision = completed.Receipt.Adoption.Revision + 1UL,
+ };
+ completed.Receipt = completed.Receipt with
+ {
+ Adoption = revisedAdoption,
+ Continuations = revised.Continuations,
+ };
+ }
+ return revised;
+ }
+
+ internal bool CanEnqueueAcceptedPosition(
+ RuntimeEntityRecord record,
+ in RuntimeInitialCreateResidenceLease prior,
+ in WorldSession.EntityPositionUpdate update)
+ {
+ ArgumentNullException.ThrowIfNull(record);
+ if (record.Key is not { } key
+ || update.Guid != record.ServerGuid
+ || !RuntimeAuthoritativePositionRouteClassifier
+ .IsValidCreateWirePosition(update.Position))
+ {
+ return false;
+ }
+ if (_entries.TryGetValue(key, out Entry? entry))
+ {
+ return ReferenceEquals(entry.Record, record)
+ && entry.Lease.Token == prior.Token
+ && IsCurrent(entry)
+ && entry.Lease.Continuations.Length < int.MaxValue;
+ }
+ return _completed.TryGetValue(key, out CompletedEntry? completed)
+ && ReferenceEquals(completed.Record, record)
+ && completed.Lease.Token == prior.Token
+ && IsCompletedCurrent(completed)
+ && completed.Lease.Continuations.Length < int.MaxValue
+ && completed.Receipt.Adoption.Revision < ulong.MaxValue;
+ }
+
+ internal bool TryGetTransaction(
+ RuntimeEntityRecord record,
+ out RuntimeInitialCreateResidenceLease lease)
+ {
+ if (TryGetCurrent(record, out lease))
+ return true;
+ if (record.Key is { } key
+ && _completed.TryGetValue(key, out CompletedEntry? completed)
+ && ReferenceEquals(completed.Record, record))
+ {
+ if (IsCompletedCurrent(completed))
+ {
+ lease = completed.Lease;
+ return true;
+ }
+ Retire(completed);
+ }
+ lease = default;
+ return false;
+ }
+
+ private RuntimeInitialCreateResidenceLease Own(
+ RuntimeEntityRecord record,
+ in RuntimeAuthoritativePositionRoute route)
+ {
+ RuntimeEntityKey key = record.Key!.Value;
+ if (_entries.ContainsKey(key)
+ || _nextLeaseId == ulong.MaxValue)
+ {
+ return default;
+ }
+ ulong leaseId = _nextLeaseId + 1UL;
+
+ RuntimeEntityPlacementToken placement = default;
+ if (route.PerformsSetPosition)
+ {
+ placement = _setPosition.TryBeginExclusiveAuthoredPlacement(
+ record,
+ record.PositionAuthorityVersion,
+ route.OperationKind);
+ if (!placement.IsValid)
+ return default;
+ if (!_setPosition.WatchPlacementCompletion(placement))
+ {
+ _ = _setPosition.ForgetExactPlacement(placement);
+ return default;
+ }
+ }
+ else if (!route.Accepted)
+ {
+ return default;
+ }
+
+ var token = new RuntimeInitialCreateResidenceToken(
+ key,
+ leaseId,
+ _entities.SessionLifetimeVersion,
+ record.PositionAuthorityVersion,
+ record.CreateIntegrationVersion,
+ record.PlacementCommitVersion);
+ var lease = new RuntimeInitialCreateResidenceLease(
+ token,
+ route,
+ placement,
+ ImmutableArray.Empty);
+ _entries.Add(key, new Entry
+ {
+ Record = record,
+ Lease = lease,
+ });
+ _nextLeaseId = leaseId;
+ return lease;
+ }
+
+ internal bool TryGetCurrent(
+ RuntimeEntityRecord record,
+ out RuntimeInitialCreateResidenceLease lease)
+ {
+ ArgumentNullException.ThrowIfNull(record);
+ if (record.Key is { } key
+ && _entries.TryGetValue(key, out Entry? entry)
+ && ReferenceEquals(entry.Record, record))
+ {
+ if (IsCurrent(entry))
+ {
+ lease = entry.Lease;
+ return true;
+ }
+ Retire(entry);
+ }
+ lease = default;
+ return false;
+ }
+
+ internal RuntimeInitialCreateResidenceCompletionStatus Complete(
+ RuntimeEntityRecord record,
+ in RuntimeInitialCreateResidenceToken token,
+ out RuntimeInitialCreateResidenceReceipt receipt)
+ {
+ ArgumentNullException.ThrowIfNull(record);
+ receipt = default;
+ if (token.IsValid
+ && _completed.TryGetValue(token.Entity, out CompletedEntry? completed)
+ && completed.Receipt.Token == token
+ && ReferenceEquals(completed.Record, record))
+ {
+ if (IsCompletedCurrent(completed))
+ {
+ receipt = completed.Receipt;
+ return RuntimeInitialCreateResidenceCompletionStatus.Completed;
+ }
+ Retire(completed);
+ return RuntimeInitialCreateResidenceCompletionStatus
+ .RejectedAuthority;
+ }
+ if (!token.IsValid
+ || !_entries.TryGetValue(token.Entity, out Entry? entry)
+ || entry.Lease.Token != token
+ || !ReferenceEquals(entry.Record, record))
+ {
+ return RuntimeInitialCreateResidenceCompletionStatus.RejectedToken;
+ }
+ if (!IsCurrent(entry))
+ {
+ Retire(entry);
+ return RuntimeInitialCreateResidenceCompletionStatus
+ .RejectedAuthority;
+ }
+
+ RuntimeInitialCreateResidenceLease lease = entry.Lease;
+ RuntimePlacementProjectionToken projection = default;
+ if (lease.Route.PerformsSetPosition)
+ {
+ if (_setPosition.IsPlacementCurrent(lease.Placement))
+ {
+ return RuntimeInitialCreateResidenceCompletionStatus
+ .PendingPlacement;
+ }
+ if (!_setPosition.TryPeekAcknowledgedPlacement(
+ lease.Placement,
+ out projection)
+ || projection.Entity != token.Entity
+ || projection.PositionAuthorityVersion
+ != token.PositionAuthorityVersion
+ || projection.SessionLifetimeVersion
+ != token.SessionLifetimeVersion
+ || projection.ExactCellId == 0u
+ || projection.ExactCellId != record.FullCellId
+ || projection.PlacementCommitVersion
+ <= token.SourcePlacementCommitVersion
+ || projection.PlacementCommitVersion
+ != record.PlacementCommitVersion)
+ {
+ Retire(entry);
+ return RuntimeInitialCreateResidenceCompletionStatus
+ .RejectedAuthority;
+ }
+ }
+ else if (record.FullCellId != 0u)
+ {
+ Retire(entry);
+ return RuntimeInitialCreateResidenceCompletionStatus
+ .RejectedAuthority;
+ }
+
+ var adoption = new RuntimeInitialCreateResidenceAdoptionToken(
+ token.Entity,
+ token.LeaseId,
+ token.LeaseId,
+ token.SessionLifetimeVersion,
+ Revision: 1UL);
+ receipt = new RuntimeInitialCreateResidenceReceipt(
+ token,
+ lease.Route.TeleportHookPhase,
+ projection,
+ record.FullCellId,
+ record.PlacementCommitVersion,
+ adoption,
+ lease.Continuations);
+ _entries.Remove(token.Entity);
+ _completed.Add(token.Entity, new CompletedEntry
+ {
+ Record = record,
+ Lease = lease,
+ Receipt = receipt,
+ });
+ return RuntimeInitialCreateResidenceCompletionStatus.Completed;
+ }
+
+ internal bool AcknowledgeAdoption(
+ RuntimeEntityRecord record,
+ in RuntimeInitialCreateResidenceAdoptionToken token)
+ {
+ ArgumentNullException.ThrowIfNull(record);
+ if (!token.IsValid)
+ return false;
+ if (!_completed.TryGetValue(token.Entity, out CompletedEntry? current)
+ || !ReferenceEquals(current.Record, record)
+ || current.Receipt.Adoption != token)
+ {
+ return false;
+ }
+ if (!IsCompletedCurrent(current))
+ {
+ Retire(current);
+ return false;
+ }
+ if (current.Lease.Route.PerformsSetPosition
+ && !_setPosition.ConsumeAcknowledgedPlacement(
+ current.Lease.Placement,
+ current.Receipt.Projection))
+ {
+ return false;
+ }
+ return _completed.Remove(token.Entity);
+ }
+
+ internal bool Forget(
+ RuntimeEntityRecord record,
+ out RuntimeInitialCreateResidenceLease lease,
+ out RuntimePlacementCancellationReceipt cancellation)
+ {
+ ArgumentNullException.ThrowIfNull(record);
+ cancellation = default;
+ if (record.Key is { } key
+ && _entries.TryGetValue(key, out Entry? entry)
+ && ReferenceEquals(entry.Record, record)
+ && _entries.Remove(key))
+ {
+ lease = entry.Lease;
+ cancellation = _setPosition.ForgetExactPlacement(
+ lease.Placement);
+ return true;
+ }
+ if (record.Key is { } completedKey
+ && _completed.TryGetValue(
+ completedKey,
+ out CompletedEntry? completed)
+ && ReferenceEquals(completed.Record, record)
+ && _completed.Remove(completedKey))
+ {
+ lease = completed.Lease;
+ cancellation = _setPosition.ForgetExactPlacement(
+ lease.Placement);
+ return true;
+ }
+ lease = default;
+ return false;
+ }
+
+ internal void Clear()
+ {
+ Entry[] active = _entries.Values.ToArray();
+ CompletedEntry[] completed = _completed.Values.ToArray();
+ var cancellations = new RuntimePlacementCancellationReceipt[
+ active.Length + completed.Length];
+ _entries.Clear();
+ _completed.Clear();
+ int cancellationCount = 0;
+ foreach (Entry entry in active)
+ {
+ RuntimePlacementCancellationReceipt cancellation =
+ _setPosition.ForgetExactPlacement(
+ entry.Lease.Placement);
+ if (cancellation.IsValid)
+ cancellations[cancellationCount++] = cancellation;
+ }
+ foreach (CompletedEntry entry in completed)
+ {
+ RuntimePlacementCancellationReceipt cancellation =
+ _setPosition.ForgetExactPlacement(
+ entry.Lease.Placement);
+ if (cancellation.IsValid)
+ cancellations[cancellationCount++] = cancellation;
+ }
+ for (int index = 0; index < cancellationCount; index++)
+ {
+ _setPosition.PublishCancellation(cancellations[index]);
+ }
+ }
+
+ internal RuntimeInitialCreateResidenceOwnershipSnapshot CaptureOwnership() =>
+ new(_entries.Count, _completed.Count, _nextLeaseId);
+
+ private bool IsCurrent(Entry entry)
+ {
+ RuntimeInitialCreateResidenceToken token = entry.Lease.Token;
+ bool placementCurrent = !entry.Lease.Route.PerformsSetPosition
+ || _setPosition.IsPlacementCompletionTracked(
+ entry.Lease.Placement);
+ return placementCurrent
+ && _entities.IsCurrent(entry.Record)
+ && entry.Record.Key == token.Entity
+ && _entities.SessionLifetimeVersion
+ == token.SessionLifetimeVersion
+ && entry.Record.PositionAuthorityVersion
+ == token.PositionAuthorityVersion
+ && entry.Record.CreateIntegrationVersion
+ == token.CreateIntegrationVersion
+ && entry.Lease.Route.Authority.Generation
+ == CurrentGeneration();
+ }
+
+ private RuntimeGenerationToken CurrentGeneration()
+ {
+ return _generation?.Invoke() ?? default;
+ }
+
+ private bool IsCompletedCurrent(CompletedEntry entry)
+ {
+ RuntimeInitialCreateResidenceReceipt receipt = entry.Receipt;
+ return _entities.IsCurrent(entry.Record)
+ && entry.Record.Key == receipt.Token.Entity
+ && _entities.SessionLifetimeVersion
+ == receipt.Token.SessionLifetimeVersion
+ && entry.Record.PositionAuthorityVersion
+ == receipt.Token.PositionAuthorityVersion
+ && entry.Record.CreateIntegrationVersion
+ == receipt.Token.CreateIntegrationVersion
+ && entry.Record.FullCellId == receipt.FullCellId
+ && entry.Record.PlacementCommitVersion
+ == receipt.PlacementCommitVersion
+ && entry.Lease.Route.Authority.Generation
+ == CurrentGeneration()
+ && receipt.Token.SessionLifetimeVersion
+ == receipt.Adoption.SessionLifetimeVersion
+ && receipt.Token.LeaseId == receipt.Adoption.LeaseId
+ && (!entry.Lease.Route.PerformsSetPosition
+ || _setPosition.IsPlacementCompletionTracked(
+ entry.Lease.Placement));
+ }
+
+ private void Retire(Entry entry)
+ {
+ _entries.Remove(entry.Lease.Token.Entity);
+ RuntimePlacementCancellationReceipt cancellation =
+ _setPosition.ForgetExactPlacement(entry.Lease.Placement);
+ _setPosition.PublishCancellation(cancellation);
+ }
+
+ private void Retire(CompletedEntry entry)
+ {
+ _completed.Remove(entry.Receipt.Token.Entity);
+ RuntimePlacementCancellationReceipt cancellation =
+ _setPosition.ForgetExactPlacement(entry.Lease.Placement);
+ _setPosition.PublishCancellation(cancellation);
+ }
+}
diff --git a/src/AcDream.Runtime/Physics/RuntimeAuthoritativePositionRouteClassifier.cs b/src/AcDream.Runtime/Physics/RuntimeAuthoritativePositionRouteClassifier.cs
index ab849c48..28c70f8e 100644
--- a/src/AcDream.Runtime/Physics/RuntimeAuthoritativePositionRouteClassifier.cs
+++ b/src/AcDream.Runtime/Physics/RuntimeAuthoritativePositionRouteClassifier.cs
@@ -180,6 +180,9 @@ internal static class RuntimeAuthoritativePositionRouteClassifier
| PhysicsSetPositionFlags.Slide
| PhysicsSetPositionFlags.SendPositionEvent;
+ internal static bool IsValidCreateWirePosition(
+ in CreateObject.ServerPosition position) => ValidPosition(position);
+
internal static RuntimeAuthoritativePositionRoute ClassifyCreate(
in RuntimeCreatePositionRouteRequest request)
{
@@ -205,7 +208,10 @@ internal static class RuntimeAuthoritativePositionRouteClassifier
0u,
UnparentBeforeRouting: false,
ApplyPlacementFrameBeforeRouting: false,
- LeaveWorld: true,
+ // A fresh cellless CreateObject has never entered world.
+ // Parent composition and later pickup events own their own
+ // callbacks; initial residence invents no withdrawal edge.
+ LeaveWorld: false,
TeleportHookPhase: RuntimeTeleportHookPhase.None,
StopInterpolating: false,
ConstrainAfterRouting: false,
@@ -294,7 +300,7 @@ internal static class RuntimeAuthoritativePositionRouteClassifier
{
return new RuntimeAuthoritativePositionRoute(
request.Authority,
- RuntimeAuthoritativePositionDisposition.SetPosition,
+ RuntimeAuthoritativePositionDisposition.SetPositionSimple,
operation,
AuthoritativeTeleportFlags,
placement,
diff --git a/src/AcDream.Runtime/Physics/RuntimeSetPositionState.cs b/src/AcDream.Runtime/Physics/RuntimeSetPositionState.cs
index 86aae1d4..4f6dc5bb 100644
--- a/src/AcDream.Runtime/Physics/RuntimeSetPositionState.cs
+++ b/src/AcDream.Runtime/Physics/RuntimeSetPositionState.cs
@@ -250,6 +250,8 @@ internal readonly record struct RuntimeSetPositionOwnershipSnapshot(
int UnboundDeferredCellOrderCount,
int PreparedMoverCount,
int MoverPreparationAuthorityCount,
+ int PlacementCompletionWatchCount,
+ int AcknowledgedPlacementCompletionCount,
int CollisionPrefixQuiescenceCount,
int PendingQuiescenceProjectionCount)
{
@@ -276,6 +278,8 @@ internal readonly record struct RuntimeSetPositionOwnershipSnapshot(
&& UnboundDeferredCellOrderCount == 0
&& PreparedMoverCount == 0
&& MoverPreparationAuthorityCount == 0
+ && PlacementCompletionWatchCount == 0
+ && AcknowledgedPlacementCompletionCount == 0
&& CollisionPrefixQuiescenceCount == 0
&& PendingQuiescenceProjectionCount == 0;
}
@@ -414,6 +418,10 @@ internal sealed class RuntimeSetPositionState : IDisposable
_preparedMovers = [];
private readonly Dictionary
_moverPreparationAuthorities = [];
+ private readonly HashSet
+ _placementCompletionWatches = [];
+ private readonly Dictionary _acknowledgedPlacementCompletions = [];
private readonly Dictionary
_collisionPrefixQuiescence = [];
private readonly LinkedList _expiredLostCells = [];
@@ -477,11 +485,15 @@ internal sealed class RuntimeSetPositionState : IDisposable
_unboundDeferredCellOrder.Count,
_preparedMovers.Count,
_moverPreparationAuthorities.Count,
+ _placementCompletionWatches.Count,
+ _acknowledgedPlacementCompletions.Count,
_collisionPrefixQuiescence.Count,
pendingQuiescenceProjections);
}
internal int PendingProjectionCount => _pendingProjection.Count;
+ internal bool CanBeginAuthoredPlacementSequence =>
+ _nextOperationId != ulong.MaxValue;
internal bool IsCollisionPrefixQuiescing(uint landblockId) =>
_collisionPrefixQuiescence.ContainsKey(
@@ -812,6 +824,111 @@ internal sealed class RuntimeSetPositionState : IDisposable
portal,
captureMoverPreparationAuthority: true);
+ internal bool IsPlacementCurrent(
+ in RuntimeEntityPlacementToken token)
+ {
+ EnsureNotDisposed();
+ return token.IsValid
+ && _operations.TryGetValue(token.Entity, out Operation? operation)
+ && operation.Token == token
+ && IsCurrent(operation);
+ }
+
+ ///
+ /// Reserves the exact acknowledgement edge for a higher-level Runtime
+ /// transaction. Ordinary SetPosition operations retain no completion
+ /// history; only explicitly watched tokens survive operation retirement.
+ ///
+ internal bool WatchPlacementCompletion(
+ in RuntimeEntityPlacementToken token)
+ {
+ EnsureNotDisposed();
+ return IsPlacementCurrent(token)
+ && _placementCompletionWatches.Add(token);
+ }
+
+ internal bool IsPlacementCompletionTracked(
+ in RuntimeEntityPlacementToken token)
+ {
+ EnsureNotDisposed();
+ return token.IsValid
+ && (IsPlacementCurrent(token)
+ && _placementCompletionWatches.Contains(token)
+ || _acknowledgedPlacementCompletions.ContainsKey(token));
+ }
+
+ internal bool TryPeekAcknowledgedPlacement(
+ in RuntimeEntityPlacementToken token,
+ out RuntimePlacementProjectionToken projection)
+ {
+ EnsureNotDisposed();
+ if (token.IsValid
+ && _acknowledgedPlacementCompletions.TryGetValue(
+ token,
+ out projection))
+ {
+ return true;
+ }
+ projection = default;
+ return false;
+ }
+
+ internal bool ConsumeAcknowledgedPlacement(
+ in RuntimeEntityPlacementToken token,
+ in RuntimePlacementProjectionToken expected)
+ {
+ EnsureNotDisposed();
+ return token.IsValid
+ && _acknowledgedPlacementCompletions.TryGetValue(
+ token,
+ out RuntimePlacementProjectionToken current)
+ && current == expected
+ && _acknowledgedPlacementCompletions.Remove(token);
+ }
+
+ internal void ForgetPlacementCompletion(
+ in RuntimeEntityPlacementToken token)
+ {
+ EnsureNotDisposed();
+ ForgetPlacementCompletionCore(token);
+ }
+
+ internal RuntimePlacementCancellationReceipt ForgetExactPlacement(
+ in RuntimeEntityPlacementToken token)
+ {
+ EnsureNotDisposed();
+ ForgetPlacementCompletionCore(token);
+ if (!token.IsValid
+ || !_operations.TryGetValue(token.Entity, out Operation? operation)
+ || operation.Token != token)
+ {
+ return default;
+ }
+ return CancelCore(operation);
+ }
+
+ internal RuntimeEntityPlacementToken TryBeginExclusiveAuthoredPlacement(
+ RuntimeEntityRecord record,
+ ulong expectedPositionAuthorityVersion,
+ RuntimeSetPositionOperationKind kind,
+ RuntimePortalPlacementAuthority portal = default)
+ {
+ EnsureNotDisposed();
+ ArgumentNullException.ThrowIfNull(record);
+ if (record.Key is not { } key
+ || _operations.ContainsKey(key)
+ || HasRetainedCompletion(key))
+ {
+ return default;
+ }
+ return BeginAcceptedPlacementCore(
+ record,
+ expectedPositionAuthorityVersion,
+ kind,
+ portal,
+ captureMoverPreparationAuthority: true);
+ }
+
internal void PrepareDormantLocalActivationOwnership(
RuntimeEntityRecord record,
PhysicsBody body,
@@ -853,6 +970,7 @@ internal sealed class RuntimeSetPositionState : IDisposable
record.Snapshot.Physics?.Position ?? record.Snapshot.Position;
if (record.Key is not { } key
|| !_entities.IsCurrent(record)
+ || HasRetainedCompletion(key)
|| record.PositionAuthorityVersion
!= expectedPositionAuthorityVersion
|| (captureMoverPreparationAuthority
@@ -947,6 +1065,17 @@ internal sealed class RuntimeSetPositionState : IDisposable
return IsCurrent(replacement) ? token : default;
}
+ private bool HasRetainedCompletion(RuntimeEntityKey key)
+ {
+ foreach (RuntimeEntityPlacementToken token
+ in _acknowledgedPlacementCompletions.Keys)
+ {
+ if (token.Entity == key)
+ return true;
+ }
+ return false;
+ }
+
internal RuntimeSetPositionMoverPreparationStatus PrepareMover(
in RuntimeEntityPlacementToken token,
in RuntimeSetPositionMoverPreparation preparation,
@@ -2206,6 +2335,12 @@ internal sealed class RuntimeSetPositionState : IDisposable
operation.ProjectionSequence = 0UL;
if (pending.Kind is RuntimePlacementProjectionKind.Place)
{
+ if (_placementCompletionWatches.Remove(operation.Token))
+ {
+ _acknowledgedPlacementCompletions.Add(
+ operation.Token,
+ pending.Token);
+ }
operation.Stage = RuntimeEntityPlacementStage.AwaitingCommitAcknowledgement;
_moverPreparationAuthorities.Remove(operation.Key);
return _operations.Remove(operation.Key);
@@ -3041,6 +3176,8 @@ internal sealed class RuntimeSetPositionState : IDisposable
_lostDeadlineNodeIndex.Clear();
_preparedMovers.Clear();
_moverPreparationAuthorities.Clear();
+ _placementCompletionWatches.Clear();
+ _acknowledgedPlacementCompletions.Clear();
_collisionPrefixQuiescence.Clear();
_pendingProjection.Clear();
_expiredLostCells.Clear();
@@ -3859,6 +3996,7 @@ internal sealed class RuntimeSetPositionState : IDisposable
CancelExactLostKey(key);
if (!_operations.Remove(key, out Operation? operation))
return false;
+ ForgetPlacementCompletionCore(operation.Token);
_moverPreparationAuthorities.Remove(key);
UnindexDeferred(operation);
if (!preserveLostFamily && cancelLostFamily)
@@ -3929,6 +4067,15 @@ internal sealed class RuntimeSetPositionState : IDisposable
: default;
}
+ private void ForgetPlacementCompletionCore(
+ in RuntimeEntityPlacementToken token)
+ {
+ if (!token.IsValid)
+ return;
+ _placementCompletionWatches.Remove(token);
+ _acknowledgedPlacementCompletions.Remove(token);
+ }
+
private void IndexDeferred(Operation operation)
{
if (operation.ExactCellId == 0u
diff --git a/tests/AcDream.Core.Tests/Physics/MotionSequenceGateTests.cs b/tests/AcDream.Core.Tests/Physics/MotionSequenceGateTests.cs
index 8941dcf3..e8562f3d 100644
--- a/tests/AcDream.Core.Tests/Physics/MotionSequenceGateTests.cs
+++ b/tests/AcDream.Core.Tests/Physics/MotionSequenceGateTests.cs
@@ -118,6 +118,35 @@ public class PhysicsTimestampGateTests
Assert.True(gate.TryAcceptMovementEvent(4, 0x9011, 2));
}
+ [Fact]
+ public void PreviewCreateObject_MatchesSeedDispositionWithoutMutation()
+ {
+ var gate = new PhysicsTimestampGate();
+
+ Assert.Equal(CreateObjectTimestampDisposition.InitialGeneration,
+ gate.PreviewCreateObject(instance: 7));
+ Assert.Equal(CreateObjectTimestampDisposition.InitialGeneration,
+ gate.SeedForCreateObject(10, 20, 30, 40, 50, 60, 70, 80, 7));
+
+ Assert.Equal(CreateObjectTimestampDisposition.ExistingGeneration,
+ gate.PreviewCreateObject(instance: 7));
+ Assert.Equal(CreateObjectTimestampDisposition.NewGeneration,
+ gate.PreviewCreateObject(instance: 8));
+ Assert.Equal(CreateObjectTimestampDisposition.StaleGeneration,
+ gate.PreviewCreateObject(instance: 6));
+
+ // Preview is observational: all timestamp channels still compare
+ // against the original instance-seven seed afterward.
+ Assert.True(gate.TryAcceptMovementEvent(
+ instance: 7,
+ movement: 21,
+ serverControlledMove: 60));
+ Assert.False(gate.TryAcceptMovementEvent(
+ instance: 8,
+ movement: 22,
+ serverControlledMove: 60));
+ }
+
[Fact]
public void SameGenerationCreateObject_MixedChannelsAreAcceptedIndependently()
{
diff --git a/tests/AcDream.Runtime.Tests/Entities/RuntimeInitialCreateResidenceStateTests.cs b/tests/AcDream.Runtime.Tests/Entities/RuntimeInitialCreateResidenceStateTests.cs
new file mode 100644
index 00000000..1ebebabb
--- /dev/null
+++ b/tests/AcDream.Runtime.Tests/Entities/RuntimeInitialCreateResidenceStateTests.cs
@@ -0,0 +1,1792 @@
+using System.Collections.Immutable;
+using System.Numerics;
+using AcDream.Core.Net;
+using AcDream.Core.Net.Messages;
+using AcDream.Core.Physics;
+using AcDream.Runtime.Entities;
+using AcDream.Runtime.Physics;
+
+namespace AcDream.Runtime.Tests.Entities;
+
+public sealed class RuntimeInitialCreateResidenceStateTests
+{
+ private const uint Landblock = 0xA9B40000u;
+ private const uint Cell = Landblock | 0x0001u;
+
+ [Theory]
+ [InlineData(true, false,
+ RuntimeSetPositionOperationKind.InitialLogin,
+ RuntimeTeleportHookPhase.AfterEnterWorld)]
+ [InlineData(false, false,
+ RuntimeSetPositionOperationKind.RemoteAuthoritative,
+ RuntimeTeleportHookPhase.None)]
+ [InlineData(false, true,
+ RuntimeSetPositionOperationKind.ProjectileAuthoritative,
+ RuntimeTeleportHookPhase.None)]
+ internal void TopLevelRegistrationOwnsExactCelllessLeaseBeforeObservers(
+ bool isLocalPlayer,
+ bool missile,
+ RuntimeSetPositionOperationKind expectedOperation,
+ RuntimeTeleportHookPhase expectedHook)
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ Bind(lifetime, 7UL);
+ RuntimeInitialCreateResidenceLease observed = default;
+ uint observedCell = uint.MaxValue;
+ using IDisposable subscription = lifetime.Events.Subscribe(
+ new EntityObserver(delta =>
+ {
+ if (delta.Change is not RuntimeEntityChange.Registered)
+ return;
+ Assert.True(lifetime.Entities.TryGetByLocalId(
+ delta.Entity.Identity.LocalEntityId,
+ out RuntimeEntityRecord record));
+ observedCell = record.FullCellId;
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ record,
+ out observed));
+ }));
+
+ RuntimeEntityRegistrationResult registration = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(0x70003001u, 1, missile: missile),
+ isLocalPlayer);
+ RuntimeEntityRecord canonical = registration.Canonical!;
+
+ Assert.Equal(0u, observedCell);
+ Assert.Equal(0u, canonical.FullCellId);
+ Assert.Equal(Cell, canonical.Snapshot.Position!.Value.LandblockId);
+ Assert.True(observed.IsValid);
+ Assert.Equal(observed.Token.Entity, observed.Route.Authority.Entity);
+ Assert.Equal(expectedOperation, observed.Route.OperationKind);
+ Assert.Equal(expectedHook, observed.Route.TeleportHookPhase);
+ Assert.Equal(0x11u, (uint)observed.Route.SetPositionFlags);
+ Assert.True(observed.Placement.IsValid);
+ Assert.Equal(1, lifetime.CaptureOwnership()
+ .InitialCreateResidenceLeaseCount);
+ }
+
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void FreshParentedAndPickedCreateRemainCelllessWithoutWithdrawal(
+ bool parented)
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ Bind(lifetime, 8UL);
+ RuntimeEntityRecord canonical = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(
+ 0x70003002u,
+ 1,
+ includePosition: false,
+ parentGuid: parented ? 0x70004000u : null),
+ isLocalPlayer: false)
+ .Canonical!;
+
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease lease));
+ Assert.Equal(0u, canonical.FullCellId);
+ Assert.False(lease.Placement.IsValid);
+ Assert.Equal(RuntimeAuthoritativePositionDisposition.AwaitFreshPosition,
+ lease.Route.Disposition);
+ Assert.False(lease.Route.LeaveWorld);
+ Assert.Equal(
+ RuntimeInitialCreateResidenceCompletionStatus.Completed,
+ lifetime.CompleteInitialCreateResidence(
+ canonical,
+ lease.Token,
+ out RuntimeInitialCreateResidenceReceipt receipt));
+ Assert.Equal(0u, receipt.FullCellId);
+ Assert.Equal(RuntimeTeleportHookPhase.None,
+ receipt.TeleportHookPhase);
+ Assert.True(lifetime.AcknowledgeInitialCreateResidenceAdoption(
+ canonical,
+ receipt.Adoption));
+ Assert.Equal(0, lifetime.CaptureOwnership()
+ .InitialCreateResidenceLeaseCount);
+ Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership()
+ .ActiveOperationCount);
+ }
+
+ [Fact]
+ public void MissingCellDefersExactLeaseWithoutCommittingWireResidence()
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ Bind(lifetime, 9UL);
+ RuntimeEntityRecord canonical = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(0x70003003u, 1, setupId: null),
+ isLocalPlayer: false)
+ .Canonical!;
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease lease));
+ AttachDormantBody(lifetime, canonical);
+ RuntimeSetPositionCommand command = Prepare(
+ lifetime,
+ lease,
+ RuntimeSetPositionMoverSetup.ResolvedAbsent);
+
+ RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition
+ .SubmitPreparedPlacement(lease.Placement, command);
+
+ Assert.Equal(RuntimeSetPositionStatus.DeferredCell, outcome.Status);
+ Assert.Equal(0u, canonical.FullCellId);
+ Assert.Equal(Cell, canonical.Snapshot.Position!.Value.LandblockId);
+ Assert.Equal(
+ RuntimeInitialCreateResidenceCompletionStatus.PendingPlacement,
+ lifetime.CompleteInitialCreateResidence(
+ canonical,
+ lease.Token,
+ out _));
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease retained));
+ Assert.Equal(lease, retained);
+ }
+
+ [Theory]
+ [InlineData(true, RuntimeTeleportHookPhase.AfterEnterWorld)]
+ [InlineData(false, RuntimeTeleportHookPhase.None)]
+ internal void CommittedAndAcknowledgedCreateYieldsExactEnterWorldReceipt(
+ bool isLocalPlayer,
+ RuntimeTeleportHookPhase expectedHook)
+ {
+ var engine = new PhysicsEngine { DataCache = new PhysicsDataCache() };
+ engine.AddLandblock(
+ Landblock,
+ new TerrainSurface(new byte[81], new float[256]),
+ Array.Empty(),
+ Array.Empty(),
+ worldOffsetX: 0f,
+ worldOffsetY: 0f);
+ using var lifetime = new RuntimeEntityObjectLifetime(engine);
+ Bind(lifetime, 10UL);
+ RuntimeEntityRecord canonical = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(0x70003004u, 1, setupId: null),
+ isLocalPlayer)
+ .Canonical!;
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease lease));
+ AttachDormantBody(lifetime, canonical);
+ RuntimeSetPositionCommand command = Prepare(
+ lifetime,
+ lease,
+ RuntimeSetPositionMoverSetup.ResolvedAbsent);
+
+ RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition
+ .SubmitPreparedPlacement(lease.Placement, command);
+
+ Assert.Equal(
+ RuntimeSetPositionStatus.CommittedHostAcknowledgementPending,
+ outcome.Status);
+ Assert.Equal(Cell, canonical.FullCellId);
+ Assert.Equal(
+ RuntimeInitialCreateResidenceCompletionStatus.PendingPlacement,
+ lifetime.CompleteInitialCreateResidence(
+ canonical,
+ lease.Token,
+ out _));
+ Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
+ outcome.Projection));
+ Assert.Equal(
+ RuntimeInitialCreateResidenceCompletionStatus.Completed,
+ lifetime.CompleteInitialCreateResidence(
+ canonical,
+ lease.Token,
+ out RuntimeInitialCreateResidenceReceipt receipt));
+ Assert.Equal(expectedHook, receipt.TeleportHookPhase);
+ Assert.Equal(Cell, receipt.FullCellId);
+ Assert.Equal(canonical.PlacementCommitVersion,
+ receipt.PlacementCommitVersion);
+ Assert.True(receipt.Adoption.IsValid);
+ Assert.Empty(receipt.Continuations);
+ Assert.Equal(
+ RuntimeInitialCreateResidenceCompletionStatus.Completed,
+ lifetime.CompleteInitialCreateResidence(
+ canonical,
+ lease.Token,
+ out RuntimeInitialCreateResidenceReceipt repeated));
+ Assert.Equal(receipt, repeated);
+ Assert.False(lifetime.Physics.SetPosition
+ .TryBeginExclusiveAuthoredPlacement(
+ canonical,
+ canonical.PositionAuthorityVersion,
+ RuntimeSetPositionOperationKind.RemoteAuthoritative)
+ .IsValid);
+ Assert.True(lifetime.AcknowledgeInitialCreateResidenceAdoption(
+ canonical,
+ receipt.Adoption));
+ RuntimeEntityPlacementToken afterAdoption = lifetime.Physics.SetPosition
+ .TryBeginExclusiveAuthoredPlacement(
+ canonical,
+ canonical.PositionAuthorityVersion,
+ RuntimeSetPositionOperationKind.RemoteAuthoritative);
+ Assert.True(afterAdoption.IsValid);
+ _ = lifetime.Physics.SetPosition.ForgetExactPlacement(afterAdoption);
+ Assert.False(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out _));
+ }
+
+ [Fact]
+ public void GenerationReplacementForgetsOldLeaseAndOwnsOnlySuccessor()
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ Bind(lifetime, 11UL);
+ RuntimeEntityRecord first = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(0x70003005u, 1),
+ isLocalPlayer: false)
+ .Canonical!;
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ first,
+ out RuntimeInitialCreateResidenceLease oldLease));
+
+ RuntimeEntityRecord second = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(0x70003005u, 2),
+ isLocalPlayer: false)
+ .Canonical!;
+
+ Assert.False(lifetime.TryGetInitialCreateResidence(first, out _));
+ Assert.Equal(
+ RuntimeInitialCreateResidenceCompletionStatus.RejectedToken,
+ lifetime.CompleteInitialCreateResidence(
+ first,
+ oldLease.Token,
+ out _));
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ second,
+ out RuntimeInitialCreateResidenceLease successor));
+ Assert.NotEqual(oldLease.Token, successor.Token);
+ Assert.Equal(1, lifetime.CaptureOwnership()
+ .InitialCreateResidenceLeaseCount);
+ }
+
+ [Fact]
+ public void RegistrationObserverReentryCannotResurrectOuterLease()
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ Bind(lifetime, 12UL);
+ RuntimeEntityRegistrationResult nested = default;
+ bool reentered = false;
+ using IDisposable subscription = lifetime.Events.Subscribe(
+ new EntityObserver(delta =>
+ {
+ if (reentered
+ || delta.Change is not RuntimeEntityChange.Registered)
+ return;
+ reentered = true;
+ nested = lifetime.RegisterEntityWithInitialResidence(
+ Spawn(0x70003006u, 2),
+ isLocalPlayer: false);
+ }));
+
+ RuntimeEntityRegistrationResult outer = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(0x70003006u, 1),
+ isLocalPlayer: false);
+
+ Assert.Equal(CreateObjectTimestampDisposition.StaleGeneration,
+ outer.Inbound.Disposition);
+ RuntimeEntityRecord current = nested.Canonical!;
+ Assert.True(lifetime.Entities.IsCurrent(current));
+ Assert.Equal((ushort)2, current.Incarnation);
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ current,
+ out RuntimeInitialCreateResidenceLease lease));
+ Assert.Equal(current.Key, lease.Token.Entity);
+ Assert.Equal(1, lifetime.CaptureOwnership()
+ .InitialCreateResidenceLeaseCount);
+ }
+
+ [Fact]
+ public void DeleteResetAndDisposeConvergeEveryInitialResidenceOwner()
+ {
+ var lifetime = new RuntimeEntityObjectLifetime();
+ Bind(lifetime, 13UL);
+ RuntimeEntityRecord deleted = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(0x70003007u, 1),
+ isLocalPlayer: false)
+ .Canonical!;
+ Assert.True(lifetime.TryAcceptDelete(
+ new DeleteObject.Parsed(deleted.ServerGuid, deleted.Incarnation),
+ isLocalPlayer: false,
+ removeRetainedObject: true,
+ out RuntimeEntityDeleteAcceptance acceptance));
+ Assert.Equal(0, lifetime.CaptureOwnership()
+ .InitialCreateResidenceLeaseCount);
+ lifetime.CompleteAcceptedDelete(acceptance);
+ Assert.Null(lifetime.RetireCanonicalOnly(deleted));
+
+ _ = lifetime.RegisterEntityWithInitialResidence(
+ Spawn(0x70003008u, 1),
+ isLocalPlayer: false);
+ IReadOnlyList retirements =
+ lifetime.BeginSessionClear();
+ Assert.Equal(0, lifetime.CaptureOwnership()
+ .InitialCreateResidenceLeaseCount);
+ foreach (RuntimeEntityRecord record in retirements)
+ lifetime.CompleteSessionEntityRetirement(record);
+ Assert.True(lifetime.CompleteSessionClearIfConverged());
+
+ lifetime.Dispose();
+ Assert.True(lifetime.CaptureOwnership().IsConverged);
+ }
+
+ [Fact]
+ public void ResetCancelsCompletedUnadoptedContinuationBatch()
+ {
+ var engine = new PhysicsEngine { DataCache = new PhysicsDataCache() };
+ engine.AddLandblock(
+ Landblock,
+ new TerrainSurface(new byte[81], new float[256]),
+ Array.Empty(),
+ Array.Empty(),
+ worldOffsetX: 0f,
+ worldOffsetY: 0f);
+ using var lifetime = new RuntimeEntityObjectLifetime(engine);
+ Bind(lifetime, 32UL);
+ RuntimeEntityRecord canonical = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(0x7000301Eu, 1, setupId: null),
+ isLocalPlayer: true)
+ .Canonical!;
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease lease));
+ AttachDormantBody(lifetime, canonical);
+ RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition
+ .SubmitPreparedPlacement(
+ lease.Placement,
+ Prepare(
+ lifetime,
+ lease,
+ RuntimeSetPositionMoverSetup.ResolvedAbsent));
+ Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
+ outcome.Projection));
+ Assert.Equal(
+ RuntimeInitialCreateResidenceCompletionStatus.Completed,
+ lifetime.CompleteInitialCreateResidence(
+ canonical,
+ lease.Token,
+ out RuntimeInitialCreateResidenceReceipt receipt));
+ Assert.Equal(1, lifetime.CaptureOwnership()
+ .InitialCreateResidenceLeaseCount);
+
+ IReadOnlyList retirements =
+ lifetime.BeginSessionClear();
+
+ Assert.False(lifetime.AcknowledgeInitialCreateResidenceAdoption(
+ canonical,
+ receipt.Adoption));
+ Assert.Equal(0, lifetime.CaptureOwnership()
+ .InitialCreateResidenceLeaseCount);
+ RuntimeSetPositionOwnershipSnapshot ownership =
+ lifetime.Physics.SetPosition.CaptureOwnership();
+ Assert.Equal(0, ownership.ActiveOperationCount);
+ Assert.Equal(0, ownership.PlacementCompletionWatchCount);
+ Assert.Equal(0, ownership.AcknowledgedPlacementCompletionCount);
+ foreach (RuntimeEntityRecord record in retirements)
+ lifetime.CompleteSessionEntityRetirement(record);
+ Assert.True(lifetime.CompleteSessionClearIfConverged());
+ }
+
+ [Fact]
+ public void PostCompletionRebucketRejectsTopLevelAdoptionAndConverges()
+ {
+ var engine = new PhysicsEngine { DataCache = new PhysicsDataCache() };
+ engine.AddLandblock(
+ Landblock,
+ new TerrainSurface(new byte[81], new float[256]),
+ Array.Empty(),
+ Array.Empty(),
+ worldOffsetX: 0f,
+ worldOffsetY: 0f);
+ using var lifetime = new RuntimeEntityObjectLifetime(engine);
+ Bind(lifetime, 33UL);
+ RuntimeEntityRecord canonical = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(0x7000301Fu, 1, setupId: null),
+ isLocalPlayer: false)
+ .Canonical!;
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease lease));
+ AttachDormantBody(lifetime, canonical);
+ RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition
+ .SubmitPreparedPlacement(
+ lease.Placement,
+ Prepare(
+ lifetime,
+ lease,
+ RuntimeSetPositionMoverSetup.ResolvedAbsent));
+ Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
+ outcome.Projection));
+ Assert.Equal(
+ RuntimeInitialCreateResidenceCompletionStatus.Completed,
+ lifetime.CompleteInitialCreateResidence(
+ canonical,
+ lease.Token,
+ out RuntimeInitialCreateResidenceReceipt receipt));
+
+ lifetime.Entities.SetFullCell(
+ canonical,
+ Landblock | 0x0002u,
+ Landblock);
+
+ Assert.False(lifetime.AcknowledgeInitialCreateResidenceAdoption(
+ canonical,
+ receipt.Adoption));
+ Assert.Equal(0, lifetime.CaptureOwnership()
+ .InitialCreateResidenceLeaseCount);
+ RuntimeSetPositionOwnershipSnapshot ownership =
+ lifetime.Physics.SetPosition.CaptureOwnership();
+ Assert.Equal(0, ownership.ActiveOperationCount);
+ Assert.Equal(0, ownership.PlacementCompletionWatchCount);
+ Assert.Equal(0, ownership.AcknowledgedPlacementCompletionCount);
+ }
+
+ [Fact]
+ public void PostCompletionAuthorityChangeRejectsCelllessAdoption()
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ Bind(lifetime, 34UL);
+ RuntimeEntityRecord canonical = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(
+ 0x70003020u,
+ 1,
+ includePosition: false,
+ parentGuid: 0x70004020u),
+ isLocalPlayer: false)
+ .Canonical!;
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease lease));
+ Assert.Equal(
+ RuntimeInitialCreateResidenceCompletionStatus.Completed,
+ lifetime.CompleteInitialCreateResidence(
+ canonical,
+ lease.Token,
+ out RuntimeInitialCreateResidenceReceipt receipt));
+
+ lifetime.Entities.AdvancePositionAuthority(canonical);
+
+ Assert.False(lifetime.AcknowledgeInitialCreateResidenceAdoption(
+ canonical,
+ receipt.Adoption));
+ Assert.Equal(0, lifetime.CaptureOwnership()
+ .InitialCreateResidenceLeaseCount);
+ Assert.True(lifetime.Physics.SetPosition.CaptureOwnership()
+ .IsConverged);
+ }
+
+ [Fact]
+ public void CelllessPendingAdoptionRevisionsForFreshPositionWithoutLeak()
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ Bind(lifetime, 36UL);
+ const uint guid = 0x70003024u;
+ RuntimeEntityRecord canonical = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(
+ guid,
+ 1,
+ includePosition: false,
+ parentGuid: 0x70004024u),
+ isLocalPlayer: false)
+ .Canonical!;
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease lease));
+ ulong positionAuthority = canonical.PositionAuthorityVersion;
+ Assert.Equal(
+ RuntimeInitialCreateResidenceCompletionStatus.Completed,
+ lifetime.CompleteInitialCreateResidence(
+ canonical,
+ lease.Token,
+ out RuntimeInitialCreateResidenceReceipt original));
+ int callbacks = 0;
+ WorldSession.EntityPositionUpdate update = PositionUpdate(
+ guid,
+ positionSequence: 2,
+ teleportSequence: 1,
+ forcePositionSequence: 0,
+ positionX: 25f);
+
+ Assert.True(lifetime.TryApplyPosition(
+ update,
+ isLocalPlayer: false,
+ forcePositionRotation: null,
+ currentLocalVelocity: null,
+ projectionRequiresTeleportHook: false,
+ acknowledgeProjection: _ => callbacks++,
+ out PositionTimestampDisposition disposition,
+ out _,
+ out _));
+
+ Assert.Equal(PositionTimestampDisposition.Apply, disposition);
+ Assert.Equal(0, callbacks);
+ Assert.Equal(positionAuthority, canonical.PositionAuthorityVersion);
+ Assert.Null(canonical.Snapshot.Position);
+ Assert.Equal(0u, canonical.FullCellId);
+ Assert.Equal(
+ RuntimeInitialCreateResidenceCompletionStatus.Completed,
+ lifetime.CompleteInitialCreateResidence(
+ canonical,
+ lease.Token,
+ out RuntimeInitialCreateResidenceReceipt revised));
+ Assert.Equal(original.Adoption.Revision + 1UL,
+ revised.Adoption.Revision);
+ RuntimeInitialCreateResidenceContinuation continuation =
+ Assert.Single(revised.Continuations);
+ Assert.Equal(25f, continuation.AcceptedWirePosition.PositionX);
+ Assert.Equal((ushort)1, continuation.AcceptedTeleportSequence);
+ Assert.False(lifetime.AcknowledgeInitialCreateResidenceAdoption(
+ canonical,
+ original.Adoption));
+ Assert.True(lifetime.AcknowledgeInitialCreateResidenceAdoption(
+ canonical,
+ revised.Adoption));
+ Assert.Equal(0, lifetime.CaptureOwnership()
+ .InitialCreateResidenceLeaseCount);
+ Assert.True(lifetime.Physics.SetPosition.CaptureOwnership()
+ .IsConverged);
+ }
+
+ [Fact]
+ public void ResetSnapshotsAllResidenceOwnersBeforeReentrantDiscardObserver()
+ {
+ var engine = new PhysicsEngine { DataCache = new PhysicsDataCache() };
+ engine.AddLandblock(
+ Landblock,
+ new TerrainSurface(new byte[81], new float[256]),
+ Array.Empty(),
+ Array.Empty(),
+ worldOffsetX: 0f,
+ worldOffsetY: 0f);
+ using var lifetime = new RuntimeEntityObjectLifetime(engine);
+ Bind(lifetime, 35UL);
+ RuntimeEntityRecord completedRecord = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(
+ 0x70003023u,
+ 1,
+ setupId: null,
+ positionX: 70f),
+ isLocalPlayer: false)
+ .Canonical!;
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ completedRecord,
+ out RuntimeInitialCreateResidenceLease completedLease));
+ AttachDormantBody(lifetime, completedRecord);
+ RuntimeSetPositionOutcome completedOutcome = lifetime.Physics.SetPosition
+ .SubmitPreparedPlacement(
+ completedLease.Placement,
+ Prepare(
+ lifetime,
+ completedLease,
+ RuntimeSetPositionMoverSetup.ResolvedAbsent));
+ Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
+ completedOutcome.Projection));
+ Assert.Equal(
+ RuntimeInitialCreateResidenceCompletionStatus.Completed,
+ lifetime.CompleteInitialCreateResidence(
+ completedRecord,
+ completedLease.Token,
+ out _));
+
+ var pending = new List();
+ uint[] pendingGuids = [0x70003021u, 0x70003022u];
+ for (int index = 0; index < pendingGuids.Length; index++)
+ {
+ uint guid = pendingGuids[index];
+ RuntimeEntityRecord record = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(
+ guid,
+ 1,
+ setupId: null,
+ positionX: 10f + index * 30f),
+ isLocalPlayer: false)
+ .Canonical!;
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ record,
+ out RuntimeInitialCreateResidenceLease lease));
+ AttachDormantBody(lifetime, record);
+ pending.Add(lifetime.Physics.SetPosition.SubmitPreparedPlacement(
+ lease.Placement,
+ Prepare(
+ lifetime,
+ lease,
+ RuntimeSetPositionMoverSetup.ResolvedAbsent)));
+ }
+
+ var discards = new List();
+ int nestedRetirementCount = -1;
+ using IDisposable subscription = lifetime.Events.SubscribePlacement(
+ new PlacementObserver(delta =>
+ {
+ if (delta.Placement.Kind
+ is not RuntimePlacementProjectionKind.Discard)
+ {
+ return;
+ }
+ discards.Add(delta.Placement.Token.Sequence);
+ if (nestedRetirementCount < 0)
+ {
+ nestedRetirementCount =
+ lifetime.BeginSessionClear().Count;
+ }
+ }));
+
+ IReadOnlyList retirements =
+ lifetime.BeginSessionClear();
+
+ Assert.Equal(0, nestedRetirementCount);
+ Assert.Equal(
+ pending.Select(static item => item.Projection.Sequence)
+ .Order(),
+ discards.Order());
+ Assert.Equal(0, lifetime.Events.DispatchFailureCount);
+ Assert.Equal(0, lifetime.CaptureOwnership()
+ .InitialCreateResidenceLeaseCount);
+ foreach (RuntimeEntityRecord record in retirements)
+ lifetime.CompleteSessionEntityRetirement(record);
+ Assert.True(lifetime.CompleteSessionClearIfConverged());
+ }
+
+ [Fact]
+ public void InvalidGenerationFailsBeforeInboundOrCanonicalAcceptance()
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ ulong generation = 0UL;
+ lifetime.BindEventContext(
+ () => new RuntimeGenerationToken(generation),
+ static () => 1UL);
+ WorldSession.EntitySpawn spawn = Spawn(0x70003009u, 1);
+
+ Assert.Throws(() => lifetime
+ .RegisterEntityWithInitialResidence(spawn, isLocalPlayer: false));
+ Assert.Equal(0, lifetime.Entities.Count);
+ Assert.Equal(0, lifetime.Entities.ClaimedLocalIdCount);
+ Assert.Empty(lifetime.Entities.Snapshots);
+ Assert.Equal(0, lifetime.CaptureOwnership()
+ .InitialCreateResidenceLeaseCount);
+ Assert.True(lifetime.Physics.SetPosition.CaptureOwnership()
+ .IsConverged);
+
+ generation = 14UL;
+ RuntimeEntityRegistrationResult recovered = lifetime
+ .RegisterEntityWithInitialResidence(spawn, isLocalPlayer: false);
+ Assert.Equal(CreateObjectTimestampDisposition.InitialGeneration,
+ recovered.Inbound.Disposition);
+ Assert.NotNull(recovered.Canonical);
+ }
+
+ [Fact]
+ public void UnboundGenerationFailsWithoutFabricatingSessionAuthority()
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+
+ Assert.Throws(() => lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(0x70003014u, 1),
+ isLocalPlayer: false));
+
+ Assert.Empty(lifetime.Entities.Snapshots);
+ Assert.Equal(0, lifetime.Entities.Count);
+ Assert.Equal(0, lifetime.Entities.ClaimedLocalIdCount);
+ }
+
+ [Theory]
+ [InlineData((ushort)1)]
+ [InlineData((ushort)2)]
+ public void EqualOrStaleMalformedCreateDoesNotDisplaceCurrentAdmission(
+ ushort currentIncarnation)
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ Bind(lifetime, 24UL);
+ RuntimeEntityRecord canonical = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(0x70003015u, currentIncarnation),
+ isLocalPlayer: false)
+ .Canonical!;
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease initial));
+ ushort incomingIncarnation = 1;
+
+ RuntimeEntityRegistrationResult result = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(
+ 0x70003015u,
+ incomingIncarnation,
+ positionX: float.NaN),
+ isLocalPlayer: false);
+
+ Assert.Equal(
+ currentIncarnation == incomingIncarnation
+ ? CreateObjectTimestampDisposition.ExistingGeneration
+ : CreateObjectTimestampDisposition.StaleGeneration,
+ result.Inbound.Disposition);
+ Assert.True(lifetime.Entities.IsCurrent(canonical));
+ Assert.Equal(10f, canonical.Snapshot.Position!.Value.PositionX);
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease retained));
+ Assert.Equal(initial, retained);
+ }
+
+ [Fact]
+ public void MalformedInitialPositionDoesNotPoisonSameInstanceRecovery()
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ Bind(lifetime, 15UL);
+ WorldSession.EntitySpawn malformed = Spawn(
+ 0x7000300Au,
+ 1,
+ positionX: float.NaN);
+
+ Assert.Throws(() => lifetime
+ .RegisterEntityWithInitialResidence(
+ malformed,
+ isLocalPlayer: false));
+ Assert.Empty(lifetime.Entities.Snapshots);
+ Assert.Equal(0, lifetime.Entities.Count);
+
+ WorldSession.EntitySpawn corrected = Spawn(0x7000300Au, 1);
+ RuntimeEntityRegistrationResult recovered = lifetime
+ .RegisterEntityWithInitialResidence(
+ corrected,
+ isLocalPlayer: false);
+ Assert.Equal(CreateObjectTimestampDisposition.InitialGeneration,
+ recovered.Inbound.Disposition);
+ Assert.Equal(corrected.Position,
+ recovered.Canonical!.Snapshot.Position);
+ Assert.Equal(0u, recovered.Canonical.FullCellId);
+ }
+
+ [Fact]
+ public void ExistingPlacementCollisionCannotStealOrPartiallyOwnLease()
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ Bind(lifetime, 16UL);
+ RuntimeEntityRegistrationResult registration = lifetime.RegisterEntity(
+ Spawn(0x7000300Bu, 1));
+ RuntimeEntityRecord canonical = registration.Canonical!;
+ lifetime.Entities.SetFullCell(canonical, 0u, 0u);
+ lifetime.Entities.AdvancePositionAuthority(canonical);
+ RuntimeEntityPlacementToken conflict = lifetime.Physics.SetPosition
+ .BeginAuthoredPlacement(
+ canonical,
+ canonical.PositionAuthorityVersion,
+ RuntimeSetPositionOperationKind.RemoteAuthoritative);
+ Assert.True(conflict.IsValid);
+
+ RuntimeInitialCreateResidenceLease rejected =
+ lifetime.InitialCreateResidences.Begin(
+ canonical,
+ registration.Inbound,
+ isLocalPlayer: false);
+
+ Assert.False(rejected.IsValid);
+ Assert.True(lifetime.Physics.SetPosition.IsPlacementCurrent(conflict));
+ Assert.Equal(0, lifetime.CaptureOwnership()
+ .InitialCreateResidenceLeaseCount);
+ Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership()
+ .PlacementCompletionWatchCount);
+ }
+
+ [Fact]
+ public void ParentPrecedesCombinedWirePositionAndCreatesNoWorldPlacement()
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ Bind(lifetime, 17UL);
+ RuntimeEntityRecord canonical = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(
+ 0x7000300Cu,
+ 1,
+ includePosition: true,
+ parentGuid: 0x70004001u),
+ isLocalPlayer: false)
+ .Canonical!;
+
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease lease));
+ Assert.Equal(RuntimeAuthoritativePositionDisposition.AwaitFreshPosition,
+ lease.Route.Disposition);
+ Assert.False(lease.Placement.IsValid);
+ Assert.Equal(0u, canonical.FullCellId);
+ Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership()
+ .ActiveOperationCount);
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public void AbsentAndPresentZeroCellCreateAwaitFreshPosition(
+ bool presentZeroCell)
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ Bind(lifetime, 25UL);
+ RuntimeEntityRecord canonical = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(
+ presentZeroCell ? 0x70003016u : 0x70003017u,
+ 1,
+ includePosition: presentZeroCell,
+ positionCell: 0u),
+ isLocalPlayer: false)
+ .Canonical!;
+
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease lease));
+ Assert.Equal(RuntimeAuthoritativePositionDisposition.AwaitFreshPosition,
+ lease.Route.Disposition);
+ Assert.False(lease.Placement.IsValid);
+ Assert.Equal(0u, canonical.FullCellId);
+ Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership()
+ .ActiveOperationCount);
+ }
+
+ [Fact]
+ public void EqualAndNewerSameGenerationCreateDoNotChurnPendingPlacement()
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ Bind(lifetime, 18UL);
+ RuntimeEntityRecord canonical = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(0x7000300Du, 1),
+ isLocalPlayer: false)
+ .Canonical!;
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease initial));
+ ulong positionAuthority = canonical.PositionAuthorityVersion;
+ ulong spatialAuthority = canonical.SpatialAuthorityVersion;
+
+ RuntimeEntityRegistrationResult duplicate = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(0x7000300Du, 1),
+ isLocalPlayer: false);
+ Assert.Equal(CreateObjectTimestampDisposition.ExistingGeneration,
+ duplicate.Inbound.Disposition);
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease afterDuplicate));
+ Assert.Equal(initial, afterDuplicate);
+ Assert.Equal(positionAuthority, canonical.PositionAuthorityVersion);
+ Assert.Equal(spatialAuthority, canonical.SpatialAuthorityVersion);
+
+ RuntimeEntityRegistrationResult newer = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(
+ 0x7000300Du,
+ 1,
+ positionSequence: 2,
+ positionX: 30f),
+ isLocalPlayer: false);
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease beforeFreshRoute));
+ Assert.Equal(initial, beforeFreshRoute);
+ Assert.Equal(10f, canonical.Snapshot.Position!.Value.PositionX);
+ Assert.NotNull(newer.Inbound.SameGenerationEvents);
+
+ WorldSession.EntityPositionUpdate update =
+ newer.Inbound.SameGenerationEvents!.Value.Position!.Value;
+ Assert.True(lifetime.TryApplyPosition(
+ update,
+ isLocalPlayer: false,
+ forcePositionRotation: null,
+ currentLocalVelocity: null,
+ projectionRequiresTeleportHook: false,
+ acknowledgeProjection: null,
+ out PositionTimestampDisposition disposition,
+ out _,
+ out _));
+ Assert.Equal(PositionTimestampDisposition.Apply, disposition);
+ Assert.Equal(0u, canonical.FullCellId);
+ Assert.Equal(10f, canonical.Snapshot.Position!.Value.PositionX);
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease successor));
+ Assert.Equal(initial.Token, successor.Token);
+ Assert.Equal(RuntimeSetPositionOperationKind.RemoteAuthoritative,
+ successor.Route.OperationKind);
+ RuntimeInitialCreateResidenceContinuation continuation =
+ Assert.Single(successor.Continuations);
+ Assert.Equal(30f, continuation.AcceptedWirePosition.PositionX);
+ Assert.Equal((ushort)2, continuation.PositionSequence);
+ Assert.Equal(PositionTimestampDisposition.Apply,
+ continuation.TimestampDisposition);
+ Assert.True(successor.Placement.IsValid);
+ Assert.Equal(1, lifetime.Physics.SetPosition.CaptureOwnership()
+ .ActiveOperationCount);
+ }
+
+ [Fact]
+ public void AlreadyPlacedSameGenerationCreateCannotReclaimWireCell()
+ {
+ var engine = new PhysicsEngine { DataCache = new PhysicsDataCache() };
+ engine.AddLandblock(
+ Landblock,
+ new TerrainSurface(new byte[81], new float[256]),
+ Array.Empty(),
+ Array.Empty(),
+ worldOffsetX: 0f,
+ worldOffsetY: 0f);
+ using var lifetime = new RuntimeEntityObjectLifetime(engine);
+ Bind(lifetime, 19UL);
+ RuntimeEntityRecord canonical = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(0x7000300Eu, 1, setupId: null),
+ isLocalPlayer: false)
+ .Canonical!;
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease lease));
+ AttachDormantBody(lifetime, canonical);
+ RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition
+ .SubmitPreparedPlacement(
+ lease.Placement,
+ Prepare(
+ lifetime,
+ lease,
+ RuntimeSetPositionMoverSetup.ResolvedAbsent));
+ Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
+ outcome.Projection));
+ Assert.Equal(
+ RuntimeInitialCreateResidenceCompletionStatus.Completed,
+ lifetime.CompleteInitialCreateResidence(
+ canonical,
+ lease.Token,
+ out _));
+ ulong spatialAuthority = canonical.SpatialAuthorityVersion;
+
+ _ = lifetime.RegisterEntityWithInitialResidence(
+ Spawn(0x7000300Eu, 1, setupId: null),
+ isLocalPlayer: false);
+
+ Assert.Equal(Cell, canonical.FullCellId);
+ Assert.Equal(spatialAuthority, canonical.SpatialAuthorityVersion);
+ Assert.False(lifetime.TryGetInitialCreateResidence(canonical, out _));
+ }
+
+ [Fact]
+ public void LocalNoTeleportFreshPositionRemainsInitialWorldAdmission()
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ Bind(lifetime, 22UL);
+ RuntimeEntityRecord canonical = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(0x70003012u, 1),
+ isLocalPlayer: true)
+ .Canonical!;
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease initial));
+ RuntimeEntityRegistrationResult newer = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(
+ 0x70003012u,
+ 1,
+ positionSequence: 2,
+ positionX: 40f),
+ isLocalPlayer: true);
+ WorldSession.EntityPositionUpdate update =
+ newer.Inbound.SameGenerationEvents!.Value.Position!.Value;
+
+ Assert.True(lifetime.TryApplyPosition(
+ update,
+ isLocalPlayer: true,
+ forcePositionRotation: null,
+ currentLocalVelocity: null,
+ projectionRequiresTeleportHook: false,
+ acknowledgeProjection: null,
+ out PositionTimestampDisposition disposition,
+ out _,
+ out AcceptedPhysicsTimestamps timestamps));
+
+ Assert.Equal(PositionTimestampDisposition.Apply, disposition);
+ Assert.False(timestamps.TeleportAdvanced);
+ Assert.Equal(0u, canonical.FullCellId);
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease successor));
+ Assert.Equal(initial.Token, successor.Token);
+ Assert.Equal(RuntimeAuthoritativePositionDisposition.SetPosition,
+ successor.Route.Disposition);
+ Assert.Equal(RuntimeSetPositionOperationKind.InitialLogin,
+ successor.Route.OperationKind);
+ Assert.Equal(RuntimeTeleportHookPhase.AfterEnterWorld,
+ successor.Route.TeleportHookPhase);
+ Assert.True(successor.Placement.IsValid);
+ RuntimeInitialCreateResidenceContinuation continuation =
+ Assert.Single(successor.Continuations);
+ Assert.Equal(PositionTimestampDisposition.Apply,
+ continuation.TimestampDisposition);
+ Assert.Equal((ushort)0, continuation.PreviousTeleportSequence);
+ Assert.Equal((ushort)0, continuation.TeleportSequence);
+ }
+
+ [Fact]
+ public void LocalForcePositionSuffixIsQueuedBehindInitialAdmission()
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ Bind(lifetime, 26UL);
+ RuntimeInitialCreateResidenceLease successor = ApplyFreshSuccessor(
+ lifetime,
+ guid: 0x70003018u,
+ isLocalPlayer: true,
+ teleportSequence: 0,
+ forcePositionSequence: 1,
+ out PositionTimestampDisposition disposition);
+
+ Assert.Equal(PositionTimestampDisposition.ForcePosition, disposition);
+ Assert.Equal(RuntimeSetPositionOperationKind.InitialLogin,
+ successor.Route.OperationKind);
+ RuntimeInitialCreateResidenceContinuation continuation =
+ Assert.Single(successor.Continuations);
+ Assert.Equal(RuntimePositionEntityKind.LocalPlayer,
+ continuation.EntityKind);
+ Assert.Equal((ushort)1, continuation.ForcePositionSequence);
+ Assert.Equal((ushort)0, continuation.PreviousTeleportSequence);
+ Assert.Equal((ushort)0, continuation.TeleportSequence);
+ Assert.NotNull(continuation.ForcePositionRotation);
+ }
+
+ [Fact]
+ public void LocalTeleportSuffixIsQueuedBehindInitialAdmission()
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ Bind(lifetime, 27UL);
+ RuntimeInitialCreateResidenceLease successor = ApplyFreshSuccessor(
+ lifetime,
+ guid: 0x70003019u,
+ isLocalPlayer: true,
+ teleportSequence: 1,
+ forcePositionSequence: 0,
+ out PositionTimestampDisposition disposition);
+
+ Assert.Equal(PositionTimestampDisposition.Apply, disposition);
+ RuntimeInitialCreateResidenceContinuation continuation =
+ Assert.Single(successor.Continuations);
+ Assert.Equal(RuntimePositionEntityKind.LocalPlayer,
+ continuation.EntityKind);
+ Assert.Equal((ushort)0, continuation.PreviousTeleportSequence);
+ Assert.Equal((ushort)1, continuation.TeleportSequence);
+ Assert.Equal(Vector3.Zero, continuation.AcceptedVelocity);
+ }
+
+ [Fact]
+ public void RemoteTeleportSuffixIsQueuedBehindInitialAdmission()
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ Bind(lifetime, 28UL);
+ RuntimeInitialCreateResidenceLease successor = ApplyFreshSuccessor(
+ lifetime,
+ guid: 0x7000301Au,
+ isLocalPlayer: false,
+ teleportSequence: 1,
+ forcePositionSequence: 0,
+ out PositionTimestampDisposition disposition);
+
+ Assert.Equal(PositionTimestampDisposition.Apply, disposition);
+ RuntimeInitialCreateResidenceContinuation continuation =
+ Assert.Single(successor.Continuations);
+ Assert.Equal(RuntimePositionEntityKind.Remote,
+ continuation.EntityKind);
+ Assert.Equal((ushort)0, continuation.PreviousTeleportSequence);
+ Assert.Equal((ushort)1, continuation.TeleportSequence);
+ }
+
+ [Fact]
+ public void AcceptedPositionsRemainMonotonicRawFifoWithoutMutatingInitialAdmission()
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ Bind(lifetime, 29UL);
+ const uint guid = 0x7000301Bu;
+ RuntimeEntityRecord canonical = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(guid, 1),
+ isLocalPlayer: true)
+ .Canonical!;
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease initial));
+ ulong positionAuthority = canonical.PositionAuthorityVersion;
+ int callbacks = 0;
+
+ ApplyQueuedPosition(
+ lifetime,
+ guid,
+ positionSequence: 2,
+ teleportSequence: 0,
+ forcePositionSequence: 0,
+ positionX: 20f,
+ projectionRequiresTeleportHook: false,
+ () => callbacks++);
+ ApplyQueuedPosition(
+ lifetime,
+ guid,
+ positionSequence: 3,
+ teleportSequence: 1,
+ forcePositionSequence: 0,
+ positionX: 30f,
+ projectionRequiresTeleportHook: true,
+ () => callbacks++);
+ ApplyQueuedPosition(
+ lifetime,
+ guid,
+ positionSequence: 4,
+ teleportSequence: 2,
+ forcePositionSequence: 0,
+ positionX: 40f,
+ projectionRequiresTeleportHook: false,
+ () => callbacks++);
+
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease retained));
+ Assert.Equal(initial.Token, retained.Token);
+ Assert.Equal(initial.Route, retained.Route);
+ Assert.Equal(initial.Placement, retained.Placement);
+ Assert.Equal(10f, canonical.Snapshot.Position!.Value.PositionX);
+ Assert.Equal(positionAuthority, canonical.PositionAuthorityVersion);
+ Assert.Equal(0, callbacks);
+ Assert.Equal([1UL, 2UL, 3UL],
+ retained.Continuations.Select(static item => item.Sequence));
+ Assert.Equal([20f, 30f, 40f],
+ retained.Continuations.Select(
+ static item => item.AcceptedWirePosition.PositionX));
+ Assert.Equal([(ushort)0, (ushort)0, (ushort)1],
+ retained.Continuations.Select(
+ static item => item.PreviousTeleportSequence));
+ Assert.Equal([(ushort)0, (ushort)1, (ushort)2],
+ retained.Continuations.Select(
+ static item => item.TeleportSequence));
+ Assert.False(retained.Continuations[0]
+ .ProjectionRequiresTeleportHook);
+ Assert.True(retained.Continuations[1]
+ .ProjectionRequiresTeleportHook);
+ }
+
+ [Fact]
+ public void MalformedQueuedPositionIsRejectedBeforeTimestampConsumption()
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ Bind(lifetime, 30UL);
+ const uint guid = 0x7000301Cu;
+ RuntimeEntityRecord canonical = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(guid, 1),
+ isLocalPlayer: false)
+ .Canonical!;
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease initial));
+ WorldSession.EntityPositionUpdate malformed = PositionUpdate(
+ guid,
+ positionSequence: 2,
+ teleportSequence: 0,
+ forcePositionSequence: 0,
+ positionX: float.NaN);
+
+ Assert.False(lifetime.TryApplyPosition(
+ malformed,
+ isLocalPlayer: false,
+ forcePositionRotation: null,
+ currentLocalVelocity: null,
+ projectionRequiresTeleportHook: false,
+ acknowledgeProjection: null,
+ out PositionTimestampDisposition disposition,
+ out _,
+ out _));
+
+ Assert.Equal(PositionTimestampDisposition.Rejected, disposition);
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease retained));
+ Assert.Equal(initial, retained);
+ Assert.Equal(10f, canonical.Snapshot.Position!.Value.PositionX);
+
+ ApplyQueuedPosition(
+ lifetime,
+ guid,
+ positionSequence: 2,
+ teleportSequence: 0,
+ forcePositionSequence: 0,
+ positionX: 25f,
+ projectionRequiresTeleportHook: false,
+ callback: null,
+ isLocalPlayer: false);
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out retained));
+ Assert.Equal((ushort)2,
+ Assert.Single(retained.Continuations).PositionSequence);
+ }
+
+ [Fact]
+ public void CompletionRequiresExactAcknowledgedOriginalPlacement()
+ {
+ var engine = new PhysicsEngine { DataCache = new PhysicsDataCache() };
+ engine.AddLandblock(
+ Landblock,
+ new TerrainSurface(new byte[81], new float[256]),
+ Array.Empty(),
+ Array.Empty(),
+ worldOffsetX: 0f,
+ worldOffsetY: 0f);
+ using var lifetime = new RuntimeEntityObjectLifetime(engine);
+ Bind(lifetime, 20UL);
+ RuntimeEntityRecord canonical = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(0x7000300Fu, 1, setupId: null),
+ isLocalPlayer: false)
+ .Canonical!;
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease original));
+ AttachDormantBody(lifetime, canonical);
+
+ RuntimeEntityPlacementToken replacement = lifetime.Physics.SetPosition
+ .BeginAuthoredPlacement(
+ canonical,
+ canonical.PositionAuthorityVersion,
+ RuntimeSetPositionOperationKind.RemoteAuthoritative);
+ var preparation = new RuntimeSetPositionMoverPreparation(
+ RuntimeSetPositionMoverSetup.ResolvedAbsent,
+ RuntimeSetPositionOperationKind.RemoteAuthoritative,
+ GameTime: 1d,
+ PhysicsPlacementClass.Ordinary,
+ original.Route.SetPositionFlags);
+ Assert.Equal(
+ RuntimeSetPositionMoverPreparationStatus.Prepared,
+ lifetime.Physics.SetPosition.PrepareMover(
+ replacement,
+ preparation,
+ out RuntimeSetPositionCommand command));
+ RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition
+ .SubmitPreparedPlacement(replacement, command);
+ Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
+ outcome.Projection));
+
+ Assert.Equal(
+ RuntimeInitialCreateResidenceCompletionStatus.RejectedAuthority,
+ lifetime.CompleteInitialCreateResidence(
+ canonical,
+ original.Token,
+ out _));
+ Assert.Equal(0, lifetime.CaptureOwnership()
+ .InitialCreateResidenceLeaseCount);
+ Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership()
+ .AcknowledgedPlacementCompletionCount);
+ }
+
+ [Fact]
+ public void FreshPositionQueuesBehindCommittedUnacknowledgedInitialAdmission()
+ {
+ var engine = new PhysicsEngine { DataCache = new PhysicsDataCache() };
+ engine.AddLandblock(
+ Landblock,
+ new TerrainSurface(new byte[81], new float[256]),
+ Array.Empty(),
+ Array.Empty(),
+ worldOffsetX: 0f,
+ worldOffsetY: 0f);
+ using var lifetime = new RuntimeEntityObjectLifetime(engine);
+ Bind(lifetime, 23UL);
+ int deleted = 0;
+ using IDisposable subscription = lifetime.Events.Subscribe(
+ new EntityObserver(delta =>
+ {
+ if (delta.Change is RuntimeEntityChange.Deleted)
+ deleted++;
+ }));
+ RuntimeEntityRecord canonical = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(0x70003013u, 1, setupId: null),
+ isLocalPlayer: true)
+ .Canonical!;
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease initial));
+ AttachDormantBody(lifetime, canonical);
+ RuntimeSetPositionOutcome first = lifetime.Physics.SetPosition
+ .SubmitPreparedPlacement(
+ initial.Placement,
+ Prepare(
+ lifetime,
+ initial,
+ RuntimeSetPositionMoverSetup.ResolvedAbsent));
+ Assert.Equal(
+ RuntimeSetPositionStatus.CommittedHostAcknowledgementPending,
+ first.Status);
+ Assert.Equal(Cell, canonical.FullCellId);
+
+ RuntimeEntityRegistrationResult newer = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(
+ 0x70003013u,
+ 1,
+ setupId: null,
+ positionSequence: 2,
+ positionX: 45f),
+ isLocalPlayer: true);
+ WorldSession.EntityPositionUpdate update =
+ newer.Inbound.SameGenerationEvents!.Value.Position!.Value;
+ Assert.True(lifetime.TryApplyPosition(
+ update,
+ isLocalPlayer: true,
+ forcePositionRotation: null,
+ currentLocalVelocity: null,
+ projectionRequiresTeleportHook: false,
+ acknowledgeProjection: null,
+ out PositionTimestampDisposition disposition,
+ out _,
+ out _));
+ Assert.Equal(PositionTimestampDisposition.Apply, disposition);
+ Assert.Equal(0, deleted);
+ Assert.True(lifetime.Physics.SetPosition.TryPeekProjection(
+ out RuntimePlacementProjectionSnapshot place));
+ Assert.Equal(RuntimePlacementProjectionKind.Place, place.Kind);
+ Assert.Equal(first.Projection.Sequence, place.Token.Sequence);
+ Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
+ place.Token));
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease successor));
+ Assert.Equal(initial.Token, successor.Token);
+ Assert.Equal(initial.Route, successor.Route);
+ Assert.Equal(initial.Placement, successor.Placement);
+ Assert.Equal(RuntimeTeleportHookPhase.AfterEnterWorld,
+ successor.Route.TeleportHookPhase);
+ RuntimeInitialCreateResidenceContinuation continuation =
+ Assert.Single(successor.Continuations);
+ Assert.Equal(45f, continuation.AcceptedWirePosition.PositionX);
+ Assert.Equal(
+ RuntimeInitialCreateResidenceCompletionStatus.Completed,
+ lifetime.CompleteInitialCreateResidence(
+ canonical,
+ successor.Token,
+ out RuntimeInitialCreateResidenceReceipt receipt));
+ Assert.Equal(RuntimeTeleportHookPhase.AfterEnterWorld,
+ receipt.TeleportHookPhase);
+ Assert.Equal(first.Projection, receipt.Projection);
+ Assert.Equal(successor.Continuations, receipt.Continuations);
+ ApplyQueuedPosition(
+ lifetime,
+ canonical.ServerGuid,
+ positionSequence: 3,
+ teleportSequence: 1,
+ forcePositionSequence: 0,
+ positionX: 55f,
+ projectionRequiresTeleportHook: true,
+ callback: null);
+ Assert.Equal(
+ RuntimeInitialCreateResidenceCompletionStatus.Completed,
+ lifetime.CompleteInitialCreateResidence(
+ canonical,
+ successor.Token,
+ out RuntimeInitialCreateResidenceReceipt repeated));
+ Assert.Equal(receipt.Adoption.Revision + 1UL,
+ repeated.Adoption.Revision);
+ Assert.Equal(2, repeated.Continuations.Length);
+ Assert.Equal(55f,
+ repeated.Continuations[1].AcceptedWirePosition.PositionX);
+ Assert.False(lifetime.AcknowledgeInitialCreateResidenceAdoption(
+ canonical,
+ receipt.Adoption));
+ Assert.True(lifetime.AcknowledgeInitialCreateResidenceAdoption(
+ canonical,
+ repeated.Adoption));
+ Assert.Equal(
+ RuntimeInitialCreateResidenceCompletionStatus.RejectedToken,
+ lifetime.CompleteInitialCreateResidence(
+ canonical,
+ successor.Token,
+ out _));
+ Assert.Equal(0, deleted);
+ }
+
+ [Fact]
+ public void PreAcknowledgementSpatialCorruptionRetiresOperationAsDiscard()
+ {
+ var engine = new PhysicsEngine { DataCache = new PhysicsDataCache() };
+ engine.AddLandblock(
+ Landblock,
+ new TerrainSurface(new byte[81], new float[256]),
+ Array.Empty(),
+ Array.Empty(),
+ worldOffsetX: 0f,
+ worldOffsetY: 0f);
+ using var lifetime = new RuntimeEntityObjectLifetime(engine);
+ Bind(lifetime, 31UL);
+ RuntimeEntityRecord canonical = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(0x7000301Du, 1, setupId: null),
+ isLocalPlayer: false)
+ .Canonical!;
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease lease));
+ AttachDormantBody(lifetime, canonical);
+ RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition
+ .SubmitPreparedPlacement(
+ lease.Placement,
+ Prepare(
+ lifetime,
+ lease,
+ RuntimeSetPositionMoverSetup.ResolvedAbsent));
+ Assert.Equal(
+ RuntimeSetPositionStatus.CommittedHostAcknowledgementPending,
+ outcome.Status);
+
+ lifetime.Entities.SetFullCell(
+ canonical,
+ Landblock | 0x0002u,
+ Landblock);
+
+ Assert.Equal(
+ RuntimeInitialCreateResidenceCompletionStatus.RejectedAuthority,
+ lifetime.CompleteInitialCreateResidence(
+ canonical,
+ lease.Token,
+ out _));
+ Assert.True(lifetime.Physics.SetPosition.TryPeekProjection(
+ out RuntimePlacementProjectionSnapshot discard));
+ Assert.Equal(RuntimePlacementProjectionKind.Discard, discard.Kind);
+ Assert.Equal(outcome.Projection.Sequence, discard.Token.Sequence);
+ Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
+ discard.Token));
+ RuntimeSetPositionOwnershipSnapshot ownership =
+ lifetime.Physics.SetPosition.CaptureOwnership();
+ Assert.Equal(0, ownership.ActiveOperationCount);
+ Assert.Equal(0, ownership.PlacementCompletionWatchCount);
+ Assert.Equal(0, ownership.AcknowledgedPlacementCompletionCount);
+ Assert.Equal(0, lifetime.CaptureOwnership()
+ .InitialCreateResidenceLeaseCount);
+ }
+
+ [Fact]
+ public void CorruptedPostAcknowledgementAuthorityRetiresProofAndLease()
+ {
+ var engine = new PhysicsEngine { DataCache = new PhysicsDataCache() };
+ engine.AddLandblock(
+ Landblock,
+ new TerrainSurface(new byte[81], new float[256]),
+ Array.Empty(),
+ Array.Empty(),
+ worldOffsetX: 0f,
+ worldOffsetY: 0f);
+ using var lifetime = new RuntimeEntityObjectLifetime(engine);
+ Bind(lifetime, 21UL);
+ RuntimeEntityRecord canonical = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(0x70003010u, 1, setupId: null),
+ isLocalPlayer: false)
+ .Canonical!;
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease lease));
+ AttachDormantBody(lifetime, canonical);
+ RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition
+ .SubmitPreparedPlacement(
+ lease.Placement,
+ Prepare(
+ lifetime,
+ lease,
+ RuntimeSetPositionMoverSetup.ResolvedAbsent));
+ Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
+ outcome.Projection));
+ lifetime.Entities.AdvancePlacementCommit(canonical);
+
+ Assert.Equal(
+ RuntimeInitialCreateResidenceCompletionStatus.RejectedAuthority,
+ lifetime.CompleteInitialCreateResidence(
+ canonical,
+ lease.Token,
+ out _));
+ Assert.Equal(0, lifetime.CaptureOwnership()
+ .InitialCreateResidenceLeaseCount);
+ RuntimeSetPositionOwnershipSnapshot ownership =
+ lifetime.Physics.SetPosition.CaptureOwnership();
+ Assert.Equal(0, ownership.PlacementCompletionWatchCount);
+ Assert.Equal(0, ownership.AcknowledgedPlacementCompletionCount);
+ }
+
+ [Fact]
+ public void LegacyRegistrationAdvancesSpatialAuthorityOnlyOnce()
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ RuntimeEntityRecord canonical = lifetime.RegisterEntity(
+ Spawn(0x70003011u, 1)).Canonical!;
+
+ Assert.Equal(Cell, canonical.FullCellId);
+ Assert.Equal(1UL, canonical.SpatialAuthorityVersion);
+ }
+
+ private static RuntimeSetPositionCommand Prepare(
+ RuntimeEntityObjectLifetime lifetime,
+ in RuntimeInitialCreateResidenceLease lease,
+ in RuntimeSetPositionMoverSetup setup)
+ {
+ var preparation = new RuntimeSetPositionMoverPreparation(
+ setup,
+ lease.Route.OperationKind,
+ GameTime: 1d,
+ PhysicsPlacementClass.Ordinary,
+ lease.Route.SetPositionFlags);
+ Assert.Equal(
+ RuntimeSetPositionMoverPreparationStatus.Prepared,
+ lifetime.Physics.SetPosition.PrepareMover(
+ lease.Placement,
+ preparation,
+ out RuntimeSetPositionCommand command));
+ return command;
+ }
+
+ private static RuntimeInitialCreateResidenceLease ApplyFreshSuccessor(
+ RuntimeEntityObjectLifetime lifetime,
+ uint guid,
+ bool isLocalPlayer,
+ ushort teleportSequence,
+ ushort forcePositionSequence,
+ out PositionTimestampDisposition disposition)
+ {
+ RuntimeEntityRecord canonical = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(guid, 1),
+ isLocalPlayer)
+ .Canonical!;
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease initial));
+ RuntimeEntityRegistrationResult newer = lifetime
+ .RegisterEntityWithInitialResidence(
+ Spawn(
+ guid,
+ 1,
+ positionSequence: 2,
+ positionX: 40f,
+ teleportSequence: teleportSequence,
+ forcePositionSequence: forcePositionSequence),
+ isLocalPlayer);
+ WorldSession.EntityPositionUpdate update =
+ newer.Inbound.SameGenerationEvents!.Value.Position!.Value;
+ var preservedRotation = Quaternion.CreateFromYawPitchRoll(
+ 0.5f,
+ 0.25f,
+ 0.125f);
+ var currentVelocity = new Vector3(1f, 2f, 3f);
+
+ Assert.True(lifetime.TryApplyPosition(
+ update,
+ isLocalPlayer,
+ forcePositionRotation: preservedRotation,
+ currentLocalVelocity: currentVelocity,
+ projectionRequiresTeleportHook: true,
+ acknowledgeProjection: null,
+ out disposition,
+ out _,
+ out _));
+ Assert.True(lifetime.TryGetInitialCreateResidence(
+ canonical,
+ out RuntimeInitialCreateResidenceLease retained));
+ Assert.Equal(initial.Token, retained.Token);
+ Assert.Equal(initial.Route, retained.Route);
+ Assert.Equal(initial.Placement, retained.Placement);
+ return retained;
+ }
+
+ private static void ApplyQueuedPosition(
+ RuntimeEntityObjectLifetime lifetime,
+ uint guid,
+ ushort positionSequence,
+ ushort teleportSequence,
+ ushort forcePositionSequence,
+ float positionX,
+ bool projectionRequiresTeleportHook,
+ Action? callback,
+ bool isLocalPlayer = true)
+ {
+ WorldSession.EntityPositionUpdate update = PositionUpdate(
+ guid,
+ positionSequence,
+ teleportSequence,
+ forcePositionSequence,
+ positionX);
+ Assert.True(lifetime.TryApplyPosition(
+ update,
+ isLocalPlayer,
+ forcePositionRotation: Quaternion.Identity,
+ currentLocalVelocity: new Vector3(1f, 2f, 3f),
+ projectionRequiresTeleportHook,
+ acknowledgeProjection: callback is null
+ ? null
+ : _ => callback(),
+ out PositionTimestampDisposition disposition,
+ out _,
+ out _));
+ Assert.True(disposition is PositionTimestampDisposition.Apply
+ or PositionTimestampDisposition.ForcePosition);
+ }
+
+ private static WorldSession.EntityPositionUpdate PositionUpdate(
+ uint guid,
+ ushort positionSequence,
+ ushort teleportSequence,
+ ushort forcePositionSequence,
+ float positionX)
+ {
+ return new WorldSession.EntityPositionUpdate(
+ guid,
+ new CreateObject.ServerPosition(
+ Cell,
+ positionX,
+ 20f,
+ 7f,
+ 1f,
+ 0f,
+ 0f,
+ 0f),
+ new Vector3(positionSequence, 2f, 3f),
+ PlacementId: positionSequence,
+ IsGrounded: true,
+ InstanceSequence: 1,
+ PositionSequence: positionSequence,
+ TeleportSequence: teleportSequence,
+ ForcePositionSequence: forcePositionSequence);
+ }
+
+ private static void AttachDormantBody(
+ RuntimeEntityObjectLifetime lifetime,
+ RuntimeEntityRecord canonical)
+ {
+ var body = new PhysicsBody
+ {
+ State = canonical.FinalPhysicsState,
+ Orientation = Quaternion.Identity,
+ InWorld = false,
+ };
+ lifetime.Entities.SetPhysicsBody(canonical, body);
+ }
+
+ private static void Bind(
+ RuntimeEntityObjectLifetime lifetime,
+ ulong generation)
+ {
+ var token = new RuntimeGenerationToken(generation);
+ lifetime.BindEventContext(() => token, static () => 1UL);
+ }
+
+ private static WorldSession.EntitySpawn Spawn(
+ uint guid,
+ ushort incarnation,
+ bool missile = false,
+ bool includePosition = true,
+ uint? parentGuid = null,
+ uint? setupId = 0x02000001u,
+ ushort positionSequence = 1,
+ float positionX = 10f,
+ uint positionCell = Cell,
+ ushort teleportSequence = 0,
+ ushort forcePositionSequence = 0)
+ {
+ CreateObject.ServerPosition? position = includePosition
+ ? new CreateObject.ServerPosition(
+ positionCell,
+ positionX,
+ 20f,
+ 7f,
+ 1f,
+ 0f,
+ 0f,
+ 0f)
+ : null;
+ uint rawState = (uint)(PhysicsStateFlags.Gravity
+ | (missile ? PhysicsStateFlags.Missile : 0));
+ var timestamps = new PhysicsTimestamps(
+ Position: positionSequence,
+ Movement: 1,
+ State: 1,
+ Vector: 1,
+ Teleport: teleportSequence,
+ ServerControlledMove: 1,
+ ForcePosition: forcePositionSequence,
+ ObjDesc: 1,
+ Instance: incarnation);
+ var physics = new PhysicsSpawnData(
+ rawState,
+ position,
+ Movement: null,
+ AnimationFrame: null,
+ setupId,
+ MotionTableId: null,
+ SoundTableId: null,
+ PhysicsScriptTableId: null,
+ Parent: parentGuid is { } parent
+ ? new PhysicsAttachment(parent, 1u)
+ : null,
+ Children: null,
+ Scale: null,
+ Friction: null,
+ Elasticity: null,
+ Translucency: null,
+ Velocity: null,
+ Acceleration: null,
+ AngularVelocity: null,
+ DefaultScriptType: null,
+ DefaultScriptIntensity: null,
+ timestamps);
+ return new WorldSession.EntitySpawn(
+ guid,
+ position,
+ setupId,
+ Array.Empty(),
+ Array.Empty(),
+ Array.Empty(),
+ BasePaletteId: null,
+ ObjScale: null,
+ Name: "initial-create",
+ ItemType: null,
+ MotionState: null,
+ MotionTableId: null,
+ PhysicsState: rawState,
+ InstanceSequence: incarnation,
+ PositionSequence: positionSequence,
+ ParentGuid: parentGuid,
+ ParentLocation: parentGuid is null ? null : 1u,
+ Physics: physics);
+ }
+
+ private sealed class EntityObserver(Action onEntity)
+ : IRuntimeEntityObjectObserver
+ {
+ public void OnEntity(in RuntimeEntityDelta delta) => onEntity(delta);
+ public void OnInventory(in RuntimeInventoryDelta delta)
+ {
+ }
+ }
+
+ private sealed class PlacementObserver(
+ Action onPlacement)
+ : IRuntimePlacementObserver
+ {
+ public void OnPlacement(in RuntimePlacementDelta delta) =>
+ onPlacement(delta);
+ }
+}
diff --git a/tests/AcDream.Runtime.Tests/Physics/RuntimeAuthoritativePositionRouteClassifierTests.cs b/tests/AcDream.Runtime.Tests/Physics/RuntimeAuthoritativePositionRouteClassifierTests.cs
index d895998e..5db0b5fa 100644
--- a/tests/AcDream.Runtime.Tests/Physics/RuntimeAuthoritativePositionRouteClassifierTests.cs
+++ b/tests/AcDream.Runtime.Tests/Physics/RuntimeAuthoritativePositionRouteClassifierTests.cs
@@ -48,7 +48,7 @@ public sealed class RuntimeAuthoritativePositionRouteClassifierTests
[Theory]
[InlineData(RuntimeCreateResidenceKind.Parented)]
[InlineData(RuntimeCreateResidenceKind.PickedUp)]
- internal void NewParentedOrPickedUpCreate_LeavesWorldAndAwaitsPosition(
+ internal void NewParentedOrPickedUpCreate_RemainsCelllessAndAwaitsPosition(
RuntimeCreateResidenceKind residence)
{
RuntimeAuthoritativePositionRoute route =
@@ -62,7 +62,7 @@ public sealed class RuntimeAuthoritativePositionRouteClassifierTests
Assert.Equal(RuntimeAuthoritativePositionDisposition.AwaitFreshPosition,
route.Disposition);
- Assert.True(route.LeaveWorld);
+ Assert.False(route.LeaveWorld);
Assert.False(route.PerformsSetPosition);
}
@@ -294,7 +294,7 @@ public sealed class RuntimeAuthoritativePositionRouteClassifierTests
RuntimeAuthoritativePositionRoute route = ClassifyLocal(
Authority(ushort.MaxValue, 0));
- Assert.Equal(RuntimeAuthoritativePositionDisposition.SetPosition,
+ Assert.Equal(RuntimeAuthoritativePositionDisposition.SetPositionSimple,
route.Disposition);
Assert.Equal(0x1012u, (uint)route.SetPositionFlags);
Assert.True(route.UnparentBeforeRouting);