using AcDream.Core.Net; using AcDream.Core.Net.Messages; using AcDream.Core.Physics; using AcDream.Runtime; using AcDream.Runtime.Entities; namespace AcDream.Runtime.Tests.Entities; /// /// C4 route 7 (pickup / parent / delete). Focused Runtime tests for D1 /// (attach re-cell), D2 (crossing propagation at the directory funnel), D3 /// (withdrawal/delete/EndGeneration edges), and D7 (pickup ordering). See /// docs/research/2026-08-04-c4-route-7-contract.md. /// public sealed class RuntimeEntityChildCellPropagationTests { private const uint Landblock = 0xA9C60000u; private const uint Cell = Landblock | 0x0001u; [Fact] public void Attach_ParentCelled_ChildEndsAtParentCellAndStaysSuspended() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 1UL); const uint parentGuid = 0x70040001u; const uint childGuid = 0x70040002u; RuntimeEntityRecord parent = lifetime.RegisterEntity(Spawn(parentGuid, 1)).Canonical!; lifetime.RegisterEntity(Spawn(childGuid, 1, includePosition: false)); // A4 (architecture review): D1's re-cell runs BEFORE this commit's // publish, so the Withdrawn delta it emits carries the child's // ACTUAL resulting (non-zero, parent) cell rather than the stale // zero every Withdrawn from this method carried before D1 existed. var deltas = new List(); using IDisposable subscription = lifetime.Events.Subscribe( new RecordingEntityObserver(deltas)); Assert.True(CommitAttachment(lifetime, parentGuid, 1, childGuid, 2)); Assert.True(lifetime.Entities.TryGetActive( childGuid, out RuntimeEntityRecord child)); // D1: the child ends at the parent's exact cell in the SAME // synchronous transaction — never observably cell-less afterward. Assert.Equal(parent.FullCellId, child.FullCellId); Assert.Equal(parent.CanonicalLandblockId, child.CanonicalLandblockId); Assert.NotEqual(0u, child.FullCellId); // Invariant 2 / P2: the child is parent-suspended, never a // self-simulating object (retail update_object @0x00515D40). Assert.False(child.ObjectClock.IsActive); Assert.Null(child.Snapshot.Position); RuntimeEntityDelta withdrawn = Assert.Single( deltas, d => d.Change is RuntimeEntityChange.Withdrawn && d.Entity.Identity.ServerGuid == childGuid); Assert.Equal(parent.FullCellId, withdrawn.Entity.CellId); Assert.NotEqual(0u, withdrawn.Entity.CellId); } private sealed class RecordingEntityObserver(List destination) : IRuntimeEntityObjectObserver { public void OnEntity(in RuntimeEntityDelta delta) => destination.Add(delta); public void OnInventory(in RuntimeInventoryDelta delta) { } } [Fact] public void Attach_ParentCellless_ChildStaysCellless_ThenLaterParentCellCommitRecellsViaPropagation() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 1UL); const uint parentGuid = 0x70040101u; const uint childGuid = 0x70040102u; lifetime.RegisterEntity(Spawn(parentGuid, 1, includePosition: false)); lifetime.RegisterEntity(Spawn(childGuid, 1, includePosition: false)); Assert.True(CommitAttachment(lifetime, parentGuid, 1, childGuid, 2)); Assert.True(lifetime.Entities.TryGetActive( parentGuid, out RuntimeEntityRecord parent)); Assert.True(lifetime.Entities.TryGetActive( childGuid, out RuntimeEntityRecord child)); Assert.Equal(0u, parent.FullCellId); // @0x00515AD1: parent->cell == 0 -> the child stays cell-less too. Assert.Equal(0u, child.FullCellId); // The deferred-attach catch-up retail gets for free from // propagation: a LATER parent cell commit re-cells the child // through D2, with no separate mechanism. Assert.True(lifetime.CommitRebucket(parent, Cell, Landblock | 0xFFFFu)); Assert.Equal(Cell, child.FullCellId); } [Fact] public void PropagationChokepoint_RecursesThroughGrandchildAndIsIdempotentOnASameCellCommit() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 1UL); const uint parentGuid = 0x70040201u; const uint childGuid = 0x70040202u; const uint grandchildGuid = 0x70040203u; RuntimeEntityRecord parent = lifetime.RegisterEntity(Spawn(parentGuid, 1)).Canonical!; lifetime.RegisterEntity(Spawn(childGuid, 1, includePosition: false)); lifetime.RegisterEntity(Spawn(grandchildGuid, 1, includePosition: false)); Assert.True(CommitAttachment(lifetime, parentGuid, 1, childGuid, 2)); Assert.True(CommitAttachment(lifetime, childGuid, 1, grandchildGuid, 2)); Assert.True(lifetime.Entities.TryGetActive( childGuid, out RuntimeEntityRecord child)); Assert.True(lifetime.Entities.TryGetActive( grandchildGuid, out RuntimeEntityRecord grandchild)); Assert.Equal(parent.FullCellId, child.FullCellId); Assert.Equal(parent.FullCellId, grandchild.FullCellId); // The production writer (CommitRebucket) crosses the parent's cell. uint newCell = parent.FullCellId + 0x0002u; uint newLandblock = (newCell & 0xFFFF0000u) | 0xFFFFu; Assert.True(lifetime.CommitRebucket(parent, newCell, newLandblock)); Assert.Equal(newCell, child.FullCellId); // Unbounded-depth recursion: the grandchild follows too. Assert.Equal(newCell, grandchild.FullCellId); // Idempotence: a same-cell re-commit does not churn the child's // SpatialAuthorityVersion (D2's explicit short-circuit). ulong childVersionBefore = child.SpatialAuthorityVersion; ulong grandchildVersionBefore = grandchild.SpatialAuthorityVersion; Assert.True(lifetime.CommitRebucket(parent, newCell, newLandblock)); Assert.Equal(childVersionBefore, child.SpatialAuthorityVersion); Assert.Equal(grandchildVersionBefore, grandchild.SpatialAuthorityVersion); } [Fact] public void PropagationChokepoint_TerminatesAHostileTwoCycleInsteadOfLoopingForever() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 1UL); const uint aGuid = 0x70040301u; const uint bGuid = 0x70040302u; RuntimeEntityRecord a = lifetime.RegisterEntity(Spawn(aGuid, 1)).Canonical!; lifetime.RegisterEntity(Spawn(bGuid, 1, includePosition: false)); // A hostile wire commits BOTH directions: B is A's child, and A is // ALSO B's child (a relation cycle no single-relation check like // self-parenting rejection catches). Assert.True(CommitAttachment(lifetime, aGuid, 1, bGuid, 2)); Assert.True(CommitAttachment(lifetime, bGuid, 1, aGuid, 2)); Assert.True(lifetime.Entities.TryGetActive(bGuid, out RuntimeEntityRecord b)); uint newCell = a.FullCellId + 0x0002u; // Must terminate (not stack-overflow / infinite-loop) and still // propagate once around the cycle. Assert.True(lifetime.CommitRebucket(a, newCell, (newCell & 0xFFFF0000u) | 0xFFFFu)); Assert.Equal(newCell, a.FullCellId); Assert.Equal(newCell, b.FullCellId); } [Fact] public void RefreshSnapshot_WireMergeCellChange_PropagatesToCommittedChild() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 1UL); const uint parentGuid = 0x70040401u; const uint childGuid = 0x70040402u; RuntimeEntityRecord parent = lifetime.RegisterEntity(Spawn(parentGuid, 1)).Canonical!; lifetime.RegisterEntity(Spawn(childGuid, 1, includePosition: false)); Assert.True(CommitAttachment(lifetime, parentGuid, 1, childGuid, 2)); Assert.True(lifetime.Entities.TryGetActive( childGuid, out RuntimeEntityRecord child)); Assert.Equal(parent.FullCellId, child.FullCellId); WorldSession.EntitySpawn moved = parent.Snapshot with { Position = parent.Snapshot.Position!.Value with { LandblockId = parent.Snapshot.Position!.Value.LandblockId + 0x0002u, }, }; lifetime.Entities.RefreshSnapshot(parent, moved, refreshPosition: true); Assert.NotEqual(0u, parent.FullCellId); Assert.Equal(parent.FullCellId, child.FullCellId); } [Fact] public void P1_SnapshotMutationOfACommittedChild_LeavesItsCanonicalCellTrackingTheParent() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 1UL); const uint parentGuid = 0x70040501u; const uint childGuid = 0x70040502u; RuntimeEntityRecord parent = lifetime.RegisterEntity(Spawn(parentGuid, 1)).Canonical!; lifetime.RegisterEntity(Spawn(childGuid, 1, includePosition: false)); Assert.True(CommitAttachment(lifetime, parentGuid, 1, childGuid, 2)); Assert.True(lifetime.Entities.TryGetActive( childGuid, out RuntimeEntityRecord child)); uint expectedCell = parent.FullCellId; // A representative snapshot-mutation family: ObjDesc. The child's // Snapshot.Position stays null (contract §0 item 10), so // RefreshDerivedState's cell stamp can never fire from the child's // own merge and cannot fight the propagation. var objDesc = new ObjDescEvent.Parsed( childGuid, new CreateObject.ModelData( BasePaletteId: null, SubPalettes: [], TextureChanges: [], AnimPartChanges: []), InstanceSequence: 1, ObjDescSequence: 2); Assert.True(lifetime.TryApplyObjDesc( objDesc, acknowledgeProjection: null, out _)); Assert.Null(child.Snapshot.Position); Assert.Equal(expectedCell, child.FullCellId); } [Fact] public void Withdrawal_PickupOfParent_ZeroesCommittedChildrenRecursively() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 1UL); const uint parentGuid = 0x70040601u; const uint childGuid = 0x70040602u; const uint grandchildGuid = 0x70040603u; lifetime.RegisterEntity(Spawn(parentGuid, 1)); lifetime.RegisterEntity(Spawn(childGuid, 1, includePosition: false)); lifetime.RegisterEntity(Spawn(grandchildGuid, 1, includePosition: false)); Assert.True(CommitAttachment(lifetime, parentGuid, 1, childGuid, 2)); Assert.True(CommitAttachment(lifetime, childGuid, 1, grandchildGuid, 2)); Assert.True(lifetime.Entities.TryGetActive( childGuid, out RuntimeEntityRecord child)); Assert.True(lifetime.Entities.TryGetActive( grandchildGuid, out RuntimeEntityRecord grandchild)); Assert.NotEqual(0u, child.FullCellId); Assert.NotEqual(0u, grandchild.FullCellId); Assert.True(lifetime.TryApplyPickup( new PickupEvent.Parsed(parentGuid, InstanceSequence: 1, PositionSequence: 2), acknowledgeProjection: null, out _)); Assert.Equal(0u, child.FullCellId); Assert.Equal(0u, grandchild.FullCellId); } /// /// #319 route-7 invariant 2 re-run, dual-parent-class. The propagation /// mechanism itself (D1/D2/D3) was always guid-range-agnostic - these /// tests construct relations directly via /// rather than through the buggy producer - but #319 exists precisely /// because no existing test in this file used a PLAYER-range parent /// guid. Closes that gap for the removal path (AP-142 clause (a)): the /// key fix must not perturb withdrawal for either parent class. /// [Theory] [InlineData(0x50000601u, (ushort)8)] [InlineData(0x70040609u, (ushort)0)] public void Withdrawal_PickupOfParent_ZeroesCommittedChildren_DualParentClass( uint parentGuid, ushort parentInstance) { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 1UL); const uint childGuid = 0x7004060Au; lifetime.RegisterEntity(Spawn(parentGuid, parentInstance)); lifetime.RegisterEntity(Spawn(childGuid, 1, includePosition: false)); Assert.True(CommitAttachment(lifetime, parentGuid, parentInstance, childGuid, 2)); Assert.True(lifetime.Entities.TryGetActive( childGuid, out RuntimeEntityRecord child)); Assert.NotEqual(0u, child.FullCellId); Assert.True(lifetime.TryApplyPickup( new PickupEvent.Parsed(parentGuid, InstanceSequence: parentInstance, PositionSequence: 2), acknowledgeProjection: null, out _)); Assert.Equal(0u, child.FullCellId); } [Fact] public void Withdrawal_CommitWithdrawalOfParent_ZeroesCommittedChildren() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 1UL); const uint parentGuid = 0x70040701u; const uint childGuid = 0x70040702u; RuntimeEntityRecord parent = lifetime.RegisterEntity(Spawn(parentGuid, 1)).Canonical!; lifetime.RegisterEntity(Spawn(childGuid, 1, includePosition: false)); Assert.True(CommitAttachment(lifetime, parentGuid, 1, childGuid, 2)); Assert.True(lifetime.Entities.TryGetActive( childGuid, out RuntimeEntityRecord child)); Assert.NotEqual(0u, child.FullCellId); Assert.True(lifetime.CommitWithdrawal(parent)); Assert.Equal(0u, parent.FullCellId); Assert.Equal(0u, child.FullCellId); } [Fact] public void Delete_ZeroesChildrenBeforeRelationsAreTornDown_P7() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 1UL); const uint parentGuid = 0x70040801u; const uint childGuid = 0x70040802u; const uint grandchildGuid = 0x70040803u; lifetime.RegisterEntity(Spawn(parentGuid, 1)); lifetime.RegisterEntity(Spawn(childGuid, 1, includePosition: false)); lifetime.RegisterEntity(Spawn(grandchildGuid, 1, includePosition: false)); Assert.True(CommitAttachment(lifetime, parentGuid, 1, childGuid, 2)); Assert.True(CommitAttachment(lifetime, childGuid, 1, grandchildGuid, 2)); Assert.True(lifetime.Entities.TryGetActive( childGuid, out RuntimeEntityRecord child)); Assert.True(lifetime.Entities.TryGetActive( grandchildGuid, out RuntimeEntityRecord grandchild)); Assert.NotEqual(0u, child.FullCellId); Assert.NotEqual(0u, grandchild.FullCellId); Assert.True(lifetime.TryAcceptDelete( new DeleteObject.Parsed(parentGuid, 1), isLocalPlayer: false, removeRetainedObject: true, out RuntimeEntityDeleteAcceptance acceptance)); lifetime.CompleteAcceptedDelete(acceptance); // The children's own leave-world edge ran before DeleteGeneration // removed the relation ledger (P7) — both the direct and the // grand-attached child went cell-less. DeleteObject unparents only // the deleted object's DIRECT children (retail unparent_children): // the child's own relation to the deleted parent is gone, but the // grandchild's relation to the (still-alive, merely cell-less) // child is untouched — exactly retail's shape. Assert.Equal(0u, child.FullCellId); Assert.Equal(0u, grandchild.FullCellId); Assert.False(lifetime.Entities.ParentAttachments.HasCommittedParent(childGuid)); Assert.True(lifetime.Entities.ParentAttachments.HasCommittedParent(grandchildGuid)); } [Fact] public void EndGeneration_ReplacementParentGeneration_ZeroesFormerlyCommittedChildren() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 1UL); const uint parentGuid = 0x70040901u; const uint childGuid = 0x70040902u; lifetime.RegisterEntity(Spawn(parentGuid, 1)); lifetime.RegisterEntity(Spawn(childGuid, 1, includePosition: false)); Assert.True(CommitAttachment(lifetime, parentGuid, 1, childGuid, 2)); Assert.True(lifetime.Entities.TryGetActive( childGuid, out RuntimeEntityRecord child)); Assert.NotEqual(0u, child.FullCellId); // A new CreateObject generation for the SAME parent guid — the // replacement-generation path has the same stranding shape as // delete (D3). lifetime.RegisterEntity(Spawn(parentGuid, 2)); Assert.Equal(0u, child.FullCellId); Assert.False(lifetime.Entities.ParentAttachments.HasCommittedParent(childGuid)); } [Fact] public void PickupOfTheChildItself_D7_RelationGoneCellZeroClockSuspendedAndOwnChildrenAlsoCellless() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 1UL); const uint parentGuid = 0x70040A01u; const uint childGuid = 0x70040A02u; const uint grandchildGuid = 0x70040A03u; lifetime.RegisterEntity(Spawn(parentGuid, 1)); lifetime.RegisterEntity(Spawn(childGuid, 1, includePosition: false)); lifetime.RegisterEntity(Spawn(grandchildGuid, 1, includePosition: false)); Assert.True(CommitAttachment(lifetime, parentGuid, 1, childGuid, 2)); Assert.True(CommitAttachment(lifetime, childGuid, 1, grandchildGuid, 2)); Assert.True(lifetime.Entities.TryGetActive( childGuid, out RuntimeEntityRecord child)); Assert.True(lifetime.Entities.TryGetActive( grandchildGuid, out RuntimeEntityRecord grandchild)); // Pick up the CHILD itself (not the parent) — retail's // unset_parent-then-leave_world order (D7). PositionSequence 3: // the attach commit already consumed 2. Assert.True(lifetime.TryApplyPickup( new PickupEvent.Parsed(childGuid, InstanceSequence: 1, PositionSequence: 3), acknowledgeProjection: null, out _)); Assert.False(lifetime.Entities.ParentAttachments.HasCommittedParent(childGuid)); Assert.Equal(0u, child.FullCellId); Assert.False(child.ObjectClock.IsActive); // The picked-up child's OWN children went cell-less too — the // pickup's SetFullCell(0,0) is a value like any other at D2's // chokepoint. Assert.Equal(0u, grandchild.FullCellId); } [Fact] public void NeverArmPartition_D8_RouteSevenEventsNeverEngagePlacementOrParkMachinery() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 1UL); const uint parentGuid = 0x70040B01u; const uint childGuid = 0x70040B02u; lifetime.RegisterEntity(Spawn(parentGuid, 1)); lifetime.RegisterEntity(Spawn(childGuid, 1, includePosition: false)); int placementOpsBefore = lifetime.Physics.SetPosition.CaptureOwnership().ActiveOperationCount; Assert.True(CommitAttachment(lifetime, parentGuid, 1, childGuid, 2)); Assert.True(lifetime.Entities.TryGetActive( parentGuid, out RuntimeEntityRecord parent)); for (int i = 0; i < 5; i++) { uint nextCell = parent.FullCellId + 0x0002u; Assert.True(lifetime.CommitRebucket( parent, nextCell, (nextCell & 0xFFFF0000u) | 0xFFFFu)); } Assert.True(lifetime.TryApplyPickup( new PickupEvent.Parsed(childGuid, InstanceSequence: 1, PositionSequence: 3), acknowledgeProjection: null, out _)); Assert.True(lifetime.TryAcceptDelete( new DeleteObject.Parsed(parentGuid, 1), isLocalPlayer: false, removeRetainedObject: true, out RuntimeEntityDeleteAcceptance acceptance)); lifetime.CompleteAcceptedDelete(acceptance); // No placement, park, or ConstrainTo machinery engaged at any point // (P3 / D8): attach, five crossings, pickup, and delete leave the // placement operation ledger exactly where it started. Assert.Equal( placementOpsBefore, lifetime.Physics.SetPosition.CaptureOwnership().ActiveOperationCount); // Ledger convergence (invariant 13): both relations are gone. Assert.Equal( 0, lifetime.CaptureOwnership().CommittedParentRelationCount); } /// /// Round-3 remediation: the recursion + depth cap were replaced outright /// by an iterative worklist (both the retail and architecture reviews /// independently found the same defect — a capped recursive version /// left a truncated tail at a STALE NONZERO cell forever, the #184 /// shape verbatim, on the withdraw path). Builds a chain well beyond /// the OLD 64-level cap (200 nodes) and proves the ENTIRE chain — not /// just a prefix — follows the parent on BOTH a write (crossing) and a /// withdraw (zero) with no truncation and no /// . /// [Fact] public void PropagationChokepoint_DeepChain_FullyPropagatesOnBothWriteAndWithdraw() { using RuntimeEntityObjectLifetime lifetime = EngineLifetime(); Bind(lifetime, 1UL); const int chainLength = 200; // well beyond the retired 64-level cap var guids = new uint[chainLength + 1]; for (int i = 0; i <= chainLength; i++) guids[i] = 0x70050000u + (uint)i; lifetime.RegisterEntity(Spawn(guids[0], 1)); for (int i = 1; i <= chainLength; i++) lifetime.RegisterEntity(Spawn(guids[i], 1, includePosition: false)); for (int i = 0; i < chainLength; i++) { Assert.True(CommitAttachment( lifetime, guids[i], 1, guids[i + 1], childPositionSequence: 2)); } Assert.True(lifetime.Entities.TryGetActive( guids[0], out RuntimeEntityRecord root)); // D1's attach re-cell already propagated the ORIGINAL cell down the // whole chain one attach at a time — confirm the tail matches the // root before testing the crossing, so the assertions below cannot // pass by coincidence. uint originalCell = root.FullCellId; Assert.True(lifetime.Entities.TryGetActive( guids[chainLength], out RuntimeEntityRecord tailBeforeCrossing)); Assert.Equal(originalCell, tailBeforeCrossing.FullCellId); Assert.NotEqual(0u, originalCell); // WRITE path: the whole 200-deep chain follows a crossing, tail // included — no truncation, no stack overflow. uint newCell = originalCell + 0x0002u; Assert.True(lifetime.CommitRebucket( root, newCell, (newCell & 0xFFFF0000u) | 0xFFFFu)); for (int i = 0; i <= chainLength; i++) { Assert.True(lifetime.Entities.TryGetActive( guids[i], out RuntimeEntityRecord record)); Assert.Equal(newCell, record.FullCellId); } // WITHDRAW path: the whole chain follows a withdrawal to zero too — // this is the #184-shaped half both reviews flagged (a truncated // tail left at a STALE NONZERO cell is exactly the "resident but // isn't" defect AP-142 clause (a) exists to reject). Assert.True(lifetime.CommitWithdrawal(root)); for (int i = 0; i <= chainLength; i++) { Assert.True(lifetime.Entities.TryGetActive( guids[i], out RuntimeEntityRecord record)); Assert.Equal(0u, record.FullCellId); } } // --------------------------------------------------------------- // 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 void Bind(RuntimeEntityObjectLifetime lifetime, ulong generation) { var token = new RuntimeGenerationToken(generation); lifetime.BindEventContext(() => token, static () => 1UL); } /// /// Drives the SAME staged -> committed protocol the graphical /// EquippedChildRenderController.PrepareAndTryRealize and the /// headless D5 drive both run: seed the staged relation, consume the /// child's POSITION_TS gate, commit the relation /// (), mark it /// committed (), /// then run the cell-less edge that D1 extends /// (). /// private static bool CommitAttachment( RuntimeEntityObjectLifetime lifetime, uint parentGuid, ushort parentInstance, uint childGuid, ushort childPositionSequence, uint parentLocation = 0u, uint placementId = 0u) { var relation = new ParentAttachmentRelation( parentGuid, childGuid, parentLocation, placementId, parentInstance, childPositionSequence); lifetime.Entities.ParentAttachments.AcceptCreateObjectRelation(relation); var update = new ParentEvent.Parsed( parentGuid, childGuid, parentLocation, placementId, parentInstance, childPositionSequence); if (!lifetime.TryApplyParent(update, acknowledgeProjection: null, out _)) return false; if (!lifetime.TryCommitParent(relation, acknowledgeProjection: null, out _)) return false; if (!lifetime.Entities.ParentAttachments.CommitProjection(relation)) return false; if (!lifetime.Entities.TryGetActive(childGuid, out RuntimeEntityRecord canonical)) return false; return lifetime.CommitAcceptedParentCellless( canonical, canonical.PositionAuthorityVersion, acknowledgeProjection: null); } private static WorldSession.EntitySpawn Spawn( uint guid, ushort incarnation, bool includePosition = true) { CreateObject.ServerPosition? position = includePosition ? new CreateObject.ServerPosition(Cell, 10f, 20f, 7f, 1f, 0f, 0f, 0f) : null; var timestamps = new PhysicsTimestamps( Position: 1, Movement: 1, State: 1, Vector: 1, Teleport: 0, ServerControlledMove: 1, ForcePosition: 0, ObjDesc: 1, Instance: incarnation); var physics = new PhysicsSpawnData( RawState: (uint)PhysicsStateFlags.Gravity, Position: position, Movement: null, AnimationFrame: null, SetupTableId: null, MotionTableId: null, SoundTableId: null, PhysicsScriptTableId: null, Parent: null, Children: null, Scale: null, Friction: null, Elasticity: null, Translucency: null, Velocity: null, Acceleration: null, AngularVelocity: null, DefaultScriptType: null, DefaultScriptIntensity: null, Timestamps: timestamps); return new WorldSession.EntitySpawn( guid, position, SetupTableId: null, Array.Empty(), Array.Empty(), Array.Empty(), BasePaletteId: null, ObjScale: null, Name: "route-7 fixture", ItemType: null, MotionState: null, MotionTableId: null, PhysicsState: physics.RawState, InstanceSequence: incarnation, MovementSequence: 1, ServerControlSequence: 1, PositionSequence: 1, Physics: physics); } }