using System.Numerics; using AcDream.Core.Net; using AcDream.Core.Net.Messages; using AcDream.Core.Physics; using AcDream.Runtime; using AcDream.Runtime.Entities; using AcDream.Runtime.Physics; namespace AcDream.Runtime.Tests.Entities; public sealed class InboundPhysicsStateControllerTests { [Fact] public void SameGenerationCreate_RoutesChannelsIndependently() { var controller = new InboundPhysicsStateController(); CreateObject.ServerPosition original = Position(0x0101FFFFu, 10f); CreateObject.ServerPosition incoming = Position(0x0101FFFFu, 20f); controller.AcceptCreate(Spawn(0x70000001u, 1, 10, 10, original, 0x408u)); InboundCreateResult refresh = controller.AcceptCreate( Spawn(0x70000001u, 1, 9, 11, incoming, 0x448u)); Assert.Equal(CreateObjectTimestampDisposition.ExistingGeneration, refresh.Disposition); Assert.NotNull(refresh.SameGenerationEvents); SameGenerationCreateObjectEvents events = refresh.SameGenerationEvents.Value; Assert.True(controller.TryApplyPosition( events.Position!.Value, isLocalPlayer: false, forcePositionRotation: null, currentLocalVelocity: null, out PositionTimestampDisposition positionResult, out _, out _)); Assert.Equal(PositionTimestampDisposition.Rejected, positionResult); Assert.True(controller.TryApplyState(events.State, out _)); Assert.True(controller.TryGetSnapshot(0x70000001u, out WorldSession.EntitySpawn accepted)); Assert.Equal(original, accepted.Position); Assert.Equal(0x448u, accepted.PhysicsState); } [Fact] public void PickupRetainsGeneration_AndFreshPositionReturnsToWorld() { var controller = new InboundPhysicsStateController(); controller.AcceptCreate(Spawn( 0x70000002u, 7, 20, 1, Position(0x0101FFFFu, 10f), 0x408u)); Assert.True(controller.TryApplyPickup( new PickupEvent.Parsed(0x70000002u, 7, 21), out WorldSession.EntitySpawn pickedUp)); Assert.Null(pickedUp.Position); var update = new WorldSession.EntityPositionUpdate( 0x70000002u, Position(0x0101FFFFu, 30f), null, null, true, 7, 22, 0, 0); Assert.True(controller.TryApplyPosition( update, false, null, null, out var disposition, out WorldSession.EntitySpawn returned, out _)); Assert.Equal(PositionTimestampDisposition.Apply, disposition); Assert.Equal(30f, returned.Position!.Value.PositionX); } /// /// C5b (#275) rewrite of the pre-classification test: the retail-zero /// rule (PositionPack::UnPack @0x00516740 initializes an absent /// placement id to zero and HandleReceivedPosition forwards that /// exact value to SetPlacementFrame) is only reachable when the /// gate ABOVE it passes — CPhysicsObj::HasAnims(arg2) == 0 /// @0x00454137. The seed is therefore explicitly NON-animated; the /// animated counterpart is /// . /// [Fact] public void PositionPlacementAbsentAndPresentZeroBothApplyRetailZero() { var controller = new InboundPhysicsStateController(); WorldSession.EntitySpawn seed = Spawn( 0x70000009u, 7, 20, 1, Position(0x0101FFFFu, 10f), 0x408u, motionTableId: null); seed = seed with { PlacementId = 7, Physics = seed.Physics!.Value with { AnimationFrame = 7 }, }; controller.AcceptCreate(seed); Assert.True(controller.TryApplyPosition( new WorldSession.EntityPositionUpdate( seed.Guid, Position(0x0101FFFFu, 20f), null, null, true, 7, 21, 0, 0), false, null, null, out PositionTimestampDisposition firstDisposition, out WorldSession.EntitySpawn first, out _)); Assert.Equal(PositionTimestampDisposition.Apply, firstDisposition); Assert.Equal((uint)0, first.PlacementId); Assert.Equal((uint)0, first.Physics!.Value.AnimationFrame); Assert.True(controller.TryApplyPosition( new WorldSession.EntityPositionUpdate( seed.Guid, Position(0x0101FFFFu, 30f), null, 0, true, 7, 22, 0, 0), false, null, null, out PositionTimestampDisposition secondDisposition, out WorldSession.EntitySpawn second, out _)); Assert.Equal(PositionTimestampDisposition.Apply, secondDisposition); Assert.Equal((uint)0, second.PlacementId); Assert.Equal((uint)0, second.Physics!.Value.AnimationFrame); } /// /// C5b (#275) / AP-131 retirement, half 1. Retail gates /// CPhysicsObj::SetPlacementFrame @0x00454142 on /// CPhysicsObj::HasAnims(arg2) == 0 @0x00454137: an ANIMATED body /// never takes the wire placement frame, because its animation sequencer /// owns the pose. Before C5b the steady-state merge stamped it anyway, /// which is the animation snap/reset half of AP-131. /// [Fact] public void ApplyOnAnimatedEntity_NeverInstallsTheWirePlacementFrame() { var controller = new InboundPhysicsStateController(); WorldSession.EntitySpawn seed = Spawn( 0x8000C001u, 7, 20, 1, Position(0x0101FFFFu, 10f), 0x408u, motionTableId: 0x09000001u); seed = seed with { PlacementId = 7, Physics = seed.Physics!.Value with { AnimationFrame = 7 }, }; controller.AcceptCreate(seed); Assert.True(controller.TryApplyPosition( new WorldSession.EntityPositionUpdate( seed.Guid, Position(0x0101FFFFu, 20f), Velocity: null, PlacementId: 5, IsGrounded: true, InstanceSequence: 7, PositionSequence: 21, TeleportSequence: 0, ForcePositionSequence: 0), isLocalPlayer: false, forcePositionRotation: null, currentLocalVelocity: null, out PositionTimestampDisposition disposition, out WorldSession.EntitySpawn accepted, out _)); Assert.Equal(PositionTimestampDisposition.Apply, disposition); // The gate above SetPlacementFrame never opened. Assert.Equal((uint)7, accepted.PlacementId); Assert.Equal((uint)7, accepted.Physics!.Value.AnimationFrame); // The rest of the merge is unaffected: pose and POSITION_TS still land. Assert.Equal(20f, accepted.Position!.Value.PositionX); Assert.Equal((ushort)21, accepted.PositionSequence); // unset_parent @0x00454129 is unconditional on this path. Assert.Null(accepted.ParentGuid); } /// /// C5b (#275) / AP-131 retirement, half 1 (the other direction). A /// NON-animated body reaches SetPlacementFrame @0x00454142 and /// takes the wire frame verbatim. /// [Fact] public void ApplyOnNonAnimatedEntity_InstallsTheWirePlacementFrame() { var controller = new InboundPhysicsStateController(); WorldSession.EntitySpawn seed = Spawn( 0x8000C002u, 7, 20, 1, Position(0x0101FFFFu, 10f), 0x408u, motionTableId: null); seed = seed with { PlacementId = 7, Physics = seed.Physics!.Value with { AnimationFrame = 7 }, }; controller.AcceptCreate(seed); Assert.True(controller.TryApplyPosition( new WorldSession.EntityPositionUpdate( seed.Guid, Position(0x0101FFFFu, 20f), Velocity: null, PlacementId: 5, IsGrounded: true, InstanceSequence: 7, PositionSequence: 21, TeleportSequence: 0, ForcePositionSequence: 0), isLocalPlayer: false, forcePositionRotation: null, currentLocalVelocity: null, out PositionTimestampDisposition disposition, out WorldSession.EntitySpawn accepted, out _)); Assert.Equal(PositionTimestampDisposition.Apply, disposition); Assert.Equal((uint)5, accepted.PlacementId); Assert.Equal((uint)5, accepted.Physics!.Value.AnimationFrame); } /// /// C5b (#275) / AP-131 retirement, half 2. Retail's Gate A /// (@0x0045400C, the local-player FORCE_POSITION self-echo shortcut) /// returns @0x0045409D BEFORE CPhysicsObj::unset_parent /// @0x00454129, so a ForcePosition never unparents. Parent RETENTION is /// the discriminating observable for the force row: the placement-frame /// half is inert there (appliedPlacement keeps /// old.PlacementId under either flag value), so sabotaging it on a /// ForcePosition proves nothing. /// [Fact] public void ForcePositionOnParentedLocalPlayer_RetainsTheParentAttachment() { var controller = new InboundPhysicsStateController(); WorldSession.EntitySpawn seed = WithTimestamps( Spawn(0x50000021u, 3, 10, 1, Position(0x0101FFFFu, 10f), 0x408u), teleport: 10, forcePosition: 0); seed = seed with { ParentGuid = 0x70004444u, ParentLocation = 9u, Physics = seed.Physics!.Value with { Parent = new PhysicsAttachment(0x70004444u, 9u), }, }; controller.AcceptCreate(seed); var preserved = new Quaternion(0.1f, 0.2f, 0.3f, 0.9f); Assert.True(controller.TryApplyPosition( PositionUpdate( seed.Guid, instance: 3, position: 9, teleport: 10, forcePosition: 1), isLocalPlayer: true, forcePositionRotation: preserved, currentLocalVelocity: Vector3.Zero, out PositionTimestampDisposition disposition, out WorldSession.EntitySpawn accepted, out _)); Assert.Equal(PositionTimestampDisposition.ForcePosition, disposition); Assert.Equal(0x70004444u, accepted.ParentGuid); Assert.Equal(9u, accepted.ParentLocation); Assert.Equal( new PhysicsAttachment(0x70004444u, 9u), accepted.Physics!.Value.Parent); // Heading preservation (BlipPlayer's get_heading/set_heading pair // @0x00454056-68) is unchanged by C5b. Assert.Equal(preserved.W, accepted.Position!.Value.RotationW); Assert.Equal(preserved.X, accepted.Position!.Value.RotationX); } /// /// C5b (#275) proof obligation 1: the merge's two pre-placement flags ARE /// 's /// ApplyPlacementFrameBeforeRouting/UnparentBeforeRouting /// rows. The production classifier is the oracle — this test does not /// re-encode the truth table, it drives both computations over the packet /// matrix and asserts the merged snapshot equals what the route's own /// flags would have produced. Dual parent classes (player 0x5… and /// creature 0x8…) per the #319 discipline. /// [Theory] // guid, isLocalPlayer, animated, parented, force [InlineData(0x50000031u, true, true, true, false)] [InlineData(0x50000032u, true, true, false, false)] [InlineData(0x50000033u, true, false, true, false)] [InlineData(0x50000034u, true, false, false, false)] [InlineData(0x50000035u, true, true, true, true)] [InlineData(0x50000036u, true, true, false, true)] [InlineData(0x50000037u, true, false, true, true)] [InlineData(0x50000038u, true, false, false, true)] [InlineData(0x80000031u, false, true, true, false)] [InlineData(0x80000032u, false, true, false, false)] [InlineData(0x80000033u, false, false, true, false)] [InlineData(0x80000034u, false, false, false, false)] public void MergedPrePlacementFieldsMatchTheClassifiedRouteFlags( uint guid, bool isLocalPlayer, bool animated, bool parented, bool force) { WorldSession.EntitySpawn seed = WithTimestamps( Spawn( guid, 3, 10, 1, Position(0x0101FFFFu, 10f), 0x408u, motionTableId: animated ? 0x09000001u : null), teleport: 10, forcePosition: 0); seed = seed with { PlacementId = 7, Physics = seed.Physics!.Value with { AnimationFrame = 7 }, }; if (parented) { seed = seed with { ParentGuid = 0x70004444u, ParentLocation = 9u, Physics = seed.Physics!.Value with { Parent = new PhysicsAttachment(0x70004444u, 9u), }, }; } WorldSession.EntityPositionUpdate update = new( guid, Position(0x0101FFFFu, 20f), Velocity: null, PlacementId: 5, IsGrounded: true, InstanceSequence: 3, PositionSequence: force ? (ushort)9 : (ushort)11, TeleportSequence: 10, ForcePositionSequence: force ? (ushort)1 : (ushort)0); var merging = new InboundPhysicsStateController(); merging.AcceptCreate(seed); Assert.True(merging.TryApplyPosition( update, isLocalPlayer, forcePositionRotation: isLocalPlayer ? Quaternion.Identity : null, currentLocalVelocity: isLocalPlayer ? Vector3.Zero : null, out PositionTimestampDisposition disposition, out WorldSession.EntitySpawn merged, out AcceptedPhysicsTimestamps timestamps)); Assert.Equal( force ? PositionTimestampDisposition.ForcePosition : PositionTimestampDisposition.Apply, disposition); // The oracle: the production classifier, fed the same wire packet and // the same static HasAnimations proxy. // // C5b review L3 (2026-08-05): the request is now BUILT by the // production constructor rather than hand-written. The old version // passed `HasAnimations: animated` as a literal, so the merge's // `old.MotionTableId ?? old.Physics?.MotionTableId` and // RuntimeAcceptedPositionRouteRequests.Build's canonical-snapshot // equivalent were textually identical and pinned by nothing — either // could have been edited without a red test. Routing the oracle // through Build makes the equality of the two expressions the thing // this matrix actually asserts. using RuntimeEntityObjectLifetime oracle = OracleLifetime(); RuntimeEntityRecord canonical = RegisterOracleSnapshot(oracle, seed); RuntimeAuthoritativePositionRoute route = RuntimeAuthoritativePositionRouteClassifier.ClassifyAcceptedPosition( RuntimeAcceptedPositionRouteRequests.Build( new RuntimeGenerationToken(7), canonical, new RuntimeEntityKey(guid, 1), update, isLocalPlayer ? RuntimePositionEntityKind.LocalPlayer : RuntimePositionEntityKind.Remote, RuntimeAcceptedPositionSource.PositionEvent, disposition, timestamps.PreviousTeleport, timestamps.Teleport, playerDistance: 0f, usePositionFromServer: true, committedCellId: 0x0101FFFFu)); // Rebuild what the classified route would have installed, through the // SAME production merge, and require field equality. var classified = new InboundPhysicsStateController(); classified.AcceptCreate(seed); Assert.True(classified.ApplyAcceptedPositionSnapshot( guid, update, disposition, timestamps, isLocalPlayer, isLocalPlayer ? Quaternion.Identity : null, isLocalPlayer ? Vector3.Zero : null, installPlacementFrame: route.ApplyPlacementFrameBeforeRouting, clearParent: route.UnparentBeforeRouting, out WorldSession.EntitySpawn expected)); Assert.Equal(expected.PlacementId, merged.PlacementId); Assert.Equal( expected.Physics!.Value.AnimationFrame, merged.Physics!.Value.AnimationFrame); Assert.Equal(expected.ParentGuid, merged.ParentGuid); Assert.Equal(expected.ParentLocation, merged.ParentLocation); Assert.Equal( expected.Physics!.Value.Parent, merged.Physics!.Value.Parent); } [Fact] public void RemotePositionWithoutVelocityAppliesUnpackedZeroVector() { var controller = new InboundPhysicsStateController(); WorldSession.EntitySpawn seed = Spawn( 0x7000000Au, 7, 20, 1, Position(0x0101FFFFu, 10f), 0x408u); seed = seed with { Physics = seed.Physics!.Value with { Velocity = new Vector3(4f, 5f, 6f) }, }; controller.AcceptCreate(seed); Assert.True(controller.TryApplyPosition( new WorldSession.EntityPositionUpdate( seed.Guid, Position(0x0101FFFFu, 20f), null, null, true, 7, 21, 0, 0), false, null, null, out PositionTimestampDisposition disposition, out WorldSession.EntitySpawn accepted, out _)); Assert.Equal(PositionTimestampDisposition.Apply, disposition); Assert.Equal(Vector3.Zero, accepted.Physics!.Value.Velocity); } [Fact] public void LocalNormalCorrectionRetainsLiveVelocity_ButFreshTeleportZerosIt() { var controller = new InboundPhysicsStateController(); WorldSession.EntitySpawn seed = WithTimestamps( Spawn(0x5000000Au, 7, 20, 1, Position(0x0101FFFFu, 10f), 0x408u), teleport: 10); controller.AcceptCreate(seed); Vector3 liveVelocity = new(4f, 5f, 6f); Assert.True(controller.TryApplyPosition( new WorldSession.EntityPositionUpdate( seed.Guid, Position(0x0101FFFFu, 20f), new Vector3(90f, 80f, 70f), null, true, 7, 21, 10, 0), true, null, liveVelocity, out PositionTimestampDisposition normalDisposition, out WorldSession.EntitySpawn normal, out AcceptedPhysicsTimestamps normalTimestamps)); Assert.Equal(PositionTimestampDisposition.Apply, normalDisposition); Assert.Equal(liveVelocity, normal.Physics!.Value.Velocity); Assert.False(normalTimestamps.TeleportAdvanced); Assert.True(controller.TryApplyPosition( new WorldSession.EntityPositionUpdate( seed.Guid, Position(0x0101FFFFu, 30f), new Vector3(9f, 8f, 7f), null, true, 7, 22, 11, 0), true, null, liveVelocity, out PositionTimestampDisposition teleportDisposition, out WorldSession.EntitySpawn teleported, out AcceptedPhysicsTimestamps teleportTimestamps)); Assert.Equal(PositionTimestampDisposition.Apply, teleportDisposition); Assert.Equal(Vector3.Zero, teleported.Physics!.Value.Velocity); Assert.True(teleportTimestamps.TeleportAdvanced); } [Fact] public void ParentEventWaitsForParent_ThenStagesTimestampUntilValidatedCommit() { var controller = new InboundPhysicsStateController(); controller.AcceptCreate(Spawn( 0x70000003u, 3, 4, 1, Position(0x0101FFFFu, 10f), 0x408u)); var update = new ParentEvent.Parsed( 0x70000004u, 0x70000003u, 1, 2, 9, 5); Assert.False(controller.TryApplyParent(update, out _)); controller.AcceptCreate(Spawn( 0x70000004u, 9, 1, 1, Position(0x0101FFFFu, 15f), 0x408u)); Assert.True(controller.TryApplyParent(update, out WorldSession.EntitySpawn staged)); Assert.Null(staged.ParentGuid); Assert.NotNull(staged.Position); Assert.Equal((ushort)5, staged.PositionSequence); Assert.True(controller.TryCommitParent( update.ChildGuid, update.ParentGuid, update.ParentLocation, update.PlacementId, update.ChildPositionSequence, out WorldSession.EntitySpawn attached)); Assert.Equal(0x70000004u, attached.ParentGuid); Assert.Null(attached.Position); Assert.False(controller.TryApplyParent(update, out _)); } [Fact] public void LocalDeleteIsRejected_AndClearDropsSessionState() { var controller = new InboundPhysicsStateController(); controller.AcceptCreate(Spawn( 0x50000001u, 4, 1, 1, Position(0x0101FFFFu, 10f), 0x408u)); Assert.False(controller.TryDelete(new DeleteObject.Parsed(0x50000001u, 4), true)); Assert.True(controller.TryGetSnapshot(0x50000001u, out _)); controller.Clear(); Assert.Empty(controller.Snapshots); Assert.False(controller.IsFreshTeleportStart(0x50000001u, 1)); } [Fact] public void DeleteForUnknownGenerationIsRejected() { var controller = new InboundPhysicsStateController(); Assert.False(controller.TryDelete( new DeleteObject.Parsed(0x5000FFFFu, 9), isLocalPlayer: false)); } [Fact] public void TeleportStartIsComparedWithoutAdvancingTeleportTimestamp() { var controller = new InboundPhysicsStateController(); WorldSession.EntitySpawn spawn = Spawn( 0x50000002u, 2, 1, 1, Position(0x0101FFFFu, 10f), 0x408u); PhysicsSpawnData physics = spawn.Physics!.Value with { Timestamps = spawn.Physics.Value.Timestamps with { Teleport = 10 }, }; controller.AcceptCreate(spawn with { Physics = physics }); Assert.True(controller.IsFreshTeleportStart(0x50000002u, 10)); Assert.False(controller.IsFreshTeleportStart(0x50000002u, 9)); Assert.True(controller.IsFreshTeleportStart(0x50000002u, 11)); Assert.True(controller.IsFreshTeleportStart(0x50000002u, 11)); } [Fact] public void RejectedServerControlStillMirrorsConsumedMovementTimestamp() { var controller = new InboundPhysicsStateController(); WorldSession.EntitySpawn spawn = WithTimestamps( Spawn(0x70000005u, 3, 1, 1, Position(0x0101FFFFu, 10f), 0x408u), movement: 1, serverControl: 5); controller.AcceptCreate(spawn); bool applied = controller.TryApplyMotion( new WorldSession.EntityMotionUpdate( spawn.Guid, new CreateObject.ServerMotionState(0x3d, 0x11), 3, 2, 4, false), retainPayload: true, out _, out _); Assert.False(applied); Assert.True(controller.TryGetSnapshot(spawn.Guid, out WorldSession.EntitySpawn retained)); Assert.Equal((ushort)2, retained.MovementSequence); Assert.Equal((ushort)5, retained.ServerControlSequence); Assert.Equal((ushort)2, retained.Physics!.Value.Timestamps.Movement); Assert.Equal((ushort)5, retained.Physics.Value.Timestamps.ServerControlledMove); Assert.Equal(spawn.MotionState, retained.MotionState); } [Fact] public void AutonomousLocalEchoRetainsPayloadButMirrorsAcceptedTimestamps() { var controller = new InboundPhysicsStateController(); WorldSession.EntitySpawn spawn = WithTimestamps( Spawn(0x50000006u, 3, 1, 1, Position(0x0101FFFFu, 10f), 0x408u), movement: 1, serverControl: 5) with { MotionState = new CreateObject.ServerMotionState(0x3d, 0x10), }; controller.AcceptCreate(spawn); Assert.True(controller.TryApplyMotion( new WorldSession.EntityMotionUpdate( spawn.Guid, new CreateObject.ServerMotionState(0x3d, 0x12), 3, 2, 5, true), retainPayload: false, out WorldSession.EntitySpawn retained, out _)); Assert.Equal(spawn.MotionState, retained.MotionState); Assert.Equal((ushort)2, retained.MovementSequence); Assert.Equal((ushort)5, retained.ServerControlSequence); Assert.Equal((ushort)2, retained.Physics!.Value.Timestamps.Movement); } [Fact] public void StaleMovementTimestampLeavesLegacySnapshotByteIdentical() { var controller = new InboundPhysicsStateController(); WorldSession.EntitySpawn spawn = WithTimestamps( Spawn(0x70000008u, 3, 1, 1, Position(0x0101FFFFu, 10f), 0x408u), movement: 5, serverControl: 5); controller.AcceptCreate(spawn); Assert.True(controller.TryGetSnapshot(spawn.Guid, out WorldSession.EntitySpawn before)); // MOVEMENT_TS 3 is OLDER than the stored 5: // PhysicsTimestampGate.TryAcceptMovementEvent's // AdvanceStrict(Movement, ...) fails before ever consulting // SERVER_CONTROLLED_MOVE_TS - the gate is entirely untouched, and // the canonical snapshot must not move either (the regression this // guards: ApplyAcceptedMotion stamping the WIRE's own stale // MovementSequence instead of the gate's unchanged post-call value). bool applied = controller.TryApplyMotion( new WorldSession.EntityMotionUpdate( spawn.Guid, new CreateObject.ServerMotionState(0x3d, 0x99), InstanceSequence: 3, MovementSequence: 3, ServerControlSequence: 9, IsAutonomous: false), retainPayload: true, out WorldSession.EntitySpawn accepted, out _); Assert.False(applied); Assert.Equal(default, accepted); Assert.True(controller.TryGetSnapshot(spawn.Guid, out WorldSession.EntitySpawn after)); Assert.Equal(before, after); } [Fact] public void InstanceMismatchedMovementLeavesLegacySnapshotByteIdentical() { var controller = new InboundPhysicsStateController(); WorldSession.EntitySpawn spawn = WithTimestamps( Spawn(0x70000009u, 3, 1, 1, Position(0x0101FFFFu, 10f), 0x408u), movement: 1, serverControl: 1); controller.AcceptCreate(spawn); Assert.True(controller.TryGetSnapshot(spawn.Guid, out WorldSession.EntitySpawn before)); // A different INSTANCE_TS: TryAcceptInstance fails immediately, // before MOVEMENT_TS or SERVER_CONTROLLED_MOVE_TS are ever // consulted. Nothing in the gate or the canonical snapshot may move // - a stale duplicate or foreign-incarnation packet must not // silently drag a live entity's movement timestamps forward. bool applied = controller.TryApplyMotion( new WorldSession.EntityMotionUpdate( spawn.Guid, new CreateObject.ServerMotionState(0x3d, 0x99), InstanceSequence: 4, MovementSequence: 9, ServerControlSequence: 9, IsAutonomous: false), retainPayload: true, out WorldSession.EntitySpawn accepted, out _); Assert.False(applied); Assert.Equal(default, accepted); Assert.True(controller.TryGetSnapshot(spawn.Guid, out WorldSession.EntitySpawn after)); Assert.Equal(before, after); } [Fact] public void FreshForceWithOlderTeleportMirrorsForceButRejectsPose() { var controller = new InboundPhysicsStateController(); WorldSession.EntitySpawn spawn = WithTimestamps( Spawn(0x50000007u, 3, 10, 1, Position(0x0101FFFFu, 10f), 0x408u), teleport: 10, forcePosition: 0); controller.AcceptCreate(spawn); Assert.True(controller.TryApplyPosition( new WorldSession.EntityPositionUpdate( spawn.Guid, Position(0x0101FFFFu, 99f), null, null, true, 3, 11, 9, 1), isLocalPlayer: true, forcePositionRotation: Quaternion.Identity, currentLocalVelocity: Vector3.Zero, out PositionTimestampDisposition disposition, out WorldSession.EntitySpawn retained, out _)); Assert.Equal(PositionTimestampDisposition.Rejected, disposition); Assert.Equal(10f, retained.Position!.Value.PositionX); Assert.Equal((ushort)10, retained.PositionSequence); Assert.Equal((ushort)1, retained.Physics!.Value.Timestamps.ForcePosition); Assert.Equal((ushort)10, retained.Physics.Value.Timestamps.Position); } [Fact] public void ForcePositionStoresPreservedLocalHeadingForLaterHydration() { var controller = new InboundPhysicsStateController(); WorldSession.EntitySpawn spawn = WithTimestamps( Spawn(0x50000008u, 3, 10, 1, Position(0x0101FFFFu, 10f), 0x408u), teleport: 10, forcePosition: 0); controller.AcceptCreate(spawn); Quaternion heading = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, 0.75f); Vector3 liveVelocity = new(1f, 2f, 3f); Assert.True(controller.TryApplyPosition( new WorldSession.EntityPositionUpdate( spawn.Guid, Position(0x0101FFFFu, 99f), new Vector3(90f, 80f, 70f), null, true, 3, 9, 10, 1), isLocalPlayer: true, forcePositionRotation: heading, currentLocalVelocity: liveVelocity, out PositionTimestampDisposition disposition, out WorldSession.EntitySpawn retained, out _)); Assert.Equal(PositionTimestampDisposition.ForcePosition, disposition); Assert.Equal(99f, retained.Position!.Value.PositionX); Assert.Equal(heading.W, retained.Position.Value.RotationW, precision: 6); Assert.Equal(heading.X, retained.Position.Value.RotationX, precision: 6); Assert.Equal(heading.Y, retained.Position.Value.RotationY, precision: 6); Assert.Equal(heading.Z, retained.Position.Value.RotationZ, precision: 6); Assert.Equal(retained.Position, retained.Physics!.Value.Position); Assert.Equal(liveVelocity, retained.Physics.Value.Velocity); } /// /// #307: the live Position path must report the TELEPORT_TS value the gate /// held BEFORE the event, not 0. Every consumer of /// AcceptedPhysicsTimestamps.PreviousTeleport compares it against /// the accepted stamp; a hard 0 makes "this packet did not advance /// TELEPORT_TS" indistinguishable from "this packet regressed it" for any /// entity that has ever teleported. /// [Fact] public void TryApplyPosition_ReportsThePreEventTeleportStamp() { var controller = new InboundPhysicsStateController(); WorldSession.EntitySpawn spawn = WithTimestamps( Spawn(0x50000010u, 3, 10, 1, Position(0x0101FFFFu, 10f), 0x408u), teleport: 10, forcePosition: 0); controller.AcceptCreate(spawn); // An ordinary Position carrying the SAME TELEPORT_TS: previous and // accepted must both be the live stamp, and nothing advanced. Assert.True(controller.TryApplyPosition( PositionUpdate(spawn.Guid, instance: 3, position: 11, teleport: 10), isLocalPlayer: false, forcePositionRotation: null, currentLocalVelocity: null, out PositionTimestampDisposition steady, out _, out AcceptedPhysicsTimestamps steadyStamps)); Assert.Equal(PositionTimestampDisposition.Apply, steady); Assert.Equal((ushort)10, steadyStamps.PreviousTeleport); Assert.Equal((ushort)10, steadyStamps.Teleport); Assert.False(steadyStamps.TeleportAdvanced); // A fresh TELEPORT_TS: previous is the PRE-event stamp, accepted is // the new one. Capturing after the gate mutated would collapse both // onto 11 and silently lose the advance. Assert.True(controller.TryApplyPosition( PositionUpdate(spawn.Guid, instance: 3, position: 12, teleport: 11), isLocalPlayer: false, forcePositionRotation: null, currentLocalVelocity: null, out PositionTimestampDisposition advanced, out _, out AcceptedPhysicsTimestamps advancedStamps)); Assert.Equal(PositionTimestampDisposition.Apply, advanced); Assert.Equal((ushort)10, advancedStamps.PreviousTeleport); Assert.Equal((ushort)11, advancedStamps.Teleport); Assert.True(advancedStamps.TeleportAdvanced); } /// /// #307, the shipped consequence: a local player who has portalled or /// recalled this session holds a nonzero TELEPORT_TS. Retail's /// FORCE_POSITION branch (SmartBox::HandleReceivedPosition /// 0x00453FD0) fires only when the packet's teleport stamp EQUALS the live /// one and never advances it, so the authority C4 route 2 builds from /// these timestamps must satisfy /// PreviousTeleportSequence == AcceptedTeleportSequence. With /// PreviousTeleport pinned to 0 the classifier rejected the authority and /// the force correction was silently dropped. /// [Fact] public void LocalPlayerForcePositionAfterATeleport_ClassifiesAsAnAcceptedForceCorrection() { var controller = new InboundPhysicsStateController(); WorldSession.EntitySpawn spawn = WithTimestamps( Spawn(0x50000011u, 3, 10, 1, Position(0x0101FFFFu, 10f), 0x408u), teleport: 10, forcePosition: 0); controller.AcceptCreate(spawn); Assert.True(controller.TryApplyPosition( PositionUpdate( spawn.Guid, instance: 3, position: 9, teleport: 10, forcePosition: 1), isLocalPlayer: true, forcePositionRotation: Quaternion.Identity, currentLocalVelocity: Vector3.Zero, out PositionTimestampDisposition disposition, out WorldSession.EntitySpawn accepted, out AcceptedPhysicsTimestamps timestamps)); Assert.Equal(PositionTimestampDisposition.ForcePosition, disposition); // Built exactly as the route-2 drive builds it from these outputs. var authority = new RuntimeAuthoritativePositionAuthority( new RuntimeGenerationToken(7), new RuntimeEntityKey(spawn.Guid, 1), PositionAuthorityVersion: 4UL, AcceptedPositionSequence: 9, timestamps.PreviousTeleport, timestamps.Teleport, disposition); RuntimeAuthoritativePositionRoute route = RuntimeAuthoritativePositionRouteClassifier.ClassifyAcceptedPosition( new RuntimeAcceptedPositionRouteRequest( authority, RuntimePositionEntityKind.LocalPlayer, RuntimeAcceptedPositionSource.PositionEvent, accepted.Position!.Value, PlacementFrame: 0u, PositionPackVelocity: Vector3.Zero, CommittedCellId: 0x0101FFFFu, HasContact: true, PlayerDistance: 0f, UsePositionFromServer: true, HasAnimations: false, default)); Assert.Equal( RuntimeAuthoritativePositionDisposition.SetPositionSimple, route.Disposition); Assert.True(route.SendPositionImmediately); Assert.Equal((ushort)10, timestamps.PreviousTeleport); Assert.Equal((ushort)10, timestamps.Teleport); } private static WorldSession.EntityPositionUpdate PositionUpdate( uint guid, ushort instance, ushort position, ushort teleport, ushort forcePosition = 0) => new( guid, Position(0x0101FFFFu, 20f), null, null, true, instance, position, teleport, forcePosition); /// /// C5b review L4 (2026-08-05). The merge's HasAnimations proxy is /// old.MotionTableId ?? old.Physics?.MotionTableId — a coalesce /// whose whole point (per the production comment on /// RuntimeAcceptedPositionRouteRequests.Build) is that /// WeenieDescription and ObjDesc merges populate only ONE of the two /// fields, depending on which stage last touched appearance vs /// description. Every pre-existing fixture set both halves to the same /// value, so deleting either operand of the ?? was undetectable /// while the production comment claimed the mixed case is the real-world /// one. These rows are the mixed cases, and the oracle is still /// Build + the production classifier, so the two independent /// copies of the coalesce stay pinned equal on exactly the inputs that /// discriminate between them. /// /// The explicit-zero row is the third discriminator: a present-but- /// zero top half is NOT null, so ?? never reaches the nonzero /// physics half and the entity is correctly unanimated. Swapping the /// coalesce operands would flip that row alone. /// [Theory] // topLevelMotionTableId, physicsMotionTableId, expectedAnimated [InlineData(null, 0x09000001u, true)] [InlineData(0x09000001u, null, true)] [InlineData(0x09000001u, 0x09000002u, true)] [InlineData(null, null, false)] [InlineData(0u, 0x09000001u, false)] [InlineData(null, 0u, false)] public void MixedMotionTableSourcesDriveTheSamePlacementFrameGateAsTheRoute( uint? topLevelMotionTableId, uint? physicsMotionTableId, bool expectedAnimated) { const uint guid = 0x80000041u; WorldSession.EntitySpawn seed = WithTimestamps( Spawn(guid, 3, 10, 1, Position(0x0101FFFFu, 10f), 0x408u), teleport: 10, forcePosition: 0); seed = seed with { PlacementId = 7, MotionTableId = topLevelMotionTableId, Physics = seed.Physics!.Value with { AnimationFrame = 7, MotionTableId = physicsMotionTableId, }, }; WorldSession.EntityPositionUpdate update = new( guid, Position(0x0101FFFFu, 20f), Velocity: null, PlacementId: 5, IsGrounded: true, InstanceSequence: 3, PositionSequence: 11, TeleportSequence: 10, ForcePositionSequence: 0); var merging = new InboundPhysicsStateController(); merging.AcceptCreate(seed); Assert.True(merging.TryApplyPosition( update, isLocalPlayer: false, forcePositionRotation: null, currentLocalVelocity: null, out PositionTimestampDisposition disposition, out WorldSession.EntitySpawn merged, out AcceptedPhysicsTimestamps timestamps)); Assert.Equal(PositionTimestampDisposition.Apply, disposition); using RuntimeEntityObjectLifetime oracle = OracleLifetime(); RuntimeEntityRecord canonical = RegisterOracleSnapshot(oracle, seed); RuntimeAuthoritativePositionRoute route = RuntimeAuthoritativePositionRouteClassifier.ClassifyAcceptedPosition( RuntimeAcceptedPositionRouteRequests.Build( new RuntimeGenerationToken(7), canonical, new RuntimeEntityKey(guid, 1), update, RuntimePositionEntityKind.Remote, RuntimeAcceptedPositionSource.PositionEvent, disposition, timestamps.PreviousTeleport, timestamps.Teleport, playerDistance: 0f, usePositionFromServer: true, committedCellId: 0x0101FFFFu)); // The classified route's own gate agrees with the fixture's intent… Assert.Equal(!expectedAnimated, route.ApplyPlacementFrameBeforeRouting); // …and the merge, which computed its own copy, lands the SAME frame. // Animated => retail's HasAnims gate @0x00454137 skips SetPlacementFrame // entirely, so the seed's 7 survives; unanimated => the wire's 5 lands. Assert.Equal(expectedAnimated ? 7u : 5u, merged.PlacementId); Assert.Equal( expectedAnimated ? 7u : 5u, merged.Physics!.Value.AnimationFrame); } /// /// A minimal Runtime lifetime whose only purpose is to own a canonical /// so the production /// RuntimeAcceptedPositionRouteRequests.Build can be the oracle /// (C5b review L3). The fixture landblock matches the 0x0101FFFF cells /// every packet in this file uses. /// private static RuntimeEntityObjectLifetime OracleLifetime() { var engine = new PhysicsEngine { DataCache = new PhysicsDataCache() }; engine.AddLandblock( 0x01010000u, new TerrainSurface(new byte[81], new float[256]), Array.Empty(), Array.Empty(), worldOffsetX: 0f, worldOffsetY: 0f); return new RuntimeEntityObjectLifetime(engine); } private static RuntimeEntityRecord RegisterOracleSnapshot( RuntimeEntityObjectLifetime lifetime, WorldSession.EntitySpawn seed) { RuntimeEntityRecord canonical = lifetime.RegisterEntity(seed).Canonical!; // The pin is only worth anything if the canonical snapshot really is // the same spawn the merge retained. Assert.Equal(seed.MotionTableId, canonical.Snapshot.MotionTableId); Assert.Equal( seed.Physics!.Value.MotionTableId, canonical.Snapshot.Physics!.Value.MotionTableId); return canonical; } private static WorldSession.EntitySpawn WithTimestamps( WorldSession.EntitySpawn spawn, ushort? movement = null, ushort? serverControl = null, ushort? teleport = null, ushort? forcePosition = null) { PhysicsSpawnData physics = spawn.Physics!.Value; PhysicsTimestamps timestamps = physics.Timestamps with { Movement = movement ?? physics.Timestamps.Movement, ServerControlledMove = serverControl ?? physics.Timestamps.ServerControlledMove, Teleport = teleport ?? physics.Timestamps.Teleport, ForcePosition = forcePosition ?? physics.Timestamps.ForcePosition, }; return spawn with { MovementSequence = timestamps.Movement, ServerControlSequence = timestamps.ServerControlledMove, Physics = physics with { Timestamps = timestamps }, }; } /// /// C5b (#275): drives AP-130's static /// HasAnimations proxy, which now gates the steady-state merge's /// placement-frame install exactly as retail's /// CPhysicsObj::HasAnims check @0x00454137 gates /// SetPlacementFrame. The default keeps every pre-existing fixture /// animated, which is what the retail population overwhelmingly is. /// private static WorldSession.EntitySpawn Spawn( uint guid, ushort instance, ushort positionSequence, ushort stateSequence, CreateObject.ServerPosition? position, uint state, uint? motionTableId = 0x09000001u) { var timestamps = new PhysicsTimestamps( positionSequence, 1, stateSequence, 1, 0, 1, 0, 1, instance); var physics = new PhysicsSpawnData( RawState: state, Position: position, Movement: null, AnimationFrame: null, SetupTableId: 0x02000001u, MotionTableId: motionTableId, SoundTableId: null, PhysicsScriptTableId: null, Parent: null, Children: null, Scale: null, Friction: null, Elasticity: null, Translucency: null, Velocity: null, Acceleration: null, AngularVelocity: null, DefaultScriptType: null, DefaultScriptIntensity: null, Timestamps: timestamps); return new WorldSession.EntitySpawn( guid, position, 0x02000001u, Array.Empty(), Array.Empty(), Array.Empty(), null, null, "fixture", null, null, motionTableId, PhysicsState: state, InstanceSequence: instance, MovementSequence: 1, ServerControlSequence: 1, PositionSequence: positionSequence, Physics: physics); } private static CreateObject.ServerPosition Position(uint cell, float x) => new(cell, x, 10f, 5f, 1f, 0f, 0f, 0f); }