using System.Collections.Immutable; using System.Numerics; using System.Reflection; 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 RuntimeInitialCreateContinuationExecutorTests { private const uint Landblock = 0xA9B50000u; private const uint Cell = Landblock | 0x0001u; private static readonly RuntimeInitialCreateExecutionInputs NoContact = new(UsePositionFromServer: false, PlayerDistance: 0f); // --------------------------------------------------------------- // A. Basic // --------------------------------------------------------------- [Fact] public void CompletedTopLevelCreateAdoptsOnceEmitsHookAndConvergesEveryLedger() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 1UL); RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(0x70020001u, 1), isLocalPlayer: true) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); Assert.Equal(Cell, receipt.FullCellId); Assert.Equal(RuntimeTeleportHookPhase.AfterEnterWorld, receipt.TeleportHookPhase); Assert.Equal(0, receipt.ReplayedDeferredChildCount); Assert.Equal( [ RuntimeInitialCreateExecutedActionKind.InitialAdoption, RuntimeInitialCreateExecutedActionKind.TeleportHookRequest, ], receipt.Trace.Select(static a => a.Kind)); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); Assert.Equal( 0, lifetime.Physics.SetPosition.CaptureOwnership().AcknowledgedPlacementCompletionCount); Assert.False(lifetime.TryGetInitialCreateResidence(canonical, out _)); // Retrying with the now-stale token is a distinct, safe no-op. Assert.Equal( RuntimeInitialCreateExecutionStatus.RejectedToken, lifetime.InitialCreateExecution.Execute( canonical, lease.Token, NoContact, out _)); } [Fact] public void MixedSimpleContinuationsDrainInExactSequenceOrderAndMutateSnapshot() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 2UL); const uint parentGuid = 0x70021100u; const uint guid = 0x70021000u; _ = lifetime.RegisterEntity(Spawn(parentGuid, 1)); RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false, parentGuid: parentGuid), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); Assert.False(lease.Placement.IsValid); // parented -> AwaitFreshPosition, no placement var appearance = new ObjDescEvent.Parsed( guid, new CreateObject.ModelData( 0x04000002u, Array.Empty(), Array.Empty(), Array.Empty()), InstanceSequence: 1, ObjDescSequence: 2); Assert.True(lifetime.TryApplyObjDesc(appearance, null, out _)); var vector = new VectorUpdate.Parsed( guid, new Vector3(1f, 2f, 3f), Vector3.Zero, InstanceSequence: 1, VectorSequence: 2); Assert.True(lifetime.TryApplyVector(vector, null, out _)); var state = new SetState.Parsed( guid, (uint)PhysicsStateFlags.Gravity, InstanceSequence: 1, StateSequence: 2); Assert.True(lifetime.TryApplyState(state, null, out _, out _)); var observed = new List(); using IDisposable subscription = lifetime.Events.Subscribe( new EntityObserver(delta => observed.Add(delta.Change))); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); Assert.Equal( [ RuntimeInitialCreateExecutedActionKind.InitialAdoption, RuntimeInitialCreateExecutedActionKind.ObjDesc, RuntimeInitialCreateExecutedActionKind.Vector, RuntimeInitialCreateExecutedActionKind.State, ], receipt.Trace.Select(static a => a.Kind)); Assert.Equal([1UL, 2UL, 3UL], receipt.Trace .Where(static a => a.Sequence != 0UL) .Select(static a => a.Sequence)); Assert.Equal( [RuntimeEntityChange.Updated, RuntimeEntityChange.Updated, RuntimeEntityChange.Updated], observed); Assert.Equal(0x04000002u, canonical.Snapshot.BasePaletteId); Assert.Equal(new Vector3(1f, 2f, 3f), canonical.Snapshot.Physics!.Value.Velocity); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } // Round 4 R4-4 (mandated regression test): a FIFO of ObjDesc then Vector // - NEITHER touches any of the four executor-tracked baseline fields // (PositionAuthorityVersion/CreateIntegrationVersion/FullCellId/ // PlacementCommitVersion), so NEITHER may call AdvanceExecutorBaseline // at all. An observer bumps PositionAuthorityVersion externally during // ObjDesc's own publish; because ObjDesc's apply never rebaselines that // field, the STALE Expected value survives into the next check. The // drain must detect this external race at ConsumeExecuted (the "next // step") and abandon - NOT silently absorb it and report Released, which // a blanket four-field rebaseline (the pre-R4-4 shape) would have done. [Fact] public void FieldMaskedBaselinePrecisionDetectsAnExternalPositionRaceDuringAnUnrelatedObjDescPublish() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 96UL); const uint parentGuid = 0x70038000u; const uint guid = 0x70038001u; _ = lifetime.RegisterEntity(Spawn(parentGuid, 1)); RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false, parentGuid: parentGuid), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); var appearance = new ObjDescEvent.Parsed( guid, new CreateObject.ModelData( 0x07000002u, Array.Empty(), Array.Empty(), Array.Empty()), InstanceSequence: 1, ObjDescSequence: 2); Assert.True(lifetime.TryApplyObjDesc(appearance, null, out _)); var vector = new VectorUpdate.Parsed( guid, new Vector3(1f, 2f, 3f), Vector3.Zero, InstanceSequence: 1, VectorSequence: 2); Assert.True(lifetime.TryApplyVector(vector, null, out _)); bool bumped = false; using IDisposable subscription = lifetime.Events.Subscribe(new EntityObserver(delta => { if (bumped || delta.Change is not RuntimeEntityChange.Updated) return; bumped = true; // External, non-executor mutation of PositionAuthorityVersion // during the ObjDesc stage's OWN publish. lifetime.Entities.AdvancePositionAuthority(canonical); })); Assert.Equal( RuntimeInitialCreateExecutionStatus.RejectedAuthority, lifetime.InitialCreateExecution.Execute( canonical, lease.Token, NoContact, out RuntimeInitialCreateExecutionReceipt receipt)); Assert.True(bumped); Assert.Equal(default, receipt); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } // Round 3 A1 (mandated regression test): the executor's applies must // write InboundPhysicsStateController's OWN _snapshots[guid] - the // legacy merge base - in lockstep with RuntimeEntityRecord.Snapshot. If // they only wrote the record's own Snapshot (as before A1), the FIRST // legacy wire apply reached after the residence drain would re-merge // against a STALE _snapshots[guid] base and silently revert every fact // the drain just committed. [Fact] public void DrainedAppearanceAndPoseSurviveTheNextLegacyWireApply() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 30UL); const uint parentGuid = 0x7002D100u; const uint guid = 0x7002D000u; _ = lifetime.RegisterEntity(Spawn(parentGuid, 1)); RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false, parentGuid: parentGuid), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); // Drain an ObjDesc continuation that changes appearance (a fresh // BasePaletteId) as part of the residence FIFO. var appearance = new ObjDescEvent.Parsed( guid, new CreateObject.ModelData( 0x06000002u, Array.Empty(), Array.Empty(), Array.Empty()), InstanceSequence: 1, ObjDescSequence: 2); Assert.True(lifetime.TryApplyObjDesc(appearance, null, out _)); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); Assert.Contains( RuntimeInitialCreateExecutedActionKind.ObjDesc, receipt.Trace.Select(static a => a.Kind)); Assert.Equal(0x06000002u, canonical.Snapshot.BasePaletteId); // Now run an ORDINARY legacy wire apply (Vector) on a channel the // drain never touched. Its merge base is InboundPhysicsStateController's // OWN _snapshots[guid] - if that store still held the pre-drain // appearance (A1's bug), this apply's `old with { ... }` merge would // carry the STALE BasePaletteId back into canonical.Snapshot via // RefreshSnapshot, silently reverting the drained fact. var vector = new VectorUpdate.Parsed( guid, new Vector3(4f, 5f, 6f), Vector3.Zero, InstanceSequence: 1, VectorSequence: 2); Assert.True(lifetime.TryApplyVector(vector, null, out _)); Assert.Equal(0x06000002u, canonical.Snapshot.BasePaletteId); Assert.Equal(new Vector3(4f, 5f, 6f), canonical.Snapshot.Physics!.Value.Velocity); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } // Round 3 B12: the WeenieDescription tail action must drive the object // table the same way RuntimeLiveEntitySessionController.OnSpawned does // for the non-residence direct-host Create path (ApplyAcceptedSpawn) - // the prior "zero callers" claim for this wiring was false. A // residence-pending admission deliberately never wires the object table // at the initial Create (RegisterEntityCore's beginInitialResidence // branch); the FIRST time this guid's entry can appear is exactly here. [Fact] public void WeenieDescriptionStageWiresTheObjectTableExactlyOnce() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 34UL); const uint guid = 0x7002E400u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); int objectCountBeforeDrain = lifetime.Objects.ObjectCount; WorldSession.EntitySpawn sameCreate = Spawn( guid, 1, includePosition: false, positionSequence: 2); PhysicsSpawnData physics = sameCreate.Physics!.Value; sameCreate = sameCreate with { Physics = physics with { Timestamps = physics.Timestamps with { State = 2, Vector = 2 }, }, }; _ = lifetime.RegisterEntityWithInitialResidence(sameCreate, isLocalPlayer: false); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); Assert.Contains( RuntimeInitialCreateExecutedActionKind.WeenieDescription, receipt.Trace.Select(static a => a.Kind)); Assert.Equal(objectCountBeforeDrain + 1, lifetime.Objects.ObjectCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } // Round 4 R4-3: ApplyWeenieDescriptionAction's object-table apply window. // ClientObjectTable.Ingest publishes ObjectAdded/ObjectUpdated // SYNCHRONOUSLY - a subscriber may re-enter a wire apply for the SAME // entity from inside that dispatch. Because AdvanceExecutorBaseline now // runs BEFORE _applyAcceptedSpawn (not after), the residence's baseline // is already current at the moment the reentrant call runs; the // residence is not retired and the envelope still completes. [Fact] public void ObjectTableSubscriberReenteringAWireApplyDuringIngestDoesNotRetireTheResidenceAndTheEnvelopeCompletes() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 94UL); const uint guid = 0x70037000u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); WorldSession.EntitySpawn sameCreate = Spawn( guid, 1, includePosition: false, positionSequence: 2); PhysicsSpawnData physics = sameCreate.Physics!.Value; sameCreate = sameCreate with { Physics = physics with { Timestamps = physics.Timestamps with { State = 2, Vector = 2 }, }, }; _ = lifetime.RegisterEntityWithInitialResidence(sameCreate, isLocalPlayer: false); bool reentered = false; lifetime.Objects.ObjectAdded += addedObject => { if (reentered) return; reentered = true; // A reentrant wire apply for a channel this envelope does NOT // itself carry a dedicated stage for at this admission // (Vector IS one of this envelope's own stages here, but the // residence is still "pending" mid-drain, so this legitimately // defers into the SAME FIFO rather than applying immediately - // proving the residence survives the reentrant call). var vector = new VectorUpdate.Parsed( guid, new Vector3(9f, 8f, 7f), Vector3.Zero, InstanceSequence: 1, VectorSequence: 3); Assert.True(lifetime.TryApplyVector(vector, null, out _)); }; RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); Assert.True(reentered); Assert.Contains( RuntimeInitialCreateExecutedActionKind.WeenieDescription, receipt.Trace.Select(static a => a.Kind)); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } // Round 4 R4-3: a NESTED REPLACEMENT (a newer incarnation Create for the // SAME guid) arriving from within Ingest's own synchronous dispatch // invalidates the exact canonical incarnation the drain is still // executing against. ApplyAcceptedSpawn re-checks currency AFTER its own // object-table apply (mirroring RuntimeLiveEntitySessionController.cs:87's // gate on that same call's result) and returns false; the executor must // treat this as a typed abandonment - the remaining tail (ResidentCellCleanup) // never runs. [Fact] public void NestedReplacementDuringObjectTableIngestAbandonsTheWeenieDescriptionStageWithNoFurtherStages() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 95UL); const uint guid = 0x70037100u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); WorldSession.EntitySpawn sameCreate = Spawn( guid, 1, includePosition: false, positionSequence: 2); PhysicsSpawnData physics = sameCreate.Physics!.Value; sameCreate = sameCreate with { Physics = physics with { Timestamps = physics.Timestamps with { State = 2, Vector = 2 }, }, }; _ = lifetime.RegisterEntityWithInitialResidence(sameCreate, isLocalPlayer: false); bool reentered = false; lifetime.Objects.ObjectAdded += addedObject => { if (reentered) return; reentered = true; _ = lifetime.RegisterEntity(Spawn(guid, 2, includePosition: false)); }; Assert.Equal( RuntimeInitialCreateExecutionStatus.RejectedAuthority, lifetime.InitialCreateExecution.Execute( canonical, lease.Token, NoContact, out RuntimeInitialCreateExecutionReceipt receipt)); Assert.True(reentered); Assert.Equal(default, receipt); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } // Round 3 A2 (mandated regression test): a standalone ObjDesc // continuation admits a fresh palette (bumping the gate's ObjDesc // channel and proving A1's snapshot lockstep feeds entry 2's merge // base), THEN a same-incarnation Create arrives whose OWN raw // WeenieDescription packet carries a DIFFERENT MotionTableId. Appearance // fields (BasePaletteId etc.) are legitimately re-applied by the // envelope's OWN dedicated ObjDesc stage (retail-faithful: a same- // generation Create's tail always re-runs its own ObjDesc first) - that // is NOT what A2 fixes. MotionTableId has NO dedicated envelope stage; // WeenieDescription's merge is the ONLY place it can move, which // isolates MergeUntimestampedCreate's "retained wins" rule cleanly: the // prior wholesale RefreshSnapshot bug would have silently overwritten it // with the incoming raw packet's value instead. [Fact] public void SameIncarnationWeenieDescriptionMergePreservesRetainedMotionTableId() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 31UL); const uint parentGuid = 0x7002D300u; const uint guid = 0x7002D200u; _ = lifetime.RegisterEntity(Spawn(parentGuid, 1)); WorldSession.EntitySpawn initial = Spawn(guid, 1, includePosition: false, parentGuid: parentGuid); PhysicsSpawnData initialPhysics = initial.Physics!.Value; initial = initial with { MotionTableId = 0x12345678u, Physics = initialPhysics with { MotionTableId = 0x12345678u }, }; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(initial, isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); // FIFO entry 1: standalone ObjDesc with a NEW palette, bumping the // gate's ObjDesc channel to 2. var newAppearance = new ObjDescEvent.Parsed( guid, new CreateObject.ModelData( 0x07000002u, Array.Empty(), Array.Empty(), Array.Empty()), InstanceSequence: 1, ObjDescSequence: 2); Assert.True(lifetime.TryApplyObjDesc(newAppearance, null, out _)); // FIFO entry 2: a same-incarnation Create whose OWN raw // WeenieDescription packet carries a DIFFERENT MotionTableId and a // NEWER-still ObjDesc channel stamp (3). WorldSession.EntitySpawn sameCreate = Spawn( guid, 1, includePosition: false, parentGuid: parentGuid, positionSequence: 2); PhysicsSpawnData samePhysics = sameCreate.Physics!.Value; sameCreate = sameCreate with { MotionTableId = 0x99999999u, Physics = samePhysics with { MotionTableId = 0x99999999u, Timestamps = samePhysics.Timestamps with { ObjDesc = 3, State = 2, Vector = 2 }, }, }; RuntimeEntityRegistrationResult same = lifetime .RegisterEntityWithInitialResidence(sameCreate, isLocalPlayer: false); Assert.Equal(CreateObjectTimestampDisposition.ExistingGeneration, same.Inbound.Disposition); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); Assert.Contains( RuntimeInitialCreateExecutedActionKind.WeenieDescription, receipt.Trace.Select(static a => a.Kind)); // The envelope's OWN ObjDesc stage correctly re-applies THIS // Create's own appearance (sameCreate never set its own palette, so // it legitimately reverts to null - retail-faithful, not what A2 // fixes). Entry 1's transient fresh palette was always going to be // superseded by entry 2's OWN ObjDesc stage; that stage - not // WeenieDescription - owns this field. Assert.Null(canonical.Snapshot.BasePaletteId); // MotionTableId has no dedicated envelope stage - WeenieDescription's // merge must keep the RETAINED value, never adopt incoming's raw // packet wholesale. Assert.Equal(0x12345678u, canonical.Snapshot.Physics!.Value.MotionTableId); Assert.Equal(0x12345678u, canonical.Snapshot.MotionTableId); // ObjDesc's timestamp DOES have a dedicated stage that legitimately // advances it further as part of entry 2's own admission (unlike // MotionTableId). Assert.Equal(3, canonical.Snapshot.Physics!.Value.Timestamps.ObjDesc); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } // --------------------------------------------------------------- // B. Standalone continuations (Movement/Pickup/Parent) + enqueue- // during-drain (ConsumeExecuted's Revised arm). // --------------------------------------------------------------- [Fact] public void StandaloneMovementContinuationAppliesPayloadAndPublishesUpdated() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 40UL); const uint guid = 0x7002F000u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); var motion = new WorldSession.EntityMotionUpdate( guid, new CreateObject.ServerMotionState(0x3d, 0x11), InstanceSequence: 1, MovementSequence: 2, ServerControlSequence: 1, IsAutonomous: false); // Movement (2) advances and ServerControl (1) is equal-not-stale, so // the gate accepts the payload outright. Assert.True(lifetime.TryApplyMotion(motion, retainPayload: true, null, out _, out _)); var observed = new List(); using IDisposable subscription = lifetime.Events.Subscribe( new EntityObserver(delta => observed.Add(delta.Change))); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); Assert.Equal( [ RuntimeInitialCreateExecutedActionKind.InitialAdoption, RuntimeInitialCreateExecutedActionKind.Movement, ], receipt.Trace.Select(static a => a.Kind)); Assert.Equal(new CreateObject.ServerMotionState(0x3d, 0x11), canonical.Snapshot.MotionState); Assert.Equal(2, canonical.Snapshot.Physics!.Value.Timestamps.Movement); Assert.Equal([RuntimeEntityChange.Updated], observed); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } [Fact] public void StandaloneMovementContinuationTimestampOnlyStampsWithoutPayloadOrPublish() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 41UL); const uint guid = 0x7002F100u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); Assert.Null(canonical.Snapshot.MotionState); var motion = new WorldSession.EntityMotionUpdate( guid, new CreateObject.ServerMotionState(0x3d, 0x11), InstanceSequence: 1, MovementSequence: 2, // Retail consumes MOVEMENT_TS before discovering a stale // SERVER_CONTROLLED_MOVE_TS (0, older than the gate's seeded 1) - // Movement itself still advances (hasTimestampMutation) even // though the overall event, and thus the payload, is rejected. ServerControlSequence: 0, IsAutonomous: false); Assert.False(lifetime.TryApplyMotion(motion, retainPayload: true, null, out _, out _)); Assert.True(lifetime.InitialCreateResidences.TryGetTransaction( canonical, out RuntimeInitialCreateResidenceLease retained)); Assert.Single(retained.Continuations); var observed = new List(); using IDisposable subscription = lifetime.Events.Subscribe( new EntityObserver(delta => observed.Add(delta.Change))); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); Assert.Contains( RuntimeInitialCreateExecutedActionKind.Movement, receipt.Trace.Select(static a => a.Kind)); // Timestamp landed; no payload, no publish (matches the legacy // timestamp-only branch). Assert.Equal(2, canonical.Snapshot.Physics!.Value.Timestamps.Movement); Assert.Null(canonical.Snapshot.MotionState); Assert.Empty(observed); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } [Fact] public void StandalonePickupContinuationLeavesWorldThroughTheDrainAndResidenceStillReleases() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 42UL); const uint guid = 0x7002F200u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); Assert.Equal(Cell, canonical.FullCellId); // A committed parent projection so Pickup's own EndChildProjection // has something real to tear down. var relation = new ParentAttachmentRelation( 0x7002F300u, guid, ParentLocation: 1u, PlacementId: 0u, ParentInstanceSequence: 1, ChildPositionSequence: 1); lifetime.Entities.ParentAttachments.AcceptCreateObjectRelation(relation); Assert.True(lifetime.Entities.ParentAttachments.CommitProjection(relation)); Assert.True(lifetime.Entities.ParentAttachments.HasCommittedParent(guid)); Assert.True(lifetime.TryApplyPickup( new PickupEvent.Parsed(guid, InstanceSequence: 1, PositionSequence: 2), null, out _)); var observed = new List(); using IDisposable subscription = lifetime.Events.Subscribe( new EntityObserver(delta => observed.Add(delta.Change))); ulong clockEpochBefore = canonical.ObjectClockEpoch; RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); Assert.Equal( [ RuntimeInitialCreateExecutedActionKind.InitialAdoption, RuntimeInitialCreateExecutedActionKind.Pickup, ], receipt.Trace.Select(static a => a.Kind)); Assert.Equal([RuntimeEntityChange.Withdrawn], observed); Assert.Equal(0u, canonical.FullCellId); // SuspendObjectClock bumps the epoch - the clock is no longer live. Assert.NotEqual(clockEpochBefore, canonical.ObjectClockEpoch); Assert.False(lifetime.Entities.ParentAttachments.HasCommittedParent(guid)); Assert.False(lifetime.Entities.ParentAttachments.TryGetRecoveryProjection(guid, out _)); Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership().ActiveOperationCount); // Residence still releases cleanly even though this continuation // left the world mid-drain. Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } [Fact] public void StandaloneParentContinuationAppliesPositionTimestampOnlyMergeWithNoReDefer() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 43UL); const uint parentGuid = 0x7002F400u; const uint childGuid = 0x7002F500u; _ = lifetime.RegisterEntity(Spawn(parentGuid, 1)); RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(childGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); var parentUpdate = new ParentEvent.Parsed( parentGuid, childGuid, ParentLocation: 1u, PlacementId: 0u, ParentInstanceSequence: 1, ChildPositionSequence: 2); // The parent is alive at admission AND stays alive all the way // through the drain - contrast with // ParentContinuationRevalidatesLiveParentAtExecutionAndReDefersOnMismatch, // which deletes the parent between admission and execution. Assert.True(lifetime.TryApplyParent(parentUpdate, null, out _)); var observed = new List(); using IDisposable subscription = lifetime.Events.Subscribe( new EntityObserver(delta => observed.Add(delta.Change))); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); Assert.Equal( [ RuntimeInitialCreateExecutedActionKind.InitialAdoption, RuntimeInitialCreateExecutedActionKind.Parent, ], receipt.Trace.Select(static a => a.Kind)); // ApplyAcceptedParent is position-timestamp-only: the shared // POSITION_TS channel advances but no pose/ParentGuid field moves // (the actual attach commit is TryCommitParent's job, out of scope // here). Assert.Equal(2, canonical.Snapshot.Physics!.Value.Timestamps.Position); Assert.Null(canonical.Snapshot.ParentGuid); Assert.Equal([RuntimeEntityChange.Updated], observed); // The successful path hands NOTHING to ParentAttachments' unresolved // bucket - the direct contrast with the re-defer test's // UnresolvedRelationCount == 1. Assert.Equal(0, lifetime.Entities.ParentAttachments.UnresolvedRelationCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } // Round 3 arc-10 (enqueue-during-drain): a continuation enqueued from an // observer mid-drain bumps the completed entry's Adoption.Revision in // place; ConsumeExecuted must return Revised (not Released) so the SAME // Execute() call's outer while(true) loop re-fetches via Complete and // drains only the newly-appended tail. The already-applied prefix must // never replay. [Fact] public void EnqueueDuringDrainExercisesConsumeExecutedRevisedArmAndDrainsTailOnlyOnce() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 44UL); const uint guid = 0x7002F600u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); var appearance = new ObjDescEvent.Parsed( guid, new CreateObject.ModelData( 0x08000002u, Array.Empty(), Array.Empty(), Array.Empty()), InstanceSequence: 1, ObjDescSequence: 2); Assert.True(lifetime.TryApplyObjDesc(appearance, null, out _)); var observed = new List(); bool enqueuedFromObserver = false; using IDisposable subscription = lifetime.Events.Subscribe(new EntityObserver(delta => { observed.Add(delta.Change); if (enqueuedFromObserver) return; enqueuedFromObserver = true; // Reentrant enqueue, mid-drain, from inside the ObjDesc // continuation's own publish: the residence is still in // RuntimeInitialCreateResidenceState's _completed dictionary // (not yet ConsumeExecuted'd), so this routes through // CanEnqueue's completed-entry branch and bumps Revision in // place. var vector = new VectorUpdate.Parsed( guid, new Vector3(7f, 8f, 9f), Vector3.Zero, InstanceSequence: 1, VectorSequence: 2); Assert.True(lifetime.TryApplyVector(vector, null, out _)); })); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); // Each action appears exactly once - the Revised loop-around must // not replay the already-applied ObjDesc prefix. Assert.Equal( [ RuntimeInitialCreateExecutedActionKind.InitialAdoption, RuntimeInitialCreateExecutedActionKind.ObjDesc, RuntimeInitialCreateExecutedActionKind.Vector, ], receipt.Trace.Select(static a => a.Kind)); Assert.Equal( [RuntimeEntityChange.Updated, RuntimeEntityChange.Updated], observed); Assert.Equal(0x08000002u, canonical.Snapshot.BasePaletteId); Assert.Equal(new Vector3(7f, 8f, 9f), canonical.Snapshot.Physics!.Value.Velocity); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } // --------------------------------------------------------------- // C. Envelope atomicity // --------------------------------------------------------------- [Fact] public void SameIncarnationEnvelopePublishesNothingUntilEveryStageCommitsThenPublishesInStageOrder() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 3UL); const uint guid = 0x70022000u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); WorldSession.EntitySpawn sameCreate = Spawn( guid, 1, includePosition: false, positionSequence: 2) with { Name = "same-incarnation", }; PhysicsSpawnData physics = sameCreate.Physics!.Value; sameCreate = sameCreate with { Physics = physics with { Timestamps = physics.Timestamps with { ObjDesc = 2, State = 2, Vector = 2 }, }, }; RuntimeEntityRegistrationResult same = lifetime .RegisterEntityWithInitialResidence(sameCreate, isLocalPlayer: false); Assert.Equal(CreateObjectTimestampDisposition.ExistingGeneration, same.Inbound.Disposition); var observed = new List(); int countAtFirstObservation = -1; using IDisposable subscription = lifetime.Events.Subscribe(new EntityObserver(delta => { observed.Add(delta.Change); countAtFirstObservation = observed.Count; })); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); // No event fired until the whole envelope committed: the very first // observation must already carry every publish, not a partial subset // trickling in one at a time. Assert.True(observed.Count >= 1); Assert.Equal(observed.Count, countAtFirstObservation); RuntimeInitialCreateExecutedActionKind[] envelopeStages = receipt.Trace .Where(static a => a.Sequence == 1UL) .Select(static a => a.Kind) .ToArray(); // A same-Create with neither a parent nor a position decomposes into // a Pickup branch (retail priority: Parent > Position > Pickup - see // InboundPhysicsStateController.BuildSameGenerationEvents), plus the // AP-119 PreTailDescriptionAdaptation stage this envelope always // carries when SameGenerationEvents is present. Assert.Equal( [ RuntimeInitialCreateExecutedActionKind.PreTailDescriptionAdaptation, RuntimeInitialCreateExecutedActionKind.ObjDesc, RuntimeInitialCreateExecutedActionKind.Pickup, RuntimeInitialCreateExecutedActionKind.State, RuntimeInitialCreateExecutedActionKind.Vector, RuntimeInitialCreateExecutedActionKind.WeenieDescription, RuntimeInitialCreateExecutedActionKind.ResidentCellCleanup, ], envelopeStages); // This entity never claimed a cell (PickedUp residence, then the // envelope's own Pickup stage nulls the position again) - Gap 4(c), // the no-cell-claimed destruction-mark disposition. RuntimeInitialCreateExecutedAction cleanup = Assert.Single( receipt.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.ResidentCellCleanup); Assert.Equal( RuntimeResidentCellCleanupDisposition.CelllessNoWeenieMarkUnreachable, cleanup.ResidentCellCleanupDisposition); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } [Fact] public void ResidentCellCleanupUnmarksWhenCellClaimedAndAlreadyResident() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 18UL); const uint guid = 0x7002A000u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); Assert.Equal(Cell, canonical.FullCellId); // A same-Create carrying its own position: PositionSource == // SameIncarnationCreate makes effectiveContact always true for the // non-local classify branch, and the entity is already resident // (FullCellId == Cell, unchanged since neither Position's own // refreshPosition:false merge nor WeenieDescription's touch it), so // this classifies to Interpolate - no new placement, no yield. WorldSession.EntitySpawn sameCreate = Spawn(guid, 1, positionSequence: 2, positionX: 15f); PhysicsSpawnData physics = sameCreate.Physics!.Value; sameCreate = sameCreate with { Physics = physics with { Timestamps = physics.Timestamps with { ObjDesc = 2 }, }, }; _ = lifetime.RegisterEntityWithInitialResidence(sameCreate, isLocalPlayer: false); var inputs = new RuntimeInitialCreateExecutionInputs( UsePositionFromServer: false, PlayerDistance: 10f); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, inputs); RuntimeInitialCreateExecutedAction cleanup = Assert.Single( receipt.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.ResidentCellCleanup); Assert.Equal( RuntimeResidentCellCleanupDisposition.ResidentUnmarked, cleanup.ResidentCellCleanupDisposition); Assert.Equal(Cell, canonical.FullCellId); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } [Fact] public void ResidentCellCleanupFailsClosedWhenClaimedCelllessOutcomeIsNotUnderLostCellOwnership() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 19UL); const uint guid = 0x7002A100u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false), isLocalPlayer: true) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); Assert.False(lease.Placement.IsValid); Assert.Equal(0u, canonical.FullCellId); // The same-Create's own position exists on the wire (claims a // cell), but with UsePositionFromServer=false this LocalPlayer // classify branch resolves to NoPositionOperation - no SetPosition // ever begins, so nothing tracks a lost-cell/deferred operation // for this entity either. The entity was never resident (PickedUp // initial, no placement ever ran) - claimed + celless + NOT under // lost-cell ownership is the exact invariant violation this test // requires. Round 3 B1: this is now a typed abandonment (Abandon // retires the residence and discards progress), never a throw // escaping Execute - the ledger must still fully converge. WorldSession.EntitySpawn sameCreate = Spawn(guid, 1, positionSequence: 2, positionX: 15f); PhysicsSpawnData physics = sameCreate.Physics!.Value; sameCreate = sameCreate with { Physics = physics with { Timestamps = physics.Timestamps with { ObjDesc = 2 }, }, }; _ = lifetime.RegisterEntityWithInitialResidence(sameCreate, isLocalPlayer: true); var inputs = new RuntimeInitialCreateExecutionInputs( UsePositionFromServer: false, PlayerDistance: 0f); RuntimeInitialCreateExecutionStatus status = lifetime.InitialCreateExecution.Execute( canonical, lease.Token, inputs, out RuntimeInitialCreateExecutionReceipt receipt); Assert.Equal(RuntimeInitialCreateExecutionStatus.RejectedAuthority, status); Assert.Equal(default, receipt); Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership().ActiveOperationCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); Assert.False(lifetime.TryGetInitialCreateResidence(canonical, out _)); // A retry with the now-retired token is a distinct, safe no-op - // proving Abandon actually retired the residence rather than // leaving it sitting fully current for a replay. Assert.Equal( RuntimeInitialCreateExecutionStatus.RejectedToken, lifetime.InitialCreateExecution.Execute( canonical, lease.Token, inputs, out RuntimeInitialCreateExecutionReceipt retryReceipt)); Assert.Equal(default, retryReceipt); } [Fact] public void EnvelopePositionStageRequiringSetPositionYieldsResumesAndPublishesOnceAfterCompletion() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 20UL); const uint guid = 0x7002B000u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); Assert.False(lease.Placement.IsValid); Assert.Equal(0u, canonical.FullCellId); AttachDormantBody(lifetime, canonical); // Cellless (CommittedCellId == canonical.FullCellId == 0) forces // SetPosition regardless of contact/distance for the non-local // classify branch - this same-Create's OWN Position stage will // require a real placement and yield mid-envelope. WorldSession.EntitySpawn sameCreate = Spawn(guid, 1, positionSequence: 2, positionX: 15f); PhysicsSpawnData physics = sameCreate.Physics!.Value; sameCreate = sameCreate with { Physics = physics with { Timestamps = physics.Timestamps with { ObjDesc = 2, State = 2, Vector = 2 }, }, }; _ = lifetime.RegisterEntityWithInitialResidence(sameCreate, isLocalPlayer: false); var observed = new List(); using IDisposable subscription = lifetime.Events.Subscribe( new EntityObserver(delta => observed.Add(delta.Change))); RuntimeInitialCreateExecutionStatus status = lifetime.InitialCreateExecution.Execute( canonical, lease.Token, NoContact, out RuntimeInitialCreateExecutionReceipt pending); Assert.Equal(RuntimeInitialCreateExecutionStatus.AwaitingContinuationPlacement, status); Assert.Equal(default, pending); // ObjDesc already committed (buffered) before Position began its // own placement lifecycle - prove NOTHING has published yet, even // though a stage before the yield point already ran. Assert.Empty(observed); RuntimeEntityKey key = canonical.Key!.Value; Assert.True(lifetime.InitialCreateExecution .TryGetPendingContinuationRoute(key, out RuntimeAuthoritativePositionRoute route)); Assert.Equal(RuntimeAuthoritativePositionDisposition.SetPosition, route.Disposition); CompletePendingContinuationPlacement(lifetime, key, route); Assert.Empty(observed); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); // Every buffered per-stage event from this ONE envelope publishes // exactly once, only now that the envelope fully committed. Assert.Equal( [ RuntimeEntityChange.Updated, RuntimeEntityChange.Updated, RuntimeEntityChange.Updated, RuntimeEntityChange.Updated, RuntimeEntityChange.Updated, ], observed); RuntimeInitialCreateExecutedActionKind[] envelopeStages = receipt.Trace .Where(static a => a.Sequence == 1UL) .Select(static a => a.Kind) .ToArray(); Assert.Equal( [ RuntimeInitialCreateExecutedActionKind.PreTailDescriptionAdaptation, RuntimeInitialCreateExecutedActionKind.ObjDesc, RuntimeInitialCreateExecutedActionKind.Position, RuntimeInitialCreateExecutedActionKind.State, RuntimeInitialCreateExecutedActionKind.Vector, RuntimeInitialCreateExecutedActionKind.WeenieDescription, RuntimeInitialCreateExecutedActionKind.ResidentCellCleanup, ], envelopeStages); RuntimeInitialCreateExecutedAction positionAction = Assert.Single( receipt.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.Position); Assert.Equal( RuntimeAuthoritativePositionDisposition.SetPosition, positionAction.PositionDisposition); // The continuation's own SetPosition committed a real cell - // ResidentCellCleanup now sees a claimed, resident entity. Assert.Equal(Cell, canonical.FullCellId); RuntimeInitialCreateExecutedAction cleanup = Assert.Single( receipt.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.ResidentCellCleanup); Assert.Equal( RuntimeResidentCellCleanupDisposition.ResidentUnmarked, cleanup.ResidentCellCleanupDisposition); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership().ActiveOperationCount); } // Gap 2 (failure injection between every same-Create envelope stage): // the Position stage is the ONLY boundary inside a SameIncarnationCreate // envelope that this suite can interrupt via a REAL yield. Every other // stage kind - PreTailDescriptionAdaptation, ObjDesc, CreateParent/ // Parent/Pickup, Movement, State, Vector, WeenieDescription, // ResidentCellCleanup - is a pure, synchronous, in-memory // RuntimeEntityRecord/InboundPhysicsStateController mutation with NO // async placement lifecycle and NO event dispatched until the whole // envelope's buffered publish flush at the very end (see ApplyEnvelope): // there is no callback, no I/O, and no point where control ever returns // to a caller (or where an event observer could reenter) mid-stage. // Injecting a synthetic "failure" between two purely synchronous stages // would require adding a diagnostic seam to production code purely to // make a test possible, which this task's own instructions forbid. The // Position stage is structurally different: it is the one action kind // whose disposition can require a real RuntimeSetPositionState // placement round-trip (Begin/Watch/yield/host-prepare-submit- // acknowledge/resume) - the SAME mechanism a standalone (non-envelope) // Position continuation uses. The two tests below exhaust that // reachable boundary: a successful resume (see // EnvelopePositionStageRequiringSetPositionYieldsResumesAndPublishesOnceAfterCompletion // above) and an abandonment at that exact boundary (immediately below). [Fact] public void EnvelopeAbandonedDuringPositionStageYieldPublishesNothingAndConvergesEveryLedger() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 21UL); const uint guid = 0x7002B100u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); Assert.False(lease.Placement.IsValid); AttachDormantBody(lifetime, canonical); // Same recipe as the successful-resume test: cellless forces // SetPosition, guaranteeing a mid-envelope yield after ObjDesc (and // Position's own snapshot-merge commit) already ran. WorldSession.EntitySpawn sameCreate = Spawn(guid, 1, positionSequence: 2, positionX: 15f); PhysicsSpawnData physics = sameCreate.Physics!.Value; sameCreate = sameCreate with { Physics = physics with { Timestamps = physics.Timestamps with { ObjDesc = 2, State = 2, Vector = 2 }, }, }; _ = lifetime.RegisterEntityWithInitialResidence(sameCreate, isLocalPlayer: false); var observed = new List(); using IDisposable subscription = lifetime.Events.Subscribe( new EntityObserver(delta => observed.Add(delta.Change))); RuntimeInitialCreateExecutionStatus status = lifetime.InitialCreateExecution.Execute( canonical, lease.Token, NoContact, out _); Assert.Equal(RuntimeInitialCreateExecutionStatus.AwaitingContinuationPlacement, status); Assert.Empty(observed); // Abandon between Execute retries at the yield: delete the entity // rather than ever completing the continuation's placement. Assert.True(lifetime.TryAcceptDelete( new DeleteObject.Parsed(guid, canonical.Incarnation), isLocalPlayer: false, removeRetainedObject: true, out RuntimeEntityDeleteAcceptance acceptance)); lifetime.CompleteAcceptedDelete(acceptance); // The delete's own Deleted is the only observed event - NONE of the // envelope's buffered stages (ObjDesc, and Position's own snapshot // merge, both of which had ALREADY committed before the yield) ever // publish. TryAcceptDelete's own unconditional Physics.SetPosition.Forget // already cancels the still-pending continuation placement here // (same key, any token); DiscardProgress ALSO forgets it // defensively (see its remarks) for callers that retire a // residence without going through the full delete path. Assert.Equal([RuntimeEntityChange.Deleted], observed); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership().ActiveOperationCount); Assert.Equal( 0, lifetime.Physics.SetPosition.CaptureOwnership().AcknowledgedPlacementCompletionCount); // A stale Execute() with the original token, after the entity is // gone, must not resurrect anything either. Assert.Equal( RuntimeInitialCreateExecutionStatus.RejectedToken, lifetime.InitialCreateExecution.Execute( canonical, lease.Token, NoContact, out RuntimeInitialCreateExecutionReceipt receipt)); Assert.Equal(default, receipt); } [Fact] public void EnvelopeStageRetryDoesNotDuplicateAlreadyCommittedStages() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 4UL); const uint guid = 0x70022100u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); WorldSession.EntitySpawn sameCreate = Spawn( guid, 1, includePosition: false, positionSequence: 2); PhysicsSpawnData physics = sameCreate.Physics!.Value; sameCreate = sameCreate with { Physics = physics with { Timestamps = physics.Timestamps with { Vector = 2 }, }, }; _ = lifetime.RegisterEntityWithInitialResidence(sameCreate, isLocalPlayer: false); // The first Execute call fully drains the envelope in one synchronous // pass (no yield point exists in this envelope). A SECOND call with // the same (now-stale, released) token must not resurrect or // reapply anything. RuntimeInitialCreateExecutionReceipt first = RunToCompletion( lifetime, canonical, lease.Token, NoContact); ulong vectorAuthorityAfterFirst = canonical.VectorAuthorityVersion; Assert.Equal( RuntimeInitialCreateExecutionStatus.RejectedToken, lifetime.InitialCreateExecution.Execute(canonical, lease.Token, NoContact, out _)); Assert.Equal(vectorAuthorityAfterFirst, canonical.VectorAuthorityVersion); Assert.NotEmpty(first.Trace); } // --------------------------------------------------------------- // D. Position routes // --------------------------------------------------------------- [Fact] public void LocalOrdinaryPositionInterpolatesWithoutWorldPlacement() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 5UL); const uint guid = 0x70023000u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: true) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); WorldSession.EntityPositionUpdate update = PositionUpdate( guid, positionSequence: 2, teleportSequence: 0, forcePositionSequence: 0, positionX: 15f); Assert.True(lifetime.TryApplyPosition( update, isLocalPlayer: true, null, null, null, out PositionTimestampDisposition disposition, out _, out _)); Assert.Equal(PositionTimestampDisposition.Apply, disposition); var inputs = new RuntimeInitialCreateExecutionInputs( UsePositionFromServer: true, PlayerDistance: 0f); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, inputs); RuntimeInitialCreateExecutedAction positionAction = Assert.Single( receipt.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.Position); Assert.Equal( RuntimeAuthoritativePositionDisposition.Interpolate, positionAction.PositionDisposition); Assert.Equal(15f, canonical.Snapshot.Position!.Value.PositionX); // Interpolate never runs a physics placement: the cell stays exactly // what the initial SetPosition already committed. Assert.Equal(Cell, canonical.FullCellId); Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership().ActiveOperationCount); // Round 4 R4-8: local ordinary route trace flags - CONSTRAIN-BEFORE // (retail-notes.md function 3 line ~93041: ConstrainTo runs // unconditionally BEFORE InterpolateTo on this route), unparent // always runs on a received Position, and no teleport hook. Assert.Equal(RuntimePositionConstrainPhase.BeforePositionOperation, positionAction.ConstrainPhase); Assert.True(positionAction.UnparentBeforeRouting); Assert.Equal(RuntimeTeleportHookPhase.None, positionAction.HookPhase); } [Fact] public void LocalTeleportContinuationDrivesItsOwnAuthoredPlacementLifecycle() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 6UL); const uint guid = 0x70023100u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: true) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); WorldSession.EntityPositionUpdate update = PositionUpdate( guid, positionSequence: 2, teleportSequence: 1, forcePositionSequence: 0, positionX: 40f); Assert.True(lifetime.TryApplyPosition( update, isLocalPlayer: true, null, null, null, out PositionTimestampDisposition disposition, out _, out _)); Assert.Equal(PositionTimestampDisposition.Apply, disposition); RuntimeInitialCreateExecutionStatus status = lifetime.InitialCreateExecution.Execute( canonical, lease.Token, NoContact, out RuntimeInitialCreateExecutionReceipt pending); Assert.Equal(RuntimeInitialCreateExecutionStatus.AwaitingContinuationPlacement, status); Assert.Equal(default, pending); // The snapshot's own position field is refreshed even before the // physics placement resolves; the derived cell residency is not. Assert.Equal(40f, canonical.Snapshot.Position!.Value.PositionX); Assert.Equal(Cell, canonical.FullCellId); RuntimeEntityKey key = canonical.Key!.Value; Assert.True(lifetime.InitialCreateExecution .TryGetPendingContinuationRoute(key, out RuntimeAuthoritativePositionRoute route)); Assert.Equal(RuntimeAuthoritativePositionDisposition.SetPositionSimple, route.Disposition); CompletePendingContinuationPlacement(lifetime, key, route); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); RuntimeInitialCreateExecutedAction positionAction = Assert.Single( receipt.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.Position); Assert.Equal( RuntimeAuthoritativePositionDisposition.SetPositionSimple, positionAction.PositionDisposition); // Round 4 R4-8: local teleport route trace flags - ZeroVelocity, // CONSTRAIN-AFTER (retail: TeleportPlayer runs first, ConstrainTo // second), the AfterPositionOperation teleport hook, and unparent // (a received Position always unsets parent). Assert.True(positionAction.ZeroVelocity); Assert.Equal(RuntimePositionConstrainPhase.AfterPositionOperation, positionAction.ConstrainPhase); Assert.Equal(RuntimeTeleportHookPhase.AfterPositionOperation, positionAction.HookPhase); Assert.True(positionAction.UnparentBeforeRouting); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership().ActiveOperationCount); } [Fact] public void RemoteNearContactPositionInterpolatesAfterInitialResidency() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 7UL); const uint guid = 0x70023200u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); WorldSession.EntityPositionUpdate update = PositionUpdate( guid, positionSequence: 2, teleportSequence: 0, forcePositionSequence: 0, positionX: 25f, isGrounded: true); Assert.True(lifetime.TryApplyPosition( update, isLocalPlayer: false, null, null, null, out PositionTimestampDisposition disposition, out _, out _)); Assert.Equal(PositionTimestampDisposition.Apply, disposition); var inputs = new RuntimeInitialCreateExecutionInputs( UsePositionFromServer: false, PlayerDistance: 10f); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, inputs); RuntimeInitialCreateExecutedAction positionAction = Assert.Single( receipt.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.Position); Assert.Equal( RuntimeAuthoritativePositionDisposition.Interpolate, positionAction.PositionDisposition); Assert.Equal(25f, canonical.Snapshot.Position!.Value.PositionX); Assert.Equal(Cell, canonical.FullCellId); Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership().ActiveOperationCount); // Round 4 R4-8: remote near route trace flag - constrain-after // (remote route: MoveOrTeleport runs first, ConstrainTo second). Assert.Equal(RuntimePositionConstrainPhase.AfterPositionOperation, positionAction.ConstrainPhase); } // Round 4 R4-7: wire IsGrounded=true disagrees with the body's OWN // contact bit (forced false) - the local ordinary interpolate gate must // follow the WIRE fact, not the body, per Round 3 A3. [Fact] public void LocalOrdinaryPositionRouteFollowsWireGroundedTrueWhenBodyContactIsFalse() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 97UL); const uint guid = 0x70039000u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: true) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); // Force the body's OWN contact bit to DISAGREE with the wire fact // below - the route must ignore this. ForceContact(canonical, inContact: false); WorldSession.EntityPositionUpdate update = PositionUpdate( guid, positionSequence: 2, teleportSequence: 0, forcePositionSequence: 0, positionX: 15f, isGrounded: true); Assert.True(lifetime.TryApplyPosition( update, isLocalPlayer: true, null, null, null, out PositionTimestampDisposition disposition, out _, out _)); Assert.Equal(PositionTimestampDisposition.Apply, disposition); var inputs = new RuntimeInitialCreateExecutionInputs( UsePositionFromServer: true, PlayerDistance: 0f); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, inputs); RuntimeInitialCreateExecutedAction positionAction = Assert.Single( receipt.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.Position); Assert.Equal( RuntimeAuthoritativePositionDisposition.Interpolate, positionAction.PositionDisposition); } // Round 4 R4-7: wire IsGrounded=false disagrees with the body's OWN // contact bit (forced true) - the remote effective-contact gate must // follow the WIRE fact, not the body. [Fact] public void RemotePositionRouteFollowsWireGroundedFalseWhenBodyContactIsTrue() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 98UL); const uint guid = 0x70039100u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); // Force the body's OWN contact bit to DISAGREE with the wire fact // below - the route must ignore this. ForceContact(canonical, inContact: true); WorldSession.EntityPositionUpdate update = PositionUpdate( guid, positionSequence: 2, teleportSequence: 0, forcePositionSequence: 0, positionX: 25f, isGrounded: false); Assert.True(lifetime.TryApplyPosition( update, isLocalPlayer: false, null, null, null, out PositionTimestampDisposition disposition, out _, out _)); Assert.Equal(PositionTimestampDisposition.Apply, disposition); var inputs = new RuntimeInitialCreateExecutionInputs( UsePositionFromServer: false, PlayerDistance: 10f); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, inputs); RuntimeInitialCreateExecutedAction positionAction = Assert.Single( receipt.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.Position); Assert.Equal( RuntimeAuthoritativePositionDisposition.NoPositionOperation, positionAction.PositionDisposition); Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership().ActiveOperationCount); } [Fact] public void RemoteFarPositionStopsInterpolatingAndRunsSetPositionSimple() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 8UL); const uint guid = 0x70023300u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); WorldSession.EntityPositionUpdate update = PositionUpdate( guid, positionSequence: 2, teleportSequence: 0, forcePositionSequence: 0, positionX: 25f); Assert.True(lifetime.TryApplyPosition( update, isLocalPlayer: false, null, null, null, out PositionTimestampDisposition disposition, out _, out _)); Assert.Equal(PositionTimestampDisposition.Apply, disposition); var inputs = new RuntimeInitialCreateExecutionInputs( UsePositionFromServer: false, PlayerDistance: 200f); RuntimeInitialCreateExecutionStatus status = lifetime.InitialCreateExecution.Execute( canonical, lease.Token, inputs, out _); Assert.Equal(RuntimeInitialCreateExecutionStatus.AwaitingContinuationPlacement, status); RuntimeEntityKey key = canonical.Key!.Value; Assert.True(lifetime.InitialCreateExecution .TryGetPendingContinuationRoute(key, out RuntimeAuthoritativePositionRoute route)); Assert.Equal(RuntimeAuthoritativePositionDisposition.SetPositionSimple, route.Disposition); Assert.True(route.StopInterpolating); CompletePendingContinuationPlacement(lifetime, key, route); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, inputs); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); RuntimeInitialCreateExecutedAction positionAction = Assert.Single( receipt.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.Position); // Round 4 R4-8: remote far route trace flags - StopInterpolating // and constrain-after. Assert.True(positionAction.StopInterpolating); Assert.Equal(RuntimePositionConstrainPhase.AfterPositionOperation, positionAction.ConstrainPhase); } [Fact] public void ParentedInitialCreateNeverRunsAWorldPlacementThroughTheExecutor() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 9UL); const uint parentGuid = 0x70024100u; const uint guid = 0x70024000u; _ = lifetime.RegisterEntity(Spawn(parentGuid, 1)); RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false, parentGuid: parentGuid), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); Assert.False(lease.Placement.IsValid); Assert.Equal(RuntimeAuthoritativePositionDisposition.AwaitFreshPosition, lease.Route.Disposition); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); Assert.Equal(0u, receipt.FullCellId); Assert.Equal(RuntimeTeleportHookPhase.None, receipt.TeleportHookPhase); Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership().ActiveOperationCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } [Fact] public void ForcePositionContinuationRecordsSetPositionSimpleWithPreservedHeadingAndNoParentClear() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 45UL); const uint guid = 0x7002F700u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: true) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); // Round 4 R4-12: give the entity a parent attachment BEFORE the // ForcePosition update arrives, to structurally pin the merge // body's deliberate combined shape - retail's FORCE_POSITION Gate A // returns before CPhysicsObj::unset_parent ever runs, so the // parent attachment must survive alongside the newly-applied // Position after this drain. const uint parentGuid = 0x7002F701u; const uint parentLocation = 3u; Assert.True(lifetime.Entities.TryCommitParent( guid, parentGuid, parentLocation, placementId: 0u, positionSequence: 1, out WorldSession.EntitySpawn parented)); lifetime.Entities.RefreshSnapshot(canonical, parented); // isLocalPlayer + a fresh FORCE_POSITION_TS (1, newer than the // seeded 0) + an EQUAL teleport sequence (0 == gate's current 0) // blips immediately per PhysicsTimestampGate.TryAcceptPositionEvent. WorldSession.EntityPositionUpdate update = PositionUpdate( guid, positionSequence: 2, teleportSequence: 0, forcePositionSequence: 1, positionX: 40f); Assert.True(lifetime.TryApplyPosition( update, isLocalPlayer: true, null, null, null, out PositionTimestampDisposition disposition, out _, out _)); Assert.Equal(PositionTimestampDisposition.ForcePosition, disposition); RuntimeInitialCreateExecutionStatus status = lifetime.InitialCreateExecution.Execute( canonical, lease.Token, NoContact, out _); Assert.Equal(RuntimeInitialCreateExecutionStatus.AwaitingContinuationPlacement, status); RuntimeEntityKey key = canonical.Key!.Value; Assert.True(lifetime.InitialCreateExecution .TryGetPendingContinuationRoute(key, out RuntimeAuthoritativePositionRoute route)); Assert.Equal(RuntimeAuthoritativePositionDisposition.SetPositionSimple, route.Disposition); CompletePendingContinuationPlacement(lifetime, key, route); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); RuntimeInitialCreateExecutedAction positionAction = Assert.Single( receipt.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.Position); Assert.Equal( RuntimeAuthoritativePositionDisposition.SetPositionSimple, positionAction.PositionDisposition); Assert.True(positionAction.PreserveHeading); Assert.True(positionAction.SendPositionImmediately); Assert.False(positionAction.StopInterpolating); Assert.False(positionAction.ZeroVelocity); Assert.Equal(RuntimePositionConstrainPhase.None, positionAction.ConstrainPhase); // The FORCE_POSITION branch precedes unset_parent in retail's // MoveOrTeleport - no parent clearing here. Assert.False(positionAction.UnparentBeforeRouting); // Round 4 R4-12: structurally pin the deliberate combined shape - // Position AND the pre-existing Parent attachment are BOTH // non-null after this merge. Assert.NotNull(canonical.Snapshot.Position); Assert.Equal(40f, canonical.Snapshot.Position!.Value.PositionX); Assert.Equal(parentGuid, canonical.Snapshot.ParentGuid); Assert.Equal(parentLocation, canonical.Snapshot.ParentLocation); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership().ActiveOperationCount); } [Fact] public void MissileFlaggedEntityPositionContinuationClassifiesToProjectileAuthoritativeOperationKind() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 46UL); const uint guid = 0x7002F800u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, missile: true), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); Assert.Equal( RuntimeSetPositionOperationKind.ProjectileAuthoritative, lease.Route.OperationKind); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); // Already resident + far distance forces SetPositionSimple (the // same "remote-shaped" branch a Projectile entity kind shares with // Remote - the classifier has no Projectile-specific branch, only a // Projectile-specific OperationKind mapping). WorldSession.EntityPositionUpdate update = PositionUpdate( guid, positionSequence: 2, teleportSequence: 0, forcePositionSequence: 0, positionX: 25f); Assert.True(lifetime.TryApplyPosition( update, isLocalPlayer: false, null, null, null, out PositionTimestampDisposition disposition, out _, out _)); Assert.Equal(PositionTimestampDisposition.Apply, disposition); var inputs = new RuntimeInitialCreateExecutionInputs( UsePositionFromServer: false, PlayerDistance: 200f); RuntimeInitialCreateExecutionStatus status = lifetime.InitialCreateExecution.Execute( canonical, lease.Token, inputs, out _); Assert.Equal(RuntimeInitialCreateExecutionStatus.AwaitingContinuationPlacement, status); RuntimeEntityKey key = canonical.Key!.Value; Assert.True(lifetime.InitialCreateExecution .TryGetPendingContinuationRoute(key, out RuntimeAuthoritativePositionRoute route)); Assert.Equal(RuntimeAuthoritativePositionDisposition.SetPositionSimple, route.Disposition); Assert.Equal(RuntimeSetPositionOperationKind.ProjectileAuthoritative, route.OperationKind); Assert.True(route.StopInterpolating); CompletePendingContinuationPlacement(lifetime, key, route); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, inputs); Assert.NotEmpty(receipt.Trace); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership().ActiveOperationCount); // Round 4 R4-8: projectile route trace flags - same StopInterpolating // + constrain-after shape as the remote-far branch (the classifier // has no Projectile-specific branch, only an OperationKind mapping). RuntimeInitialCreateExecutedAction positionAction = Assert.Single( receipt.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.Position); Assert.True(positionAction.StopInterpolating); Assert.Equal(RuntimePositionConstrainPhase.AfterPositionOperation, positionAction.ConstrainPhase); } // Contrasts with ParentedInitialCreateNeverRunsAWorldPlacementThroughTheExecutor: // the PickedUp residence kind (no parent AND no position) is the OTHER // branch that classifies to AwaitFreshPosition at Begin/Create time (see // RuntimeInitialCreateResidenceState.Begin's residence-kind switch). // NOTE (mandated-case coverage gap): a standalone Position CONTINUATION // (as opposed to this initial-lease route) can never itself classify to // AwaitFreshPosition - RuntimeAuthoritativePositionRouteClassifier. // ClassifyAcceptedPosition (the only classifier ApplyPositionAction ever // calls) has no branch that returns AwaitFreshPosition; that disposition // is produced exclusively by ClassifyCreate (Parented/PickedUp) - the // route-7 contract deleted the classifier's other AwaitFreshPosition // producer, ClassifyLeaveWorld, which had zero production callers (see // docs/research/2026-08-04-c4-route-7-contract.md D6). A // parented/picked entity's raw Position wire events are retained as // Position continuations exactly like any other entity's and are // classified with the SAME Remote/LocalPlayer logic once drained - the // "picked" or "parented" fact plays no role in that classification. This // is a structural property of the current classifier, not a gap in this // test suite; the reachable form of "AwaitFreshPosition for a // parented/picked entity" is the INITIAL LEASE route exercised here and // by ParentedInitialCreateNeverRunsAWorldPlacementThroughTheExecutor. [Fact] public void PickedUpInitialCreateNeverRunsAWorldPlacementThroughTheExecutor() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 47UL); const uint guid = 0x7002F900u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); Assert.False(lease.Placement.IsValid); Assert.Equal(RuntimeAuthoritativePositionDisposition.AwaitFreshPosition, lease.Route.Disposition); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); Assert.Equal(0u, receipt.FullCellId); Assert.Equal(RuntimeTeleportHookPhase.None, receipt.TeleportHookPhase); Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership().ActiveOperationCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } // Round 3 B10 (execution-time-rejected retained Position): admission // ACCEPTED (disposition Apply - POSITION_TS genuinely advanced), but // execution-time classification REJECTS on a nonfinite derived distance. // The gate-consumed timestamps must still land in the snapshot; no pose // ever applies. [Fact] public void ExecutionTimeRejectedPositionStampsRetainedTimestampsWithoutPoseApplication() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 48UL); const uint guid = 0x7002FA00u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); Assert.Equal(Cell, canonical.FullCellId); float originalPositionX = canonical.Snapshot.Position!.Value.PositionX; WorldSession.EntityPositionUpdate update = PositionUpdate( guid, positionSequence: 2, teleportSequence: 0, forcePositionSequence: 0, positionX: 25f); Assert.True(lifetime.TryApplyPosition( update, isLocalPlayer: false, null, null, null, out PositionTimestampDisposition disposition, out _, out _)); Assert.Equal(PositionTimestampDisposition.Apply, disposition); var inputs = new RuntimeInitialCreateExecutionInputs( UsePositionFromServer: false, PlayerDistance: float.NaN); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, inputs); RuntimeInitialCreateExecutedAction positionAction = Assert.Single( receipt.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.Position); Assert.Equal( RuntimeAuthoritativePositionDisposition.RejectedData, positionAction.PositionDisposition); // The retained Position/Teleport/ForcePosition channels the gate // actually moved land in the snapshot ... Assert.Equal(2, canonical.Snapshot.Physics!.Value.Timestamps.Position); Assert.Equal(0, canonical.Snapshot.Physics!.Value.Timestamps.Teleport); Assert.Equal(0, canonical.Snapshot.Physics!.Value.Timestamps.ForcePosition); // ... but no pose ever applies. Assert.Equal(originalPositionX, canonical.Snapshot.Position!.Value.PositionX); Assert.Equal(Cell, canonical.FullCellId); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } // --------------------------------------------------------------- // E. Missing-parent raw deferred-create replay // --------------------------------------------------------------- [Fact] public void MissingParentReplayConsumesExactAdmissionIdAndRegistersChildThroughCanonicalRoute() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 10UL); const uint parentGuid = 0x70025100u; const uint childGuid = 0x70025000u; RuntimeEntityRegistrationResult deferred = lifetime.RegisterEntityWithInitialResidence( Spawn(childGuid, 1, includePosition: false, parentGuid: parentGuid), isLocalPlayer: false); Assert.True(deferred.DeferredForParent); Assert.Equal(1, lifetime.CaptureOwnership().DeferredParentCreateCount); RuntimeEntityRecord parent = lifetime .RegisterEntityWithInitialResidence( Spawn(parentGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( parent, out RuntimeInitialCreateResidenceLease parentLease)); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, parent, parentLease.Token, NoContact); Assert.Equal(1, receipt.ReplayedDeferredChildCount); RuntimeInitialCreateExecutedAction replay = Assert.Single( receipt.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.DeferredChildReplay); Assert.Equal(RuntimeDeferredChildReplayOutcome.Registered, replay.DeferredChildOutcome); Assert.Equal(0, lifetime.CaptureOwnership().DeferredParentCreateCount); Assert.True(lifetime.Entities.TryGetActive(childGuid, out RuntimeEntityRecord child)); Assert.True(lifetime.TryGetInitialCreateResidence(child, out _)); } [Fact] public void StaleAdmissionIdCannotConsumeAReplacementQueuedAfterThePeek() { var parents = new ParentAttachmentState(); const uint parentGuid = 0x70025200u; WorldSession.EntitySpawn spawn = Spawn( 0x70025300u, 1, includePosition: false, parentGuid: parentGuid); parents.EnqueueDeferredCreate(spawn, isLocalPlayer: false); Assert.True(parents.TryPeekDeferredCreate(parentGuid, out DeferredParentCreate stale)); parents.Clear(); parents.EnqueueDeferredCreate(spawn, isLocalPlayer: false); Assert.True(parents.TryPeekDeferredCreate(parentGuid, out DeferredParentCreate replacement)); Assert.NotEqual(stale.AdmissionId, replacement.AdmissionId); Assert.False(parents.ConsumeDeferredCreate(parentGuid, stale)); Assert.Equal(1, parents.DeferredCreateCount); Assert.True(parents.ConsumeDeferredCreate(parentGuid, replacement)); Assert.Equal(0, parents.DeferredCreateCount); } // Round 3 B7: multiple children queued behind the SAME missing parent // all replay atomically through the executor's whole-bucket detach // (ParentAttachmentState.DetachDeferredCreates), in FIFO admission // order, exercised through the canonical RunInitialTail path rather than // against ParentAttachmentState directly. [Fact] public void MultipleChildrenQueuedBehindOneMissingParentReplayInFifoOrderThroughTheExecutor() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 32UL); const uint parentGuid = 0x7002E100u; const uint firstChildGuid = 0x7002E000u; const uint secondChildGuid = 0x7002E001u; Assert.True(lifetime.RegisterEntityWithInitialResidence( Spawn(firstChildGuid, 1, includePosition: false, parentGuid: parentGuid), isLocalPlayer: false) .DeferredForParent); Assert.True(lifetime.RegisterEntityWithInitialResidence( Spawn(secondChildGuid, 1, includePosition: false, parentGuid: parentGuid), isLocalPlayer: false) .DeferredForParent); Assert.Equal(2, lifetime.CaptureOwnership().DeferredParentCreateCount); RuntimeEntityRecord parent = lifetime .RegisterEntityWithInitialResidence( Spawn(parentGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( parent, out RuntimeInitialCreateResidenceLease parentLease)); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, parent, parentLease.Token, NoContact); Assert.Equal(2, receipt.ReplayedDeferredChildCount); RuntimeInitialCreateExecutedAction[] replays = receipt.Trace .Where(static a => a.Kind is RuntimeInitialCreateExecutedActionKind.DeferredChildReplay) .ToArray(); Assert.Equal(2, replays.Length); Assert.All( replays, static a => Assert.Equal(RuntimeDeferredChildReplayOutcome.Registered, a.DeferredChildOutcome)); Assert.Equal(0, lifetime.CaptureOwnership().DeferredParentCreateCount); Assert.True(lifetime.Entities.TryGetActive(firstChildGuid, out _)); Assert.True(lifetime.Entities.TryGetActive(secondChildGuid, out _)); } // Round 4 R4-1: one child's registration THROWING must not strand the // remaining siblings or escape Execute as an exception. Three children // queued behind one missing parent; the SECOND child's own registration // callback is made to throw (via a reflection-swapped // _registerDeferredChild delegate - the standard fault-injection // technique this file already uses for private-state pokes, e.g. // SetCompletedAdoptionRevision). The third child must still register. [Fact] public void DeferredChildReplayContainsOneChildsThrowingRegistrationAndContinuesWithSiblings() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 92UL); const uint parentGuid = 0x70035000u; const uint firstChildGuid = 0x70035001u; const uint secondChildGuid = 0x70035002u; const uint thirdChildGuid = 0x70035003u; Assert.True(lifetime.RegisterEntityWithInitialResidence( Spawn(firstChildGuid, 1, includePosition: false, parentGuid: parentGuid), isLocalPlayer: false) .DeferredForParent); Assert.True(lifetime.RegisterEntityWithInitialResidence( Spawn(secondChildGuid, 1, includePosition: false, parentGuid: parentGuid), isLocalPlayer: false) .DeferredForParent); Assert.True(lifetime.RegisterEntityWithInitialResidence( Spawn(thirdChildGuid, 1, includePosition: false, parentGuid: parentGuid), isLocalPlayer: false) .DeferredForParent); Assert.Equal(3, lifetime.CaptureOwnership().DeferredParentCreateCount); WrapDeferredChildRegistration(lifetime, (original, spawn, isLocalPlayer) => { if (spawn.Guid == secondChildGuid) { throw new InvalidOperationException( "Injected R4-1 containment test failure."); } return original(spawn, isLocalPlayer); }); RuntimeEntityRecord parent = lifetime .RegisterEntityWithInitialResidence( Spawn(parentGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( parent, out RuntimeInitialCreateResidenceLease parentLease)); // No exception escapes Execute - it returns a typed status. RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, parent, parentLease.Token, NoContact); Assert.Equal(3, receipt.ReplayedDeferredChildCount); RuntimeInitialCreateExecutedAction[] replays = receipt.Trace .Where(static a => a.Kind is RuntimeInitialCreateExecutedActionKind.DeferredChildReplay) .ToArray(); Assert.Equal(3, replays.Length); Assert.Equal(RuntimeDeferredChildReplayOutcome.Registered, replays[0].DeferredChildOutcome); Assert.Equal(RuntimeDeferredChildReplayOutcome.Rejected, replays[1].DeferredChildOutcome); Assert.Equal(RuntimeDeferredChildReplayOutcome.Registered, replays[2].DeferredChildOutcome); Assert.True(lifetime.Entities.TryGetActive(firstChildGuid, out _)); Assert.False(lifetime.Entities.TryGetActive(secondChildGuid, out _)); Assert.True(lifetime.Entities.TryGetActive(thirdChildGuid, out _)); Assert.Equal(0, lifetime.CaptureOwnership().DeferredParentCreateCount); // Round 5 R5-3: the contained exception is recorded on the // observable failure surface, not silently swallowed. Assert.Equal(1, lifetime.CaptureOwnership().ReplayFailureCount); Assert.True(lifetime.CaptureOwnership().HasLastReplayFailure); } // Round 4 R4-1: a mid-loop abandonment (the parent entity is no longer // current) must RESTORE the unprocessed remainder rather than // permanently destroy it. Two children queued behind one missing // parent; the FIRST child's registration callback reentrantly deletes // the PARENT (simulating a synchronous observer reaction fired from // within registration). The second child's raw Create must be back in // the deferred bucket afterward (observable via ContainsDeferredCreate), // and every ledger must still converge. [Fact] public void DeferredChildReplayRestoresTheUnprocessedRemainderWhenTheParentIsDeletedReentrantlyMidReplay() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 93UL); const uint parentGuid = 0x70036000u; const uint firstChildGuid = 0x70036001u; const uint secondChildGuid = 0x70036002u; Assert.True(lifetime.RegisterEntityWithInitialResidence( Spawn(firstChildGuid, 1, includePosition: false, parentGuid: parentGuid), isLocalPlayer: false) .DeferredForParent); Assert.True(lifetime.RegisterEntityWithInitialResidence( Spawn(secondChildGuid, 1, includePosition: false, parentGuid: parentGuid), isLocalPlayer: false) .DeferredForParent); Assert.Equal(2, lifetime.CaptureOwnership().DeferredParentCreateCount); WrapDeferredChildRegistration(lifetime, (original, spawn, isLocalPlayer) => { RuntimeEntityRegistrationResult result = original(spawn, isLocalPlayer); if (spawn.Guid == firstChildGuid) { Assert.True(lifetime.TryAcceptDelete( new DeleteObject.Parsed(parentGuid, 1), isLocalPlayer: false, removeRetainedObject: true, out RuntimeEntityDeleteAcceptance acceptance)); lifetime.CompleteAcceptedDelete(acceptance); } return result; }); RuntimeEntityRecord parent = lifetime .RegisterEntityWithInitialResidence( Spawn(parentGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( parent, out RuntimeInitialCreateResidenceLease parentLease)); Assert.Equal( RuntimeInitialCreateExecutionStatus.RejectedAuthority, lifetime.InitialCreateExecution.Execute( parent, parentLease.Token, NoContact, out RuntimeInitialCreateExecutionReceipt receipt)); Assert.Equal(default, receipt); // Child 1 legitimately registered before the reentrant delete // fired; child 2's raw Create is restored to the deferred bucket // rather than lost. Assert.True(lifetime.Entities.TryGetActive(firstChildGuid, out RuntimeEntityRecord firstChild)); Assert.True(lifetime.Entities.ParentAttachments.ContainsDeferredCreate(secondChildGuid, 1)); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); // Only the PARENT's own residence/progress was touched by this // abandoned Execute call (matching the existing // DeleteDuringDeferredChildReplayAbandonsExecutionWithoutResurrection // precedent) - child 1 is a distinct entity the deferred replay // legitimately registered through the canonical route before the // reentrant delete happened; its own (never executed) residence // lease correctly remains open, accounting for the sole surviving // lease count. Assert.Equal(1, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.True(lifetime.TryGetInitialCreateResidence(firstChild, out _)); } /// /// Round 4 R4-1 test-only fault injection: swaps the executor's private /// _registerDeferredChild delegate for one that wraps the /// original, matching this file's existing reflection-based /// private-state pokes (e.g. SetCompletedAdoptionRevision below). /// private static void WrapDeferredChildRegistration( RuntimeEntityObjectLifetime lifetime, Func< Func, WorldSession.EntitySpawn, bool, RuntimeEntityRegistrationResult> wrapper) { Type executorType = typeof(RuntimeInitialCreateContinuationExecutor); System.Reflection.FieldInfo field = executorType.GetField( "_registerDeferredChild", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)!; var original = (Func) field.GetValue(lifetime.InitialCreateExecution)!; Func wrapped = (spawn, isLocalPlayer) => wrapper(original, spawn, isLocalPlayer); field.SetValue(lifetime.InitialCreateExecution, wrapped); } /// /// Round 5 R5-1 test helper: both RuntimeEntityObjectLifetime. /// TryApplyParent (line ~908) and RegisterEntityCore's own /// missing-parent gate divert an UNADDRESSABLE-at-admission parent /// relation to a DIFFERENT mechanism entirely (the legacy unresolved- /// wait queue, or a raw deferred Create) - neither ever reaches the /// residence continuation FIFO this round's executor dispatch code /// operates on. The scenarios this round's tests exercise (drain-time/ /// replay-time staleness) all require the parent to be ADDRESSABLE at /// the moment of admission and only become stale/unaddressable /// afterward - exactly the shape the original Round 3/4 test used. /// This helper registers a real parent at /// , admits the standalone /// Parent continuation naming it, then removes the parent from the /// ACTIVE directory directly (RuntimeEntityDirectory.RemoveActive) /// rather than through the full DeleteObject-acceptance ceremony - a /// real delete retains a permanent per-(guid,generation) teardown /// tombstone (RuntimeEntityDirectory.RetainTeardown), which would /// collide if a later test step re-registers the SAME guid at the SAME /// incarnation (an invalid combination this scenario has no reason to /// exercise - retail incarnations only ever increase). RemoveActive /// makes the parent unaddressable for TryGetActive purposes /// without that permanent marker, while the gate stays intact so a /// LATER same-incarnation re-registration takes the codebase's own /// existing "recovered CreateObject" ExistingGeneration path. /// private static void AdmitThenOrphanParentRelation( RuntimeEntityObjectLifetime lifetime, uint parentGuid, uint childGuid, ushort namedParentIncarnation, uint parentLocation = 1u) { // includePosition:false - besides needing no physics engine, this // also matters for the "same incarnation returns" scenario: a // position-bearing retained snapshot would carry over through // MergeUntimestampedCreate's Position=retained.Position copy when // the SAME (guid, incarnation) is later re-registered on the // "recovered CreateObject" ExistingGeneration path, reclassifying // the recovered residence as TopLevel (SetPosition) instead of // PickedUp (AwaitFreshPosition) and stalling RunToCompletion on an // unrequested placement ack. RuntimeEntityRecord parent = lifetime.RegisterEntity( Spawn(parentGuid, namedParentIncarnation, includePosition: false)).Canonical!; var parentUpdate = new ParentEvent.Parsed( parentGuid, childGuid, ParentLocation: parentLocation, PlacementId: 0u, ParentInstanceSequence: namedParentIncarnation, ChildPositionSequence: 2); Assert.True(lifetime.TryApplyParent(parentUpdate, null, out _)); Assert.True(lifetime.Entities.RemoveActive(parent)); } // Round 3 B9 revalidated a standalone Parent continuation's parent // incarnation at EXECUTION time, not just admission time - admission // succeeds while the parent is still active; the parent is then // deleted before the drain reaches this continuation. Round 4 R4-5 // pinned the mismatch outcome as a DISCARD; Round 5 R5-1 OVERTURNS // that with hard retail evidence (QueueBlobForObject, pseudo-C 92326; // GUID-keyed CObjectMaint bucket, 271082-271088) - retail QUEUES a // relation whose parent is unaddressable under the PARENT's guid and // replays it when that guid is created; it never discards on this // path. This test now covers R5-1's mandated "parent-returns -> queued // relation applies exactly once" scenario end-to-end. [Fact] public void ParentContinuationRevalidatesLiveParentAtExecutionAndQueuesOnMismatchThenAppliesWhenTheParentArrives() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 33UL); const uint parentGuid = 0x7002E200u; const uint childGuid = 0x7002E300u; _ = lifetime.RegisterEntity(Spawn(parentGuid, 1)); RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(childGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); var parentUpdate = new ParentEvent.Parsed( parentGuid, childGuid, ParentLocation: 1u, PlacementId: 0u, ParentInstanceSequence: 1, ChildPositionSequence: 2); // Admission succeeds - the parent is still active at this moment. Assert.True(lifetime.TryApplyParent(parentUpdate, null, out _)); // The parent is deleted BEFORE the drain ever reaches this // continuation - admission-time validation cannot see this. Assert.True(lifetime.TryAcceptDelete( new DeleteObject.Parsed(parentGuid, 1), isLocalPlayer: false, removeRetainedObject: true, out RuntimeEntityDeleteAcceptance acceptance)); lifetime.CompleteAcceptedDelete(acceptance); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); // Queued (retail-faithful), not discarded: the residence still // converges cleanly and the child's own canonical record survives. RuntimeInitialCreateExecutedAction parentAction = Assert.Single( receipt.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.Parent); Assert.Equal( RuntimeParentRelationOutcome.DeferredAwaitingParent, parentAction.ParentRelationOutcome); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); Assert.True(lifetime.Entities.TryGetActive(childGuid, out _)); Assert.True(lifetime.Entities.ParentAttachments .ContainsDeferredAcceptedRelation(childGuid, canonical.Key!.Value)); Assert.Null(canonical.Snapshot.ParentGuid); } // Round 5 R5-1 mandated: "parent-returns -> queued relation applies // exactly once" (trace + attach), end-to-end. Uses // AdmitThenOrphanParentRelation (RemoveActive, not a full // DeleteObject-acceptance) specifically so the SAME (guid, incarnation) // can validly reappear afterward through the codebase's own existing // "recovered CreateObject" ExistingGeneration path, without an // artificial teardown-tombstone collision the scenario has no reason // to exercise. [Fact] public void DeferredAcceptedParentRelationAppliesExactlyOnceWhenTheSameParentIncarnationReturns() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 113UL); const uint parentGuid = 0x70049000u; const uint childGuid = 0x70049001u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(childGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AdmitThenOrphanParentRelation(lifetime, parentGuid, childGuid, namedParentIncarnation: 1); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); RuntimeInitialCreateExecutedAction parentAction = Assert.Single( receipt.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.Parent); Assert.Equal(RuntimeParentRelationOutcome.DeferredAwaitingParent, parentAction.ParentRelationOutcome); Assert.True(lifetime.Entities.ParentAttachments .ContainsDeferredAcceptedRelation(childGuid, canonical.Key!.Value)); // The SAME parent incarnation (1) reappears - the queued relation // replays and applies exactly once. RuntimeEntityRecord recreatedParent = lifetime .RegisterEntityWithInitialResidence( Spawn(parentGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( recreatedParent, out RuntimeInitialCreateResidenceLease parentLease)); RuntimeInitialCreateExecutionReceipt parentReceipt = RunToCompletion( lifetime, recreatedParent, parentLease.Token, NoContact); RuntimeInitialCreateExecutedAction replayAction = Assert.Single( parentReceipt.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.ParentRelationReplay); Assert.Equal(RuntimeParentRelationOutcome.Applied, replayAction.ParentRelationOutcome); // Applied commits the SAME position-timestamp-only merge a live // Parent continuation commits (already stamped at the relation's // original drain, before it was queued) - it does not itself set // ParentGuid/ParentLocation, matching // StandaloneParentContinuationAppliesPositionTimestampOnlyMergeWithNoReDefer's // established precedent that the actual attach commit is // TryCommitParent's (App-layer EquippedChildRenderController's) job. Assert.Equal(2, canonical.Snapshot.Physics!.Value.Timestamps.Position); Assert.Null(canonical.Snapshot.ParentGuid); Assert.False(lifetime.Entities.ParentAttachments .ContainsDeferredAcceptedRelation(childGuid, canonical.Key!.Value)); Assert.Equal(0, lifetime.Entities.ParentAttachments.DeferredAcceptedRelationCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } // Round 5 R5-1 mandated: the arriving parent's incarnation is NEWER // than the one the relation named - discard per Resolve's own rule // (the current live parent supersedes the packet). [Fact] public void DeferredAcceptedParentRelationDiscardsWhenTheArrivingParentIsANewerIncarnationThanTheRelationNamed() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 104UL); const uint parentGuid = 0x7003F000u; const uint childGuid = 0x7003F001u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(childGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); // Admits the standalone Parent continuation while the parent IS // addressable (required - see AdmitThenOrphanParentRelation's // remarks), then deletes the parent so the child's OWN drain // discovers it stale and queues the relation. AdmitThenOrphanParentRelation(lifetime, parentGuid, childGuid, namedParentIncarnation: 1); _ = RunToCompletion(lifetime, canonical, lease.Token, NoContact); Assert.True(lifetime.Entities.ParentAttachments .ContainsDeferredAcceptedRelation(childGuid, canonical.Key!.Value)); // Parent arrives directly at incarnation 2 - NEWER than the // relation's named incarnation 1. RuntimeEntityRecord parent = lifetime .RegisterEntityWithInitialResidence( Spawn(parentGuid, 2, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( parent, out RuntimeInitialCreateResidenceLease parentLease)); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, parent, parentLease.Token, NoContact); RuntimeInitialCreateExecutedAction replayAction = Assert.Single( receipt.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.ParentRelationReplay); Assert.Equal(RuntimeParentRelationOutcome.DiscardedStaleParent, replayAction.ParentRelationOutcome); Assert.False(lifetime.Entities.ParentAttachments .ContainsDeferredAcceptedRelation(childGuid, canonical.Key!.Value)); Assert.Null(canonical.Snapshot.ParentGuid); Assert.Equal(0, lifetime.Entities.ParentAttachments.DeferredAcceptedRelationCount); } // Round 5 R5-1 mandated: the arriving parent's incarnation is OLDER // than the one the relation named - stays queued (wait), then applies // on the NEXT matching incarnation. This scenario is not constructible // through the normal admission ceremony: naming ParentInstanceSequence // 5 in a Parent continuation requires the LIVE parent to already BE at // instance 5 at admission time (TryApplyParent's own gate), which // pins the parent's OWN PhysicsTimestampGate at 5 - a client can never // subsequently see that SAME guid at an OLDER instance 3 afterward // (retail incarnations are per-guid monotonic; the gate enforces it). // This test instead enqueues the relation directly through // ParentAttachmentState's own public API (the same technique the ABA // test below uses) to isolate ApplyReplayedParentRelation's OWN // incarnation-compare logic from that inapplicable precondition. [Fact] public void DeferredAcceptedParentRelationStaysQueuedWhenTheArrivingParentIsOlderThanTheRelationNamedAndAppliesOnTheNextMatchingIncarnation() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 105UL); const uint parentGuid = 0x70040000u; const uint childGuid = 0x70040001u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(childGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); _ = RunToCompletion(lifetime, canonical, lease.Token, NoContact); var parentUpdate = new ParentEvent.Parsed( parentGuid, childGuid, ParentLocation: 1u, PlacementId: 0u, ParentInstanceSequence: 5, ChildPositionSequence: 2); lifetime.Entities.ParentAttachments.EnqueueDeferredAcceptedRelation( childGuid, canonical.Key!.Value, parentUpdate, null, default); // Parent arrives at incarnation 3 - OLDER than the relation's // named incarnation 5. RuntimeEntityRecord parentAtThree = lifetime .RegisterEntityWithInitialResidence( Spawn(parentGuid, 3, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( parentAtThree, out RuntimeInitialCreateResidenceLease leaseThree)); RuntimeInitialCreateExecutionReceipt receiptThree = RunToCompletion( lifetime, parentAtThree, leaseThree.Token, NoContact); RuntimeInitialCreateExecutedAction replayThree = Assert.Single( receiptThree.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.ParentRelationReplay); Assert.Equal(RuntimeParentRelationOutcome.DeferredAwaitingParent, replayThree.ParentRelationOutcome); Assert.True(lifetime.Entities.ParentAttachments .ContainsDeferredAcceptedRelation(childGuid, canonical.Key!.Value)); // Parent recreated at incarnation 5 - now matches. RuntimeEntityRecord parentAtFive = lifetime .RegisterEntityWithInitialResidence( Spawn(parentGuid, 5, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( parentAtFive, out RuntimeInitialCreateResidenceLease leaseFive)); RuntimeInitialCreateExecutionReceipt receiptFive = RunToCompletion( lifetime, parentAtFive, leaseFive.Token, NoContact); RuntimeInitialCreateExecutedAction replayFive = Assert.Single( receiptFive.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.ParentRelationReplay); Assert.Equal(RuntimeParentRelationOutcome.Applied, replayFive.ParentRelationOutcome); // Applied never sets ParentGuid itself - see // StandaloneParentContinuationAppliesPositionTimestampOnlyMergeWithNoReDefer's // established precedent (the actual attach commit is // TryCommitParent's job, out of scope for this residence drain). Assert.Null(canonical.Snapshot.ParentGuid); Assert.Equal(0, lifetime.Entities.ParentAttachments.DeferredAcceptedRelationCount); } // Round 5 R5-1 mandated: the child is deleted while its relation is // still queued (parent never showed up) - the entry is cancelled and // every ledger converges. [Fact] public void DeferredAcceptedParentRelationIsCancelledWhenTheChildIsDeletedWhileQueued() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 106UL); const uint parentGuid = 0x70041000u; const uint childGuid = 0x70041001u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(childGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AdmitThenOrphanParentRelation(lifetime, parentGuid, childGuid, namedParentIncarnation: 1); _ = RunToCompletion(lifetime, canonical, lease.Token, NoContact); Assert.Equal(1, lifetime.Entities.ParentAttachments.DeferredAcceptedRelationCount); Assert.True(lifetime.TryAcceptDelete( new DeleteObject.Parsed(childGuid, 1), isLocalPlayer: false, removeRetainedObject: true, out RuntimeEntityDeleteAcceptance acceptance)); lifetime.CompleteAcceptedDelete(acceptance); Assert.False(lifetime.Entities.ParentAttachments .ContainsDeferredAcceptedRelation(childGuid, canonical.Key!.Value)); Assert.Equal(0, lifetime.Entities.ParentAttachments.DeferredAcceptedRelationCount); } // Round 5 R5-1 mandated: a full session reset while a relation is // queued clears it and converges. [Fact] public void DeferredAcceptedParentRelationClearsOnSessionResetAndConverges() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 107UL); const uint parentGuid = 0x70042000u; const uint childGuid = 0x70042001u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(childGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AdmitThenOrphanParentRelation(lifetime, parentGuid, childGuid, namedParentIncarnation: 1); _ = RunToCompletion(lifetime, canonical, lease.Token, NoContact); Assert.Equal(1, lifetime.Entities.ParentAttachments.DeferredAcceptedRelationCount); IReadOnlyList retirements = lifetime.BeginSessionClear(); Assert.Equal(0, lifetime.Entities.ParentAttachments.DeferredAcceptedRelationCount); foreach (RuntimeEntityRecord record in retirements) lifetime.CompleteSessionEntityRetirement(record); Assert.True(lifetime.CompleteSessionClearIfConverged()); } // Round 5 R5-1 mandated ABA coverage, tested directly against // ParentAttachmentState (mirroring // StaleAdmissionIdCannotConsumeAReplacementQueuedAfterThePeek's own // direct-state style): a window token minted BEFORE a Clear() must not // restore anything afterward, even though a NEW window for the same // parent guid could otherwise reuse its Id space. [Fact] public void DeferredAcceptedParentRelationStaleWindowTokenCannotRestoreAfterClearAndRequeue() { var parents = new ParentAttachmentState(); const uint parentGuid = 0x70043000u; const uint childGuid = 0x70043001u; var childKey = new RuntimeEntityKey(1u, 1); var parentUpdate = new ParentEvent.Parsed( parentGuid, childGuid, ParentLocation: 1u, PlacementId: 0u, ParentInstanceSequence: 1, ChildPositionSequence: 2); parents.EnqueueDeferredAcceptedRelation(childGuid, childKey, parentUpdate, null, default); ImmutableArray detached = parents.DetachDeferredAcceptedRelations(parentGuid, out DeferredReplayWindowToken staleWindow); DeferredAcceptedParentRelation stale = Assert.Single(detached); // Reentrant Clear() (session reset) while the window is still open. parents.Clear(); // A restore against the now-stale token must be a silent no-op. parents.RestoreDeferredAcceptedRelations(staleWindow, [stale]); Assert.Equal(0, parents.DeferredAcceptedRelationCount); Assert.False(parents.ContainsDeferredAcceptedRelation(childGuid, childKey)); } // Round 5 R5-1 mandated: the envelope's CreateParent stage, end-to-end, // for the unaddressable-parent flavor (addressability-only, no // incarnation to compare). RegisterEntityCore's OWN missing-parent gate // (checked before PreviewCreateDisposition, for ANY beginInitialResidence // call) diverts a same-incarnation update naming an UNADDRESSABLE parent // to a raw deferred Create entirely, bypassing the envelope/CreateParent // stage - so, exactly like the standalone Parent continuation, this // scenario requires the parent to be ADDRESSABLE at the moment of // admission and only orphaned afterward. [Fact] public void EnvelopeCreateParentQueuesForUnaddressableParentAndAppliesWhenTheParentArrives() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 108UL); const uint parentGuid = 0x70044000u; const uint childGuid = 0x70044001u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(childGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); // includePosition:false, matching AdmitThenOrphanParentRelation's own // remarks - avoids both the physics-engine dependency and a // position-bearing retained snapshot bleeding into the LATER // same-incarnation "recovered CreateObject" merge below. RuntimeEntityRecord orphanedParent = lifetime.RegisterEntity( Spawn(parentGuid, 1, includePosition: false)).Canonical!; WorldSession.EntitySpawn sameCreate = Spawn( childGuid, 1, includePosition: false, positionSequence: 2, parentGuid: parentGuid); PhysicsSpawnData physics = sameCreate.Physics!.Value; sameCreate = sameCreate with { Physics = physics with { Timestamps = physics.Timestamps with { State = 2, Vector = 2 }, }, }; _ = lifetime.RegisterEntityWithInitialResidence(sameCreate, isLocalPlayer: false); // The parent is now orphaned - removed from the active directory // AFTER the envelope's CreateParent stage was admitted while it was // still addressable. RemoveActive (not a full DeleteObject-acceptance) // for the SAME reason AdmitThenOrphanParentRelation uses it: a real // delete retains a permanent per-(guid,generation) teardown tombstone // that would collide when the SAME incarnation is re-registered below. Assert.True(lifetime.Entities.RemoveActive(orphanedParent)); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); RuntimeInitialCreateExecutedAction createParentAction = Assert.Single( receipt.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.CreateParent); Assert.Equal(RuntimeParentRelationOutcome.DeferredAwaitingParent, createParentAction.ParentRelationOutcome); Assert.True(lifetime.Entities.ParentAttachments .ContainsDeferredAcceptedRelation(childGuid, canonical.Key!.Value)); RuntimeEntityRecord parent = lifetime .RegisterEntityWithInitialResidence( Spawn(parentGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( parent, out RuntimeInitialCreateResidenceLease parentLease)); RuntimeInitialCreateExecutionReceipt parentReceipt = RunToCompletion( lifetime, parent, parentLease.Token, NoContact); RuntimeInitialCreateExecutedAction replayAction = Assert.Single( parentReceipt.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.ParentRelationReplay); Assert.Equal(RuntimeParentRelationOutcome.Applied, replayAction.ParentRelationOutcome); // Applied never sets ParentGuid itself - see // StandaloneParentContinuationAppliesPositionTimestampOnlyMergeWithNoReDefer's // established precedent (the actual attach commit is // TryCommitParent's job, out of scope for this residence drain). Assert.Null(canonical.Snapshot.ParentGuid); Assert.Equal(0, lifetime.Entities.ParentAttachments.DeferredAcceptedRelationCount); } // --------------------------------------------------------------- // Round 5 R5-2: cancellation-aware detach/restore window, both // deferred buckets. // --------------------------------------------------------------- // Reviewer scenario (a) for the CREATES bucket: two children queued // behind one missing parent; C1's own registration callback delivers // DeleteObject(C2) then DeleteObject(parent) - C2 must NOT be restored, // the recreated parent drains nothing, and every ledger converges. [Fact] public void DeferredChildReplayWindowFiltersASiblingDeletedMidReplayFromTheRestoredRemainder() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 109UL); const uint parentGuid = 0x70045000u; const uint firstChildGuid = 0x70045001u; const uint secondChildGuid = 0x70045002u; Assert.True(lifetime.RegisterEntityWithInitialResidence( Spawn(firstChildGuid, 1, includePosition: false, parentGuid: parentGuid), isLocalPlayer: false) .DeferredForParent); Assert.True(lifetime.RegisterEntityWithInitialResidence( Spawn(secondChildGuid, 1, includePosition: false, parentGuid: parentGuid), isLocalPlayer: false) .DeferredForParent); RuntimeEntityRecord? firstChild = null; WrapDeferredChildRegistration(lifetime, (original, spawn, isLocalPlayer) => { RuntimeEntityRegistrationResult result = original(spawn, isLocalPlayer); if (spawn.Guid == firstChildGuid) { firstChild = result.Canonical; // C2 was never individually registered (still a raw // deferred blob) - its own delete legitimately reports // false (TryAcceptDelete's known-object gate never // accepted it), but CancelDeferredChildGeneration still // runs unconditionally before that gate and records the // window filter. _ = lifetime.TryAcceptDelete( new DeleteObject.Parsed(secondChildGuid, 1), isLocalPlayer: false, removeRetainedObject: true, out _); Assert.True(lifetime.TryAcceptDelete( new DeleteObject.Parsed(parentGuid, 1), isLocalPlayer: false, removeRetainedObject: true, out RuntimeEntityDeleteAcceptance parentAcceptance)); lifetime.CompleteAcceptedDelete(parentAcceptance); } return result; }); RuntimeEntityRecord parent = lifetime .RegisterEntityWithInitialResidence( Spawn(parentGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( parent, out RuntimeInitialCreateResidenceLease parentLease)); Assert.Equal( RuntimeInitialCreateExecutionStatus.RejectedAuthority, lifetime.InitialCreateExecution.Execute( parent, parentLease.Token, NoContact, out RuntimeInitialCreateExecutionReceipt receipt)); Assert.Equal(default, receipt); Assert.True(lifetime.Entities.TryGetActive(firstChildGuid, out _)); Assert.False(lifetime.Entities.ParentAttachments.ContainsDeferredCreate(secondChildGuid, 1)); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); Assert.Equal(0, lifetime.Entities.ParentAttachments.DeferredCreateCount); // C1 was itself admitted as its own ("Parented") initial-residence // registration - a REAL entity, not a raw blob - and that lease is // independent of the parentGuid-keyed deferred-create window this // test targets. Drain it too so every ledger genuinely converges // (round5-fixes.md's own "ledgers converge" wording), rather than // leaving an unrelated open lease that has nothing to do with the // window/filter mechanism under test. Assert.NotNull(firstChild); Assert.True(lifetime.TryGetInitialCreateResidence( firstChild!, out RuntimeInitialCreateResidenceLease firstChildLease)); _ = RunToCompletion(lifetime, firstChild!, firstChildLease.Token, NoContact); RuntimeEntityRecord recreatedParent = lifetime .RegisterEntityWithInitialResidence( Spawn(parentGuid, 2, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( recreatedParent, out RuntimeInitialCreateResidenceLease recreatedLease)); RuntimeInitialCreateExecutionReceipt recreatedReceipt = RunToCompletion( lifetime, recreatedParent, recreatedLease.Token, NoContact); Assert.Equal(0, recreatedReceipt.ReplayedDeferredChildCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); } // Reviewer scenario (b) for the CREATES bucket: C1's callback drives a // full session clear - restore no-ops, DeferredParentCreateCount stays // 0, IsConverged holds after teardown. [Fact] public void DeferredChildReplayWindowReleasesSilentlyWhenSessionClearFiresMidReplay() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 110UL); const uint parentGuid = 0x70046000u; const uint firstChildGuid = 0x70046001u; const uint secondChildGuid = 0x70046002u; Assert.True(lifetime.RegisterEntityWithInitialResidence( Spawn(firstChildGuid, 1, includePosition: false, parentGuid: parentGuid), isLocalPlayer: false) .DeferredForParent); Assert.True(lifetime.RegisterEntityWithInitialResidence( Spawn(secondChildGuid, 1, includePosition: false, parentGuid: parentGuid), isLocalPlayer: false) .DeferredForParent); WrapDeferredChildRegistration(lifetime, (original, spawn, isLocalPlayer) => { RuntimeEntityRegistrationResult result = original(spawn, isLocalPlayer); if (spawn.Guid == firstChildGuid) { IReadOnlyList retirements = lifetime.BeginSessionClear(); foreach (RuntimeEntityRecord record in retirements) lifetime.CompleteSessionEntityRetirement(record); } return result; }); RuntimeEntityRecord parent = lifetime .RegisterEntityWithInitialResidence( Spawn(parentGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( parent, out RuntimeInitialCreateResidenceLease parentLease)); Assert.Equal( RuntimeInitialCreateExecutionStatus.RejectedAuthority, lifetime.InitialCreateExecution.Execute( parent, parentLease.Token, NoContact, out RuntimeInitialCreateExecutionReceipt receipt)); Assert.Equal(default, receipt); Assert.Equal(0, lifetime.CaptureOwnership().DeferredParentCreateCount); Assert.True(lifetime.CompleteSessionClearIfConverged()); } // Reviewer scenario (a), relation-queue flavor: two queued relations // for the same parent; C1's own attach-commit publish delivers // DeleteObject(C2) then DeleteObject(parent) - C2's relation must NOT // be restored. [Fact] public void DeferredAcceptedRelationReplayWindowFiltersASiblingRelationDeletedMidReplay() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 111UL); const uint parentGuid = 0x70047000u; const uint firstChildGuid = 0x70047001u; const uint secondChildGuid = 0x70047002u; RuntimeEntityRecord firstChild = lifetime .RegisterEntityWithInitialResidence( Spawn(firstChildGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence(firstChild, out RuntimeInitialCreateResidenceLease firstLease)); RuntimeEntityRecord secondChild = lifetime .RegisterEntityWithInitialResidence( Spawn(secondChildGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence(secondChild, out RuntimeInitialCreateResidenceLease secondLease)); // Both continuations admit while the FIRST parent incarnation is // addressable, then the parent is orphaned once for both. // includePosition:false + RemoveActive (not a full DeleteObject- // acceptance) for the same reason AdmitThenOrphanParentRelation uses // them: a real delete retains a permanent per-(guid,generation) // teardown tombstone and a position-bearing retained snapshot would // bleed into the SAME-incarnation "recovered CreateObject" merge // when parentGuid is re-registered at incarnation 1 again below. RuntimeEntityRecord orphanedParent = lifetime.RegisterEntity( Spawn(parentGuid, 1, includePosition: false)).Canonical!; var firstParentUpdate = new ParentEvent.Parsed( parentGuid, firstChildGuid, ParentLocation: 1u, PlacementId: 0u, ParentInstanceSequence: 1, ChildPositionSequence: 2); Assert.True(lifetime.TryApplyParent(firstParentUpdate, null, out _)); var secondParentUpdate = new ParentEvent.Parsed( parentGuid, secondChildGuid, ParentLocation: 1u, PlacementId: 0u, ParentInstanceSequence: 1, ChildPositionSequence: 2); Assert.True(lifetime.TryApplyParent(secondParentUpdate, null, out _)); Assert.True(lifetime.Entities.RemoveActive(orphanedParent)); _ = RunToCompletion(lifetime, firstChild, firstLease.Token, NoContact); _ = RunToCompletion(lifetime, secondChild, secondLease.Token, NoContact); Assert.Equal(2, lifetime.Entities.ParentAttachments.DeferredAcceptedRelationCount); using IDisposable subscription = lifetime.Events.Subscribe(new EntityObserver(delta => { if (delta.Change is not RuntimeEntityChange.Updated || delta.Entity.Identity.ServerGuid != firstChildGuid) { return; } Assert.True(lifetime.TryAcceptDelete( new DeleteObject.Parsed(secondChildGuid, 1), isLocalPlayer: false, removeRetainedObject: true, out RuntimeEntityDeleteAcceptance secondAcceptance)); lifetime.CompleteAcceptedDelete(secondAcceptance); Assert.True(lifetime.TryAcceptDelete( new DeleteObject.Parsed(parentGuid, 1), isLocalPlayer: false, removeRetainedObject: true, out RuntimeEntityDeleteAcceptance parentAcceptance)); lifetime.CompleteAcceptedDelete(parentAcceptance); })); RuntimeEntityRecord parent = lifetime .RegisterEntityWithInitialResidence( Spawn(parentGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( parent, out RuntimeInitialCreateResidenceLease parentLease)); Assert.Equal( RuntimeInitialCreateExecutionStatus.RejectedAuthority, lifetime.InitialCreateExecution.Execute( parent, parentLease.Token, NoContact, out RuntimeInitialCreateExecutionReceipt receipt)); Assert.Equal(default, receipt); Assert.False(lifetime.Entities.ParentAttachments .ContainsDeferredAcceptedRelation(secondChildGuid, secondChild.Key!.Value)); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } // Reviewer scenario (b), relation-queue flavor: C1's own attach-commit // publish drives a full session clear - restore no-ops for C2's // relation, converges. [Fact] public void DeferredAcceptedRelationReplayWindowReleasesSilentlyWhenSessionClearFiresMidReplay() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 112UL); const uint parentGuid = 0x70048000u; const uint firstChildGuid = 0x70048001u; const uint secondChildGuid = 0x70048002u; RuntimeEntityRecord firstChild = lifetime .RegisterEntityWithInitialResidence( Spawn(firstChildGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence(firstChild, out RuntimeInitialCreateResidenceLease firstLease)); RuntimeEntityRecord secondChild = lifetime .RegisterEntityWithInitialResidence( Spawn(secondChildGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence(secondChild, out RuntimeInitialCreateResidenceLease secondLease)); // includePosition:false + RemoveActive (not a full DeleteObject- // acceptance) for the same reason AdmitThenOrphanParentRelation uses // them: a real delete retains a permanent per-(guid,generation) // teardown tombstone and a position-bearing retained snapshot would // bleed into the SAME-incarnation "recovered CreateObject" merge // when parentGuid is re-registered at incarnation 1 again below - // and the reentrant BeginSessionClear below retires every currently // active record (including the recreated parent), which would // collide with a stale tombstone left by a full delete here. RuntimeEntityRecord orphanedParent = lifetime.RegisterEntity( Spawn(parentGuid, 1, includePosition: false)).Canonical!; var firstParentUpdate = new ParentEvent.Parsed( parentGuid, firstChildGuid, ParentLocation: 1u, PlacementId: 0u, ParentInstanceSequence: 1, ChildPositionSequence: 2); Assert.True(lifetime.TryApplyParent(firstParentUpdate, null, out _)); var secondParentUpdate = new ParentEvent.Parsed( parentGuid, secondChildGuid, ParentLocation: 1u, PlacementId: 0u, ParentInstanceSequence: 1, ChildPositionSequence: 2); Assert.True(lifetime.TryApplyParent(secondParentUpdate, null, out _)); Assert.True(lifetime.Entities.RemoveActive(orphanedParent)); _ = RunToCompletion(lifetime, firstChild, firstLease.Token, NoContact); _ = RunToCompletion(lifetime, secondChild, secondLease.Token, NoContact); using IDisposable subscription = lifetime.Events.Subscribe(new EntityObserver(delta => { if (delta.Change is not RuntimeEntityChange.Updated || delta.Entity.Identity.ServerGuid != firstChildGuid) { return; } IReadOnlyList retirements = lifetime.BeginSessionClear(); foreach (RuntimeEntityRecord record in retirements) lifetime.CompleteSessionEntityRetirement(record); })); RuntimeEntityRecord parent = lifetime .RegisterEntityWithInitialResidence( Spawn(parentGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( parent, out RuntimeInitialCreateResidenceLease parentLease)); Assert.Equal( RuntimeInitialCreateExecutionStatus.RejectedAuthority, lifetime.InitialCreateExecution.Execute( parent, parentLease.Token, NoContact, out RuntimeInitialCreateExecutionReceipt receipt)); Assert.Equal(default, receipt); Assert.Equal(0, lifetime.Entities.ParentAttachments.DeferredAcceptedRelationCount); Assert.True(lifetime.CompleteSessionClearIfConverged()); } // Round 3 E-matrix (a)+(d): TRUE ParentAttachmentState semantics, verified // by reading the source before writing this test. ParentAttachmentState. // DeleteGeneration(parentGuid, oldIncarnation) - called from // TryAcceptDelete when the PARENT is what's being deleted - only ever // touches the _unresolvedByChild ParentEvent-relation queue (via // FilterParentCandidates, keeping relations addressed to a STRICTLY // newer parent incarnation). It never touches _deferredCreatesByParent, // the raw-netblob bucket ReplayDeferredChildren/DetachDeferredCreates // actually replays - that bucket is keyed ONLY by parent GUID, with no // per-incarnation filtering at all (PhysicsAttachment carries no parent // instance sequence to filter by). So a child queued while parent // incarnation 1 was missing survives the parent's own delete untouched, // and DOES replay against a same-GUID recreated incarnation 2 - this // mirrors retail's own GUID-keyed netblob dispatch (see // ReplayDeferredChildren's remarks: "a parent's own successful Create // replays every blob queued waiting on ITS guid"), not a defect. [Fact] public void DeferredChildQueuedForDeletedParentIncarnationStillReplaysAgainstTheRecreatedParentGuid() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 50UL); const uint parentGuid = 0x70030100u; const uint childGuid = 0x70030000u; Assert.True(lifetime.RegisterEntityWithInitialResidence( Spawn(childGuid, 1, includePosition: false, parentGuid: parentGuid), isLocalPlayer: false) .DeferredForParent); Assert.Equal(1, lifetime.CaptureOwnership().DeferredParentCreateCount); // Parent incarnation 1 arrives, then is deleted, WITHOUT ever // completing a residence (simulating "gone before the child's // replay"). RuntimeEntityRecord parentGen1 = lifetime .RegisterEntityWithInitialResidence( Spawn(parentGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryAcceptDelete( new DeleteObject.Parsed(parentGuid, 1), isLocalPlayer: false, removeRetainedObject: true, out RuntimeEntityDeleteAcceptance acceptance)); lifetime.CompleteAcceptedDelete(acceptance); Assert.False(lifetime.Entities.IsCurrent(parentGen1)); // The deferred child create survives the parent's delete untouched - // the TRUE, verified behavior. Assert.Equal(1, lifetime.CaptureOwnership().DeferredParentCreateCount); // Parent GUID reused at a NEWER incarnation. RuntimeEntityRecord parentGen2 = lifetime .RegisterEntityWithInitialResidence( Spawn(parentGuid, 2, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( parentGen2, out RuntimeInitialCreateResidenceLease parentLease)); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, parentGen2, parentLease.Token, NoContact); Assert.Equal(1, receipt.ReplayedDeferredChildCount); Assert.Equal(0, lifetime.CaptureOwnership().DeferredParentCreateCount); Assert.True(lifetime.Entities.TryGetActive(childGuid, out RuntimeEntityRecord child)); Assert.True(lifetime.TryGetInitialCreateResidence(child, out _)); } // Round 3 E-matrix (b): a child's own exact Delete before the parent's // replay cancels only that child (CancelDeferredChildGeneration's side // effect runs even though TryAcceptDelete's overall return is false for // a guid with no seeded gate - a purely-deferred child was never // registered, so it never got one). [Fact] public void ChildExactDeleteBeforeParentReplayCancelsOnlyThatChild() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 51UL); const uint parentGuid = 0x70030200u; const uint firstChildGuid = 0x70030300u; const uint secondChildGuid = 0x70030400u; Assert.True(lifetime.RegisterEntityWithInitialResidence( Spawn(firstChildGuid, 1, includePosition: false, parentGuid: parentGuid), isLocalPlayer: false) .DeferredForParent); Assert.True(lifetime.RegisterEntityWithInitialResidence( Spawn(secondChildGuid, 1, includePosition: false, parentGuid: parentGuid), isLocalPlayer: false) .DeferredForParent); Assert.Equal(2, lifetime.CaptureOwnership().DeferredParentCreateCount); // The purely-deferred child has no gate; TryAcceptDelete's overall // gate-consuming path fails, but the deferred-create cancellation // side effect at the top of the method already ran. Assert.False(lifetime.TryAcceptDelete( new DeleteObject.Parsed(firstChildGuid, 1), isLocalPlayer: false, removeRetainedObject: true, out _)); Assert.Equal(1, lifetime.CaptureOwnership().DeferredParentCreateCount); RuntimeEntityRecord parent = lifetime .RegisterEntityWithInitialResidence( Spawn(parentGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( parent, out RuntimeInitialCreateResidenceLease parentLease)); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, parent, parentLease.Token, NoContact); Assert.Equal(1, receipt.ReplayedDeferredChildCount); Assert.False(lifetime.Entities.TryGetActive(firstChildGuid, out _)); Assert.True(lifetime.Entities.TryGetActive(secondChildGuid, out _)); Assert.Equal(0, lifetime.CaptureOwnership().DeferredParentCreateCount); } // Round 3 E-matrix (c): two raw Creates queued for the SAME child guid // (instance 1 and instance 2, both waiting on the same missing parent). // Deleting the child at instance 1 cancels only the older, same-or-older // queued generation; the strictly-newer instance 2 survives and replays. [Fact] public void NewerDeferredChildGenerationSurvivesOlderGenerationCleanupAndReplays() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 52UL); const uint parentGuid = 0x70030500u; const uint childGuid = 0x70030600u; Assert.True(lifetime.RegisterEntityWithInitialResidence( Spawn(childGuid, 1, includePosition: false, parentGuid: parentGuid), isLocalPlayer: false) .DeferredForParent); Assert.True(lifetime.RegisterEntityWithInitialResidence( Spawn(childGuid, 2, includePosition: false, parentGuid: parentGuid), isLocalPlayer: false) .DeferredForParent); Assert.Equal(2, lifetime.CaptureOwnership().DeferredParentCreateCount); Assert.False(lifetime.TryAcceptDelete( new DeleteObject.Parsed(childGuid, 1), isLocalPlayer: false, removeRetainedObject: true, out _)); Assert.Equal(1, lifetime.CaptureOwnership().DeferredParentCreateCount); RuntimeEntityRecord parent = lifetime .RegisterEntityWithInitialResidence( Spawn(parentGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( parent, out RuntimeInitialCreateResidenceLease parentLease)); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, parent, parentLease.Token, NoContact); Assert.Equal(1, receipt.ReplayedDeferredChildCount); Assert.True(lifetime.Entities.TryGetActive(childGuid, out RuntimeEntityRecord child)); Assert.Equal(2, child.Incarnation); Assert.Equal(0, lifetime.CaptureOwnership().DeferredParentCreateCount); } // Round 3 E-matrix (e): child GUID reuse across a COMPLETE lifecycle - // register+replay+run-to-completion the first incarnation, delete it // fully (gate removed), then reuse the SAME guid for a fresh incarnation // that is AGAIN deferred (different missing parent) and replays cleanly // with no leakage from the torn-down first generation. [Fact] public void ChildGuidReuseAfterFullLifecycleReplaysCleanlyWithoutStaleState() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 53UL); const uint firstParentGuid = 0x70030700u; const uint secondParentGuid = 0x70030800u; const uint childGuid = 0x70030900u; Assert.True(lifetime.RegisterEntityWithInitialResidence( Spawn(childGuid, 1, includePosition: false, parentGuid: firstParentGuid), isLocalPlayer: false) .DeferredForParent); RuntimeEntityRecord firstParent = lifetime .RegisterEntityWithInitialResidence( Spawn(firstParentGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( firstParent, out RuntimeInitialCreateResidenceLease firstParentLease)); _ = RunToCompletion(lifetime, firstParent, firstParentLease.Token, NoContact); Assert.True(lifetime.Entities.TryGetActive(childGuid, out RuntimeEntityRecord firstChild)); Assert.True(lifetime.TryGetInitialCreateResidence(firstChild, out RuntimeInitialCreateResidenceLease childLease)); _ = RunToCompletion(lifetime, firstChild, childLease.Token, NoContact); // Fully delete the first incarnation (its gate is removed too). Assert.True(lifetime.TryAcceptDelete( new DeleteObject.Parsed(childGuid, 1), isLocalPlayer: false, removeRetainedObject: true, out RuntimeEntityDeleteAcceptance deleteAcceptance)); lifetime.CompleteAcceptedDelete(deleteAcceptance); Assert.False(lifetime.Entities.TryGetActive(childGuid, out _)); // The SAME guid reused for a fresh incarnation, deferred behind a // DIFFERENT missing parent. Assert.True(lifetime.RegisterEntityWithInitialResidence( Spawn(childGuid, 2, includePosition: false, parentGuid: secondParentGuid), isLocalPlayer: false) .DeferredForParent); Assert.Equal(1, lifetime.CaptureOwnership().DeferredParentCreateCount); RuntimeEntityRecord secondParent = lifetime .RegisterEntityWithInitialResidence( Spawn(secondParentGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( secondParent, out RuntimeInitialCreateResidenceLease secondParentLease)); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, secondParent, secondParentLease.Token, NoContact); Assert.Equal(1, receipt.ReplayedDeferredChildCount); Assert.True(lifetime.Entities.TryGetActive(childGuid, out RuntimeEntityRecord reusedChild)); Assert.Equal(2, reusedChild.Incarnation); Assert.Equal(0, lifetime.CaptureOwnership().DeferredParentCreateCount); } // Round 3 E-matrix (f): a full session reset before the missing parent // ever arrives clears the deferred-create bucket to zero. [Fact] public void SessionResetBeforeParentArrivalClearsDeferredBucket() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 54UL); const uint parentGuid = 0x70030A00u; const uint childGuid = 0x70030B00u; Assert.True(lifetime.RegisterEntityWithInitialResidence( Spawn(childGuid, 1, includePosition: false, parentGuid: parentGuid), isLocalPlayer: false) .DeferredForParent); Assert.Equal(1, lifetime.CaptureOwnership().DeferredParentCreateCount); IReadOnlyList retirements = lifetime.BeginSessionClear(); Assert.Equal(0, lifetime.CaptureOwnership().DeferredParentCreateCount); foreach (RuntimeEntityRecord record in retirements) lifetime.CompleteSessionEntityRetirement(record); Assert.True(lifetime.CompleteSessionClearIfConverged()); } // Round 3 E-matrix (g): a full session reset triggered REENTRANTLY from // inside the deferred-child replay's own Registered callback. The // parent's own in-flight Execute() must abandon cleanly rather than // resurrect anything, and the session-clear transaction itself must // still converge once its own retirements are drained. [Fact] public void SessionResetFromWithinReplayDrivenCallbackConverges() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 55UL); const uint parentGuid = 0x70030C00u; const uint childGuid = 0x70030D00u; Assert.True(lifetime.RegisterEntityWithInitialResidence( Spawn(childGuid, 1, includePosition: false, parentGuid: parentGuid), isLocalPlayer: false) .DeferredForParent); RuntimeEntityRecord parent = lifetime .RegisterEntityWithInitialResidence( Spawn(parentGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( parent, out RuntimeInitialCreateResidenceLease parentLease)); IReadOnlyList? retirements = null; using IDisposable subscription = lifetime.Events.Subscribe(new EntityObserver(delta => { if (delta.Change is not RuntimeEntityChange.Registered || delta.Entity.Identity.ServerGuid != childGuid || retirements is not null) { return; } retirements = lifetime.BeginSessionClear(); })); RuntimeInitialCreateExecutionStatus status = lifetime.InitialCreateExecution.Execute( parent, parentLease.Token, NoContact, out RuntimeInitialCreateExecutionReceipt receipt); // The parent's own in-flight execution is no longer current once the // reentrant reset retires everything - a typed abandonment, not a // resurrection or an escaping exception. Assert.Equal(RuntimeInitialCreateExecutionStatus.RejectedAuthority, status); Assert.Equal(default, receipt); Assert.NotNull(retirements); foreach (RuntimeEntityRecord record in retirements!) lifetime.CompleteSessionEntityRetirement(record); Assert.True(lifetime.CompleteSessionClearIfConverged()); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().DeferredParentCreateCount); } // Round 3 E-matrix (h): instance sequence zero is a normal retail // timestamp, not an empty sentinel (see ParentAttachmentState's own // remarks on CancelDeferredChildGeneration) - a deferred child at // incarnation 0 must replay exactly like any other incarnation. [Fact] public void InstanceSequenceZeroChildReplaysCorrectly() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 56UL); const uint parentGuid = 0x70030E00u; const uint childGuid = 0x70030F00u; Assert.True(lifetime.RegisterEntityWithInitialResidence( Spawn(childGuid, 0, includePosition: false, parentGuid: parentGuid), isLocalPlayer: false) .DeferredForParent); Assert.Equal(1, lifetime.CaptureOwnership().DeferredParentCreateCount); RuntimeEntityRecord parent = lifetime .RegisterEntityWithInitialResidence( Spawn(parentGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( parent, out RuntimeInitialCreateResidenceLease parentLease)); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, parent, parentLease.Token, NoContact); Assert.Equal(1, receipt.ReplayedDeferredChildCount); Assert.Equal(0, lifetime.CaptureOwnership().DeferredParentCreateCount); Assert.True(lifetime.Entities.TryGetActive(childGuid, out RuntimeEntityRecord child)); Assert.Equal(0, child.Incarnation); Assert.True(lifetime.TryGetInitialCreateResidence(child, out _)); } // --------------------------------------------------------------- // F. Failure boundaries. // // (b) delete from the hook-emission window (after InitialAdoption, // before the FIFO drain) has NO reachable callback of its own: // RunInitialTail's Adopted phase (InitialAdoption) and HookRecorded // phase (the AfterEnterWorld teleport-hook trace entry) are both pure // trace-only mutations with no event dispatch - there is no observer // boundary between them. The ONLY publish inside RunInitialTail is // ReplayDeferredChildren's per-child Registered event (the // DeferredReplayed phase), which already has dedicated coverage: // DeleteDuringDeferredChildReplayAbandonsExecutionWithoutResurrection // (above, in section G) deletes reentrantly from exactly that callback // and proves the abandonment converges without resurrecting anything. // Adding a second, structurally-identical test here would only // duplicate that coverage; the closest reachable boundary already has a // test. // --------------------------------------------------------------- [Fact] public void DeleteFromObserverBeforeInitialAdoptionAbandonsFirstExecuteCleanly() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 60UL); const uint guid = 0x70031000u; // RegisterEntityCore's InitializeAcceptedCreateResidence (Begin()) // runs BEFORE PublishEntity(Registered, ...) - a real lease/token // already exists by the time this observer fires, so it can be // captured and later handed to Execute() to observe that FIRST // call's own outcome directly, rather than merely proving no lease // survives. RuntimeEntityRecord? capturedCanonical = null; RuntimeInitialCreateResidenceLease capturedLease = default; using IDisposable subscription = lifetime.Events.Subscribe(new EntityObserver(delta => { if (delta.Change is not RuntimeEntityChange.Registered || delta.Entity.Identity.ServerGuid != guid) { return; } Assert.True(lifetime.Entities.TryGetActive(guid, out RuntimeEntityRecord active)); capturedCanonical = active; Assert.True(lifetime.TryGetInitialCreateResidence(active, out capturedLease)); // Reentrant delete BEFORE Execute() is ever called for this // entity at all - before RunInitialTail's InitialAdoption phase, // the very first thing an Execute() call would otherwise do. Assert.True(lifetime.TryAcceptDelete( new DeleteObject.Parsed(guid, active.Incarnation), isLocalPlayer: false, removeRetainedObject: true, out RuntimeEntityDeleteAcceptance acceptance)); lifetime.CompleteAcceptedDelete(acceptance); })); // The reentrant delete bumps the lifetime-mutation counter for this // guid DURING the Registered publish, so RegisterEntityCore's own // post-publish currency check reports this registration as // superseded (Canonical: null in the returned result) - the record // captured from inside the observer, above, is the one to assert // against. _ = lifetime.RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false), isLocalPlayer: false); Assert.NotNull(capturedCanonical); RuntimeEntityRecord canonical = capturedCanonical!; Assert.False(lifetime.Entities.IsCurrent(canonical)); // TryAcceptDelete's own ForgetInitialCreateResidence already retired // the residence reentrantly - nothing was ever adopted. Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); var observed = new List(); using IDisposable secondSubscription = lifetime.Events.Subscribe( new EntityObserver(delta => observed.Add(delta.Change))); // The FIRST (and only) Execute() call for this entity, using the // token captured before the reentrant delete: the residence is // already gone (RejectedToken, not RejectedAuthority - there was // never a Progress to Abandon), publishes nothing new, and // converges. Assert.Equal( RuntimeInitialCreateExecutionStatus.RejectedToken, lifetime.InitialCreateExecution.Execute( canonical, capturedLease.Token, NoContact, out RuntimeInitialCreateExecutionReceipt receipt)); Assert.Equal(default, receipt); Assert.Empty(observed); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } [Fact] public void DeleteFromObserverBetweenFifoEntriesAppliesFirstOnlyAndConverges() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 61UL); const uint guid = 0x70031100u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); var appearance = new ObjDescEvent.Parsed( guid, new CreateObject.ModelData( 0x09000002u, Array.Empty(), Array.Empty(), Array.Empty()), InstanceSequence: 1, ObjDescSequence: 2); Assert.True(lifetime.TryApplyObjDesc(appearance, null, out _)); var vector = new VectorUpdate.Parsed( guid, new Vector3(11f, 12f, 13f), Vector3.Zero, InstanceSequence: 1, VectorSequence: 2); Assert.True(lifetime.TryApplyVector(vector, null, out _)); var observed = new List(); bool deletedFromObserver = false; using IDisposable subscription = lifetime.Events.Subscribe(new EntityObserver(delta => { observed.Add(delta.Change); if (deletedFromObserver || delta.Change is not RuntimeEntityChange.Updated) return; deletedFromObserver = true; // Between FIFO entry 1 (ObjDesc, already applied+published) and // entry 2 (Vector, not yet reached) - delete reentrantly. Assert.True(lifetime.TryAcceptDelete( new DeleteObject.Parsed(guid, canonical.Incarnation), isLocalPlayer: false, removeRetainedObject: true, out RuntimeEntityDeleteAcceptance acceptance)); lifetime.CompleteAcceptedDelete(acceptance); })); RuntimeInitialCreateExecutionStatus status = lifetime.InitialCreateExecution.Execute( canonical, lease.Token, NoContact, out RuntimeInitialCreateExecutionReceipt receipt); Assert.Equal(RuntimeInitialCreateExecutionStatus.RejectedAuthority, status); Assert.Equal(default, receipt); // ObjDesc applied+published exactly once; Vector never applied // (canonical's velocity stays at its pre-drain value); the delete's // own Deleted is the only other observed event. Assert.Equal( [RuntimeEntityChange.Updated, RuntimeEntityChange.Deleted], observed); Assert.Equal(0x09000002u, canonical.Snapshot.BasePaletteId); Assert.Null(canonical.Snapshot.Physics!.Value.Velocity); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); // A stale retry never resurrects anything or reapplies Vector. Assert.Equal( RuntimeInitialCreateExecutionStatus.RejectedToken, lifetime.InitialCreateExecution.Execute( canonical, lease.Token, NoContact, out RuntimeInitialCreateExecutionReceipt retryReceipt)); Assert.Equal(default, retryReceipt); Assert.Null(canonical.Snapshot.Physics!.Value.Velocity); } // Round 3 F(d): an observer that throws during a publish must not // corrupt the drain or escape Execute() - RuntimeEntityObjectEventStream. // Dispatch contains each observer's exception per-call (RecordDispatchFailure) // and continues to the next observer/pending item. Pin that actual, // verified behavior here rather than assuming propagation. [Fact] public void ObserverThrowDuringPublishIsContainedAndDrainConvergesWithNoDuplicateRetry() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 62UL); const uint guid = 0x70031200u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); var vector = new VectorUpdate.Parsed( guid, Vector3.One, Vector3.Zero, InstanceSequence: 1, VectorSequence: 2); Assert.True(lifetime.TryApplyVector(vector, null, out _)); using IDisposable subscription = lifetime.Events.Subscribe(new EntityObserver(delta => throw new InvalidOperationException("Deliberate observer failure for F(d)."))); long failuresBefore = lifetime.Events.DispatchFailureCount; RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); Assert.Contains( RuntimeInitialCreateExecutedActionKind.Vector, receipt.Trace.Select(static a => a.Kind)); Assert.Equal(Vector3.One, canonical.Snapshot.Physics!.Value.Velocity); Assert.True(lifetime.Events.DispatchFailureCount > failuresBefore); Assert.NotNull(lifetime.Events.LastDispatchFailure); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); // A stale retry after the (contained) throw does not duplicate any // side effect. Assert.Equal( RuntimeInitialCreateExecutionStatus.RejectedToken, lifetime.InitialCreateExecution.Execute(canonical, lease.Token, NoContact, out _)); Assert.Equal(Vector3.One, canonical.Snapshot.Physics!.Value.Velocity); } // --------------------------------------------------------------- // G. Reentrancy // --------------------------------------------------------------- [Fact] public void ReentrantExecuteForTheSameEntityFailsClosedRatherThanInterleaving() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 11UL); const uint guid = 0x70026000u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); var reentrantStatuses = new List(); using IDisposable subscription = lifetime.Events.Subscribe(new EntityObserver(delta => { if (delta.Change is not RuntimeEntityChange.Updated || reentrantStatuses.Count != 0) return; // Firing a nested Execute from inside an event observer while the // outer Execute is still on the stack must fail closed. reentrantStatuses.Add(lifetime.InitialCreateExecution.Execute( canonical, lease.Token, NoContact, out _)); })); var vector = new VectorUpdate.Parsed( guid, Vector3.One, Vector3.Zero, InstanceSequence: 1, VectorSequence: 2); Assert.True(lifetime.TryApplyVector(vector, null, out _)); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); Assert.Equal( [RuntimeInitialCreateExecutionStatus.RejectedAuthority], reentrantStatuses); Assert.NotEmpty(receipt.Trace); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } [Fact] public void DeleteDuringDeferredChildReplayAbandonsExecutionWithoutResurrection() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 12UL); const uint parentGuid = 0x70026100u; const uint childGuid = 0x70026200u; Assert.True(lifetime.RegisterEntityWithInitialResidence( Spawn(childGuid, 1, includePosition: false, parentGuid: parentGuid), isLocalPlayer: false) .DeferredForParent); RuntimeEntityRecord parent = lifetime .RegisterEntityWithInitialResidence( Spawn(parentGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( parent, out RuntimeInitialCreateResidenceLease parentLease)); using IDisposable subscription = lifetime.Events.Subscribe(new EntityObserver(delta => { if (delta.Change is not RuntimeEntityChange.Registered || delta.Entity.Identity.ServerGuid != childGuid) { return; } // A reentrant delete of the PARENT itself, triggered from inside // the child-registration callback the deferred replay drives. Assert.True(lifetime.TryAcceptDelete( new DeleteObject.Parsed(parentGuid, parent.Incarnation), isLocalPlayer: false, removeRetainedObject: true, out RuntimeEntityDeleteAcceptance acceptance)); lifetime.CompleteAcceptedDelete(acceptance); })); RuntimeInitialCreateExecutionStatus status = lifetime.InitialCreateExecution.Execute( parent, parentLease.Token, NoContact, out RuntimeInitialCreateExecutionReceipt receipt); Assert.Equal(RuntimeInitialCreateExecutionStatus.RejectedAuthority, status); Assert.Equal(default, receipt); Assert.False(lifetime.Entities.IsCurrent(parent)); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); // Only the PARENT's own residence/progress was ever touched by this // abandoned Execute call. The CHILD is a distinct entity the // deferred replay legitimately registered through the canonical // route before the reentrant delete happened - its own (never // executed) residence lease correctly remains open; deleting the // parent does not cascade-tear-down an unrelated child. Assert.Equal(1, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.True(lifetime.Entities.TryGetActive(childGuid, out RuntimeEntityRecord child)); Assert.True(lifetime.TryGetInitialCreateResidence(child, out _)); } // Round 3 B2 regression (reentrancy from a publish callback): a wire // apply (TryApplyMotion) reentrantly enqueued from inside an EARLIER // continuation's own publish must NOT retire the residence, and the // continuation that was ALREADY queued before the drain even started // (Vector) must still drain - proving the reentrant enqueue never // displaces or skips the pre-existing FIFO tail. [Fact] public void WireApplyDuringDrainPublishDoesNotRetireResidenceAndPreExistingFifoStillDrains() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 63UL); const uint guid = 0x70031300u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); var appearance = new ObjDescEvent.Parsed( guid, new CreateObject.ModelData( 0x0A000002u, Array.Empty(), Array.Empty(), Array.Empty()), InstanceSequence: 1, ObjDescSequence: 2); Assert.True(lifetime.TryApplyObjDesc(appearance, null, out _)); var vector = new VectorUpdate.Parsed( guid, new Vector3(21f, 22f, 23f), Vector3.Zero, InstanceSequence: 1, VectorSequence: 2); Assert.True(lifetime.TryApplyVector(vector, null, out _)); var observed = new List(); bool reentered = false; using IDisposable subscription = lifetime.Events.Subscribe(new EntityObserver(delta => { observed.Add(delta.Change); if (reentered || delta.Change is not RuntimeEntityChange.Updated) return; reentered = true; var motion = new WorldSession.EntityMotionUpdate( guid, new CreateObject.ServerMotionState(0x3d, 0x11), InstanceSequence: 1, MovementSequence: 2, ServerControlSequence: 1, IsAutonomous: false); Assert.True(lifetime.TryApplyMotion(motion, retainPayload: true, null, out _, out _)); })); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); // Vector (already queued before the drain started) drains BEFORE // the reentrantly-enqueued Motion, and both drain exactly once. Assert.Equal( [ RuntimeInitialCreateExecutedActionKind.InitialAdoption, RuntimeInitialCreateExecutedActionKind.ObjDesc, RuntimeInitialCreateExecutedActionKind.Vector, RuntimeInitialCreateExecutedActionKind.Movement, ], receipt.Trace.Select(static a => a.Kind)); Assert.Equal( [RuntimeEntityChange.Updated, RuntimeEntityChange.Updated, RuntimeEntityChange.Updated], observed); Assert.Equal(new Vector3(21f, 22f, 23f), canonical.Snapshot.Physics!.Value.Velocity); Assert.Equal(new CreateObject.ServerMotionState(0x3d, 0x11), canonical.Snapshot.MotionState); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } [Fact] public void RegisterDifferentEntityFromCallbackDuringDrainConvergesBothIndependently() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 64UL); const uint firstGuid = 0x70031400u; const uint secondGuid = 0x70031500u; RuntimeEntityRecord first = lifetime .RegisterEntityWithInitialResidence( Spawn(firstGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( first, out RuntimeInitialCreateResidenceLease firstLease)); var vector = new VectorUpdate.Parsed( firstGuid, Vector3.One, Vector3.Zero, InstanceSequence: 1, VectorSequence: 2); Assert.True(lifetime.TryApplyVector(vector, null, out _)); RuntimeEntityRecord? secondCanonical = null; using IDisposable subscription = lifetime.Events.Subscribe(new EntityObserver(delta => { if (delta.Change is not RuntimeEntityChange.Updated || delta.Entity.Identity.ServerGuid != firstGuid || secondCanonical is not null) { return; } // A completely unrelated entity registered reentrantly, from // inside the FIRST entity's own drain publish. secondCanonical = lifetime .RegisterEntityWithInitialResidence( Spawn(secondGuid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; })); RuntimeInitialCreateExecutionReceipt firstReceipt = RunToCompletion( lifetime, first, firstLease.Token, NoContact); Assert.NotEmpty(firstReceipt.Trace); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); Assert.NotNull(secondCanonical); Assert.True(lifetime.Entities.IsCurrent(secondCanonical!)); Assert.True(lifetime.TryGetInitialCreateResidence(secondCanonical!, out RuntimeInitialCreateResidenceLease secondLease)); // The second entity's own independent residence still drains // normally afterward. RuntimeInitialCreateExecutionReceipt secondReceipt = RunToCompletion( lifetime, secondCanonical!, secondLease.Token, NoContact); Assert.Contains( RuntimeInitialCreateExecutedActionKind.InitialAdoption, secondReceipt.Trace.Select(static a => a.Kind)); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } // Round 3 G(c)/I2: recreating the SAME guid at a NEWER incarnation from // a reentrant callback mid-drain. The old execution must abandon // cleanly (its own progress+residence converge) with NO FIFO transfer // to the new incarnation; the new incarnation is a completely // independent, unaffected registration. This is the same scenario the // task's "replacement convergence" item names - one test covers both. [Fact] public void RecreateSameGuidNewerIncarnationFromCallbackAbandonsOldExecutionWithoutFifoTransfer() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 65UL); const uint guid = 0x70031600u; RuntimeEntityRecord canonicalGen1 = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonicalGen1, out RuntimeInitialCreateResidenceLease lease)); var appearance = new ObjDescEvent.Parsed( guid, new CreateObject.ModelData( 0x0B000002u, Array.Empty(), Array.Empty(), Array.Empty()), InstanceSequence: 1, ObjDescSequence: 2); Assert.True(lifetime.TryApplyObjDesc(appearance, null, out _)); var vector = new VectorUpdate.Parsed( guid, new Vector3(31f, 32f, 33f), Vector3.Zero, InstanceSequence: 1, VectorSequence: 2); Assert.True(lifetime.TryApplyVector(vector, null, out _)); bool recreated = false; using IDisposable subscription = lifetime.Events.Subscribe(new EntityObserver(delta => { if (recreated || delta.Change is not RuntimeEntityChange.Updated) return; recreated = true; // Same guid, NEWER incarnation, from inside the FIRST // continuation's (ObjDesc's) own publish - a bare Create with no // residence, to isolate the replacement mechanics from any // second drain. _ = lifetime.RegisterEntity(Spawn(guid, 2, includePosition: false)); })); RuntimeInitialCreateExecutionStatus status = lifetime.InitialCreateExecution.Execute( canonicalGen1, lease.Token, NoContact, out RuntimeInitialCreateExecutionReceipt receipt); Assert.Equal(RuntimeInitialCreateExecutionStatus.RejectedAuthority, status); Assert.Equal(default, receipt); Assert.False(lifetime.Entities.IsCurrent(canonicalGen1)); // Old progress+residence fully converge. Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); // The new incarnation is unaffected: no residence pending (a bare // RegisterEntity never begins one), and none of generation 1's // FIFO (Vector, still undrained) ever transferred to it. Assert.True(lifetime.Entities.TryGetActive(guid, out RuntimeEntityRecord canonicalGen2)); Assert.Equal(2, canonicalGen2.Incarnation); Assert.False(lifetime.TryGetInitialCreateResidence(canonicalGen2, out _)); Assert.Null(canonicalGen2.Snapshot.Physics!.Value.Velocity); } // Round 3 G(d): Dispose() called reentrantly from a publish callback. // Pin the ACTUAL observed behavior (verified by running this test) // rather than assuming either containment or propagation. [Fact] public void DisposeFromCallbackDuringDrainConvergesTheCompleteLedger() { var lifetime = new RuntimeEntityObjectLifetime(); Bind(lifetime, 66UL); const uint guid = 0x70031700u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); var vector = new VectorUpdate.Parsed( guid, Vector3.One, Vector3.Zero, InstanceSequence: 1, VectorSequence: 2); Assert.True(lifetime.TryApplyVector(vector, null, out _)); using IDisposable subscription = lifetime.Events.Subscribe(new EntityObserver(delta => { if (delta.Change is RuntimeEntityChange.Updated) lifetime.Dispose(); })); RuntimeInitialCreateExecutionStatus status = lifetime.InitialCreateExecution.Execute( canonical, lease.Token, NoContact, out RuntimeInitialCreateExecutionReceipt receipt); // Dispose() reentrantly retires everything (BeginSessionClear -> // teardown -> converge) before the outer Execute() call's own // currency check runs again; that check sees a no-longer-current // canonical and abandons in the SAME typed way any other reentrant // teardown does. No invalid enumeration/exception escapes. Assert.Equal(RuntimeInitialCreateExecutionStatus.RejectedAuthority, status); Assert.Equal(default, receipt); Assert.True(lifetime.CaptureOwnership().IsConverged); } // --------------------------------------------------------------- // H. Saturation / stale-progress // --------------------------------------------------------------- [Fact] public void StaleProgressLeaseIdIsDiscardedAndFailsClosedThenRetrySucceedsCleanly() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 13UL); const uint guid = 0x70027000u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); RuntimeEntityKey key = canonical.Key!.Value; // Directly plant a stale Progress entry under a LeaseId that does // NOT match the current lease's token - the exact "an old // incarnation's progress leaking into a reused key" scenario the // contract requires Execute to discard and fail closed on for this // one call, never silently reusing it. PlantStaleProgress(lifetime, key, lease.Token.LeaseId + 1_000UL); Assert.Equal(1, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); Assert.Equal( RuntimeInitialCreateExecutionStatus.RejectedAuthority, lifetime.InitialCreateExecution.Execute(canonical, lease.Token, NoContact, out _)); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); Assert.NotEmpty(receipt.Trace); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } private static void PlantStaleProgress( RuntimeEntityObjectLifetime lifetime, RuntimeEntityKey key, ulong staleLeaseId) { Type progressType = typeof(RuntimeInitialCreateContinuationExecutor) .GetNestedType("Progress", System.Reflection.BindingFlags.NonPublic)!; object stale = System.Runtime.CompilerServices.RuntimeHelpers .GetUninitializedObject(progressType); System.Reflection.PropertyInfo leaseIdProperty = progressType.GetProperty( "LeaseId", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic)!; leaseIdProperty.SetValue(stale, staleLeaseId); System.Reflection.FieldInfo progressField = typeof(RuntimeInitialCreateContinuationExecutor) .GetField( "_progress", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)!; var dictionary = (System.Collections.IDictionary)progressField.GetValue( lifetime.InitialCreateExecution)!; dictionary[key] = stale; } // Round 3 H (saturation): adoption Revision pinned at ulong.MaxValue - // CanEnqueue's completed-entry branch requires // "Revision < ulong.MaxValue", so a NEW continuation fails closed BEFORE // any wire consumption; whatever was ALREADY committed before saturation // still drains and converges normally through the executor. [Fact] public void AdoptionRevisionSaturationFailsClosedBeforeAnyNewContinuationCanEnqueue() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 70UL); const uint guid = 0x70032000u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); var firstVector = new VectorUpdate.Parsed( guid, Vector3.One, Vector3.Zero, InstanceSequence: 1, VectorSequence: 2); Assert.True(lifetime.TryApplyVector(firstVector, null, out _)); Assert.Equal( RuntimeInitialCreateResidenceCompletionStatus.Completed, lifetime.InitialCreateResidences.Complete(canonical, lease.Token, out _)); SetCompletedAdoptionRevision(lifetime, canonical.Key!.Value, ulong.MaxValue); var secondVector = new VectorUpdate.Parsed( guid, new Vector3(2f, 2f, 2f), Vector3.Zero, InstanceSequence: 1, VectorSequence: 3); Assert.False(lifetime.TryApplyVector(secondVector, null, out _)); Assert.True(lifetime.InitialCreateResidences.TryGetTransaction( canonical, out RuntimeInitialCreateResidenceLease afterFailedEnqueue)); Assert.Single(afterFailedEnqueue.Continuations); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); // Only the ONE continuation committed before saturation ever drains - // exactly once, never duplicated by the failed second attempt. Assert.Equal( [ RuntimeInitialCreateExecutedActionKind.InitialAdoption, RuntimeInitialCreateExecutedActionKind.Vector, ], receipt.Trace.Select(static a => a.Kind)); Assert.Equal(Vector3.One, canonical.Snapshot.Physics!.Value.Velocity); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } /// /// Local copy of RuntimeInitialCreateResidenceStateTests' /// SetCompletedAdoptionRevision reflection helper (per this task's /// explicit instruction to reuse the pattern locally rather than share /// test-project internals across files). /// private static void SetCompletedAdoptionRevision( RuntimeEntityObjectLifetime lifetime, RuntimeEntityKey key, ulong revision) { Type stateType = typeof(RuntimeInitialCreateResidenceState); System.Reflection.FieldInfo completedField = stateType.GetField( "_completed", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)!; var completed = (System.Collections.IDictionary)completedField.GetValue( lifetime.InitialCreateResidences)!; object entry = completed[key]!; System.Reflection.PropertyInfo receiptProperty = entry.GetType().GetProperty( "Receipt", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic)!; var receipt = (RuntimeInitialCreateResidenceReceipt)receiptProperty.GetValue(entry)!; receiptProperty.SetValue( entry, receipt with { Adoption = receipt.Adoption with { Revision = revision } }); } // --------------------------------------------------------------- // External-race detection on the executor's staleness baseline // (regression coverage for the narrowed pre-Complete // AdvanceExecutorBaseline guard - Finding 1) // --------------------------------------------------------------- [Fact] public void ExternalPositionAuthorityMutationWithNoPendingPlacementFailsClosedAndConverges() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 16UL); const uint guid = 0x70029000u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); // Drive the residence directly to "completed + adopted" - the exact // state RunInitialTail leaves behind before draining any // continuation, with NO pending continuation placement. This is // the only state in which the narrowed pre-Complete // AdvanceExecutorBaseline guard does NOT resync the baseline // before the next Complete() call runs. Assert.Equal( RuntimeInitialCreateResidenceCompletionStatus.Completed, lifetime.InitialCreateResidences.Complete(canonical, lease.Token, out _)); Assert.True(lifetime.InitialCreateResidences.AdoptCompletedPlacement(canonical, lease.Token)); var observed = new List(); using IDisposable subscription = lifetime.Events.Subscribe( new EntityObserver(delta => observed.Add(delta.Change))); // External, non-executor mutation of one of the four baseline // fields (never through AdvanceExecutorBaseline) - simulating a // genuine race between Execute calls with nothing in flight. Before // Finding 1's fix, the OLD unconditional pre-Complete rebaseline // would have silently absorbed this and returned Completed instead. lifetime.Entities.AdvancePositionAuthority(canonical); Assert.Equal( RuntimeInitialCreateExecutionStatus.RejectedAuthority, lifetime.InitialCreateExecution.Execute( canonical, lease.Token, NoContact, out RuntimeInitialCreateExecutionReceipt receipt)); Assert.Equal(default, receipt); Assert.Empty(observed); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } [Fact] public void ExternalFullCellMutationWithNoPendingPlacementFailsClosedAndConverges() { using RuntimeEntityObjectLifetime lifetime = new(); Bind(lifetime, 17UL); const uint guid = 0x70029100u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); Assert.Equal( RuntimeInitialCreateResidenceCompletionStatus.Completed, lifetime.InitialCreateResidences.Complete(canonical, lease.Token, out _)); Assert.True(lifetime.InitialCreateResidences.AdoptCompletedPlacement(canonical, lease.Token)); var observed = new List(); using IDisposable subscription = lifetime.Events.Subscribe( new EntityObserver(delta => observed.Add(delta.Change))); // FullCellId is the second field the reviewer explicitly named - // exercise it independently of PositionAuthorityVersion. lifetime.Entities.SetFullCell(canonical, Cell, Landblock); Assert.Equal( RuntimeInitialCreateExecutionStatus.RejectedAuthority, lifetime.InitialCreateExecution.Execute( canonical, lease.Token, NoContact, out RuntimeInitialCreateExecutionReceipt receipt)); Assert.Equal(default, receipt); Assert.Empty(observed); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); } // Round 4 R4-2 / R4-9(b): the reviewer's exact scenario - external // SetFullCell DURING the AwaitingContinuationPlacement window (after the // continuation's OWN placement has been acknowledged but before the // executor consumes it), not merely with no placement in flight at all // (that is the PRECEDING test above). Before the fix, both failure arms // in ResumePendingPlacement cleared PendingContinuationPlacement and // abandoned WITHOUT ever forgetting the retained acknowledged-placement // entry - HasRetainedCompletion for this key would then stay true // forever, blocking EVERY later placement begin (the runtime-surface.md // 3.1 deadlock this executor exists to resolve). [Fact] public void ExternalFullCellMutationDuringAwaitingContinuationPlacementForgetsThePendingPlacementAndAllowsAFreshOneToBegin() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 90UL); const uint guid = 0x70034000u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: true) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); WorldSession.EntityPositionUpdate update = PositionUpdate( guid, positionSequence: 2, teleportSequence: 1, forcePositionSequence: 0, positionX: 40f); Assert.True(lifetime.TryApplyPosition( update, isLocalPlayer: true, null, null, null, out _, out _, out _)); RuntimeInitialCreateExecutionStatus status = lifetime.InitialCreateExecution.Execute( canonical, lease.Token, NoContact, out _); Assert.Equal(RuntimeInitialCreateExecutionStatus.AwaitingContinuationPlacement, status); RuntimeEntityKey key = canonical.Key!.Value; Assert.True(lifetime.InitialCreateExecution .TryGetPendingContinuationRoute(key, out RuntimeAuthoritativePositionRoute route)); // Drives the continuation's own placement through prepare/submit/ // acknowledge - the acknowledged projection is now retained under // this exact token, ready for the NEXT Execute call to consume. CompletePendingContinuationPlacement(lifetime, key, route); // External mutation of the record's cell AFTER the continuation's // own placement was acknowledged but BEFORE the executor consumes // it - ResumePendingPlacement's projection/record agreement check // must catch this exactly like Complete() does for the initial // placement. lifetime.Entities.SetFullCell(canonical, canonical.FullCellId + 999u, Landblock); Assert.Equal( RuntimeInitialCreateExecutionStatus.RejectedAuthority, lifetime.InitialCreateExecution.Execute( canonical, lease.Token, NoContact, out RuntimeInitialCreateExecutionReceipt receipt)); Assert.Equal(default, receipt); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); RuntimeSetPositionOwnershipSnapshot physicsOwnership = lifetime.Physics.SetPosition.CaptureOwnership(); Assert.Equal(0, physicsOwnership.ActiveOperationCount); // The central R4-2 claim: no retained-completion leak. Assert.Equal(0, physicsOwnership.AcknowledgedPlacementCompletionCount); // A subsequent FRESH authored placement for the SAME entity can // begin - proves HasRetainedCompletion no longer blocks it. RuntimeEntityPlacementToken fresh = lifetime.Physics.SetPosition .TryBeginExclusiveAuthoredPlacement( canonical, canonical.PositionAuthorityVersion, RuntimeSetPositionOperationKind.LocalAuthoritative); Assert.True(fresh.IsValid); } // Round 4 R4-9(a): guards B3's residence-retirement notification edge // for a placement STILL IN FLIGHT (not yet acknowledged) - a THIRD // PARTY (not the executor) discovers residence staleness through the // residence's own host-facing TryGetTransaction query, which internally // Retires the completed entry. That Retire must notify the executor so // it forgets its OWN separately-tracked pending continuation placement // token, not just the residence's initial-lease placement. [Fact] public void ThirdPartyTryGetTransactionRetireWhileAwaitingContinuationPlacementDiscardsExecutorProgressAndForgetsThePendingPlacement() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 91UL); const uint guid = 0x70034100u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: true) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); WorldSession.EntityPositionUpdate update = PositionUpdate( guid, positionSequence: 2, teleportSequence: 1, forcePositionSequence: 0, positionX: 40f); Assert.True(lifetime.TryApplyPosition( update, isLocalPlayer: true, null, null, null, out _, out _, out _)); RuntimeInitialCreateExecutionStatus status = lifetime.InitialCreateExecution.Execute( canonical, lease.Token, NoContact, out _); Assert.Equal(RuntimeInitialCreateExecutionStatus.AwaitingContinuationPlacement, status); Assert.Equal(1, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); RuntimeEntityKey key = canonical.Key!.Value; Assert.True(lifetime.InitialCreateExecution .TryGetPendingContinuationPlacement(key, out RuntimeEntityPlacementToken pendingPlacement)); Assert.True(lifetime.Physics.SetPosition.IsPlacementCurrent(pendingPlacement)); // External, non-executor mutation of one of the four executor- // tracked baseline fields - simulates something OTHER than the // executor discovering staleness through a THIRD-PARTY call to the // residence's own host-facing TryGetTransaction, never through // Execute at all. lifetime.Entities.AdvancePositionAuthority(canonical); Assert.False(lifetime.InitialCreateResidences.TryGetTransaction(canonical, out _)); // B3's notification edge: the residence's own Retire (triggered by // a THIRD PARTY, not the executor) must notify the executor so it // forgets the pending continuation placement, not just the // residence's own initial-lease placement. Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.False(lifetime.Physics.SetPosition.IsPlacementCurrent(pendingPlacement)); RuntimeSetPositionOwnershipSnapshot physicsOwnership = lifetime.Physics.SetPosition.CaptureOwnership(); Assert.Equal(0, physicsOwnership.ActiveOperationCount); Assert.Equal(0, physicsOwnership.PlacementCompletionWatchCount); Assert.Equal(0, physicsOwnership.AcknowledgedPlacementCompletionCount); // A subsequent FRESH authored placement for the SAME entity can // begin - no lingering block from the forgotten pending placement. RuntimeEntityPlacementToken fresh = lifetime.Physics.SetPosition .TryBeginExclusiveAuthoredPlacement( canonical, canonical.PositionAuthorityVersion, RuntimeSetPositionOperationKind.LocalAuthoritative); Assert.True(fresh.IsValid); } // --------------------------------------------------------------- // I. Ownership convergence // --------------------------------------------------------------- [Fact] public void ResetDuringAwaitingContinuationPlacementConvergesEveryLedger() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 14UL); const uint guid = 0x70028000u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: true) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); WorldSession.EntityPositionUpdate update = PositionUpdate( guid, positionSequence: 2, teleportSequence: 1, forcePositionSequence: 0, positionX: 40f); Assert.True(lifetime.TryApplyPosition( update, isLocalPlayer: true, null, null, null, out _, out _, out _)); RuntimeInitialCreateExecutionStatus status = lifetime.InitialCreateExecution.Execute( canonical, lease.Token, NoContact, out _); Assert.Equal(RuntimeInitialCreateExecutionStatus.AwaitingContinuationPlacement, status); Assert.Equal(1, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); IReadOnlyList retirements = lifetime.BeginSessionClear(); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership().ActiveOperationCount); foreach (RuntimeEntityRecord record in retirements) lifetime.CompleteSessionEntityRetirement(record); Assert.True(lifetime.CompleteSessionClearIfConverged()); } [Fact] public void DisposalConvergesTheCompleteOwnershipLedgerAfterASuccessfulExecution() { var lifetime = new RuntimeEntityObjectLifetime(); Bind(lifetime, 15UL); const uint guid = 0x70028100u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence( Spawn(guid, 1, includePosition: false), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); _ = RunToCompletion(lifetime, canonical, lease.Token, NoContact); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); lifetime.Dispose(); Assert.True(lifetime.CaptureOwnership().IsConverged); } // --------------------------------------------------------------- // J. Gap closures (B1 operation-slot contention). // --------------------------------------------------------------- // Round 3 B1-gap: a Position continuation's own placement-begin can fail // on TRANSIENT operation-slot contention (another operation already owns // this entity's SetPosition slot) rather than genuine staleness. The // merge+publish must already have committed by that point (this is NOT // an abandonment); a retry after the slot frees must complete WITHOUT // re-running the merge or re-publishing. [Fact] public void OperationSlotContentionYieldsRetryableWithoutAbandoningThenRetryCompletesWithNoDuplicatePublish() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 80UL); const uint guid = 0x70033000u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); Assert.Equal(Cell, canonical.FullCellId); // Entry 1: a Vector continuation whose own publish is the injection // point for occupying the entity's SetPosition slot from OUTSIDE the // executor - by the time this fires, RunInitialTail's // AdoptCompletedPlacement has already freed the slot the INITIAL // placement held, so an external begin here genuinely succeeds. var vector = new VectorUpdate.Parsed( guid, Vector3.One, Vector3.Zero, InstanceSequence: 1, VectorSequence: 2); Assert.True(lifetime.TryApplyVector(vector, null, out _)); // Entry 2: a far-distance Remote Position continuation that will // require its OWN real SetPosition placement. WorldSession.EntityPositionUpdate update = PositionUpdate( guid, positionSequence: 2, teleportSequence: 0, forcePositionSequence: 0, positionX: 25f); Assert.True(lifetime.TryApplyPosition( update, isLocalPlayer: false, null, null, null, out PositionTimestampDisposition disposition, out _, out _)); Assert.Equal(PositionTimestampDisposition.Apply, disposition); RuntimeEntityPlacementToken unrelatedOccupant = default; var observed = new List(); using IDisposable subscription = lifetime.Events.Subscribe(new EntityObserver(delta => { observed.Add(delta.Change); if (unrelatedOccupant.IsValid || delta.Change is not RuntimeEntityChange.Updated) return; unrelatedOccupant = lifetime.Physics.SetPosition.TryBeginExclusiveAuthoredPlacement( canonical, canonical.PositionAuthorityVersion, RuntimeSetPositionOperationKind.RemoteAuthoritative); Assert.True(unrelatedOccupant.IsValid); })); var inputs = new RuntimeInitialCreateExecutionInputs( UsePositionFromServer: false, PlayerDistance: 200f); RuntimeInitialCreateExecutionStatus status = lifetime.InitialCreateExecution.Execute( canonical, lease.Token, inputs, out RuntimeInitialCreateExecutionReceipt receipt); // Contention, not staleness: retryable, residence/progress both // still open (no abandonment). Assert.Equal(RuntimeInitialCreateExecutionStatus.AwaitingContinuationPlacement, status); Assert.Equal(default, receipt); Assert.Equal(1, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(1, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); // The merge+publish for BOTH entries already committed before the // contention was even discovered. Assert.Equal( [RuntimeEntityChange.Updated, RuntimeEntityChange.Updated], observed); Assert.Equal(25f, canonical.Snapshot.Position!.Value.PositionX); // Free the slot; retry. lifetime.Physics.SetPosition.PublishCancellation( lifetime.Physics.SetPosition.ForgetExactPlacement(unrelatedOccupant)); RuntimeInitialCreateExecutionStatus retryStatus = lifetime.InitialCreateExecution.Execute( canonical, lease.Token, inputs, out RuntimeInitialCreateExecutionReceipt retryReceipt); Assert.Equal(RuntimeInitialCreateExecutionStatus.AwaitingContinuationPlacement, retryStatus); Assert.Equal(default, retryReceipt); // No re-merge, no re-publish on the contention retry itself. Assert.Equal( [RuntimeEntityChange.Updated, RuntimeEntityChange.Updated], observed); RuntimeEntityKey key = canonical.Key!.Value; Assert.True(lifetime.InitialCreateExecution .TryGetPendingContinuationRoute(key, out RuntimeAuthoritativePositionRoute route)); Assert.Equal(RuntimeAuthoritativePositionDisposition.SetPositionSimple, route.Disposition); CompletePendingContinuationPlacement(lifetime, key, route); RuntimeInitialCreateExecutionReceipt finalReceipt = RunToCompletion( lifetime, canonical, lease.Token, inputs); Assert.Equal( [ RuntimeInitialCreateExecutedActionKind.InitialAdoption, RuntimeInitialCreateExecutedActionKind.Vector, RuntimeInitialCreateExecutedActionKind.Position, ], finalReceipt.Trace.Select(static a => a.Kind)); // Still exactly two publishes across the entire contention + // retry + completion sequence. Assert.Equal( [RuntimeEntityChange.Updated, RuntimeEntityChange.Updated], observed); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateResidenceLeaseCount); Assert.Equal(0, lifetime.CaptureOwnership().InitialCreateExecutorProgressCount); Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership().ActiveOperationCount); } // --------------------------------------------------------------- // F. C0-1: executor-completion bridge / C0-2: live-input binding // --------------------------------------------------------------- [Fact] public void ExecutorCompletion_PublishesOnTheSamePlacementStreamCorrelatedWithTheFullReceipt() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 400UL); const uint guid = 0x70024000u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: true) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); var observed = new List(); using IDisposable subscription = lifetime.Events.SubscribePlacement( new PlacementObserver(delta => observed.Add(delta.Placement))); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); RuntimePlacementProjectionSnapshot completion = Assert.Single(observed); Assert.Equal( RuntimePlacementProjectionKind.ExecutorCompleted, completion.Kind); Assert.Equal(canonical.Key, completion.Token.Entity); Assert.True(lifetime.InitialCreateExecution.TryGetCompletionReceipt( completion.Token, out RuntimeInitialCreateExecutionReceipt correlated)); Assert.Equal(receipt, correlated); // Acknowledge-only, exact-head, same as every other Kind. Assert.Equal(1, lifetime.Physics.SetPosition.PendingProjectionCount); Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection( completion.Token)); Assert.Equal(0, lifetime.Physics.SetPosition.PendingProjectionCount); } [Fact] public void ExecutorCompletion_ObservedOnlyAfterAnyContinuationPlacementInFifoOrder() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 401UL); const uint guid = 0x70024001u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: true) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); // A teleport-advanced Position continuation performs its OWN // authored SetPosition - the continuation placement C0-1's contract // says already flows through the channel unchanged. WorldSession.EntityPositionUpdate update = PositionUpdate( guid, positionSequence: 2, teleportSequence: 1, forcePositionSequence: 0, positionX: 40f); Assert.True(lifetime.TryApplyPosition( update, isLocalPlayer: true, null, null, null, out PositionTimestampDisposition disposition, out _, out _)); Assert.Equal(PositionTimestampDisposition.Apply, disposition); var observed = new List(); using IDisposable subscription = lifetime.Events.SubscribePlacement( new PlacementObserver(delta => observed.Add(delta.Placement))); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); Assert.Equal( [ RuntimePlacementProjectionKind.Place, RuntimePlacementProjectionKind.ExecutorCompleted, ], observed.Select(static s => s.Kind)); Assert.Equal(canonical.Key, observed[0].Token.Entity); Assert.True(lifetime.InitialCreateExecution.TryGetCompletionReceipt( observed[1].Token, out RuntimeInitialCreateExecutionReceipt correlated)); Assert.Equal(receipt, correlated); // The continuation Place receipt was already acknowledged by // RunToCompletion's own CompletePendingContinuationPlacement helper // before Execute ever reached Completed - only the fresh // ExecutorCompleted receipt is still outstanding. Assert.Equal(1, lifetime.Physics.SetPosition.PendingProjectionCount); Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection( observed[1].Token)); } // --------------------------------------------------------------- // C3-1: the public RuntimePlacementProjectionChannel host consumption // surface for an executor completion (TryGetInitialCreateCompletion). // --------------------------------------------------------------- [Fact] public void PlacementChannel_TryGetInitialCreateCompletion_ProjectsHookPhaseCellAndReplayCount() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 420UL); const uint guid = 0x70024020u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: true) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); var observed = new List(); using IDisposable subscription = lifetime.Events.SubscribePlacement( new PlacementObserver(delta => observed.Add(delta.Placement))); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); RuntimePlacementProjectionSnapshot completion = Assert.Single(observed); var generation = new RuntimeGenerationToken(420UL); Assert.True(lifetime.Placements.TryGetInitialCreateCompletion( generation, completion.Token, out RuntimeInitialCreatePlacementCompletion publicCompletion)); Assert.Equal(canonical.Key, publicCompletion.Entity); Assert.Equal(receipt.Entity, publicCompletion.Entity); Assert.Equal(receipt.FullCellId, publicCompletion.FullCellId); Assert.Equal(receipt.ReplayedDeferredChildCount, publicCompletion.ReplayedDeferredChildCount); // This is a local-player Create (login): retail's init_player path // requests the AfterEnterWorld teleport hook (see RunInitialTail) - // the exact fact route-1/8's cutover caller needs to know whether to // run the after-enter teleport suffix. No Position continuation ran // in this scenario, so the route-fact array projects empty. Assert.Equal( RuntimeInitialCreateTeleportHookPhase.AfterEnterWorld, publicCompletion.TeleportHookPhase); Assert.Equal(RuntimeTeleportHookPhase.AfterEnterWorld, receipt.TeleportHookPhase); Assert.Empty(publicCompletion.PositionRouteFacts); } [Fact] public void PlacementChannel_TryGetInitialCreateCompletion_ProjectsPositionRouteFactsForConstrainInterpolationBinding() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 421UL); const uint guid = 0x70024021u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: true) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); // A teleport-advanced Position continuation performs its own // authored SetPosition and lands a Position trace entry with real // route facts - the same scenario as // ExecutorCompletion_ObservedOnlyAfterAnyContinuationPlacementInFifoOrder. WorldSession.EntityPositionUpdate update = PositionUpdate( guid, positionSequence: 2, teleportSequence: 1, forcePositionSequence: 0, positionX: 40f); Assert.True(lifetime.TryApplyPosition( update, isLocalPlayer: true, null, null, null, out PositionTimestampDisposition disposition, out _, out _)); Assert.Equal(PositionTimestampDisposition.Apply, disposition); var observed = new List(); using IDisposable subscription = lifetime.Events.SubscribePlacement( new PlacementObserver(delta => observed.Add(delta.Placement))); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); RuntimePlacementProjectionSnapshot executorCompletion = observed[^1]; Assert.Equal( RuntimePlacementProjectionKind.ExecutorCompleted, executorCompletion.Kind); RuntimeInitialCreateExecutedAction internalPositionTrace = Assert.Single( receipt.Trace.Where( static a => a.Kind == RuntimeInitialCreateExecutedActionKind.Position)); var generation = new RuntimeGenerationToken(421UL); Assert.True(lifetime.Placements.TryGetInitialCreateCompletion( generation, executorCompletion.Token, out RuntimeInitialCreatePlacementCompletion publicCompletion)); RuntimeInitialCreatePositionRouteFact fact = Assert.Single( publicCompletion.PositionRouteFacts); Assert.Equal(internalPositionTrace.Sequence, fact.Sequence); Assert.Equal(internalPositionTrace.StopInterpolating, fact.StopInterpolating); Assert.Equal(internalPositionTrace.ZeroVelocity, fact.ZeroVelocity); Assert.Equal(internalPositionTrace.PreserveHeading, fact.PreserveHeading); Assert.Equal( internalPositionTrace.SendPositionImmediately, fact.SendPositionImmediately); Assert.Equal( internalPositionTrace.PositionDisposition!.Value.ToString(), fact.Disposition.ToString()); Assert.Equal( internalPositionTrace.ConstrainPhase.ToString(), fact.ConstrainPhase.ToString()); Assert.Equal( internalPositionTrace.HookPhase.ToString(), fact.HookPhase.ToString()); } [Fact] public void PlacementChannel_TryGetInitialCreateCompletion_RejectsWrongGeneration() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 422UL); const uint guid = 0x70024022u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: true) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); var observed = new List(); using IDisposable subscription = lifetime.Events.SubscribePlacement( new PlacementObserver(delta => observed.Add(delta.Placement))); RunToCompletion(lifetime, canonical, lease.Token, NoContact); RuntimePlacementProjectionSnapshot completion = Assert.Single(observed); Assert.False(lifetime.Placements.TryGetInitialCreateCompletion( new RuntimeGenerationToken(999UL), completion.Token, out RuntimeInitialCreatePlacementCompletion stale)); Assert.Equal(default, stale); } [Fact] public void PlacementChannel_TryGetInitialCreateCompletion_ReturnsFalseAfterAcknowledgeReapsTheCorrelationEntry() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 423UL); const uint guid = 0x70024023u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: true) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); RunToCompletion(lifetime, canonical, lease.Token, NoContact); Assert.True(lifetime.Physics.SetPosition.TryPeekProjection( out RuntimePlacementProjectionSnapshot completion)); var generation = new RuntimeGenerationToken(423UL); Assert.True(lifetime.Placements.TryGetInitialCreateCompletion( generation, completion.Token, out _)); Assert.True(lifetime.Placements.Acknowledge(generation, completion.Token)); Assert.False(lifetime.Placements.TryGetInitialCreateCompletion( generation, completion.Token, out RuntimeInitialCreatePlacementCompletion afterAck)); Assert.Equal(default, afterAck); } /// /// Review fix (2026-08-02): the compiler's exhaustiveness net for the /// three enum-mirror switches (MapHookPhase/MapDisposition/ /// MapConstrainPhase) is gone the moment a catch-all arm exists - /// that is exactly why those catch-alls now throw instead of silently /// defaulting. This reflection-based test is the runtime replacement: /// for each (internal enum, public projection enum, private mapper /// method) triple it asserts equal arity AND drives every declared /// internal value through the mapper via reflection (the methods are /// `private static`), asserting the mapped public value's NAME equals /// the internal value's name (every mapper is a literal 1:1 name /// mirror by design - see each public enum's own doc comment). Adding a /// new member to either enum without updating the other and the mapper /// fails this test immediately, mirroring /// OperationResetAllFieldsToDefaultTouchesEveryDeclaredField's /// reflection-based completeness guard for /// 's pooled Operation fields. /// Sabotage-verified during development: temporarily adding an extra /// member to RuntimeTeleportHookPhase (with no matching arm in /// MapHookPhase or the public /// enum) failed this /// test exactly as predicted - both the arity assertion and the /// unhandled-value invocation threw - before the sabotage was reverted. /// [Fact] public void EnumProjectionMapsHaveEqualArityAndEveryInternalValueRoundTripsByName() { AssertMapIsCompleteAndNamePreserving( typeof(RuntimeTeleportHookPhase), typeof(RuntimeInitialCreateTeleportHookPhase), "MapHookPhase"); AssertMapIsCompleteAndNamePreserving( typeof(RuntimeAuthoritativePositionDisposition), typeof(RuntimeInitialCreatePositionDisposition), "MapDisposition"); AssertMapIsCompleteAndNamePreserving( typeof(RuntimePositionConstrainPhase), typeof(RuntimeInitialCreatePositionConstrainPhase), "MapConstrainPhase"); } private static void AssertMapIsCompleteAndNamePreserving( Type internalEnumType, Type publicEnumType, string mapMethodName) { MethodInfo? method = typeof(RuntimeInitialCreateContinuationExecutor) .GetMethod( mapMethodName, BindingFlags.NonPublic | BindingFlags.Static); Assert.True( method is not null, $"{nameof(RuntimeInitialCreateContinuationExecutor)} no longer " + $"declares a private static method named {mapMethodName} - " + "update this test's reflection lookup to match."); Array internalValues = Enum.GetValues(internalEnumType); Array publicValues = Enum.GetValues(publicEnumType); // Equal arity: every internal value must have exactly one public // counterpart and vice versa. A mismatch here is the first sign // either enum grew without the other (or the mapper) being updated // to match. Assert.True( internalValues.Length == publicValues.Length, $"{internalEnumType.Name} has {internalValues.Length} values " + $"but {publicEnumType.Name} has {publicValues.Length} - keep " + "the internal/public enum pair in lockstep."); foreach (object? internalValue in internalValues) { // Invoked via reflection deliberately - a value this mapper // cannot handle now throws ArgumentOutOfRangeException (see // the mapper's own doc comment), which TargetInvocationException // propagates through Invoke and fails this test with a clear // message identifying exactly which enum member is unmapped. object? mapped = method!.Invoke(null, [internalValue]); Assert.Equal(internalValue!.ToString(), mapped!.ToString()); } } [Fact] public void ExecutorCompletion_ReceiptIsReadableFromWithinTheSameSynchronousOnPlacementDispatch() { // F2: registration must happen BEFORE PublishPlacement's synchronous // observer dispatch - a subscriber reading the correlation back from // inside its OWN OnPlacement callback must already find it, not only // after RunToCompletion returns. using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 410UL); const uint guid = 0x70024010u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: true) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); RuntimeInitialCreateExecutionReceipt? observedFromInsideDispatch = null; using IDisposable subscription = lifetime.Events.SubscribePlacement( new PlacementObserver(delta => { if (delta.Placement.Kind is not RuntimePlacementProjectionKind.ExecutorCompleted) { return; } Assert.True(lifetime.InitialCreateExecution.TryGetCompletionReceipt( delta.Placement.Token, out RuntimeInitialCreateExecutionReceipt receipt)); observedFromInsideDispatch = receipt; })); RuntimeInitialCreateExecutionReceipt receiptReturned = RunToCompletion( lifetime, canonical, lease.Token, NoContact); Assert.NotNull(observedFromInsideDispatch); Assert.Equal(receiptReturned, observedFromInsideDispatch!.Value); } [Fact] public void ExecutorCompletion_ConvergenceLedgerCountsAnUnacknowledgedReceiptAsOutstandingDebtUntilAcknowledged() { // F2: PendingCompletionReceiptCount mirrors // RuntimeSetPositionOwnershipSnapshot.PendingProjectionAcknowledgementCount's // existing "unacknowledged receipt is outstanding debt" shape for // the SAME underlying receipt stream - non-zero while unacknowledged, // reaped to zero exactly on acknowledge (never before, never left // dangling after). using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 411UL); const uint guid = 0x70024011u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: true) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); Assert.Equal( 0, lifetime.CaptureOwnership().PendingCompletionReceiptCount); RunToCompletion(lifetime, canonical, lease.Token, NoContact); Assert.Equal( 1, lifetime.CaptureOwnership().PendingCompletionReceiptCount); Assert.True(lifetime.Physics.SetPosition.TryPeekProjection( out RuntimePlacementProjectionSnapshot completion)); Assert.Equal( RuntimePlacementProjectionKind.ExecutorCompleted, completion.Kind); Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection( completion.Token)); Assert.Equal( 0, lifetime.CaptureOwnership().PendingCompletionReceiptCount); Assert.False(lifetime.InitialCreateExecution.TryGetCompletionReceipt( completion.Token, out _)); } [Fact] public void ExecutorCompletion_CorrelationEntryIsReapedByDiscardProgress() { // F2: DiscardProgress (reached via ForgetInitialCreateResidence in // production) must reap this correlation cache too - it is exactly // the kind of executor-introduced state that method already owns // cleaning up. The drain already removed _progress[key] before // publishing the completion (see ExecuteCore's Released case), so // this proves DiscardProgress reaps _completionReceipts // UNCONDITIONALLY, not only when _progress still tracks the key. // Left deliberately UNACKNOWLEDGED in _pendingProjection (a single- // entity lifetime, so there is no exact-head contention to worry // about) - proving DiscardProgress reaps the correlation cache // independently of the normal acknowledge path. using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 413UL); const uint guid = 0x70024013u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: true) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); RunToCompletion(lifetime, canonical, lease.Token, NoContact); Assert.Equal( 1, lifetime.CaptureOwnership().PendingCompletionReceiptCount); lifetime.InitialCreateExecution.DiscardProgress(canonical.Key!.Value); Assert.Equal( 0, lifetime.CaptureOwnership().PendingCompletionReceiptCount); } [Fact] public void ExecutorCompletion_CorrelationEntryIsReapedByDiscardAll() { // F2: a full session clear (DiscardAll, reached via // RuntimeInitialCreateResidenceState.Clear's call site) must never // carry this correlation cache across a reset. using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 414UL); const uint guid = 0x70024014u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: true) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); RunToCompletion(lifetime, canonical, lease.Token, NoContact); Assert.Equal( 1, lifetime.CaptureOwnership().PendingCompletionReceiptCount); lifetime.InitialCreateExecution.DiscardAll(); Assert.Equal( 0, lifetime.CaptureOwnership().PendingCompletionReceiptCount); } [Fact] public void BindLiveInputs_DrivesClassificationFromTheBoundSourcesInsteadOfTheCallerStruct() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 402UL); const uint guid = 0x70024002u; RuntimeEntityRecord canonical = lifetime .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: true) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(lifetime, canonical); CompleteInitialPlacement(lifetime, lease); WorldSession.EntityPositionUpdate update = PositionUpdate( guid, positionSequence: 2, teleportSequence: 0, forcePositionSequence: 0, positionX: 15f, isGrounded: true); Assert.True(lifetime.TryApplyPosition( update, isLocalPlayer: true, null, null, null, out PositionTimestampDisposition disposition, out _, out _)); Assert.Equal(PositionTimestampDisposition.Apply, disposition); bool usePositionFromServer = true; // Position 15 units from a local player parked far away (100 units) // is irrelevant here (PlayerDistance only matters for the // Remote/Projectile near/far branch, not LocalPlayer's own // interpolate gate) - it exists purely to prove the DISTANCE source // is read at all. lifetime.InitialCreateExecution.BindLiveInputs( () => usePositionFromServer, () => new Vector3(115f, 20f, 7f)); // The caller-supplied struct says UsePositionFromServer:false - if // the bound source is actually driving classification, retail's // "UsePositionFromServer && wire-contact" local-ordinary gate must // still interpolate. RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( lifetime, canonical, lease.Token, NoContact); RuntimeInitialCreateExecutedAction positionAction = Assert.Single( receipt.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.Position); Assert.Equal( RuntimeAuthoritativePositionDisposition.Interpolate, positionAction.PositionDisposition); // C0-1: Completing the first entity's drain also published its own // ExecutorCompleted receipt on the SAME exact-head stream - it must // be acknowledged before a SECOND entity's own placement receipt can // ever become the head. Assert.True(lifetime.Physics.SetPosition.TryPeekProjection( out RuntimePlacementProjectionSnapshot firstCompletion)); Assert.Equal( RuntimePlacementProjectionKind.ExecutorCompleted, firstCompletion.Kind); Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection( firstCompletion.Token)); // Flip the bound source off and confirm the SAME struct now takes // the non-interpolating branch - proves it is read live, not cached // at bind time. usePositionFromServer = false; const uint secondGuid = 0x70024003u; RuntimeEntityRecord second = lifetime .RegisterEntityWithInitialResidence(Spawn(secondGuid, 1), isLocalPlayer: true) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( second, out RuntimeInitialCreateResidenceLease secondLease)); AttachDormantBody(lifetime, second); CompleteInitialPlacement(lifetime, secondLease); WorldSession.EntityPositionUpdate secondUpdate = PositionUpdate( secondGuid, positionSequence: 2, teleportSequence: 0, forcePositionSequence: 0, positionX: 16f, isGrounded: true); Assert.True(lifetime.TryApplyPosition( secondUpdate, isLocalPlayer: true, null, null, null, out PositionTimestampDisposition secondDisposition, out _, out _)); Assert.Equal(PositionTimestampDisposition.Apply, secondDisposition); RuntimeInitialCreateExecutionReceipt secondReceipt = RunToCompletion( lifetime, second, secondLease.Token, NoContact); RuntimeInitialCreateExecutedAction secondPositionAction = Assert.Single( secondReceipt.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.Position); Assert.Equal( RuntimeAuthoritativePositionDisposition.NoPositionOperation, secondPositionAction.PositionDisposition); } [Fact] public void BindLiveInputs_ThrowsOnASecondBindAndUnboundExecutorsUseTheCallerStructUnchanged() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); lifetime.InitialCreateExecution.BindLiveInputs( static () => true, static () => Vector3.Zero); Assert.Throws(() => lifetime.InitialCreateExecution.BindLiveInputs( static () => false, static () => Vector3.Zero)); // A SEPARATE, never-bound lifetime still honors the caller-supplied // struct verbatim - the existing bare-lifetime test contract is // unchanged by this slice. using RuntimeEntityObjectLifetime unbound = EngineLifetime(); Bind(unbound, 403UL); const uint guid = 0x70024004u; RuntimeEntityRecord canonical = unbound .RegisterEntityWithInitialResidence(Spawn(guid, 1), isLocalPlayer: true) .Canonical!; Assert.True(unbound.TryGetInitialCreateResidence( canonical, out RuntimeInitialCreateResidenceLease lease)); AttachDormantBody(unbound, canonical); CompleteInitialPlacement(unbound, lease); WorldSession.EntityPositionUpdate update = PositionUpdate( guid, positionSequence: 2, teleportSequence: 0, forcePositionSequence: 0, positionX: 15f, isGrounded: true); Assert.True(unbound.TryApplyPosition( update, isLocalPlayer: true, null, null, null, out PositionTimestampDisposition disposition, out _, out _)); Assert.Equal(PositionTimestampDisposition.Apply, disposition); RuntimeInitialCreateExecutionReceipt receipt = RunToCompletion( unbound, canonical, lease.Token, NoContact); RuntimeInitialCreateExecutedAction positionAction = Assert.Single( receipt.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.Position); // NoContact (UsePositionFromServer:false) -> not interpolated. Assert.Equal( RuntimeAuthoritativePositionDisposition.NoPositionOperation, positionAction.PositionDisposition); } [Fact] public void BindLiveInputs_PlayerDistanceIsReadFromANonNullBoundSourceAndFallsBackToTheCallerStructWhenNull() { // F3: proves BOTH directions of the nullable local-player-position // source. Entity 1: the bound source returns a REAL near position - // the caller struct claims a FAR distance (200f), so if the bound // source is genuinely read (not ignored), the entity's own near // distance must win and classify Interpolate. Entity 2: the SAME // bound source now returns null (e.g. the login-window drain before // RuntimeLocalPlayerMovementState.Controller exists) - it must fall // back to the caller struct's FAR distance exactly like an unbound // source would, never fabricate Vector3.Zero (which would compute a // small, misleadingly-near distance to the entity's own position and // wrongly classify Interpolate instead of the far SetPositionSimple // hard-snap). using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 421UL); Vector3? boundPosition = new Vector3(30f, 20f, 7f); lifetime.InitialCreateExecution.BindLiveInputs( static () => false, () => boundPosition); const uint nearGuid = 0x70024021u; RuntimeEntityRecord near = lifetime .RegisterEntityWithInitialResidence(Spawn(nearGuid, 1), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( near, out RuntimeInitialCreateResidenceLease nearLease)); AttachDormantBody(lifetime, near); CompleteInitialPlacement(lifetime, nearLease); WorldSession.EntityPositionUpdate nearUpdate = PositionUpdate( nearGuid, positionSequence: 2, teleportSequence: 0, forcePositionSequence: 0, positionX: 25f, isGrounded: true); Assert.True(lifetime.TryApplyPosition( nearUpdate, isLocalPlayer: false, null, null, null, out PositionTimestampDisposition nearDisposition, out _, out _)); Assert.Equal(PositionTimestampDisposition.Apply, nearDisposition); var farStruct = new RuntimeInitialCreateExecutionInputs( UsePositionFromServer: false, PlayerDistance: 200f); RuntimeInitialCreateExecutionReceipt nearReceipt = RunToCompletion( lifetime, near, nearLease.Token, farStruct); RuntimeInitialCreateExecutedAction nearAction = Assert.Single( nearReceipt.Trace, static a => a.Kind is RuntimeInitialCreateExecutedActionKind.Position); Assert.Equal( RuntimeAuthoritativePositionDisposition.Interpolate, nearAction.PositionDisposition); // C0-1: the completed near entity published its own ExecutorCompleted // receipt on the SAME exact-head stream - acknowledge it before the // far entity's own Place receipt can ever become the head. Assert.True(lifetime.Physics.SetPosition.TryPeekProjection( out RuntimePlacementProjectionSnapshot nearCompletion)); Assert.Equal( RuntimePlacementProjectionKind.ExecutorCompleted, nearCompletion.Kind); Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection( nearCompletion.Token)); boundPosition = null; const uint farGuid = 0x70024022u; RuntimeEntityRecord far = lifetime .RegisterEntityWithInitialResidence(Spawn(farGuid, 1), isLocalPlayer: false) .Canonical!; Assert.True(lifetime.TryGetInitialCreateResidence( far, out RuntimeInitialCreateResidenceLease farLease)); AttachDormantBody(lifetime, far); CompleteInitialPlacement(lifetime, farLease); WorldSession.EntityPositionUpdate farUpdate = PositionUpdate( farGuid, positionSequence: 2, teleportSequence: 0, forcePositionSequence: 0, positionX: 25f, isGrounded: true); Assert.True(lifetime.TryApplyPosition( farUpdate, isLocalPlayer: false, null, null, null, out PositionTimestampDisposition farDisposition, out _, out _)); Assert.Equal(PositionTimestampDisposition.Apply, farDisposition); RuntimeInitialCreateExecutionStatus farStatus = lifetime .InitialCreateExecution.Execute( far, farLease.Token, farStruct, out _); Assert.Equal( RuntimeInitialCreateExecutionStatus.AwaitingContinuationPlacement, farStatus); RuntimeEntityKey farKey = far.Key!.Value; Assert.True(lifetime.InitialCreateExecution.TryGetPendingContinuationRoute( farKey, out RuntimeAuthoritativePositionRoute farRoute)); Assert.Equal( RuntimeAuthoritativePositionDisposition.SetPositionSimple, farRoute.Disposition); Assert.True(farRoute.StopInterpolating); } // --------------------------------------------------------------- // Harness // --------------------------------------------------------------- private static RuntimeEntityObjectLifetime EngineLifetime() { 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); return new RuntimeEntityObjectLifetime(engine); } private static RuntimeInitialCreateExecutionReceipt RunToCompletion( RuntimeEntityObjectLifetime lifetime, RuntimeEntityRecord canonical, in RuntimeInitialCreateResidenceToken token, RuntimeInitialCreateExecutionInputs inputs, int maxSteps = 25) { for (int step = 0; step < maxSteps; step++) { RuntimeInitialCreateExecutionStatus status = lifetime.InitialCreateExecution.Execute( canonical, token, inputs, out RuntimeInitialCreateExecutionReceipt receipt); switch (status) { case RuntimeInitialCreateExecutionStatus.Completed: return receipt; case RuntimeInitialCreateExecutionStatus.AwaitingContinuationPlacement: { RuntimeEntityKey key = canonical.Key!.Value; Assert.True(lifetime.InitialCreateExecution .TryGetPendingContinuationRoute(key, out RuntimeAuthoritativePositionRoute route)); CompletePendingContinuationPlacement(lifetime, key, route); continue; } default: throw new InvalidOperationException( $"RunToCompletion hit unexpected status {status}; drive its precondition explicitly instead."); } } throw new InvalidOperationException("RunToCompletion exceeded its step budget."); } private static void CompleteInitialPlacement( RuntimeEntityObjectLifetime lifetime, in RuntimeInitialCreateResidenceLease lease) { RuntimeSetPositionCommand command = Prepare( lifetime, lease.Placement, lease.Route.OperationKind, lease.Route.SetPositionFlags); RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition .SubmitPreparedPlacement(lease.Placement, command); Assert.Equal( RuntimeSetPositionStatus.CommittedHostAcknowledgementPending, outcome.Status); Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(outcome.Projection)); } private static void CompletePendingContinuationPlacement( RuntimeEntityObjectLifetime lifetime, RuntimeEntityKey key, in RuntimeAuthoritativePositionRoute route) { Assert.True(lifetime.InitialCreateExecution .TryGetPendingContinuationPlacement(key, out RuntimeEntityPlacementToken placement)); RuntimeSetPositionCommand command = Prepare( lifetime, placement, route.OperationKind, route.SetPositionFlags); RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition .SubmitPreparedPlacement(placement, command); Assert.Equal( RuntimeSetPositionStatus.CommittedHostAcknowledgementPending, outcome.Status); Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(outcome.Projection)); } private static RuntimeSetPositionCommand Prepare( RuntimeEntityObjectLifetime lifetime, in RuntimeEntityPlacementToken placement, RuntimeSetPositionOperationKind operationKind, PhysicsSetPositionFlags flags) { var preparation = new RuntimeSetPositionMoverPreparation( RuntimeSetPositionMoverSetup.ResolvedAbsent, operationKind, GameTime: 1d, PhysicsPlacementClass.Ordinary, flags); Assert.Equal( RuntimeSetPositionMoverPreparationStatus.Prepared, lifetime.Physics.SetPosition.PrepareMover( placement, preparation, out RuntimeSetPositionCommand command)); return command; } private static void AttachDormantBody( RuntimeEntityObjectLifetime lifetime, RuntimeEntityRecord canonical, bool inContact = false) { var body = new PhysicsBody { State = canonical.FinalPhysicsState, Orientation = Quaternion.Identity, InWorld = false, TransientState = inContact ? TransientStateFlags.Contact : TransientStateFlags.None, }; lifetime.Entities.SetPhysicsBody(canonical, body); } /// /// The real physics sweep an initial-placement commit runs against the /// test landblock decides the body's own contact bit from scratch, /// clobbering whatever set beforehand. /// Call this AFTER the placement commits to pin the contact bit a /// Position-route test actually needs. /// private static void ForceContact(RuntimeEntityRecord canonical, bool inContact) { if (canonical.PhysicsBody is not { } body) return; body.TransientState = inContact ? body.TransientState | TransientStateFlags.Contact : body.TransientState & ~TransientStateFlags.Contact; } private static void Bind(RuntimeEntityObjectLifetime lifetime, ulong generation) { var token = new RuntimeGenerationToken(generation); lifetime.BindEventContext(() => token, static () => 1UL); } private static WorldSession.EntityPositionUpdate PositionUpdate( uint guid, ushort positionSequence, ushort teleportSequence, ushort forcePositionSequence, float positionX, bool isGrounded = true) { return new WorldSession.EntityPositionUpdate( guid, new CreateObject.ServerPosition(Cell, positionX, 20f, 7f, 1f, 0f, 0f, 0f), new Vector3(positionSequence, 2f, 3f), PlacementId: positionSequence, IsGrounded: isGrounded, InstanceSequence: 1, PositionSequence: positionSequence, TeleportSequence: teleportSequence, ForcePositionSequence: forcePositionSequence); } private static WorldSession.EntitySpawn Spawn( uint guid, ushort incarnation, bool includePosition = true, uint? parentGuid = null, ushort positionSequence = 1, float positionX = 10f, bool missile = false, ushort teleportSequence = 0, ushort forcePositionSequence = 0, ushort movementSequence = 1, ushort serverControlSequence = 1) { CreateObject.ServerPosition? position = includePosition ? new CreateObject.ServerPosition(Cell, positionX, 20f, 7f, 1f, 0f, 0f, 0f) : null; uint rawState = (uint)(PhysicsStateFlags.Gravity | (missile ? PhysicsStateFlags.Missile : 0)); var timestamps = new PhysicsTimestamps( Position: positionSequence, Movement: movementSequence, State: 1, Vector: 1, Teleport: teleportSequence, ServerControlledMove: serverControlSequence, ForcePosition: forcePositionSequence, ObjDesc: 1, Instance: incarnation); var physics = new PhysicsSpawnData( rawState, position, Movement: null, AnimationFrame: null, SetupTableId: null, 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, SetupTableId: null, Array.Empty(), Array.Empty(), Array.Empty(), BasePaletteId: null, ObjScale: null, Name: "initial-create", ItemType: null, MotionState: null, MotionTableId: null, PhysicsState: rawState, InstanceSequence: incarnation, MovementSequence: timestamps.Movement, ServerControlSequence: timestamps.ServerControlledMove, 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); } }