553 lines
21 KiB
C#
553 lines
21 KiB
C#
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_RemainsCelllessAndAwaitsPosition(
|
|
RuntimeCreateResidenceKind residence)
|
|
{
|
|
RuntimeAuthoritativePositionRoute route =
|
|
RuntimeAuthoritativePositionRouteClassifier.ClassifyCreate(
|
|
new RuntimeCreatePositionRouteRequest(
|
|
Authority(),
|
|
RuntimePositionEntityKind.Remote,
|
|
residence,
|
|
AcceptedWirePosition: null,
|
|
default));
|
|
|
|
Assert.Equal(RuntimeAuthoritativePositionDisposition.AwaitFreshPosition,
|
|
route.Disposition);
|
|
Assert.False(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.SetPositionSimple,
|
|
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<CreateObject.ServerPosition> 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);
|
|
}
|