1792 lines
71 KiB
C#
1792 lines
71 KiB
C#
using System.Collections.Immutable;
|
|
using System.Numerics;
|
|
using AcDream.Core.Net;
|
|
using AcDream.Core.Net.Messages;
|
|
using AcDream.Core.Physics;
|
|
using AcDream.Runtime.Entities;
|
|
using AcDream.Runtime.Physics;
|
|
|
|
namespace AcDream.Runtime.Tests.Entities;
|
|
|
|
public sealed class RuntimeInitialCreateResidenceStateTests
|
|
{
|
|
private const uint Landblock = 0xA9B40000u;
|
|
private const uint Cell = Landblock | 0x0001u;
|
|
|
|
[Theory]
|
|
[InlineData(true, false,
|
|
RuntimeSetPositionOperationKind.InitialLogin,
|
|
RuntimeTeleportHookPhase.AfterEnterWorld)]
|
|
[InlineData(false, false,
|
|
RuntimeSetPositionOperationKind.RemoteAuthoritative,
|
|
RuntimeTeleportHookPhase.None)]
|
|
[InlineData(false, true,
|
|
RuntimeSetPositionOperationKind.ProjectileAuthoritative,
|
|
RuntimeTeleportHookPhase.None)]
|
|
internal void TopLevelRegistrationOwnsExactCelllessLeaseBeforeObservers(
|
|
bool isLocalPlayer,
|
|
bool missile,
|
|
RuntimeSetPositionOperationKind expectedOperation,
|
|
RuntimeTeleportHookPhase expectedHook)
|
|
{
|
|
using var lifetime = new RuntimeEntityObjectLifetime();
|
|
Bind(lifetime, 7UL);
|
|
RuntimeInitialCreateResidenceLease observed = default;
|
|
uint observedCell = uint.MaxValue;
|
|
using IDisposable subscription = lifetime.Events.Subscribe(
|
|
new EntityObserver(delta =>
|
|
{
|
|
if (delta.Change is not RuntimeEntityChange.Registered)
|
|
return;
|
|
Assert.True(lifetime.Entities.TryGetByLocalId(
|
|
delta.Entity.Identity.LocalEntityId,
|
|
out RuntimeEntityRecord record));
|
|
observedCell = record.FullCellId;
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
record,
|
|
out observed));
|
|
}));
|
|
|
|
RuntimeEntityRegistrationResult registration = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(0x70003001u, 1, missile: missile),
|
|
isLocalPlayer);
|
|
RuntimeEntityRecord canonical = registration.Canonical!;
|
|
|
|
Assert.Equal(0u, observedCell);
|
|
Assert.Equal(0u, canonical.FullCellId);
|
|
Assert.Equal(Cell, canonical.Snapshot.Position!.Value.LandblockId);
|
|
Assert.True(observed.IsValid);
|
|
Assert.Equal(observed.Token.Entity, observed.Route.Authority.Entity);
|
|
Assert.Equal(expectedOperation, observed.Route.OperationKind);
|
|
Assert.Equal(expectedHook, observed.Route.TeleportHookPhase);
|
|
Assert.Equal(0x11u, (uint)observed.Route.SetPositionFlags);
|
|
Assert.True(observed.Placement.IsValid);
|
|
Assert.Equal(1, lifetime.CaptureOwnership()
|
|
.InitialCreateResidenceLeaseCount);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(true)]
|
|
[InlineData(false)]
|
|
public void FreshParentedAndPickedCreateRemainCelllessWithoutWithdrawal(
|
|
bool parented)
|
|
{
|
|
using var lifetime = new RuntimeEntityObjectLifetime();
|
|
Bind(lifetime, 8UL);
|
|
RuntimeEntityRecord canonical = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(
|
|
0x70003002u,
|
|
1,
|
|
includePosition: false,
|
|
parentGuid: parented ? 0x70004000u : null),
|
|
isLocalPlayer: false)
|
|
.Canonical!;
|
|
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease lease));
|
|
Assert.Equal(0u, canonical.FullCellId);
|
|
Assert.False(lease.Placement.IsValid);
|
|
Assert.Equal(RuntimeAuthoritativePositionDisposition.AwaitFreshPosition,
|
|
lease.Route.Disposition);
|
|
Assert.False(lease.Route.LeaveWorld);
|
|
Assert.Equal(
|
|
RuntimeInitialCreateResidenceCompletionStatus.Completed,
|
|
lifetime.CompleteInitialCreateResidence(
|
|
canonical,
|
|
lease.Token,
|
|
out RuntimeInitialCreateResidenceReceipt receipt));
|
|
Assert.Equal(0u, receipt.FullCellId);
|
|
Assert.Equal(RuntimeTeleportHookPhase.None,
|
|
receipt.TeleportHookPhase);
|
|
Assert.True(lifetime.AcknowledgeInitialCreateResidenceAdoption(
|
|
canonical,
|
|
receipt.Adoption));
|
|
Assert.Equal(0, lifetime.CaptureOwnership()
|
|
.InitialCreateResidenceLeaseCount);
|
|
Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership()
|
|
.ActiveOperationCount);
|
|
}
|
|
|
|
[Fact]
|
|
public void MissingCellDefersExactLeaseWithoutCommittingWireResidence()
|
|
{
|
|
using var lifetime = new RuntimeEntityObjectLifetime();
|
|
Bind(lifetime, 9UL);
|
|
RuntimeEntityRecord canonical = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(0x70003003u, 1, setupId: null),
|
|
isLocalPlayer: false)
|
|
.Canonical!;
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease lease));
|
|
AttachDormantBody(lifetime, canonical);
|
|
RuntimeSetPositionCommand command = Prepare(
|
|
lifetime,
|
|
lease,
|
|
RuntimeSetPositionMoverSetup.ResolvedAbsent);
|
|
|
|
RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition
|
|
.SubmitPreparedPlacement(lease.Placement, command);
|
|
|
|
Assert.Equal(RuntimeSetPositionStatus.DeferredCell, outcome.Status);
|
|
Assert.Equal(0u, canonical.FullCellId);
|
|
Assert.Equal(Cell, canonical.Snapshot.Position!.Value.LandblockId);
|
|
Assert.Equal(
|
|
RuntimeInitialCreateResidenceCompletionStatus.PendingPlacement,
|
|
lifetime.CompleteInitialCreateResidence(
|
|
canonical,
|
|
lease.Token,
|
|
out _));
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease retained));
|
|
Assert.Equal(lease, retained);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(true, RuntimeTeleportHookPhase.AfterEnterWorld)]
|
|
[InlineData(false, RuntimeTeleportHookPhase.None)]
|
|
internal void CommittedAndAcknowledgedCreateYieldsExactEnterWorldReceipt(
|
|
bool isLocalPlayer,
|
|
RuntimeTeleportHookPhase expectedHook)
|
|
{
|
|
var engine = new PhysicsEngine { DataCache = new PhysicsDataCache() };
|
|
engine.AddLandblock(
|
|
Landblock,
|
|
new TerrainSurface(new byte[81], new float[256]),
|
|
Array.Empty<CellSurface>(),
|
|
Array.Empty<PortalPlane>(),
|
|
worldOffsetX: 0f,
|
|
worldOffsetY: 0f);
|
|
using var lifetime = new RuntimeEntityObjectLifetime(engine);
|
|
Bind(lifetime, 10UL);
|
|
RuntimeEntityRecord canonical = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(0x70003004u, 1, setupId: null),
|
|
isLocalPlayer)
|
|
.Canonical!;
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease lease));
|
|
AttachDormantBody(lifetime, canonical);
|
|
RuntimeSetPositionCommand command = Prepare(
|
|
lifetime,
|
|
lease,
|
|
RuntimeSetPositionMoverSetup.ResolvedAbsent);
|
|
|
|
RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition
|
|
.SubmitPreparedPlacement(lease.Placement, command);
|
|
|
|
Assert.Equal(
|
|
RuntimeSetPositionStatus.CommittedHostAcknowledgementPending,
|
|
outcome.Status);
|
|
Assert.Equal(Cell, canonical.FullCellId);
|
|
Assert.Equal(
|
|
RuntimeInitialCreateResidenceCompletionStatus.PendingPlacement,
|
|
lifetime.CompleteInitialCreateResidence(
|
|
canonical,
|
|
lease.Token,
|
|
out _));
|
|
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
|
|
outcome.Projection));
|
|
Assert.Equal(
|
|
RuntimeInitialCreateResidenceCompletionStatus.Completed,
|
|
lifetime.CompleteInitialCreateResidence(
|
|
canonical,
|
|
lease.Token,
|
|
out RuntimeInitialCreateResidenceReceipt receipt));
|
|
Assert.Equal(expectedHook, receipt.TeleportHookPhase);
|
|
Assert.Equal(Cell, receipt.FullCellId);
|
|
Assert.Equal(canonical.PlacementCommitVersion,
|
|
receipt.PlacementCommitVersion);
|
|
Assert.True(receipt.Adoption.IsValid);
|
|
Assert.Empty(receipt.Continuations);
|
|
Assert.Equal(
|
|
RuntimeInitialCreateResidenceCompletionStatus.Completed,
|
|
lifetime.CompleteInitialCreateResidence(
|
|
canonical,
|
|
lease.Token,
|
|
out RuntimeInitialCreateResidenceReceipt repeated));
|
|
Assert.Equal(receipt, repeated);
|
|
Assert.False(lifetime.Physics.SetPosition
|
|
.TryBeginExclusiveAuthoredPlacement(
|
|
canonical,
|
|
canonical.PositionAuthorityVersion,
|
|
RuntimeSetPositionOperationKind.RemoteAuthoritative)
|
|
.IsValid);
|
|
Assert.True(lifetime.AcknowledgeInitialCreateResidenceAdoption(
|
|
canonical,
|
|
receipt.Adoption));
|
|
RuntimeEntityPlacementToken afterAdoption = lifetime.Physics.SetPosition
|
|
.TryBeginExclusiveAuthoredPlacement(
|
|
canonical,
|
|
canonical.PositionAuthorityVersion,
|
|
RuntimeSetPositionOperationKind.RemoteAuthoritative);
|
|
Assert.True(afterAdoption.IsValid);
|
|
_ = lifetime.Physics.SetPosition.ForgetExactPlacement(afterAdoption);
|
|
Assert.False(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out _));
|
|
}
|
|
|
|
[Fact]
|
|
public void GenerationReplacementForgetsOldLeaseAndOwnsOnlySuccessor()
|
|
{
|
|
using var lifetime = new RuntimeEntityObjectLifetime();
|
|
Bind(lifetime, 11UL);
|
|
RuntimeEntityRecord first = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(0x70003005u, 1),
|
|
isLocalPlayer: false)
|
|
.Canonical!;
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
first,
|
|
out RuntimeInitialCreateResidenceLease oldLease));
|
|
|
|
RuntimeEntityRecord second = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(0x70003005u, 2),
|
|
isLocalPlayer: false)
|
|
.Canonical!;
|
|
|
|
Assert.False(lifetime.TryGetInitialCreateResidence(first, out _));
|
|
Assert.Equal(
|
|
RuntimeInitialCreateResidenceCompletionStatus.RejectedToken,
|
|
lifetime.CompleteInitialCreateResidence(
|
|
first,
|
|
oldLease.Token,
|
|
out _));
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
second,
|
|
out RuntimeInitialCreateResidenceLease successor));
|
|
Assert.NotEqual(oldLease.Token, successor.Token);
|
|
Assert.Equal(1, lifetime.CaptureOwnership()
|
|
.InitialCreateResidenceLeaseCount);
|
|
}
|
|
|
|
[Fact]
|
|
public void RegistrationObserverReentryCannotResurrectOuterLease()
|
|
{
|
|
using var lifetime = new RuntimeEntityObjectLifetime();
|
|
Bind(lifetime, 12UL);
|
|
RuntimeEntityRegistrationResult nested = default;
|
|
bool reentered = false;
|
|
using IDisposable subscription = lifetime.Events.Subscribe(
|
|
new EntityObserver(delta =>
|
|
{
|
|
if (reentered
|
|
|| delta.Change is not RuntimeEntityChange.Registered)
|
|
return;
|
|
reentered = true;
|
|
nested = lifetime.RegisterEntityWithInitialResidence(
|
|
Spawn(0x70003006u, 2),
|
|
isLocalPlayer: false);
|
|
}));
|
|
|
|
RuntimeEntityRegistrationResult outer = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(0x70003006u, 1),
|
|
isLocalPlayer: false);
|
|
|
|
Assert.Equal(CreateObjectTimestampDisposition.StaleGeneration,
|
|
outer.Inbound.Disposition);
|
|
RuntimeEntityRecord current = nested.Canonical!;
|
|
Assert.True(lifetime.Entities.IsCurrent(current));
|
|
Assert.Equal((ushort)2, current.Incarnation);
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
current,
|
|
out RuntimeInitialCreateResidenceLease lease));
|
|
Assert.Equal(current.Key, lease.Token.Entity);
|
|
Assert.Equal(1, lifetime.CaptureOwnership()
|
|
.InitialCreateResidenceLeaseCount);
|
|
}
|
|
|
|
[Fact]
|
|
public void DeleteResetAndDisposeConvergeEveryInitialResidenceOwner()
|
|
{
|
|
var lifetime = new RuntimeEntityObjectLifetime();
|
|
Bind(lifetime, 13UL);
|
|
RuntimeEntityRecord deleted = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(0x70003007u, 1),
|
|
isLocalPlayer: false)
|
|
.Canonical!;
|
|
Assert.True(lifetime.TryAcceptDelete(
|
|
new DeleteObject.Parsed(deleted.ServerGuid, deleted.Incarnation),
|
|
isLocalPlayer: false,
|
|
removeRetainedObject: true,
|
|
out RuntimeEntityDeleteAcceptance acceptance));
|
|
Assert.Equal(0, lifetime.CaptureOwnership()
|
|
.InitialCreateResidenceLeaseCount);
|
|
lifetime.CompleteAcceptedDelete(acceptance);
|
|
Assert.Null(lifetime.RetireCanonicalOnly(deleted));
|
|
|
|
_ = lifetime.RegisterEntityWithInitialResidence(
|
|
Spawn(0x70003008u, 1),
|
|
isLocalPlayer: false);
|
|
IReadOnlyList<RuntimeEntityRecord> retirements =
|
|
lifetime.BeginSessionClear();
|
|
Assert.Equal(0, lifetime.CaptureOwnership()
|
|
.InitialCreateResidenceLeaseCount);
|
|
foreach (RuntimeEntityRecord record in retirements)
|
|
lifetime.CompleteSessionEntityRetirement(record);
|
|
Assert.True(lifetime.CompleteSessionClearIfConverged());
|
|
|
|
lifetime.Dispose();
|
|
Assert.True(lifetime.CaptureOwnership().IsConverged);
|
|
}
|
|
|
|
[Fact]
|
|
public void ResetCancelsCompletedUnadoptedContinuationBatch()
|
|
{
|
|
var engine = new PhysicsEngine { DataCache = new PhysicsDataCache() };
|
|
engine.AddLandblock(
|
|
Landblock,
|
|
new TerrainSurface(new byte[81], new float[256]),
|
|
Array.Empty<CellSurface>(),
|
|
Array.Empty<PortalPlane>(),
|
|
worldOffsetX: 0f,
|
|
worldOffsetY: 0f);
|
|
using var lifetime = new RuntimeEntityObjectLifetime(engine);
|
|
Bind(lifetime, 32UL);
|
|
RuntimeEntityRecord canonical = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(0x7000301Eu, 1, setupId: null),
|
|
isLocalPlayer: true)
|
|
.Canonical!;
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease lease));
|
|
AttachDormantBody(lifetime, canonical);
|
|
RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition
|
|
.SubmitPreparedPlacement(
|
|
lease.Placement,
|
|
Prepare(
|
|
lifetime,
|
|
lease,
|
|
RuntimeSetPositionMoverSetup.ResolvedAbsent));
|
|
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
|
|
outcome.Projection));
|
|
Assert.Equal(
|
|
RuntimeInitialCreateResidenceCompletionStatus.Completed,
|
|
lifetime.CompleteInitialCreateResidence(
|
|
canonical,
|
|
lease.Token,
|
|
out RuntimeInitialCreateResidenceReceipt receipt));
|
|
Assert.Equal(1, lifetime.CaptureOwnership()
|
|
.InitialCreateResidenceLeaseCount);
|
|
|
|
IReadOnlyList<RuntimeEntityRecord> retirements =
|
|
lifetime.BeginSessionClear();
|
|
|
|
Assert.False(lifetime.AcknowledgeInitialCreateResidenceAdoption(
|
|
canonical,
|
|
receipt.Adoption));
|
|
Assert.Equal(0, lifetime.CaptureOwnership()
|
|
.InitialCreateResidenceLeaseCount);
|
|
RuntimeSetPositionOwnershipSnapshot ownership =
|
|
lifetime.Physics.SetPosition.CaptureOwnership();
|
|
Assert.Equal(0, ownership.ActiveOperationCount);
|
|
Assert.Equal(0, ownership.PlacementCompletionWatchCount);
|
|
Assert.Equal(0, ownership.AcknowledgedPlacementCompletionCount);
|
|
foreach (RuntimeEntityRecord record in retirements)
|
|
lifetime.CompleteSessionEntityRetirement(record);
|
|
Assert.True(lifetime.CompleteSessionClearIfConverged());
|
|
}
|
|
|
|
[Fact]
|
|
public void PostCompletionRebucketRejectsTopLevelAdoptionAndConverges()
|
|
{
|
|
var engine = new PhysicsEngine { DataCache = new PhysicsDataCache() };
|
|
engine.AddLandblock(
|
|
Landblock,
|
|
new TerrainSurface(new byte[81], new float[256]),
|
|
Array.Empty<CellSurface>(),
|
|
Array.Empty<PortalPlane>(),
|
|
worldOffsetX: 0f,
|
|
worldOffsetY: 0f);
|
|
using var lifetime = new RuntimeEntityObjectLifetime(engine);
|
|
Bind(lifetime, 33UL);
|
|
RuntimeEntityRecord canonical = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(0x7000301Fu, 1, setupId: null),
|
|
isLocalPlayer: false)
|
|
.Canonical!;
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease lease));
|
|
AttachDormantBody(lifetime, canonical);
|
|
RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition
|
|
.SubmitPreparedPlacement(
|
|
lease.Placement,
|
|
Prepare(
|
|
lifetime,
|
|
lease,
|
|
RuntimeSetPositionMoverSetup.ResolvedAbsent));
|
|
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
|
|
outcome.Projection));
|
|
Assert.Equal(
|
|
RuntimeInitialCreateResidenceCompletionStatus.Completed,
|
|
lifetime.CompleteInitialCreateResidence(
|
|
canonical,
|
|
lease.Token,
|
|
out RuntimeInitialCreateResidenceReceipt receipt));
|
|
|
|
lifetime.Entities.SetFullCell(
|
|
canonical,
|
|
Landblock | 0x0002u,
|
|
Landblock);
|
|
|
|
Assert.False(lifetime.AcknowledgeInitialCreateResidenceAdoption(
|
|
canonical,
|
|
receipt.Adoption));
|
|
Assert.Equal(0, lifetime.CaptureOwnership()
|
|
.InitialCreateResidenceLeaseCount);
|
|
RuntimeSetPositionOwnershipSnapshot ownership =
|
|
lifetime.Physics.SetPosition.CaptureOwnership();
|
|
Assert.Equal(0, ownership.ActiveOperationCount);
|
|
Assert.Equal(0, ownership.PlacementCompletionWatchCount);
|
|
Assert.Equal(0, ownership.AcknowledgedPlacementCompletionCount);
|
|
}
|
|
|
|
[Fact]
|
|
public void PostCompletionAuthorityChangeRejectsCelllessAdoption()
|
|
{
|
|
using var lifetime = new RuntimeEntityObjectLifetime();
|
|
Bind(lifetime, 34UL);
|
|
RuntimeEntityRecord canonical = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(
|
|
0x70003020u,
|
|
1,
|
|
includePosition: false,
|
|
parentGuid: 0x70004020u),
|
|
isLocalPlayer: false)
|
|
.Canonical!;
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease lease));
|
|
Assert.Equal(
|
|
RuntimeInitialCreateResidenceCompletionStatus.Completed,
|
|
lifetime.CompleteInitialCreateResidence(
|
|
canonical,
|
|
lease.Token,
|
|
out RuntimeInitialCreateResidenceReceipt receipt));
|
|
|
|
lifetime.Entities.AdvancePositionAuthority(canonical);
|
|
|
|
Assert.False(lifetime.AcknowledgeInitialCreateResidenceAdoption(
|
|
canonical,
|
|
receipt.Adoption));
|
|
Assert.Equal(0, lifetime.CaptureOwnership()
|
|
.InitialCreateResidenceLeaseCount);
|
|
Assert.True(lifetime.Physics.SetPosition.CaptureOwnership()
|
|
.IsConverged);
|
|
}
|
|
|
|
[Fact]
|
|
public void CelllessPendingAdoptionRevisionsForFreshPositionWithoutLeak()
|
|
{
|
|
using var lifetime = new RuntimeEntityObjectLifetime();
|
|
Bind(lifetime, 36UL);
|
|
const uint guid = 0x70003024u;
|
|
RuntimeEntityRecord canonical = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(
|
|
guid,
|
|
1,
|
|
includePosition: false,
|
|
parentGuid: 0x70004024u),
|
|
isLocalPlayer: false)
|
|
.Canonical!;
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease lease));
|
|
ulong positionAuthority = canonical.PositionAuthorityVersion;
|
|
Assert.Equal(
|
|
RuntimeInitialCreateResidenceCompletionStatus.Completed,
|
|
lifetime.CompleteInitialCreateResidence(
|
|
canonical,
|
|
lease.Token,
|
|
out RuntimeInitialCreateResidenceReceipt original));
|
|
int callbacks = 0;
|
|
WorldSession.EntityPositionUpdate update = PositionUpdate(
|
|
guid,
|
|
positionSequence: 2,
|
|
teleportSequence: 1,
|
|
forcePositionSequence: 0,
|
|
positionX: 25f);
|
|
|
|
Assert.True(lifetime.TryApplyPosition(
|
|
update,
|
|
isLocalPlayer: false,
|
|
forcePositionRotation: null,
|
|
currentLocalVelocity: null,
|
|
projectionRequiresTeleportHook: false,
|
|
acknowledgeProjection: _ => callbacks++,
|
|
out PositionTimestampDisposition disposition,
|
|
out _,
|
|
out _));
|
|
|
|
Assert.Equal(PositionTimestampDisposition.Apply, disposition);
|
|
Assert.Equal(0, callbacks);
|
|
Assert.Equal(positionAuthority, canonical.PositionAuthorityVersion);
|
|
Assert.Null(canonical.Snapshot.Position);
|
|
Assert.Equal(0u, canonical.FullCellId);
|
|
Assert.Equal(
|
|
RuntimeInitialCreateResidenceCompletionStatus.Completed,
|
|
lifetime.CompleteInitialCreateResidence(
|
|
canonical,
|
|
lease.Token,
|
|
out RuntimeInitialCreateResidenceReceipt revised));
|
|
Assert.Equal(original.Adoption.Revision + 1UL,
|
|
revised.Adoption.Revision);
|
|
RuntimeInitialCreateResidenceContinuation continuation =
|
|
Assert.Single(revised.Continuations);
|
|
Assert.Equal(25f, continuation.AcceptedWirePosition.PositionX);
|
|
Assert.Equal((ushort)1, continuation.AcceptedTeleportSequence);
|
|
Assert.False(lifetime.AcknowledgeInitialCreateResidenceAdoption(
|
|
canonical,
|
|
original.Adoption));
|
|
Assert.True(lifetime.AcknowledgeInitialCreateResidenceAdoption(
|
|
canonical,
|
|
revised.Adoption));
|
|
Assert.Equal(0, lifetime.CaptureOwnership()
|
|
.InitialCreateResidenceLeaseCount);
|
|
Assert.True(lifetime.Physics.SetPosition.CaptureOwnership()
|
|
.IsConverged);
|
|
}
|
|
|
|
[Fact]
|
|
public void ResetSnapshotsAllResidenceOwnersBeforeReentrantDiscardObserver()
|
|
{
|
|
var engine = new PhysicsEngine { DataCache = new PhysicsDataCache() };
|
|
engine.AddLandblock(
|
|
Landblock,
|
|
new TerrainSurface(new byte[81], new float[256]),
|
|
Array.Empty<CellSurface>(),
|
|
Array.Empty<PortalPlane>(),
|
|
worldOffsetX: 0f,
|
|
worldOffsetY: 0f);
|
|
using var lifetime = new RuntimeEntityObjectLifetime(engine);
|
|
Bind(lifetime, 35UL);
|
|
RuntimeEntityRecord completedRecord = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(
|
|
0x70003023u,
|
|
1,
|
|
setupId: null,
|
|
positionX: 70f),
|
|
isLocalPlayer: false)
|
|
.Canonical!;
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
completedRecord,
|
|
out RuntimeInitialCreateResidenceLease completedLease));
|
|
AttachDormantBody(lifetime, completedRecord);
|
|
RuntimeSetPositionOutcome completedOutcome = lifetime.Physics.SetPosition
|
|
.SubmitPreparedPlacement(
|
|
completedLease.Placement,
|
|
Prepare(
|
|
lifetime,
|
|
completedLease,
|
|
RuntimeSetPositionMoverSetup.ResolvedAbsent));
|
|
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
|
|
completedOutcome.Projection));
|
|
Assert.Equal(
|
|
RuntimeInitialCreateResidenceCompletionStatus.Completed,
|
|
lifetime.CompleteInitialCreateResidence(
|
|
completedRecord,
|
|
completedLease.Token,
|
|
out _));
|
|
|
|
var pending = new List<RuntimeSetPositionOutcome>();
|
|
uint[] pendingGuids = [0x70003021u, 0x70003022u];
|
|
for (int index = 0; index < pendingGuids.Length; index++)
|
|
{
|
|
uint guid = pendingGuids[index];
|
|
RuntimeEntityRecord record = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(
|
|
guid,
|
|
1,
|
|
setupId: null,
|
|
positionX: 10f + index * 30f),
|
|
isLocalPlayer: false)
|
|
.Canonical!;
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
record,
|
|
out RuntimeInitialCreateResidenceLease lease));
|
|
AttachDormantBody(lifetime, record);
|
|
pending.Add(lifetime.Physics.SetPosition.SubmitPreparedPlacement(
|
|
lease.Placement,
|
|
Prepare(
|
|
lifetime,
|
|
lease,
|
|
RuntimeSetPositionMoverSetup.ResolvedAbsent)));
|
|
}
|
|
|
|
var discards = new List<ulong>();
|
|
int nestedRetirementCount = -1;
|
|
using IDisposable subscription = lifetime.Events.SubscribePlacement(
|
|
new PlacementObserver(delta =>
|
|
{
|
|
if (delta.Placement.Kind
|
|
is not RuntimePlacementProjectionKind.Discard)
|
|
{
|
|
return;
|
|
}
|
|
discards.Add(delta.Placement.Token.Sequence);
|
|
if (nestedRetirementCount < 0)
|
|
{
|
|
nestedRetirementCount =
|
|
lifetime.BeginSessionClear().Count;
|
|
}
|
|
}));
|
|
|
|
IReadOnlyList<RuntimeEntityRecord> retirements =
|
|
lifetime.BeginSessionClear();
|
|
|
|
Assert.Equal(0, nestedRetirementCount);
|
|
Assert.Equal(
|
|
pending.Select(static item => item.Projection.Sequence)
|
|
.Order(),
|
|
discards.Order());
|
|
Assert.Equal(0, lifetime.Events.DispatchFailureCount);
|
|
Assert.Equal(0, lifetime.CaptureOwnership()
|
|
.InitialCreateResidenceLeaseCount);
|
|
foreach (RuntimeEntityRecord record in retirements)
|
|
lifetime.CompleteSessionEntityRetirement(record);
|
|
Assert.True(lifetime.CompleteSessionClearIfConverged());
|
|
}
|
|
|
|
[Fact]
|
|
public void InvalidGenerationFailsBeforeInboundOrCanonicalAcceptance()
|
|
{
|
|
using var lifetime = new RuntimeEntityObjectLifetime();
|
|
ulong generation = 0UL;
|
|
lifetime.BindEventContext(
|
|
() => new RuntimeGenerationToken(generation),
|
|
static () => 1UL);
|
|
WorldSession.EntitySpawn spawn = Spawn(0x70003009u, 1);
|
|
|
|
Assert.Throws<InvalidOperationException>(() => lifetime
|
|
.RegisterEntityWithInitialResidence(spawn, isLocalPlayer: false));
|
|
Assert.Equal(0, lifetime.Entities.Count);
|
|
Assert.Equal(0, lifetime.Entities.ClaimedLocalIdCount);
|
|
Assert.Empty(lifetime.Entities.Snapshots);
|
|
Assert.Equal(0, lifetime.CaptureOwnership()
|
|
.InitialCreateResidenceLeaseCount);
|
|
Assert.True(lifetime.Physics.SetPosition.CaptureOwnership()
|
|
.IsConverged);
|
|
|
|
generation = 14UL;
|
|
RuntimeEntityRegistrationResult recovered = lifetime
|
|
.RegisterEntityWithInitialResidence(spawn, isLocalPlayer: false);
|
|
Assert.Equal(CreateObjectTimestampDisposition.InitialGeneration,
|
|
recovered.Inbound.Disposition);
|
|
Assert.NotNull(recovered.Canonical);
|
|
}
|
|
|
|
[Fact]
|
|
public void UnboundGenerationFailsWithoutFabricatingSessionAuthority()
|
|
{
|
|
using var lifetime = new RuntimeEntityObjectLifetime();
|
|
|
|
Assert.Throws<InvalidOperationException>(() => lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(0x70003014u, 1),
|
|
isLocalPlayer: false));
|
|
|
|
Assert.Empty(lifetime.Entities.Snapshots);
|
|
Assert.Equal(0, lifetime.Entities.Count);
|
|
Assert.Equal(0, lifetime.Entities.ClaimedLocalIdCount);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData((ushort)1)]
|
|
[InlineData((ushort)2)]
|
|
public void EqualOrStaleMalformedCreateDoesNotDisplaceCurrentAdmission(
|
|
ushort currentIncarnation)
|
|
{
|
|
using var lifetime = new RuntimeEntityObjectLifetime();
|
|
Bind(lifetime, 24UL);
|
|
RuntimeEntityRecord canonical = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(0x70003015u, currentIncarnation),
|
|
isLocalPlayer: false)
|
|
.Canonical!;
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease initial));
|
|
ushort incomingIncarnation = 1;
|
|
|
|
RuntimeEntityRegistrationResult result = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(
|
|
0x70003015u,
|
|
incomingIncarnation,
|
|
positionX: float.NaN),
|
|
isLocalPlayer: false);
|
|
|
|
Assert.Equal(
|
|
currentIncarnation == incomingIncarnation
|
|
? CreateObjectTimestampDisposition.ExistingGeneration
|
|
: CreateObjectTimestampDisposition.StaleGeneration,
|
|
result.Inbound.Disposition);
|
|
Assert.True(lifetime.Entities.IsCurrent(canonical));
|
|
Assert.Equal(10f, canonical.Snapshot.Position!.Value.PositionX);
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease retained));
|
|
Assert.Equal(initial, retained);
|
|
}
|
|
|
|
[Fact]
|
|
public void MalformedInitialPositionDoesNotPoisonSameInstanceRecovery()
|
|
{
|
|
using var lifetime = new RuntimeEntityObjectLifetime();
|
|
Bind(lifetime, 15UL);
|
|
WorldSession.EntitySpawn malformed = Spawn(
|
|
0x7000300Au,
|
|
1,
|
|
positionX: float.NaN);
|
|
|
|
Assert.Throws<InvalidOperationException>(() => lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
malformed,
|
|
isLocalPlayer: false));
|
|
Assert.Empty(lifetime.Entities.Snapshots);
|
|
Assert.Equal(0, lifetime.Entities.Count);
|
|
|
|
WorldSession.EntitySpawn corrected = Spawn(0x7000300Au, 1);
|
|
RuntimeEntityRegistrationResult recovered = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
corrected,
|
|
isLocalPlayer: false);
|
|
Assert.Equal(CreateObjectTimestampDisposition.InitialGeneration,
|
|
recovered.Inbound.Disposition);
|
|
Assert.Equal(corrected.Position,
|
|
recovered.Canonical!.Snapshot.Position);
|
|
Assert.Equal(0u, recovered.Canonical.FullCellId);
|
|
}
|
|
|
|
[Fact]
|
|
public void ExistingPlacementCollisionCannotStealOrPartiallyOwnLease()
|
|
{
|
|
using var lifetime = new RuntimeEntityObjectLifetime();
|
|
Bind(lifetime, 16UL);
|
|
RuntimeEntityRegistrationResult registration = lifetime.RegisterEntity(
|
|
Spawn(0x7000300Bu, 1));
|
|
RuntimeEntityRecord canonical = registration.Canonical!;
|
|
lifetime.Entities.SetFullCell(canonical, 0u, 0u);
|
|
lifetime.Entities.AdvancePositionAuthority(canonical);
|
|
RuntimeEntityPlacementToken conflict = lifetime.Physics.SetPosition
|
|
.BeginAuthoredPlacement(
|
|
canonical,
|
|
canonical.PositionAuthorityVersion,
|
|
RuntimeSetPositionOperationKind.RemoteAuthoritative);
|
|
Assert.True(conflict.IsValid);
|
|
|
|
RuntimeInitialCreateResidenceLease rejected =
|
|
lifetime.InitialCreateResidences.Begin(
|
|
canonical,
|
|
registration.Inbound,
|
|
isLocalPlayer: false);
|
|
|
|
Assert.False(rejected.IsValid);
|
|
Assert.True(lifetime.Physics.SetPosition.IsPlacementCurrent(conflict));
|
|
Assert.Equal(0, lifetime.CaptureOwnership()
|
|
.InitialCreateResidenceLeaseCount);
|
|
Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership()
|
|
.PlacementCompletionWatchCount);
|
|
}
|
|
|
|
[Fact]
|
|
public void ParentPrecedesCombinedWirePositionAndCreatesNoWorldPlacement()
|
|
{
|
|
using var lifetime = new RuntimeEntityObjectLifetime();
|
|
Bind(lifetime, 17UL);
|
|
RuntimeEntityRecord canonical = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(
|
|
0x7000300Cu,
|
|
1,
|
|
includePosition: true,
|
|
parentGuid: 0x70004001u),
|
|
isLocalPlayer: false)
|
|
.Canonical!;
|
|
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease lease));
|
|
Assert.Equal(RuntimeAuthoritativePositionDisposition.AwaitFreshPosition,
|
|
lease.Route.Disposition);
|
|
Assert.False(lease.Placement.IsValid);
|
|
Assert.Equal(0u, canonical.FullCellId);
|
|
Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership()
|
|
.ActiveOperationCount);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(false)]
|
|
[InlineData(true)]
|
|
public void AbsentAndPresentZeroCellCreateAwaitFreshPosition(
|
|
bool presentZeroCell)
|
|
{
|
|
using var lifetime = new RuntimeEntityObjectLifetime();
|
|
Bind(lifetime, 25UL);
|
|
RuntimeEntityRecord canonical = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(
|
|
presentZeroCell ? 0x70003016u : 0x70003017u,
|
|
1,
|
|
includePosition: presentZeroCell,
|
|
positionCell: 0u),
|
|
isLocalPlayer: false)
|
|
.Canonical!;
|
|
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease lease));
|
|
Assert.Equal(RuntimeAuthoritativePositionDisposition.AwaitFreshPosition,
|
|
lease.Route.Disposition);
|
|
Assert.False(lease.Placement.IsValid);
|
|
Assert.Equal(0u, canonical.FullCellId);
|
|
Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership()
|
|
.ActiveOperationCount);
|
|
}
|
|
|
|
[Fact]
|
|
public void EqualAndNewerSameGenerationCreateDoNotChurnPendingPlacement()
|
|
{
|
|
using var lifetime = new RuntimeEntityObjectLifetime();
|
|
Bind(lifetime, 18UL);
|
|
RuntimeEntityRecord canonical = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(0x7000300Du, 1),
|
|
isLocalPlayer: false)
|
|
.Canonical!;
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease initial));
|
|
ulong positionAuthority = canonical.PositionAuthorityVersion;
|
|
ulong spatialAuthority = canonical.SpatialAuthorityVersion;
|
|
|
|
RuntimeEntityRegistrationResult duplicate = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(0x7000300Du, 1),
|
|
isLocalPlayer: false);
|
|
Assert.Equal(CreateObjectTimestampDisposition.ExistingGeneration,
|
|
duplicate.Inbound.Disposition);
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease afterDuplicate));
|
|
Assert.Equal(initial, afterDuplicate);
|
|
Assert.Equal(positionAuthority, canonical.PositionAuthorityVersion);
|
|
Assert.Equal(spatialAuthority, canonical.SpatialAuthorityVersion);
|
|
|
|
RuntimeEntityRegistrationResult newer = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(
|
|
0x7000300Du,
|
|
1,
|
|
positionSequence: 2,
|
|
positionX: 30f),
|
|
isLocalPlayer: false);
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease beforeFreshRoute));
|
|
Assert.Equal(initial, beforeFreshRoute);
|
|
Assert.Equal(10f, canonical.Snapshot.Position!.Value.PositionX);
|
|
Assert.NotNull(newer.Inbound.SameGenerationEvents);
|
|
|
|
WorldSession.EntityPositionUpdate update =
|
|
newer.Inbound.SameGenerationEvents!.Value.Position!.Value;
|
|
Assert.True(lifetime.TryApplyPosition(
|
|
update,
|
|
isLocalPlayer: false,
|
|
forcePositionRotation: null,
|
|
currentLocalVelocity: null,
|
|
projectionRequiresTeleportHook: false,
|
|
acknowledgeProjection: null,
|
|
out PositionTimestampDisposition disposition,
|
|
out _,
|
|
out _));
|
|
Assert.Equal(PositionTimestampDisposition.Apply, disposition);
|
|
Assert.Equal(0u, canonical.FullCellId);
|
|
Assert.Equal(10f, canonical.Snapshot.Position!.Value.PositionX);
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease successor));
|
|
Assert.Equal(initial.Token, successor.Token);
|
|
Assert.Equal(RuntimeSetPositionOperationKind.RemoteAuthoritative,
|
|
successor.Route.OperationKind);
|
|
RuntimeInitialCreateResidenceContinuation continuation =
|
|
Assert.Single(successor.Continuations);
|
|
Assert.Equal(30f, continuation.AcceptedWirePosition.PositionX);
|
|
Assert.Equal((ushort)2, continuation.PositionSequence);
|
|
Assert.Equal(PositionTimestampDisposition.Apply,
|
|
continuation.TimestampDisposition);
|
|
Assert.True(successor.Placement.IsValid);
|
|
Assert.Equal(1, lifetime.Physics.SetPosition.CaptureOwnership()
|
|
.ActiveOperationCount);
|
|
}
|
|
|
|
[Fact]
|
|
public void AlreadyPlacedSameGenerationCreateCannotReclaimWireCell()
|
|
{
|
|
var engine = new PhysicsEngine { DataCache = new PhysicsDataCache() };
|
|
engine.AddLandblock(
|
|
Landblock,
|
|
new TerrainSurface(new byte[81], new float[256]),
|
|
Array.Empty<CellSurface>(),
|
|
Array.Empty<PortalPlane>(),
|
|
worldOffsetX: 0f,
|
|
worldOffsetY: 0f);
|
|
using var lifetime = new RuntimeEntityObjectLifetime(engine);
|
|
Bind(lifetime, 19UL);
|
|
RuntimeEntityRecord canonical = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(0x7000300Eu, 1, setupId: null),
|
|
isLocalPlayer: false)
|
|
.Canonical!;
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease lease));
|
|
AttachDormantBody(lifetime, canonical);
|
|
RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition
|
|
.SubmitPreparedPlacement(
|
|
lease.Placement,
|
|
Prepare(
|
|
lifetime,
|
|
lease,
|
|
RuntimeSetPositionMoverSetup.ResolvedAbsent));
|
|
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
|
|
outcome.Projection));
|
|
Assert.Equal(
|
|
RuntimeInitialCreateResidenceCompletionStatus.Completed,
|
|
lifetime.CompleteInitialCreateResidence(
|
|
canonical,
|
|
lease.Token,
|
|
out _));
|
|
ulong spatialAuthority = canonical.SpatialAuthorityVersion;
|
|
|
|
_ = lifetime.RegisterEntityWithInitialResidence(
|
|
Spawn(0x7000300Eu, 1, setupId: null),
|
|
isLocalPlayer: false);
|
|
|
|
Assert.Equal(Cell, canonical.FullCellId);
|
|
Assert.Equal(spatialAuthority, canonical.SpatialAuthorityVersion);
|
|
Assert.False(lifetime.TryGetInitialCreateResidence(canonical, out _));
|
|
}
|
|
|
|
[Fact]
|
|
public void LocalNoTeleportFreshPositionRemainsInitialWorldAdmission()
|
|
{
|
|
using var lifetime = new RuntimeEntityObjectLifetime();
|
|
Bind(lifetime, 22UL);
|
|
RuntimeEntityRecord canonical = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(0x70003012u, 1),
|
|
isLocalPlayer: true)
|
|
.Canonical!;
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease initial));
|
|
RuntimeEntityRegistrationResult newer = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(
|
|
0x70003012u,
|
|
1,
|
|
positionSequence: 2,
|
|
positionX: 40f),
|
|
isLocalPlayer: true);
|
|
WorldSession.EntityPositionUpdate update =
|
|
newer.Inbound.SameGenerationEvents!.Value.Position!.Value;
|
|
|
|
Assert.True(lifetime.TryApplyPosition(
|
|
update,
|
|
isLocalPlayer: true,
|
|
forcePositionRotation: null,
|
|
currentLocalVelocity: null,
|
|
projectionRequiresTeleportHook: false,
|
|
acknowledgeProjection: null,
|
|
out PositionTimestampDisposition disposition,
|
|
out _,
|
|
out AcceptedPhysicsTimestamps timestamps));
|
|
|
|
Assert.Equal(PositionTimestampDisposition.Apply, disposition);
|
|
Assert.False(timestamps.TeleportAdvanced);
|
|
Assert.Equal(0u, canonical.FullCellId);
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease successor));
|
|
Assert.Equal(initial.Token, successor.Token);
|
|
Assert.Equal(RuntimeAuthoritativePositionDisposition.SetPosition,
|
|
successor.Route.Disposition);
|
|
Assert.Equal(RuntimeSetPositionOperationKind.InitialLogin,
|
|
successor.Route.OperationKind);
|
|
Assert.Equal(RuntimeTeleportHookPhase.AfterEnterWorld,
|
|
successor.Route.TeleportHookPhase);
|
|
Assert.True(successor.Placement.IsValid);
|
|
RuntimeInitialCreateResidenceContinuation continuation =
|
|
Assert.Single(successor.Continuations);
|
|
Assert.Equal(PositionTimestampDisposition.Apply,
|
|
continuation.TimestampDisposition);
|
|
Assert.Equal((ushort)0, continuation.PreviousTeleportSequence);
|
|
Assert.Equal((ushort)0, continuation.TeleportSequence);
|
|
}
|
|
|
|
[Fact]
|
|
public void LocalForcePositionSuffixIsQueuedBehindInitialAdmission()
|
|
{
|
|
using var lifetime = new RuntimeEntityObjectLifetime();
|
|
Bind(lifetime, 26UL);
|
|
RuntimeInitialCreateResidenceLease successor = ApplyFreshSuccessor(
|
|
lifetime,
|
|
guid: 0x70003018u,
|
|
isLocalPlayer: true,
|
|
teleportSequence: 0,
|
|
forcePositionSequence: 1,
|
|
out PositionTimestampDisposition disposition);
|
|
|
|
Assert.Equal(PositionTimestampDisposition.ForcePosition, disposition);
|
|
Assert.Equal(RuntimeSetPositionOperationKind.InitialLogin,
|
|
successor.Route.OperationKind);
|
|
RuntimeInitialCreateResidenceContinuation continuation =
|
|
Assert.Single(successor.Continuations);
|
|
Assert.Equal(RuntimePositionEntityKind.LocalPlayer,
|
|
continuation.EntityKind);
|
|
Assert.Equal((ushort)1, continuation.ForcePositionSequence);
|
|
Assert.Equal((ushort)0, continuation.PreviousTeleportSequence);
|
|
Assert.Equal((ushort)0, continuation.TeleportSequence);
|
|
Assert.NotNull(continuation.ForcePositionRotation);
|
|
}
|
|
|
|
[Fact]
|
|
public void LocalTeleportSuffixIsQueuedBehindInitialAdmission()
|
|
{
|
|
using var lifetime = new RuntimeEntityObjectLifetime();
|
|
Bind(lifetime, 27UL);
|
|
RuntimeInitialCreateResidenceLease successor = ApplyFreshSuccessor(
|
|
lifetime,
|
|
guid: 0x70003019u,
|
|
isLocalPlayer: true,
|
|
teleportSequence: 1,
|
|
forcePositionSequence: 0,
|
|
out PositionTimestampDisposition disposition);
|
|
|
|
Assert.Equal(PositionTimestampDisposition.Apply, disposition);
|
|
RuntimeInitialCreateResidenceContinuation continuation =
|
|
Assert.Single(successor.Continuations);
|
|
Assert.Equal(RuntimePositionEntityKind.LocalPlayer,
|
|
continuation.EntityKind);
|
|
Assert.Equal((ushort)0, continuation.PreviousTeleportSequence);
|
|
Assert.Equal((ushort)1, continuation.TeleportSequence);
|
|
Assert.Equal(Vector3.Zero, continuation.AcceptedVelocity);
|
|
}
|
|
|
|
[Fact]
|
|
public void RemoteTeleportSuffixIsQueuedBehindInitialAdmission()
|
|
{
|
|
using var lifetime = new RuntimeEntityObjectLifetime();
|
|
Bind(lifetime, 28UL);
|
|
RuntimeInitialCreateResidenceLease successor = ApplyFreshSuccessor(
|
|
lifetime,
|
|
guid: 0x7000301Au,
|
|
isLocalPlayer: false,
|
|
teleportSequence: 1,
|
|
forcePositionSequence: 0,
|
|
out PositionTimestampDisposition disposition);
|
|
|
|
Assert.Equal(PositionTimestampDisposition.Apply, disposition);
|
|
RuntimeInitialCreateResidenceContinuation continuation =
|
|
Assert.Single(successor.Continuations);
|
|
Assert.Equal(RuntimePositionEntityKind.Remote,
|
|
continuation.EntityKind);
|
|
Assert.Equal((ushort)0, continuation.PreviousTeleportSequence);
|
|
Assert.Equal((ushort)1, continuation.TeleportSequence);
|
|
}
|
|
|
|
[Fact]
|
|
public void AcceptedPositionsRemainMonotonicRawFifoWithoutMutatingInitialAdmission()
|
|
{
|
|
using var lifetime = new RuntimeEntityObjectLifetime();
|
|
Bind(lifetime, 29UL);
|
|
const uint guid = 0x7000301Bu;
|
|
RuntimeEntityRecord canonical = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(guid, 1),
|
|
isLocalPlayer: true)
|
|
.Canonical!;
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease initial));
|
|
ulong positionAuthority = canonical.PositionAuthorityVersion;
|
|
int callbacks = 0;
|
|
|
|
ApplyQueuedPosition(
|
|
lifetime,
|
|
guid,
|
|
positionSequence: 2,
|
|
teleportSequence: 0,
|
|
forcePositionSequence: 0,
|
|
positionX: 20f,
|
|
projectionRequiresTeleportHook: false,
|
|
() => callbacks++);
|
|
ApplyQueuedPosition(
|
|
lifetime,
|
|
guid,
|
|
positionSequence: 3,
|
|
teleportSequence: 1,
|
|
forcePositionSequence: 0,
|
|
positionX: 30f,
|
|
projectionRequiresTeleportHook: true,
|
|
() => callbacks++);
|
|
ApplyQueuedPosition(
|
|
lifetime,
|
|
guid,
|
|
positionSequence: 4,
|
|
teleportSequence: 2,
|
|
forcePositionSequence: 0,
|
|
positionX: 40f,
|
|
projectionRequiresTeleportHook: false,
|
|
() => callbacks++);
|
|
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease retained));
|
|
Assert.Equal(initial.Token, retained.Token);
|
|
Assert.Equal(initial.Route, retained.Route);
|
|
Assert.Equal(initial.Placement, retained.Placement);
|
|
Assert.Equal(10f, canonical.Snapshot.Position!.Value.PositionX);
|
|
Assert.Equal(positionAuthority, canonical.PositionAuthorityVersion);
|
|
Assert.Equal(0, callbacks);
|
|
Assert.Equal([1UL, 2UL, 3UL],
|
|
retained.Continuations.Select(static item => item.Sequence));
|
|
Assert.Equal([20f, 30f, 40f],
|
|
retained.Continuations.Select(
|
|
static item => item.AcceptedWirePosition.PositionX));
|
|
Assert.Equal([(ushort)0, (ushort)0, (ushort)1],
|
|
retained.Continuations.Select(
|
|
static item => item.PreviousTeleportSequence));
|
|
Assert.Equal([(ushort)0, (ushort)1, (ushort)2],
|
|
retained.Continuations.Select(
|
|
static item => item.TeleportSequence));
|
|
Assert.False(retained.Continuations[0]
|
|
.ProjectionRequiresTeleportHook);
|
|
Assert.True(retained.Continuations[1]
|
|
.ProjectionRequiresTeleportHook);
|
|
}
|
|
|
|
[Fact]
|
|
public void MalformedQueuedPositionIsRejectedBeforeTimestampConsumption()
|
|
{
|
|
using var lifetime = new RuntimeEntityObjectLifetime();
|
|
Bind(lifetime, 30UL);
|
|
const uint guid = 0x7000301Cu;
|
|
RuntimeEntityRecord canonical = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(guid, 1),
|
|
isLocalPlayer: false)
|
|
.Canonical!;
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease initial));
|
|
WorldSession.EntityPositionUpdate malformed = PositionUpdate(
|
|
guid,
|
|
positionSequence: 2,
|
|
teleportSequence: 0,
|
|
forcePositionSequence: 0,
|
|
positionX: float.NaN);
|
|
|
|
Assert.False(lifetime.TryApplyPosition(
|
|
malformed,
|
|
isLocalPlayer: false,
|
|
forcePositionRotation: null,
|
|
currentLocalVelocity: null,
|
|
projectionRequiresTeleportHook: false,
|
|
acknowledgeProjection: null,
|
|
out PositionTimestampDisposition disposition,
|
|
out _,
|
|
out _));
|
|
|
|
Assert.Equal(PositionTimestampDisposition.Rejected, disposition);
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease retained));
|
|
Assert.Equal(initial, retained);
|
|
Assert.Equal(10f, canonical.Snapshot.Position!.Value.PositionX);
|
|
|
|
ApplyQueuedPosition(
|
|
lifetime,
|
|
guid,
|
|
positionSequence: 2,
|
|
teleportSequence: 0,
|
|
forcePositionSequence: 0,
|
|
positionX: 25f,
|
|
projectionRequiresTeleportHook: false,
|
|
callback: null,
|
|
isLocalPlayer: false);
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out retained));
|
|
Assert.Equal((ushort)2,
|
|
Assert.Single(retained.Continuations).PositionSequence);
|
|
}
|
|
|
|
[Fact]
|
|
public void CompletionRequiresExactAcknowledgedOriginalPlacement()
|
|
{
|
|
var engine = new PhysicsEngine { DataCache = new PhysicsDataCache() };
|
|
engine.AddLandblock(
|
|
Landblock,
|
|
new TerrainSurface(new byte[81], new float[256]),
|
|
Array.Empty<CellSurface>(),
|
|
Array.Empty<PortalPlane>(),
|
|
worldOffsetX: 0f,
|
|
worldOffsetY: 0f);
|
|
using var lifetime = new RuntimeEntityObjectLifetime(engine);
|
|
Bind(lifetime, 20UL);
|
|
RuntimeEntityRecord canonical = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(0x7000300Fu, 1, setupId: null),
|
|
isLocalPlayer: false)
|
|
.Canonical!;
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease original));
|
|
AttachDormantBody(lifetime, canonical);
|
|
|
|
RuntimeEntityPlacementToken replacement = lifetime.Physics.SetPosition
|
|
.BeginAuthoredPlacement(
|
|
canonical,
|
|
canonical.PositionAuthorityVersion,
|
|
RuntimeSetPositionOperationKind.RemoteAuthoritative);
|
|
var preparation = new RuntimeSetPositionMoverPreparation(
|
|
RuntimeSetPositionMoverSetup.ResolvedAbsent,
|
|
RuntimeSetPositionOperationKind.RemoteAuthoritative,
|
|
GameTime: 1d,
|
|
PhysicsPlacementClass.Ordinary,
|
|
original.Route.SetPositionFlags);
|
|
Assert.Equal(
|
|
RuntimeSetPositionMoverPreparationStatus.Prepared,
|
|
lifetime.Physics.SetPosition.PrepareMover(
|
|
replacement,
|
|
preparation,
|
|
out RuntimeSetPositionCommand command));
|
|
RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition
|
|
.SubmitPreparedPlacement(replacement, command);
|
|
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
|
|
outcome.Projection));
|
|
|
|
Assert.Equal(
|
|
RuntimeInitialCreateResidenceCompletionStatus.RejectedAuthority,
|
|
lifetime.CompleteInitialCreateResidence(
|
|
canonical,
|
|
original.Token,
|
|
out _));
|
|
Assert.Equal(0, lifetime.CaptureOwnership()
|
|
.InitialCreateResidenceLeaseCount);
|
|
Assert.Equal(0, lifetime.Physics.SetPosition.CaptureOwnership()
|
|
.AcknowledgedPlacementCompletionCount);
|
|
}
|
|
|
|
[Fact]
|
|
public void FreshPositionQueuesBehindCommittedUnacknowledgedInitialAdmission()
|
|
{
|
|
var engine = new PhysicsEngine { DataCache = new PhysicsDataCache() };
|
|
engine.AddLandblock(
|
|
Landblock,
|
|
new TerrainSurface(new byte[81], new float[256]),
|
|
Array.Empty<CellSurface>(),
|
|
Array.Empty<PortalPlane>(),
|
|
worldOffsetX: 0f,
|
|
worldOffsetY: 0f);
|
|
using var lifetime = new RuntimeEntityObjectLifetime(engine);
|
|
Bind(lifetime, 23UL);
|
|
int deleted = 0;
|
|
using IDisposable subscription = lifetime.Events.Subscribe(
|
|
new EntityObserver(delta =>
|
|
{
|
|
if (delta.Change is RuntimeEntityChange.Deleted)
|
|
deleted++;
|
|
}));
|
|
RuntimeEntityRecord canonical = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(0x70003013u, 1, setupId: null),
|
|
isLocalPlayer: true)
|
|
.Canonical!;
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease initial));
|
|
AttachDormantBody(lifetime, canonical);
|
|
RuntimeSetPositionOutcome first = lifetime.Physics.SetPosition
|
|
.SubmitPreparedPlacement(
|
|
initial.Placement,
|
|
Prepare(
|
|
lifetime,
|
|
initial,
|
|
RuntimeSetPositionMoverSetup.ResolvedAbsent));
|
|
Assert.Equal(
|
|
RuntimeSetPositionStatus.CommittedHostAcknowledgementPending,
|
|
first.Status);
|
|
Assert.Equal(Cell, canonical.FullCellId);
|
|
|
|
RuntimeEntityRegistrationResult newer = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(
|
|
0x70003013u,
|
|
1,
|
|
setupId: null,
|
|
positionSequence: 2,
|
|
positionX: 45f),
|
|
isLocalPlayer: true);
|
|
WorldSession.EntityPositionUpdate update =
|
|
newer.Inbound.SameGenerationEvents!.Value.Position!.Value;
|
|
Assert.True(lifetime.TryApplyPosition(
|
|
update,
|
|
isLocalPlayer: true,
|
|
forcePositionRotation: null,
|
|
currentLocalVelocity: null,
|
|
projectionRequiresTeleportHook: false,
|
|
acknowledgeProjection: null,
|
|
out PositionTimestampDisposition disposition,
|
|
out _,
|
|
out _));
|
|
Assert.Equal(PositionTimestampDisposition.Apply, disposition);
|
|
Assert.Equal(0, deleted);
|
|
Assert.True(lifetime.Physics.SetPosition.TryPeekProjection(
|
|
out RuntimePlacementProjectionSnapshot place));
|
|
Assert.Equal(RuntimePlacementProjectionKind.Place, place.Kind);
|
|
Assert.Equal(first.Projection.Sequence, place.Token.Sequence);
|
|
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
|
|
place.Token));
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease successor));
|
|
Assert.Equal(initial.Token, successor.Token);
|
|
Assert.Equal(initial.Route, successor.Route);
|
|
Assert.Equal(initial.Placement, successor.Placement);
|
|
Assert.Equal(RuntimeTeleportHookPhase.AfterEnterWorld,
|
|
successor.Route.TeleportHookPhase);
|
|
RuntimeInitialCreateResidenceContinuation continuation =
|
|
Assert.Single(successor.Continuations);
|
|
Assert.Equal(45f, continuation.AcceptedWirePosition.PositionX);
|
|
Assert.Equal(
|
|
RuntimeInitialCreateResidenceCompletionStatus.Completed,
|
|
lifetime.CompleteInitialCreateResidence(
|
|
canonical,
|
|
successor.Token,
|
|
out RuntimeInitialCreateResidenceReceipt receipt));
|
|
Assert.Equal(RuntimeTeleportHookPhase.AfterEnterWorld,
|
|
receipt.TeleportHookPhase);
|
|
Assert.Equal(first.Projection, receipt.Projection);
|
|
Assert.Equal(successor.Continuations, receipt.Continuations);
|
|
ApplyQueuedPosition(
|
|
lifetime,
|
|
canonical.ServerGuid,
|
|
positionSequence: 3,
|
|
teleportSequence: 1,
|
|
forcePositionSequence: 0,
|
|
positionX: 55f,
|
|
projectionRequiresTeleportHook: true,
|
|
callback: null);
|
|
Assert.Equal(
|
|
RuntimeInitialCreateResidenceCompletionStatus.Completed,
|
|
lifetime.CompleteInitialCreateResidence(
|
|
canonical,
|
|
successor.Token,
|
|
out RuntimeInitialCreateResidenceReceipt repeated));
|
|
Assert.Equal(receipt.Adoption.Revision + 1UL,
|
|
repeated.Adoption.Revision);
|
|
Assert.Equal(2, repeated.Continuations.Length);
|
|
Assert.Equal(55f,
|
|
repeated.Continuations[1].AcceptedWirePosition.PositionX);
|
|
Assert.False(lifetime.AcknowledgeInitialCreateResidenceAdoption(
|
|
canonical,
|
|
receipt.Adoption));
|
|
Assert.True(lifetime.AcknowledgeInitialCreateResidenceAdoption(
|
|
canonical,
|
|
repeated.Adoption));
|
|
Assert.Equal(
|
|
RuntimeInitialCreateResidenceCompletionStatus.RejectedToken,
|
|
lifetime.CompleteInitialCreateResidence(
|
|
canonical,
|
|
successor.Token,
|
|
out _));
|
|
Assert.Equal(0, deleted);
|
|
}
|
|
|
|
[Fact]
|
|
public void PreAcknowledgementSpatialCorruptionRetiresOperationAsDiscard()
|
|
{
|
|
var engine = new PhysicsEngine { DataCache = new PhysicsDataCache() };
|
|
engine.AddLandblock(
|
|
Landblock,
|
|
new TerrainSurface(new byte[81], new float[256]),
|
|
Array.Empty<CellSurface>(),
|
|
Array.Empty<PortalPlane>(),
|
|
worldOffsetX: 0f,
|
|
worldOffsetY: 0f);
|
|
using var lifetime = new RuntimeEntityObjectLifetime(engine);
|
|
Bind(lifetime, 31UL);
|
|
RuntimeEntityRecord canonical = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(0x7000301Du, 1, setupId: null),
|
|
isLocalPlayer: false)
|
|
.Canonical!;
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease lease));
|
|
AttachDormantBody(lifetime, canonical);
|
|
RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition
|
|
.SubmitPreparedPlacement(
|
|
lease.Placement,
|
|
Prepare(
|
|
lifetime,
|
|
lease,
|
|
RuntimeSetPositionMoverSetup.ResolvedAbsent));
|
|
Assert.Equal(
|
|
RuntimeSetPositionStatus.CommittedHostAcknowledgementPending,
|
|
outcome.Status);
|
|
|
|
lifetime.Entities.SetFullCell(
|
|
canonical,
|
|
Landblock | 0x0002u,
|
|
Landblock);
|
|
|
|
Assert.Equal(
|
|
RuntimeInitialCreateResidenceCompletionStatus.RejectedAuthority,
|
|
lifetime.CompleteInitialCreateResidence(
|
|
canonical,
|
|
lease.Token,
|
|
out _));
|
|
Assert.True(lifetime.Physics.SetPosition.TryPeekProjection(
|
|
out RuntimePlacementProjectionSnapshot discard));
|
|
Assert.Equal(RuntimePlacementProjectionKind.Discard, discard.Kind);
|
|
Assert.Equal(outcome.Projection.Sequence, discard.Token.Sequence);
|
|
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
|
|
discard.Token));
|
|
RuntimeSetPositionOwnershipSnapshot ownership =
|
|
lifetime.Physics.SetPosition.CaptureOwnership();
|
|
Assert.Equal(0, ownership.ActiveOperationCount);
|
|
Assert.Equal(0, ownership.PlacementCompletionWatchCount);
|
|
Assert.Equal(0, ownership.AcknowledgedPlacementCompletionCount);
|
|
Assert.Equal(0, lifetime.CaptureOwnership()
|
|
.InitialCreateResidenceLeaseCount);
|
|
}
|
|
|
|
[Fact]
|
|
public void CorruptedPostAcknowledgementAuthorityRetiresProofAndLease()
|
|
{
|
|
var engine = new PhysicsEngine { DataCache = new PhysicsDataCache() };
|
|
engine.AddLandblock(
|
|
Landblock,
|
|
new TerrainSurface(new byte[81], new float[256]),
|
|
Array.Empty<CellSurface>(),
|
|
Array.Empty<PortalPlane>(),
|
|
worldOffsetX: 0f,
|
|
worldOffsetY: 0f);
|
|
using var lifetime = new RuntimeEntityObjectLifetime(engine);
|
|
Bind(lifetime, 21UL);
|
|
RuntimeEntityRecord canonical = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(0x70003010u, 1, setupId: null),
|
|
isLocalPlayer: false)
|
|
.Canonical!;
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease lease));
|
|
AttachDormantBody(lifetime, canonical);
|
|
RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition
|
|
.SubmitPreparedPlacement(
|
|
lease.Placement,
|
|
Prepare(
|
|
lifetime,
|
|
lease,
|
|
RuntimeSetPositionMoverSetup.ResolvedAbsent));
|
|
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
|
|
outcome.Projection));
|
|
lifetime.Entities.AdvancePlacementCommit(canonical);
|
|
|
|
Assert.Equal(
|
|
RuntimeInitialCreateResidenceCompletionStatus.RejectedAuthority,
|
|
lifetime.CompleteInitialCreateResidence(
|
|
canonical,
|
|
lease.Token,
|
|
out _));
|
|
Assert.Equal(0, lifetime.CaptureOwnership()
|
|
.InitialCreateResidenceLeaseCount);
|
|
RuntimeSetPositionOwnershipSnapshot ownership =
|
|
lifetime.Physics.SetPosition.CaptureOwnership();
|
|
Assert.Equal(0, ownership.PlacementCompletionWatchCount);
|
|
Assert.Equal(0, ownership.AcknowledgedPlacementCompletionCount);
|
|
}
|
|
|
|
[Fact]
|
|
public void LegacyRegistrationAdvancesSpatialAuthorityOnlyOnce()
|
|
{
|
|
using var lifetime = new RuntimeEntityObjectLifetime();
|
|
RuntimeEntityRecord canonical = lifetime.RegisterEntity(
|
|
Spawn(0x70003011u, 1)).Canonical!;
|
|
|
|
Assert.Equal(Cell, canonical.FullCellId);
|
|
Assert.Equal(1UL, canonical.SpatialAuthorityVersion);
|
|
}
|
|
|
|
private static RuntimeSetPositionCommand Prepare(
|
|
RuntimeEntityObjectLifetime lifetime,
|
|
in RuntimeInitialCreateResidenceLease lease,
|
|
in RuntimeSetPositionMoverSetup setup)
|
|
{
|
|
var preparation = new RuntimeSetPositionMoverPreparation(
|
|
setup,
|
|
lease.Route.OperationKind,
|
|
GameTime: 1d,
|
|
PhysicsPlacementClass.Ordinary,
|
|
lease.Route.SetPositionFlags);
|
|
Assert.Equal(
|
|
RuntimeSetPositionMoverPreparationStatus.Prepared,
|
|
lifetime.Physics.SetPosition.PrepareMover(
|
|
lease.Placement,
|
|
preparation,
|
|
out RuntimeSetPositionCommand command));
|
|
return command;
|
|
}
|
|
|
|
private static RuntimeInitialCreateResidenceLease ApplyFreshSuccessor(
|
|
RuntimeEntityObjectLifetime lifetime,
|
|
uint guid,
|
|
bool isLocalPlayer,
|
|
ushort teleportSequence,
|
|
ushort forcePositionSequence,
|
|
out PositionTimestampDisposition disposition)
|
|
{
|
|
RuntimeEntityRecord canonical = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(guid, 1),
|
|
isLocalPlayer)
|
|
.Canonical!;
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease initial));
|
|
RuntimeEntityRegistrationResult newer = lifetime
|
|
.RegisterEntityWithInitialResidence(
|
|
Spawn(
|
|
guid,
|
|
1,
|
|
positionSequence: 2,
|
|
positionX: 40f,
|
|
teleportSequence: teleportSequence,
|
|
forcePositionSequence: forcePositionSequence),
|
|
isLocalPlayer);
|
|
WorldSession.EntityPositionUpdate update =
|
|
newer.Inbound.SameGenerationEvents!.Value.Position!.Value;
|
|
var preservedRotation = Quaternion.CreateFromYawPitchRoll(
|
|
0.5f,
|
|
0.25f,
|
|
0.125f);
|
|
var currentVelocity = new Vector3(1f, 2f, 3f);
|
|
|
|
Assert.True(lifetime.TryApplyPosition(
|
|
update,
|
|
isLocalPlayer,
|
|
forcePositionRotation: preservedRotation,
|
|
currentLocalVelocity: currentVelocity,
|
|
projectionRequiresTeleportHook: true,
|
|
acknowledgeProjection: null,
|
|
out disposition,
|
|
out _,
|
|
out _));
|
|
Assert.True(lifetime.TryGetInitialCreateResidence(
|
|
canonical,
|
|
out RuntimeInitialCreateResidenceLease retained));
|
|
Assert.Equal(initial.Token, retained.Token);
|
|
Assert.Equal(initial.Route, retained.Route);
|
|
Assert.Equal(initial.Placement, retained.Placement);
|
|
return retained;
|
|
}
|
|
|
|
private static void ApplyQueuedPosition(
|
|
RuntimeEntityObjectLifetime lifetime,
|
|
uint guid,
|
|
ushort positionSequence,
|
|
ushort teleportSequence,
|
|
ushort forcePositionSequence,
|
|
float positionX,
|
|
bool projectionRequiresTeleportHook,
|
|
Action? callback,
|
|
bool isLocalPlayer = true)
|
|
{
|
|
WorldSession.EntityPositionUpdate update = PositionUpdate(
|
|
guid,
|
|
positionSequence,
|
|
teleportSequence,
|
|
forcePositionSequence,
|
|
positionX);
|
|
Assert.True(lifetime.TryApplyPosition(
|
|
update,
|
|
isLocalPlayer,
|
|
forcePositionRotation: Quaternion.Identity,
|
|
currentLocalVelocity: new Vector3(1f, 2f, 3f),
|
|
projectionRequiresTeleportHook,
|
|
acknowledgeProjection: callback is null
|
|
? null
|
|
: _ => callback(),
|
|
out PositionTimestampDisposition disposition,
|
|
out _,
|
|
out _));
|
|
Assert.True(disposition is PositionTimestampDisposition.Apply
|
|
or PositionTimestampDisposition.ForcePosition);
|
|
}
|
|
|
|
private static WorldSession.EntityPositionUpdate PositionUpdate(
|
|
uint guid,
|
|
ushort positionSequence,
|
|
ushort teleportSequence,
|
|
ushort forcePositionSequence,
|
|
float positionX)
|
|
{
|
|
return new WorldSession.EntityPositionUpdate(
|
|
guid,
|
|
new CreateObject.ServerPosition(
|
|
Cell,
|
|
positionX,
|
|
20f,
|
|
7f,
|
|
1f,
|
|
0f,
|
|
0f,
|
|
0f),
|
|
new Vector3(positionSequence, 2f, 3f),
|
|
PlacementId: positionSequence,
|
|
IsGrounded: true,
|
|
InstanceSequence: 1,
|
|
PositionSequence: positionSequence,
|
|
TeleportSequence: teleportSequence,
|
|
ForcePositionSequence: forcePositionSequence);
|
|
}
|
|
|
|
private static void AttachDormantBody(
|
|
RuntimeEntityObjectLifetime lifetime,
|
|
RuntimeEntityRecord canonical)
|
|
{
|
|
var body = new PhysicsBody
|
|
{
|
|
State = canonical.FinalPhysicsState,
|
|
Orientation = Quaternion.Identity,
|
|
InWorld = false,
|
|
};
|
|
lifetime.Entities.SetPhysicsBody(canonical, body);
|
|
}
|
|
|
|
private static void Bind(
|
|
RuntimeEntityObjectLifetime lifetime,
|
|
ulong generation)
|
|
{
|
|
var token = new RuntimeGenerationToken(generation);
|
|
lifetime.BindEventContext(() => token, static () => 1UL);
|
|
}
|
|
|
|
private static WorldSession.EntitySpawn Spawn(
|
|
uint guid,
|
|
ushort incarnation,
|
|
bool missile = false,
|
|
bool includePosition = true,
|
|
uint? parentGuid = null,
|
|
uint? setupId = 0x02000001u,
|
|
ushort positionSequence = 1,
|
|
float positionX = 10f,
|
|
uint positionCell = Cell,
|
|
ushort teleportSequence = 0,
|
|
ushort forcePositionSequence = 0)
|
|
{
|
|
CreateObject.ServerPosition? position = includePosition
|
|
? new CreateObject.ServerPosition(
|
|
positionCell,
|
|
positionX,
|
|
20f,
|
|
7f,
|
|
1f,
|
|
0f,
|
|
0f,
|
|
0f)
|
|
: null;
|
|
uint rawState = (uint)(PhysicsStateFlags.Gravity
|
|
| (missile ? PhysicsStateFlags.Missile : 0));
|
|
var timestamps = new PhysicsTimestamps(
|
|
Position: positionSequence,
|
|
Movement: 1,
|
|
State: 1,
|
|
Vector: 1,
|
|
Teleport: teleportSequence,
|
|
ServerControlledMove: 1,
|
|
ForcePosition: forcePositionSequence,
|
|
ObjDesc: 1,
|
|
Instance: incarnation);
|
|
var physics = new PhysicsSpawnData(
|
|
rawState,
|
|
position,
|
|
Movement: null,
|
|
AnimationFrame: null,
|
|
setupId,
|
|
MotionTableId: null,
|
|
SoundTableId: null,
|
|
PhysicsScriptTableId: null,
|
|
Parent: parentGuid is { } parent
|
|
? new PhysicsAttachment(parent, 1u)
|
|
: null,
|
|
Children: null,
|
|
Scale: null,
|
|
Friction: null,
|
|
Elasticity: null,
|
|
Translucency: null,
|
|
Velocity: null,
|
|
Acceleration: null,
|
|
AngularVelocity: null,
|
|
DefaultScriptType: null,
|
|
DefaultScriptIntensity: null,
|
|
timestamps);
|
|
return new WorldSession.EntitySpawn(
|
|
guid,
|
|
position,
|
|
setupId,
|
|
Array.Empty<CreateObject.AnimPartChange>(),
|
|
Array.Empty<CreateObject.TextureChange>(),
|
|
Array.Empty<CreateObject.SubPaletteSwap>(),
|
|
BasePaletteId: null,
|
|
ObjScale: null,
|
|
Name: "initial-create",
|
|
ItemType: null,
|
|
MotionState: null,
|
|
MotionTableId: null,
|
|
PhysicsState: rawState,
|
|
InstanceSequence: incarnation,
|
|
PositionSequence: positionSequence,
|
|
ParentGuid: parentGuid,
|
|
ParentLocation: parentGuid is null ? null : 1u,
|
|
Physics: physics);
|
|
}
|
|
|
|
private sealed class EntityObserver(Action<RuntimeEntityDelta> onEntity)
|
|
: IRuntimeEntityObjectObserver
|
|
{
|
|
public void OnEntity(in RuntimeEntityDelta delta) => onEntity(delta);
|
|
public void OnInventory(in RuntimeInventoryDelta delta)
|
|
{
|
|
}
|
|
}
|
|
|
|
private sealed class PlacementObserver(
|
|
Action<RuntimePlacementDelta> onPlacement)
|
|
: IRuntimePlacementObserver
|
|
{
|
|
public void OnPlacement(in RuntimePlacementDelta delta) =>
|
|
onPlacement(delta);
|
|
}
|
|
}
|