diff --git a/src/AcDream.Runtime/Physics/RuntimeAuthoritativePositionRouteClassifier.cs b/src/AcDream.Runtime/Physics/RuntimeAuthoritativePositionRouteClassifier.cs
new file mode 100644
index 00000000..ab849c48
--- /dev/null
+++ b/src/AcDream.Runtime/Physics/RuntimeAuthoritativePositionRouteClassifier.cs
@@ -0,0 +1,554 @@
+using System.Numerics;
+using AcDream.Core.Net.Messages;
+using AcDream.Core.Physics;
+using AcDream.Runtime.Entities;
+
+namespace AcDream.Runtime.Physics;
+
+internal enum RuntimePositionEntityKind : byte
+{
+ Unknown,
+ LocalPlayer,
+ Remote,
+ Projectile,
+}
+
+internal enum RuntimeCreateResidenceKind : byte
+{
+ Unknown,
+ TopLevel,
+ Parented,
+ PickedUp,
+}
+
+internal enum RuntimeAcceptedPositionSource : byte
+{
+ Unknown,
+ PositionEvent,
+ SameIncarnationCreate,
+}
+
+internal enum RuntimeLeaveWorldCause : byte
+{
+ Unknown,
+ Pickup,
+ Parent,
+}
+
+internal enum RuntimeAuthoritativePositionDisposition : byte
+{
+ RejectedAuthority,
+ RejectedData,
+ AwaitFreshPosition,
+ NoPositionOperation,
+ Interpolate,
+ SetPosition,
+ SetPositionSimple,
+}
+
+internal enum RuntimeTeleportHookPhase : byte
+{
+ None,
+ BeforePositionOperation,
+ AfterPositionOperation,
+ AfterEnterWorld,
+}
+
+///
+/// Exact accepted-wire authority for one presentation-independent position
+/// route. identifies the incarnation while
+/// rejects a superseded accepted pose.
+/// The previous and accepted TELEPORT_TS values remain explicit so the pure
+/// classifier can apply retail's wrap-safe freshness rule without consulting
+/// App state.
+///
+internal readonly record struct RuntimeAuthoritativePositionAuthority(
+ RuntimeGenerationToken Generation,
+ RuntimeEntityKey Entity,
+ ulong PositionAuthorityVersion,
+ ushort AcceptedPositionSequence,
+ ushort PreviousTeleportSequence,
+ ushort AcceptedTeleportSequence,
+ PositionTimestampDisposition TimestampDisposition)
+{
+ internal bool IsStructurallyValid => Generation.Value != 0UL
+ && Entity.LocalEntityId != 0u
+ && PositionAuthorityVersion != 0UL
+ && TimestampDisposition is PositionTimestampDisposition.Apply
+ or PositionTimestampDisposition.ForcePosition;
+
+ internal bool TeleportAdvanced =>
+ TimestampDisposition is PositionTimestampDisposition.Apply
+ && PhysicsTimestampGate.IsNewer(
+ PreviousTeleportSequence,
+ AcceptedTeleportSequence);
+
+ internal bool TeleportRegressed =>
+ PhysicsTimestampGate.IsNewer(
+ AcceptedTeleportSequence,
+ PreviousTeleportSequence);
+}
+
+///
+/// Placement facts which must not be collapsed into presentation visibility.
+/// Hidden and NoDraw objects still run retail SetPosition. Hidden suppresses
+/// the complete collision batch after its retail state rewrite; NoDraw is
+/// render-only. Source/peer ReportCollisions and IgnoreCollisions decisions
+/// remain downstream in RuntimeCollisionReportingState.
+///
+internal readonly record struct RuntimePositionPlacementFacts(
+ PhysicsStateFlags PhysicsState,
+ bool HasAuthoredMoverShape)
+{
+ internal bool CollisionBatchEligible =>
+ (PhysicsState & PhysicsStateFlags.Hidden) == 0;
+}
+
+internal readonly record struct RuntimeCreatePositionRouteRequest(
+ RuntimeAuthoritativePositionAuthority Authority,
+ RuntimePositionEntityKind EntityKind,
+ RuntimeCreateResidenceKind Residence,
+ CreateObject.ServerPosition? AcceptedWirePosition,
+ RuntimePositionPlacementFacts PlacementFacts);
+
+internal readonly record struct RuntimeAcceptedPositionRouteRequest(
+ RuntimeAuthoritativePositionAuthority Authority,
+ RuntimePositionEntityKind EntityKind,
+ RuntimeAcceptedPositionSource Source,
+ CreateObject.ServerPosition AcceptedWirePosition,
+ uint? PlacementFrame,
+ Vector3? PositionPackVelocity,
+ uint? CommittedCellId,
+ bool HasContact,
+ float PlayerDistance,
+ bool UsePositionFromServer,
+ bool HasAnimations,
+ RuntimePositionPlacementFacts PlacementFacts);
+
+internal readonly record struct RuntimeLeaveWorldRouteRequest(
+ RuntimeAuthoritativePositionAuthority Authority,
+ RuntimePositionEntityKind EntityKind,
+ RuntimeLeaveWorldCause Cause,
+ RuntimePositionPlacementFacts PlacementFacts);
+
+///
+/// Immutable action plan for retail HandleReceivedPosition/MoveOrTeleport.
+/// It deliberately contains no renderer, world entity, UI, or host callback.
+/// Graphical and headless hosts will consume the same plan during Group 2.
+///
+internal readonly record struct RuntimeAuthoritativePositionRoute(
+ RuntimeAuthoritativePositionAuthority Authority,
+ RuntimeAuthoritativePositionDisposition Disposition,
+ RuntimeSetPositionOperationKind OperationKind,
+ PhysicsSetPositionFlags SetPositionFlags,
+ uint PlacementFrame,
+ bool UnparentBeforeRouting,
+ bool ApplyPlacementFrameBeforeRouting,
+ bool LeaveWorld,
+ RuntimeTeleportHookPhase TeleportHookPhase,
+ bool StopInterpolating,
+ bool ConstrainAfterRouting,
+ bool PreserveHeading,
+ bool ZeroVelocity,
+ bool SendPositionImmediately,
+ bool CollisionBatchEligible)
+{
+ internal bool Accepted => Disposition is not
+ RuntimeAuthoritativePositionDisposition.RejectedAuthority
+ and not RuntimeAuthoritativePositionDisposition.RejectedData;
+
+ internal bool PerformsSetPosition => Disposition is
+ RuntimeAuthoritativePositionDisposition.SetPosition
+ or RuntimeAuthoritativePositionDisposition.SetPositionSimple;
+
+ internal bool RunsTeleportHook =>
+ TeleportHookPhase is not RuntimeTeleportHookPhase.None;
+}
+
+///
+/// Pure retail route classifier for new CreateObject residence,
+/// SmartBox::HandleReceivedPosition (0x00453FD0), and
+/// CPhysicsObj::MoveOrTeleport (0x00516330).
+///
+internal static class RuntimeAuthoritativePositionRouteClassifier
+{
+ private const float MaxPhysicsDistance = 96f;
+ private const PhysicsSetPositionFlags InitialCreateFlags =
+ PhysicsSetPositionFlags.Placement | PhysicsSetPositionFlags.Slide;
+ private const PhysicsSetPositionFlags AuthoritativeTeleportFlags =
+ PhysicsSetPositionFlags.Teleport
+ | PhysicsSetPositionFlags.Slide
+ | PhysicsSetPositionFlags.SendPositionEvent;
+
+ internal static RuntimeAuthoritativePositionRoute ClassifyCreate(
+ in RuntimeCreatePositionRouteRequest request)
+ {
+ if (!ValidCreateAuthority(request.Authority)
+ || !ValidEntityKind(request.EntityKind)
+ || request.Residence is RuntimeCreateResidenceKind.Unknown)
+ {
+ return RejectedAuthority(request.Authority);
+ }
+
+ RuntimeSetPositionOperationKind operation = OperationKind(
+ request.EntityKind,
+ initialCreate: true);
+ bool reporting = request.PlacementFacts.CollisionBatchEligible;
+ if (request.Residence is RuntimeCreateResidenceKind.Parented
+ or RuntimeCreateResidenceKind.PickedUp)
+ {
+ return new RuntimeAuthoritativePositionRoute(
+ request.Authority,
+ RuntimeAuthoritativePositionDisposition.AwaitFreshPosition,
+ operation,
+ PhysicsSetPositionFlags.None,
+ 0u,
+ UnparentBeforeRouting: false,
+ ApplyPlacementFrameBeforeRouting: false,
+ LeaveWorld: true,
+ TeleportHookPhase: RuntimeTeleportHookPhase.None,
+ StopInterpolating: false,
+ ConstrainAfterRouting: false,
+ PreserveHeading: false,
+ ZeroVelocity: false,
+ SendPositionImmediately: false,
+ reporting);
+ }
+
+ if (request.AcceptedWirePosition is not { } position
+ || !ValidPosition(position))
+ {
+ return RejectedData(request.Authority, operation, reporting);
+ }
+
+ // New CreateObject enters through CPhysicsObj::enter_world(Position*)
+ // -> enter_world(1), which uses 0x11 for every top-level object.
+ // Static state and an authored-shapeless Setup do not suppress the
+ // SetPosition transaction; Core supplies retail's placement dummy
+ // sphere when required.
+ return new RuntimeAuthoritativePositionRoute(
+ request.Authority,
+ RuntimeAuthoritativePositionDisposition.SetPosition,
+ operation,
+ InitialCreateFlags,
+ 0u,
+ UnparentBeforeRouting: false,
+ ApplyPlacementFrameBeforeRouting: false,
+ LeaveWorld: false,
+ TeleportHookPhase: request.EntityKind
+ is RuntimePositionEntityKind.LocalPlayer
+ ? RuntimeTeleportHookPhase.AfterEnterWorld
+ : RuntimeTeleportHookPhase.None,
+ StopInterpolating: false,
+ ConstrainAfterRouting: false,
+ PreserveHeading: false,
+ ZeroVelocity: false,
+ SendPositionImmediately: false,
+ reporting);
+ }
+
+ internal static RuntimeAuthoritativePositionRoute ClassifyAcceptedPosition(
+ in RuntimeAcceptedPositionRouteRequest request)
+ {
+ RuntimeSetPositionOperationKind operation = OperationKind(
+ request.EntityKind,
+ initialCreate: false);
+ bool reporting = request.PlacementFacts.CollisionBatchEligible;
+ if (!ValidAcceptedAuthority(request.Authority, request.EntityKind)
+ || request.Source is RuntimeAcceptedPositionSource.Unknown
+ || !ValidEntityKind(request.EntityKind))
+ {
+ return RejectedAuthority(request.Authority, operation, reporting);
+ }
+ if (!ValidPosition(request.AcceptedWirePosition))
+ return RejectedData(request.Authority, operation, reporting);
+
+ bool force = request.Authority.TimestampDisposition
+ is PositionTimestampDisposition.ForcePosition;
+ if (force)
+ {
+ // The FORCE_POSITION branch precedes unset_parent and
+ // SetPlacementFrame in HandleReceivedPosition.
+ return new RuntimeAuthoritativePositionRoute(
+ request.Authority,
+ RuntimeAuthoritativePositionDisposition.SetPositionSimple,
+ operation,
+ AuthoritativeTeleportFlags,
+ request.PlacementFrame ?? 0u,
+ UnparentBeforeRouting: false,
+ ApplyPlacementFrameBeforeRouting: false,
+ LeaveWorld: false,
+ TeleportHookPhase: RuntimeTeleportHookPhase.None,
+ StopInterpolating: false,
+ ConstrainAfterRouting: false,
+ PreserveHeading: true,
+ ZeroVelocity: false,
+ SendPositionImmediately: true,
+ reporting);
+ }
+
+ uint placement = request.PlacementFrame ?? 0u;
+ if (request.EntityKind is RuntimePositionEntityKind.LocalPlayer)
+ {
+ if (request.Authority.TeleportAdvanced)
+ {
+ return new RuntimeAuthoritativePositionRoute(
+ request.Authority,
+ RuntimeAuthoritativePositionDisposition.SetPosition,
+ operation,
+ AuthoritativeTeleportFlags,
+ placement,
+ UnparentBeforeRouting: true,
+ ApplyPlacementFrameBeforeRouting: !request.HasAnimations,
+ LeaveWorld: false,
+ TeleportHookPhase: RuntimeTeleportHookPhase.AfterPositionOperation,
+ StopInterpolating: false,
+ ConstrainAfterRouting: true,
+ PreserveHeading: false,
+ ZeroVelocity: true,
+ SendPositionImmediately: false,
+ reporting);
+ }
+
+ bool interpolate = request.UsePositionFromServer
+ && request.HasContact;
+ return new RuntimeAuthoritativePositionRoute(
+ request.Authority,
+ interpolate
+ ? RuntimeAuthoritativePositionDisposition.Interpolate
+ : RuntimeAuthoritativePositionDisposition.NoPositionOperation,
+ operation,
+ PhysicsSetPositionFlags.None,
+ placement,
+ UnparentBeforeRouting: true,
+ ApplyPlacementFrameBeforeRouting: !request.HasAnimations,
+ LeaveWorld: false,
+ TeleportHookPhase: RuntimeTeleportHookPhase.None,
+ StopInterpolating: false,
+ ConstrainAfterRouting: true,
+ PreserveHeading: false,
+ ZeroVelocity: false,
+ SendPositionImmediately: false,
+ reporting);
+ }
+
+ // Accepted wire position is intentionally distinct from committed
+ // residence. A target frame with a nonzero cell does not make a
+ // cellless canonical body resident; only a later Runtime SetPosition
+ // or simulation commit may change FullCellId.
+ bool cellless = !request.CommittedCellId.HasValue
+ || request.CommittedCellId.Value == 0u;
+ if (request.Authority.TeleportAdvanced || cellless)
+ {
+ return new RuntimeAuthoritativePositionRoute(
+ request.Authority,
+ RuntimeAuthoritativePositionDisposition.SetPosition,
+ operation,
+ AuthoritativeTeleportFlags,
+ placement,
+ UnparentBeforeRouting: true,
+ ApplyPlacementFrameBeforeRouting: !request.HasAnimations,
+ LeaveWorld: false,
+ TeleportHookPhase: RuntimeTeleportHookPhase.BeforePositionOperation,
+ StopInterpolating: false,
+ ConstrainAfterRouting: true,
+ PreserveHeading: false,
+ ZeroVelocity: false,
+ SendPositionImmediately: false,
+ reporting);
+ }
+
+ // Same-incarnation CreateObject passes arg5=true directly to
+ // HandleReceivedPosition; it never consults the body's current
+ // contact bit for MoveOrTeleport classification.
+ bool effectiveContact = request.Source
+ is RuntimeAcceptedPositionSource.SameIncarnationCreate
+ || request.HasContact;
+ if (!effectiveContact)
+ {
+ return new RuntimeAuthoritativePositionRoute(
+ request.Authority,
+ RuntimeAuthoritativePositionDisposition.NoPositionOperation,
+ operation,
+ PhysicsSetPositionFlags.None,
+ placement,
+ UnparentBeforeRouting: true,
+ ApplyPlacementFrameBeforeRouting: !request.HasAnimations,
+ LeaveWorld: false,
+ TeleportHookPhase: RuntimeTeleportHookPhase.None,
+ StopInterpolating: false,
+ ConstrainAfterRouting: false,
+ PreserveHeading: false,
+ ZeroVelocity: false,
+ SendPositionImmediately: false,
+ reporting);
+ }
+
+ // Retail has no explicit NaN policy here. Reject a nonfinite derived
+ // distance at the modern authority boundary rather than recreating an
+ // x87 unordered-compare accident as gameplay behavior.
+ if (!float.IsFinite(request.PlayerDistance)
+ || request.PlayerDistance < 0f)
+ {
+ return RejectedData(request.Authority, operation, reporting);
+ }
+
+ bool nearby = request.PlayerDistance < MaxPhysicsDistance;
+ return new RuntimeAuthoritativePositionRoute(
+ request.Authority,
+ nearby
+ ? RuntimeAuthoritativePositionDisposition.Interpolate
+ : RuntimeAuthoritativePositionDisposition.SetPositionSimple,
+ operation,
+ nearby ? PhysicsSetPositionFlags.None : AuthoritativeTeleportFlags,
+ placement,
+ UnparentBeforeRouting: true,
+ ApplyPlacementFrameBeforeRouting: !request.HasAnimations,
+ LeaveWorld: false,
+ TeleportHookPhase: RuntimeTeleportHookPhase.None,
+ StopInterpolating: !nearby,
+ ConstrainAfterRouting: true,
+ PreserveHeading: false,
+ ZeroVelocity: false,
+ SendPositionImmediately: false,
+ reporting);
+ }
+
+ internal static RuntimeAuthoritativePositionRoute ClassifyLeaveWorld(
+ in RuntimeLeaveWorldRouteRequest request)
+ {
+ RuntimeSetPositionOperationKind operation = OperationKind(
+ request.EntityKind,
+ initialCreate: false);
+ bool reporting = request.PlacementFacts.CollisionBatchEligible;
+ if (!ValidCreateAuthority(request.Authority)
+ || !ValidEntityKind(request.EntityKind)
+ || request.Cause is RuntimeLeaveWorldCause.Unknown)
+ {
+ return RejectedAuthority(request.Authority, operation, reporting);
+ }
+
+ return new RuntimeAuthoritativePositionRoute(
+ request.Authority,
+ RuntimeAuthoritativePositionDisposition.AwaitFreshPosition,
+ operation,
+ PhysicsSetPositionFlags.None,
+ 0u,
+ UnparentBeforeRouting: false,
+ ApplyPlacementFrameBeforeRouting: false,
+ LeaveWorld: true,
+ TeleportHookPhase: RuntimeTeleportHookPhase.None,
+ StopInterpolating: false,
+ ConstrainAfterRouting: false,
+ PreserveHeading: false,
+ ZeroVelocity: false,
+ SendPositionImmediately: false,
+ reporting);
+ }
+
+ private static bool ValidCreateAuthority(
+ in RuntimeAuthoritativePositionAuthority authority) =>
+ authority.IsStructurallyValid
+ && authority.TimestampDisposition is PositionTimestampDisposition.Apply
+ && authority.PreviousTeleportSequence == authority.AcceptedTeleportSequence;
+
+ private static bool ValidAcceptedAuthority(
+ in RuntimeAuthoritativePositionAuthority authority,
+ RuntimePositionEntityKind kind)
+ {
+ if (!authority.IsStructurallyValid || authority.TeleportRegressed)
+ return false;
+ return authority.TimestampDisposition switch
+ {
+ PositionTimestampDisposition.Apply => true,
+ PositionTimestampDisposition.ForcePosition =>
+ kind is RuntimePositionEntityKind.LocalPlayer
+ && authority.PreviousTeleportSequence
+ == authority.AcceptedTeleportSequence,
+ _ => false,
+ };
+ }
+
+ private static bool ValidEntityKind(RuntimePositionEntityKind kind) =>
+ kind is RuntimePositionEntityKind.LocalPlayer
+ or RuntimePositionEntityKind.Remote
+ or RuntimePositionEntityKind.Projectile;
+
+ private static bool ValidPosition(in CreateObject.ServerPosition position)
+ {
+ var origin = new Vector3(
+ position.PositionX,
+ position.PositionY,
+ position.PositionZ);
+ var orientation = new Quaternion(
+ position.RotationX,
+ position.RotationY,
+ position.RotationZ,
+ position.RotationW);
+ return float.IsFinite(origin.X)
+ && float.IsFinite(origin.Y)
+ && float.IsFinite(origin.Z)
+ && float.IsFinite(orientation.X)
+ && float.IsFinite(orientation.Y)
+ && float.IsFinite(orientation.Z)
+ && float.IsFinite(orientation.W)
+ && PositionFrameValidation.IsValid(
+ position.LandblockId,
+ origin,
+ orientation);
+ }
+
+ private static RuntimeSetPositionOperationKind OperationKind(
+ RuntimePositionEntityKind kind,
+ bool initialCreate) => kind switch
+ {
+ RuntimePositionEntityKind.LocalPlayer when initialCreate =>
+ RuntimeSetPositionOperationKind.InitialLogin,
+ RuntimePositionEntityKind.LocalPlayer =>
+ RuntimeSetPositionOperationKind.LocalAuthoritative,
+ RuntimePositionEntityKind.Projectile =>
+ RuntimeSetPositionOperationKind.ProjectileAuthoritative,
+ _ => RuntimeSetPositionOperationKind.RemoteAuthoritative,
+ };
+
+ private static RuntimeAuthoritativePositionRoute RejectedAuthority(
+ in RuntimeAuthoritativePositionAuthority authority,
+ RuntimeSetPositionOperationKind operation =
+ RuntimeSetPositionOperationKind.RemoteAuthoritative,
+ bool reporting = false) => new(
+ authority,
+ RuntimeAuthoritativePositionDisposition.RejectedAuthority,
+ operation,
+ PhysicsSetPositionFlags.None,
+ 0u,
+ UnparentBeforeRouting: false,
+ ApplyPlacementFrameBeforeRouting: false,
+ LeaveWorld: false,
+ TeleportHookPhase: RuntimeTeleportHookPhase.None,
+ StopInterpolating: false,
+ ConstrainAfterRouting: false,
+ PreserveHeading: false,
+ ZeroVelocity: false,
+ SendPositionImmediately: false,
+ reporting);
+
+ private static RuntimeAuthoritativePositionRoute RejectedData(
+ in RuntimeAuthoritativePositionAuthority authority,
+ RuntimeSetPositionOperationKind operation,
+ bool reporting) => new(
+ authority,
+ RuntimeAuthoritativePositionDisposition.RejectedData,
+ operation,
+ PhysicsSetPositionFlags.None,
+ 0u,
+ UnparentBeforeRouting: false,
+ ApplyPlacementFrameBeforeRouting: false,
+ LeaveWorld: false,
+ TeleportHookPhase: RuntimeTeleportHookPhase.None,
+ StopInterpolating: false,
+ ConstrainAfterRouting: false,
+ PreserveHeading: false,
+ ZeroVelocity: false,
+ SendPositionImmediately: false,
+ reporting);
+}
diff --git a/tests/AcDream.Runtime.Tests/Physics/RuntimeAuthoritativePositionRouteClassifierTests.cs b/tests/AcDream.Runtime.Tests/Physics/RuntimeAuthoritativePositionRouteClassifierTests.cs
new file mode 100644
index 00000000..d895998e
--- /dev/null
+++ b/tests/AcDream.Runtime.Tests/Physics/RuntimeAuthoritativePositionRouteClassifierTests.cs
@@ -0,0 +1,553 @@
+using System.Numerics;
+using AcDream.Core.Net.Messages;
+using AcDream.Core.Physics;
+using AcDream.Runtime.Entities;
+using AcDream.Runtime.Physics;
+
+namespace AcDream.Runtime.Tests.Physics;
+
+public sealed class RuntimeAuthoritativePositionRouteClassifierTests
+{
+ [Theory]
+ [InlineData(RuntimePositionEntityKind.LocalPlayer,
+ RuntimeSetPositionOperationKind.InitialLogin,
+ RuntimeTeleportHookPhase.AfterEnterWorld)]
+ [InlineData(RuntimePositionEntityKind.Remote,
+ RuntimeSetPositionOperationKind.RemoteAuthoritative,
+ RuntimeTeleportHookPhase.None)]
+ [InlineData(RuntimePositionEntityKind.Projectile,
+ RuntimeSetPositionOperationKind.ProjectileAuthoritative,
+ RuntimeTeleportHookPhase.None)]
+ internal void TopLevelCreate_AlwaysUsesRetailEnterWorldFlags(
+ RuntimePositionEntityKind kind,
+ RuntimeSetPositionOperationKind expectedKind,
+ RuntimeTeleportHookPhase expectedHookPhase)
+ {
+ RuntimeAuthoritativePositionRoute route =
+ RuntimeAuthoritativePositionRouteClassifier.ClassifyCreate(
+ new RuntimeCreatePositionRouteRequest(
+ Authority(),
+ kind,
+ RuntimeCreateResidenceKind.TopLevel,
+ Position(),
+ new RuntimePositionPlacementFacts(
+ PhysicsStateFlags.Static
+ | PhysicsStateFlags.Hidden
+ | PhysicsStateFlags.NoDraw,
+ HasAuthoredMoverShape: false)));
+
+ Assert.Equal(RuntimeAuthoritativePositionDisposition.SetPosition,
+ route.Disposition);
+ Assert.Equal(expectedKind, route.OperationKind);
+ Assert.Equal(0x11u, (uint)route.SetPositionFlags);
+ Assert.Equal(expectedHookPhase, route.TeleportHookPhase);
+ Assert.True(route.PerformsSetPosition);
+ Assert.False(route.CollisionBatchEligible);
+ }
+
+ [Theory]
+ [InlineData(RuntimeCreateResidenceKind.Parented)]
+ [InlineData(RuntimeCreateResidenceKind.PickedUp)]
+ internal void NewParentedOrPickedUpCreate_LeavesWorldAndAwaitsPosition(
+ RuntimeCreateResidenceKind residence)
+ {
+ RuntimeAuthoritativePositionRoute route =
+ RuntimeAuthoritativePositionRouteClassifier.ClassifyCreate(
+ new RuntimeCreatePositionRouteRequest(
+ Authority(),
+ RuntimePositionEntityKind.Remote,
+ residence,
+ AcceptedWirePosition: null,
+ default));
+
+ Assert.Equal(RuntimeAuthoritativePositionDisposition.AwaitFreshPosition,
+ route.Disposition);
+ Assert.True(route.LeaveWorld);
+ Assert.False(route.PerformsSetPosition);
+ }
+
+ [Theory]
+ [MemberData(nameof(InvalidPositions))]
+ internal void CreateAndPositionRejectMalformedAuthoritativeFrames(
+ CreateObject.ServerPosition invalid)
+ {
+ RuntimeAuthoritativePositionRoute create =
+ RuntimeAuthoritativePositionRouteClassifier.ClassifyCreate(
+ new RuntimeCreatePositionRouteRequest(
+ Authority(),
+ RuntimePositionEntityKind.Remote,
+ RuntimeCreateResidenceKind.TopLevel,
+ invalid,
+ default));
+ RuntimeAuthoritativePositionRoute update = ClassifyRemote(
+ position: invalid,
+ committedCellId: Cell,
+ hasContact: true,
+ playerDistance: 1f);
+
+ Assert.Equal(RuntimeAuthoritativePositionDisposition.RejectedData,
+ create.Disposition);
+ Assert.Equal(RuntimeAuthoritativePositionDisposition.RejectedData,
+ update.Disposition);
+ }
+
+ [Theory]
+ [InlineData(RuntimeAcceptedPositionSource.PositionEvent)]
+ [InlineData(RuntimeAcceptedPositionSource.SameIncarnationCreate)]
+ internal void AcceptedPosition_UnparentsAndAppliesDefaultZeroPlacementBeforeRoute(
+ RuntimeAcceptedPositionSource source)
+ {
+ RuntimeAuthoritativePositionRoute route = ClassifyRemote(
+ source: source,
+ placementFrame: null,
+ committedCellId: Cell,
+ hasContact: true,
+ playerDistance: 10f);
+
+ Assert.Equal(RuntimeAuthoritativePositionDisposition.Interpolate,
+ route.Disposition);
+ Assert.True(route.UnparentBeforeRouting);
+ Assert.True(route.ApplyPlacementFrameBeforeRouting);
+ Assert.Equal(0u, route.PlacementFrame);
+ }
+
+ [Fact]
+ public void AnimatedAcceptedPosition_UnparentsButSkipsPlacementFrameReset()
+ {
+ RuntimeAuthoritativePositionRoute route = ClassifyRemote(
+ committedCellId: Cell,
+ hasContact: true,
+ playerDistance: 10f,
+ hasAnimations: true);
+
+ Assert.Equal(RuntimeAuthoritativePositionDisposition.Interpolate,
+ route.Disposition);
+ Assert.True(route.UnparentBeforeRouting);
+ Assert.False(route.ApplyPlacementFrameBeforeRouting);
+ }
+
+ [Theory]
+ [InlineData(95.999f, RuntimeAuthoritativePositionDisposition.Interpolate)]
+ [InlineData(96f, RuntimeAuthoritativePositionDisposition.SetPositionSimple)]
+ internal void SameIncarnationCreate_ForcesContactRoute(
+ float distance,
+ RuntimeAuthoritativePositionDisposition expected)
+ {
+ RuntimeAuthoritativePositionRoute route = ClassifyRemote(
+ source: RuntimeAcceptedPositionSource.SameIncarnationCreate,
+ committedCellId: Cell,
+ hasContact: false,
+ playerDistance: distance);
+
+ Assert.Equal(expected, route.Disposition);
+ Assert.True(route.ConstrainAfterRouting);
+ }
+
+ [Theory]
+ [InlineData(ushort.MaxValue, 0)]
+ [InlineData(0x8000, 0)]
+ internal void RemoteFreshTeleport_UsesWrapSafeTimestampAndRetailHook(
+ ushort previousTeleport,
+ ushort acceptedTeleport)
+ {
+ RuntimeAuthoritativePositionRoute route = ClassifyRemote(
+ authority: Authority(previousTeleport, acceptedTeleport),
+ committedCellId: Cell,
+ hasContact: false,
+ playerDistance: float.NaN);
+
+ Assert.Equal(RuntimeAuthoritativePositionDisposition.SetPosition,
+ route.Disposition);
+ Assert.Equal(0x1012u, (uint)route.SetPositionFlags);
+ Assert.Equal(RuntimeTeleportHookPhase.BeforePositionOperation,
+ route.TeleportHookPhase);
+ Assert.True(route.ConstrainAfterRouting);
+ }
+
+ [Fact]
+ public void RemoteCellless_UsesCommittedResidenceNotAcceptedWireCell()
+ {
+ CreateObject.ServerPosition accepted = Position(Cell, 200f);
+
+ RuntimeAuthoritativePositionRoute route = ClassifyRemote(
+ position: accepted,
+ committedCellId: null,
+ hasContact: true,
+ playerDistance: 1f);
+
+ Assert.Equal(RuntimeAuthoritativePositionDisposition.SetPosition,
+ route.Disposition);
+ Assert.Equal(RuntimeTeleportHookPhase.BeforePositionOperation,
+ route.TeleportHookPhase);
+ Assert.Equal(Cell, accepted.LandblockId);
+ }
+
+ [Fact]
+ public void RemoteNoContact_PerformsNoPositionOperationOrConstraint()
+ {
+ RuntimeAuthoritativePositionRoute route = ClassifyRemote(
+ committedCellId: Cell,
+ hasContact: false,
+ playerDistance: float.NaN);
+
+ Assert.Equal(RuntimeAuthoritativePositionDisposition.NoPositionOperation,
+ route.Disposition);
+ Assert.False(route.ConstrainAfterRouting);
+ Assert.False(route.PerformsSetPosition);
+ }
+
+ [Theory]
+ [InlineData(0f, RuntimeAuthoritativePositionDisposition.Interpolate, false)]
+ [InlineData(95.999f, RuntimeAuthoritativePositionDisposition.Interpolate, false)]
+ [InlineData(96f, RuntimeAuthoritativePositionDisposition.SetPositionSimple, true)]
+ [InlineData(500f, RuntimeAuthoritativePositionDisposition.SetPositionSimple, true)]
+ internal void RemoteContact_UsesExactNinetySixBoundary(
+ float distance,
+ RuntimeAuthoritativePositionDisposition expected,
+ bool stopInterpolating)
+ {
+ RuntimeAuthoritativePositionRoute route = ClassifyRemote(
+ committedCellId: Cell,
+ hasContact: true,
+ playerDistance: distance);
+
+ Assert.Equal(expected, route.Disposition);
+ Assert.Equal(stopInterpolating, route.StopInterpolating);
+ Assert.True(route.ConstrainAfterRouting);
+ Assert.Equal(stopInterpolating ? 0x1012u : 0u,
+ (uint)route.SetPositionFlags);
+ }
+
+ [Theory]
+ [InlineData(float.NaN)]
+ [InlineData(float.PositiveInfinity)]
+ [InlineData(float.NegativeInfinity)]
+ [InlineData(-0.001f)]
+ internal void RemoteContact_RejectsMalformedDerivedDistance(float distance)
+ {
+ RuntimeAuthoritativePositionRoute route = ClassifyRemote(
+ committedCellId: Cell,
+ hasContact: true,
+ playerDistance: distance);
+
+ Assert.Equal(RuntimeAuthoritativePositionDisposition.RejectedData,
+ route.Disposition);
+ }
+
+ [Fact]
+ public void RemotePositionPackVelocity_IsDeadRoutingInput()
+ {
+ RuntimeAcceptedPositionRouteRequest request = RemoteRequest(
+ committedCellId: Cell,
+ hasContact: true,
+ playerDistance: 10f) with
+ {
+ PositionPackVelocity = new Vector3(float.NaN, float.PositiveInfinity, 7f),
+ };
+
+ RuntimeAuthoritativePositionRoute route =
+ RuntimeAuthoritativePositionRouteClassifier
+ .ClassifyAcceptedPosition(request);
+
+ Assert.Equal(RuntimeAuthoritativePositionDisposition.Interpolate,
+ route.Disposition);
+ Assert.True(route.Accepted);
+ }
+
+ [Fact]
+ public void LocalFreshForce_PreservesHeadingBlipsAndAcknowledgesImmediately()
+ {
+ RuntimeAuthoritativePositionRoute route = ClassifyLocal(
+ Authority(
+ previousTeleport: 10,
+ acceptedTeleport: 10,
+ disposition: PositionTimestampDisposition.ForcePosition));
+
+ Assert.Equal(RuntimeAuthoritativePositionDisposition.SetPositionSimple,
+ route.Disposition);
+ Assert.Equal(0x1012u, (uint)route.SetPositionFlags);
+ Assert.True(route.PreserveHeading);
+ Assert.True(route.SendPositionImmediately);
+ Assert.False(route.UnparentBeforeRouting);
+ Assert.False(route.ApplyPlacementFrameBeforeRouting);
+ Assert.False(route.ConstrainAfterRouting);
+ }
+
+ [Fact]
+ public void ForcePosition_IsRejectedForRemoteOrUnequalTeleport()
+ {
+ RuntimeAuthoritativePositionAuthority force = Authority(
+ 10, 10, PositionTimestampDisposition.ForcePosition);
+ RuntimeAuthoritativePositionRoute remote = ClassifyRemote(authority: force);
+ RuntimeAuthoritativePositionRoute unequal = ClassifyLocal(
+ Authority(10, 11, PositionTimestampDisposition.ForcePosition));
+
+ Assert.Equal(RuntimeAuthoritativePositionDisposition.RejectedAuthority,
+ remote.Disposition);
+ Assert.Equal(RuntimeAuthoritativePositionDisposition.RejectedAuthority,
+ unequal.Disposition);
+ }
+
+ [Fact]
+ public void LocalFreshTeleport_SetsPositionConstrainsAndZerosVelocity()
+ {
+ RuntimeAuthoritativePositionRoute route = ClassifyLocal(
+ Authority(ushort.MaxValue, 0));
+
+ Assert.Equal(RuntimeAuthoritativePositionDisposition.SetPosition,
+ route.Disposition);
+ Assert.Equal(0x1012u, (uint)route.SetPositionFlags);
+ Assert.True(route.UnparentBeforeRouting);
+ Assert.True(route.ApplyPlacementFrameBeforeRouting);
+ Assert.True(route.ConstrainAfterRouting);
+ Assert.True(route.ZeroVelocity);
+ Assert.Equal(RuntimeTeleportHookPhase.AfterPositionOperation,
+ route.TeleportHookPhase);
+ }
+
+ [Theory]
+ [InlineData(false, false, false, RuntimeAuthoritativePositionDisposition.NoPositionOperation)]
+ [InlineData(false, true, true, RuntimeAuthoritativePositionDisposition.NoPositionOperation)]
+ [InlineData(true, false, true, RuntimeAuthoritativePositionDisposition.NoPositionOperation)]
+ [InlineData(true, true, false, RuntimeAuthoritativePositionDisposition.Interpolate)]
+ [InlineData(true, true, true, RuntimeAuthoritativePositionDisposition.Interpolate)]
+ internal void LocalOrdinary_AlwaysConstrainsAndInterpolatesOnlyByOptionAndContact(
+ bool usePositionFromServer,
+ bool hasContact,
+ bool hasVelocity,
+ RuntimeAuthoritativePositionDisposition expected)
+ {
+ RuntimeAuthoritativePositionRoute route = ClassifyLocal(
+ Authority(),
+ usePositionFromServer,
+ hasContact,
+ hasVelocity
+ ? new Vector3(float.NaN, float.PositiveInfinity, 1f)
+ : null);
+
+ Assert.Equal(expected, route.Disposition);
+ Assert.True(route.ConstrainAfterRouting);
+ }
+
+ [Theory]
+ [InlineData(RuntimeLeaveWorldCause.Pickup)]
+ [InlineData(RuntimeLeaveWorldCause.Parent)]
+ internal void PickupAndParent_LeaveWorldAndAwaitLaterFreshPosition(
+ RuntimeLeaveWorldCause cause)
+ {
+ RuntimeAuthoritativePositionRoute route =
+ RuntimeAuthoritativePositionRouteClassifier.ClassifyLeaveWorld(
+ new RuntimeLeaveWorldRouteRequest(
+ Authority(),
+ RuntimePositionEntityKind.Projectile,
+ cause,
+ default));
+
+ Assert.Equal(RuntimeAuthoritativePositionDisposition.AwaitFreshPosition,
+ route.Disposition);
+ Assert.Equal(RuntimeSetPositionOperationKind.ProjectileAuthoritative,
+ route.OperationKind);
+ Assert.True(route.LeaveWorld);
+ Assert.False(route.PerformsSetPosition);
+ }
+
+ [Fact]
+ public void HiddenAndNoDrawDoNotGatePlacement_ButReportingRemainsSeparate()
+ {
+ RuntimePositionPlacementFacts visible = new(
+ PhysicsStateFlags.None,
+ HasAuthoredMoverShape: true);
+ RuntimePositionPlacementFacts noDraw = visible with
+ {
+ PhysicsState = PhysicsStateFlags.NoDraw,
+ };
+ RuntimePositionPlacementFacts hidden = visible with
+ {
+ PhysicsState = PhysicsStateFlags.Hidden,
+ };
+ RuntimePositionPlacementFacts ignored = visible with
+ {
+ PhysicsState = PhysicsStateFlags.IgnoreCollisions,
+ };
+
+ RuntimeAuthoritativePositionRoute visibleRoute =
+ RuntimeAuthoritativePositionRouteClassifier.ClassifyCreate(
+ new RuntimeCreatePositionRouteRequest(
+ Authority(),
+ RuntimePositionEntityKind.Remote,
+ RuntimeCreateResidenceKind.TopLevel,
+ Position(),
+ visible));
+ RuntimeAuthoritativePositionRoute noDrawRoute =
+ RuntimeAuthoritativePositionRouteClassifier.ClassifyCreate(
+ new RuntimeCreatePositionRouteRequest(
+ Authority(),
+ RuntimePositionEntityKind.Remote,
+ RuntimeCreateResidenceKind.TopLevel,
+ Position(),
+ noDraw));
+ RuntimeAuthoritativePositionRoute hiddenRoute =
+ RuntimeAuthoritativePositionRouteClassifier.ClassifyCreate(
+ new RuntimeCreatePositionRouteRequest(
+ Authority(),
+ RuntimePositionEntityKind.Remote,
+ RuntimeCreateResidenceKind.TopLevel,
+ Position(),
+ hidden));
+ RuntimeAuthoritativePositionRoute ignoredRoute =
+ RuntimeAuthoritativePositionRouteClassifier.ClassifyCreate(
+ new RuntimeCreatePositionRouteRequest(
+ Authority(),
+ RuntimePositionEntityKind.Remote,
+ RuntimeCreateResidenceKind.TopLevel,
+ Position(),
+ ignored));
+
+ Assert.All([visibleRoute, noDrawRoute, hiddenRoute, ignoredRoute], route =>
+ Assert.Equal(RuntimeAuthoritativePositionDisposition.SetPosition,
+ route.Disposition));
+ Assert.True(visibleRoute.CollisionBatchEligible);
+ Assert.True(noDrawRoute.CollisionBatchEligible);
+ Assert.False(hiddenRoute.CollisionBatchEligible);
+ Assert.True(ignoredRoute.CollisionBatchEligible);
+ }
+
+ [Fact]
+ public void ProjectilePosition_UsesRemoteMoveOrTeleportClassification()
+ {
+ RuntimeAcceptedPositionRouteRequest request = RemoteRequest(
+ committedCellId: Cell,
+ hasContact: true,
+ playerDistance: 96f) with
+ {
+ EntityKind = RuntimePositionEntityKind.Projectile,
+ };
+
+ RuntimeAuthoritativePositionRoute route =
+ RuntimeAuthoritativePositionRouteClassifier
+ .ClassifyAcceptedPosition(request);
+
+ Assert.Equal(RuntimeSetPositionOperationKind.ProjectileAuthoritative,
+ route.OperationKind);
+ Assert.Equal(RuntimeAuthoritativePositionDisposition.SetPositionSimple,
+ route.Disposition);
+ }
+
+ [Fact]
+ public void StaleOrMalformedAuthorityIsRejectedWithoutRouting()
+ {
+ RuntimeAuthoritativePositionRoute stale = ClassifyRemote(
+ authority: Authority(previousTeleport: 0, acceptedTeleport: ushort.MaxValue));
+ RuntimeAuthoritativePositionRoute wrongGeneration = ClassifyRemote(
+ authority: Authority() with { Generation = default });
+ RuntimeAuthoritativePositionRoute wrongEntity = ClassifyRemote(
+ authority: Authority() with { Entity = default });
+ RuntimeAuthoritativePositionRoute wrongVersion = ClassifyRemote(
+ authority: Authority() with { PositionAuthorityVersion = 0UL });
+
+ Assert.All([stale, wrongGeneration, wrongEntity, wrongVersion], route =>
+ Assert.Equal(RuntimeAuthoritativePositionDisposition.RejectedAuthority,
+ route.Disposition));
+ }
+
+ public static TheoryData InvalidPositions => new()
+ {
+ Position(cell: 0u),
+ Position(x: float.NaN),
+ Position(x: float.PositiveInfinity),
+ Position(rotationW: 0f),
+ Position(rotationW: float.NaN),
+ };
+
+ private const uint Cell = 0x0101FFFFu;
+
+ private static RuntimeAuthoritativePositionAuthority Authority(
+ ushort previousTeleport = 10,
+ ushort acceptedTeleport = 10,
+ PositionTimestampDisposition disposition = PositionTimestampDisposition.Apply) =>
+ new(
+ new RuntimeGenerationToken(7),
+ new RuntimeEntityKey(0x70000001u, 3),
+ PositionAuthorityVersion: 11UL,
+ AcceptedPositionSequence: 20,
+ previousTeleport,
+ acceptedTeleport,
+ disposition);
+
+ private static RuntimeAuthoritativePositionRoute ClassifyRemote(
+ RuntimeAuthoritativePositionAuthority? authority = null,
+ RuntimeAcceptedPositionSource source = RuntimeAcceptedPositionSource.PositionEvent,
+ CreateObject.ServerPosition? position = null,
+ uint? placementFrame = 0u,
+ uint? committedCellId = Cell,
+ bool hasContact = true,
+ float playerDistance = 10f,
+ bool hasAnimations = false) =>
+ RuntimeAuthoritativePositionRouteClassifier.ClassifyAcceptedPosition(
+ RemoteRequest(
+ authority,
+ source,
+ position,
+ placementFrame,
+ committedCellId,
+ hasContact,
+ playerDistance,
+ hasAnimations));
+
+ private static RuntimeAcceptedPositionRouteRequest RemoteRequest(
+ RuntimeAuthoritativePositionAuthority? authority = null,
+ RuntimeAcceptedPositionSource source = RuntimeAcceptedPositionSource.PositionEvent,
+ CreateObject.ServerPosition? position = null,
+ uint? placementFrame = 0u,
+ uint? committedCellId = Cell,
+ bool hasContact = true,
+ float playerDistance = 10f,
+ bool hasAnimations = false) =>
+ new(
+ authority ?? Authority(),
+ RuntimePositionEntityKind.Remote,
+ source,
+ position ?? Position(),
+ placementFrame,
+ PositionPackVelocity: Vector3.One,
+ committedCellId,
+ hasContact,
+ playerDistance,
+ UsePositionFromServer: false,
+ hasAnimations,
+ default);
+
+ private static RuntimeAuthoritativePositionRoute ClassifyLocal(
+ RuntimeAuthoritativePositionAuthority authority,
+ bool usePositionFromServer = false,
+ bool hasContact = true,
+ Vector3? velocity = null) =>
+ RuntimeAuthoritativePositionRouteClassifier.ClassifyAcceptedPosition(
+ new RuntimeAcceptedPositionRouteRequest(
+ authority,
+ RuntimePositionEntityKind.LocalPlayer,
+ RuntimeAcceptedPositionSource.PositionEvent,
+ Position(),
+ PlacementFrame: null,
+ PositionPackVelocity: velocity,
+ CommittedCellId: Cell,
+ hasContact,
+ PlayerDistance: 0f,
+ usePositionFromServer,
+ HasAnimations: false,
+ default));
+
+ private static CreateObject.ServerPosition Position(
+ uint cell = Cell,
+ float x = 10f,
+ float rotationW = 1f) =>
+ new(
+ cell,
+ x,
+ 20f,
+ 30f,
+ rotationW,
+ 0f,
+ 0f,
+ 0f);
+}