feat(runtime): freeze initial placement inbound admission
This commit is contained in:
parent
9d601817b8
commit
30012361e1
9 changed files with 2506 additions and 323 deletions
|
|
@ -1,3 +1,4 @@
|
|||
using System.Collections.Immutable;
|
||||
using AcDream.Core.Items;
|
||||
using AcDream.Core.Net;
|
||||
using AcDream.Core.Net.Messages;
|
||||
|
|
@ -11,7 +12,8 @@ public readonly record struct RuntimeEntityRegistrationResult(
|
|||
RuntimeEntityRecord? Canonical,
|
||||
bool LogicalRegistrationCreated,
|
||||
bool ReplacedExistingGeneration,
|
||||
Exception? PriorGenerationCleanupFailure = null);
|
||||
Exception? PriorGenerationCleanupFailure = null,
|
||||
bool DeferredForParent = false);
|
||||
|
||||
public readonly record struct RuntimeEntityObjectOwnershipSnapshot(
|
||||
int ActiveEntityCount,
|
||||
|
|
@ -19,6 +21,7 @@ public readonly record struct RuntimeEntityObjectOwnershipSnapshot(
|
|||
int ClaimedLocalIdCount,
|
||||
int AcceptedSnapshotCount,
|
||||
int UnresolvedParentRelationCount,
|
||||
int DeferredParentCreateCount,
|
||||
int StagedParentRelationCount,
|
||||
int RecoveryParentRelationCount,
|
||||
int CommittedParentRelationCount,
|
||||
|
|
@ -44,6 +47,7 @@ public readonly record struct RuntimeEntityObjectOwnershipSnapshot(
|
|||
&& ClaimedLocalIdCount == 0
|
||||
&& AcceptedSnapshotCount == 0
|
||||
&& UnresolvedParentRelationCount == 0
|
||||
&& DeferredParentCreateCount == 0
|
||||
&& StagedParentRelationCount == 0
|
||||
&& RecoveryParentRelationCount == 0
|
||||
&& CommittedParentRelationCount == 0
|
||||
|
|
@ -205,6 +209,7 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
Entities.ClaimedLocalIdCount,
|
||||
Entities.Snapshots.Count,
|
||||
parents.UnresolvedRelationCount,
|
||||
parents.DeferredCreateCount,
|
||||
parents.StagedRelationCount,
|
||||
parents.RecoveryRelationCount,
|
||||
parents.CommittedRelationCount,
|
||||
|
|
@ -272,6 +277,35 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
throw new InvalidOperationException(
|
||||
"A Runtime entity cannot register while its session lifetime is clearing.");
|
||||
}
|
||||
if (beginInitialResidence
|
||||
&& !HasConsistentCreateIdentityAndParent(incoming))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"CreateObject 0x{incoming.Guid:X8} has inconsistent instance or parent projections.");
|
||||
}
|
||||
if (beginInitialResidence)
|
||||
incoming = RuntimeInitialCreateAdmissionFreezer.Freeze(incoming);
|
||||
uint parentGuid = incoming.ParentGuid
|
||||
?? incoming.Physics?.Parent?.Guid
|
||||
?? 0u;
|
||||
if (beginInitialResidence
|
||||
&& parentGuid != 0u
|
||||
&& !Entities.TryGetActive(parentGuid, out _))
|
||||
{
|
||||
// SmartBox::HandleCreateObject resolves a nonzero parent before
|
||||
// object lookup or timestamp admission. Retain the complete raw
|
||||
// CreateObject so no child gate/canonical/projection state can
|
||||
// escape before the parent becomes addressable.
|
||||
Entities.ParentAttachments.EnqueueDeferredCreate(
|
||||
incoming,
|
||||
isLocalPlayer);
|
||||
return new RuntimeEntityRegistrationResult(
|
||||
SupersededCreateResult(),
|
||||
Canonical: null,
|
||||
LogicalRegistrationCreated: false,
|
||||
ReplacedExistingGeneration: false,
|
||||
DeferredForParent: true);
|
||||
}
|
||||
CreateObjectTimestampDisposition preview =
|
||||
Entities.PreviewCreateDisposition(incoming);
|
||||
bool requiresFreshResidenceAdmission = preview is
|
||||
|
|
@ -285,7 +319,44 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
$"CreateObject 0x{incoming.Guid:X8} cannot acquire a structurally valid initial residence lease.");
|
||||
}
|
||||
|
||||
InboundCreateResult result = Entities.AcceptCreate(incoming);
|
||||
RuntimeEntityRecord? pendingResidenceRecord = null;
|
||||
RuntimeInitialCreateResidenceLease pendingResidence = default;
|
||||
bool admitIntoPendingResidence = beginInitialResidence
|
||||
&& preview is CreateObjectTimestampDisposition.ExistingGeneration
|
||||
&& Entities.TryGetActive(
|
||||
incoming.Guid,
|
||||
out pendingResidenceRecord)
|
||||
&& InitialCreateResidences.TryGetTransaction(
|
||||
pendingResidenceRecord,
|
||||
out pendingResidence);
|
||||
if (admitIntoPendingResidence
|
||||
&& !InitialCreateResidences.CanEnqueue(
|
||||
pendingResidenceRecord!,
|
||||
pendingResidence))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"CreateObject 0x{incoming.Guid:X8} cannot append to its pending initial residence FIFO.");
|
||||
}
|
||||
if (admitIntoPendingResidence
|
||||
&& !IsStructurallyValidDeferredCreate(incoming))
|
||||
{
|
||||
_ = Entities.TryGetAcceptedTimestamps(
|
||||
incoming.Guid,
|
||||
out AcceptedPhysicsTimestamps timestamps);
|
||||
return new RuntimeEntityRegistrationResult(
|
||||
new InboundCreateResult(
|
||||
CreateObjectTimestampDisposition.ExistingGeneration,
|
||||
pendingResidenceRecord!.Snapshot,
|
||||
SameGenerationEvents: null,
|
||||
timestamps),
|
||||
pendingResidenceRecord,
|
||||
LogicalRegistrationCreated: false,
|
||||
ReplacedExistingGeneration: false);
|
||||
}
|
||||
|
||||
InboundCreateResult result = admitIntoPendingResidence
|
||||
? Entities.AcceptCreateDeferredSameGeneration(incoming)
|
||||
: Entities.AcceptCreate(incoming);
|
||||
if (result.Disposition
|
||||
is CreateObjectTimestampDisposition.StaleGeneration)
|
||||
{
|
||||
|
|
@ -307,6 +378,33 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
incoming.Guid,
|
||||
out RuntimeEntityRecord retained))
|
||||
{
|
||||
if (admitIntoPendingResidence)
|
||||
{
|
||||
if (!ReferenceEquals(retained, pendingResidenceRecord)
|
||||
|| !AdmitSameGenerationCreate(
|
||||
retained,
|
||||
pendingResidence,
|
||||
incoming,
|
||||
result,
|
||||
isLocalPlayer))
|
||||
{
|
||||
throw FailInitialResidenceRegistration(
|
||||
retained,
|
||||
publishDeleted: true);
|
||||
}
|
||||
|
||||
InboundCreateResult dormant = result with
|
||||
{
|
||||
Snapshot = retained.Snapshot,
|
||||
SameGenerationEvents = null,
|
||||
};
|
||||
return new RuntimeEntityRegistrationResult(
|
||||
dormant,
|
||||
retained,
|
||||
LogicalRegistrationCreated: false,
|
||||
ReplacedExistingGeneration: false);
|
||||
}
|
||||
|
||||
// Existing-generation CreateObject contributes untimestamped
|
||||
// description fields here. Position/Parent/Pickup/State/etc.
|
||||
// remain separate freshness-gated events and must not churn a
|
||||
|
|
@ -592,6 +690,33 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
out WorldSession.EntitySpawn accepted)
|
||||
{
|
||||
EnsureNotDisposed();
|
||||
if (TryGetPendingInitialResidence(
|
||||
update.Guid,
|
||||
out RuntimeEntityRecord pending,
|
||||
out RuntimeInitialCreateResidenceLease lease))
|
||||
{
|
||||
if (!InitialCreateResidences.CanEnqueue(pending, lease))
|
||||
{
|
||||
accepted = pending.Snapshot;
|
||||
return false;
|
||||
}
|
||||
bool acceptedByGate = Entities.TryAcceptDeferredObjDesc(
|
||||
update,
|
||||
out _);
|
||||
accepted = pending.Snapshot;
|
||||
if (!acceptedByGate)
|
||||
return false;
|
||||
EnqueueDormant(
|
||||
pending,
|
||||
lease,
|
||||
RuntimeInitialCreateContinuationKind.ObjDesc,
|
||||
RuntimeAcceptedPositionSource.Unknown,
|
||||
new RuntimeInitialCreateTailAction(
|
||||
RuntimeInitialCreateTailActionKind.ObjDesc,
|
||||
update.Guid,
|
||||
ObjDesc: update));
|
||||
return true;
|
||||
}
|
||||
bool applied = Entities.TryApplyObjDesc(update, out accepted);
|
||||
if (!applied
|
||||
|| !Entities.TryGetActive(
|
||||
|
|
@ -617,6 +742,33 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
out WorldSession.EntitySpawn accepted)
|
||||
{
|
||||
EnsureNotDisposed();
|
||||
if (TryGetPendingInitialResidence(
|
||||
update.Guid,
|
||||
out RuntimeEntityRecord pending,
|
||||
out RuntimeInitialCreateResidenceLease lease))
|
||||
{
|
||||
if (!InitialCreateResidences.CanEnqueue(pending, lease))
|
||||
{
|
||||
accepted = pending.Snapshot;
|
||||
return false;
|
||||
}
|
||||
bool acceptedByGate = Entities.TryAcceptDeferredPickup(
|
||||
update,
|
||||
out _);
|
||||
accepted = pending.Snapshot;
|
||||
if (!acceptedByGate)
|
||||
return false;
|
||||
EnqueueDormant(
|
||||
pending,
|
||||
lease,
|
||||
RuntimeInitialCreateContinuationKind.Pickup,
|
||||
RuntimeAcceptedPositionSource.Unknown,
|
||||
new RuntimeInitialCreateTailAction(
|
||||
RuntimeInitialCreateTailActionKind.Pickup,
|
||||
update.Guid,
|
||||
Pickup: update));
|
||||
return true;
|
||||
}
|
||||
bool applied = Entities.TryApplyPickup(update, out accepted);
|
||||
if (!applied
|
||||
|| !Entities.TryGetActive(
|
||||
|
|
@ -655,6 +807,14 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
out WorldSession.EntitySpawn accepted)
|
||||
{
|
||||
EnsureNotDisposed();
|
||||
if (TryGetPendingInitialResidence(
|
||||
update.ChildGuid,
|
||||
out _,
|
||||
out _))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"A CreateObject parent relation must be admitted inside its atomic same-generation Create envelope.");
|
||||
}
|
||||
bool applied = Entities.TryApplyCreateParent(update, out accepted);
|
||||
return CommitPositionChannelUpdate(
|
||||
applied,
|
||||
|
|
@ -669,6 +829,44 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
out WorldSession.EntitySpawn accepted)
|
||||
{
|
||||
EnsureNotDisposed();
|
||||
if (TryGetPendingInitialResidence(
|
||||
update.ChildGuid,
|
||||
out RuntimeEntityRecord pending,
|
||||
out RuntimeInitialCreateResidenceLease lease))
|
||||
{
|
||||
if (!InitialCreateResidences.CanEnqueue(pending, lease))
|
||||
{
|
||||
accepted = pending.Snapshot;
|
||||
return false;
|
||||
}
|
||||
if (!Entities.TryGetActive(
|
||||
update.ParentGuid,
|
||||
out RuntimeEntityRecord parent)
|
||||
|| parent.Incarnation != update.ParentInstanceSequence)
|
||||
{
|
||||
Entities.ParentAttachments.Enqueue(update);
|
||||
accepted = pending.Snapshot;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool acceptedByGate = Entities.TryAcceptDeferredParent(
|
||||
update,
|
||||
out AcceptedPhysicsTimestamps timestamps);
|
||||
accepted = pending.Snapshot;
|
||||
if (!acceptedByGate)
|
||||
return false;
|
||||
EnqueueDormant(
|
||||
pending,
|
||||
lease,
|
||||
RuntimeInitialCreateContinuationKind.Parent,
|
||||
RuntimeAcceptedPositionSource.Unknown,
|
||||
new RuntimeInitialCreateTailAction(
|
||||
RuntimeInitialCreateTailActionKind.Parent,
|
||||
update.ChildGuid,
|
||||
Parent: update,
|
||||
AcceptedTimestamps: timestamps));
|
||||
return true;
|
||||
}
|
||||
bool applied = Entities.TryApplyParent(update, out accepted);
|
||||
return CommitPositionChannelUpdate(
|
||||
applied,
|
||||
|
|
@ -750,6 +948,39 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
out AcceptedPhysicsTimestamps timestamps)
|
||||
{
|
||||
EnsureNotDisposed();
|
||||
if (TryGetPendingInitialResidence(
|
||||
update.Guid,
|
||||
out RuntimeEntityRecord pending,
|
||||
out RuntimeInitialCreateResidenceLease lease))
|
||||
{
|
||||
if (!InitialCreateResidences.CanEnqueue(pending, lease))
|
||||
{
|
||||
accepted = pending.Snapshot;
|
||||
timestamps = default;
|
||||
return false;
|
||||
}
|
||||
bool payloadApplied = Entities.TryAcceptDeferredMotion(
|
||||
update,
|
||||
out timestamps,
|
||||
out bool timestampMutation);
|
||||
accepted = pending.Snapshot;
|
||||
if (!payloadApplied && !timestampMutation)
|
||||
return false;
|
||||
EnqueueDormant(
|
||||
pending,
|
||||
lease,
|
||||
RuntimeInitialCreateContinuationKind.Movement,
|
||||
RuntimeAcceptedPositionSource.Unknown,
|
||||
new RuntimeInitialCreateTailAction(
|
||||
RuntimeInitialCreateTailActionKind.Movement,
|
||||
update.Guid,
|
||||
Movement: update,
|
||||
AcceptedTimestamps: timestamps,
|
||||
AppliesMovementPayload: payloadApplied,
|
||||
RetainMovementPayload: retainPayload,
|
||||
HasTimestampMutation: timestampMutation));
|
||||
return payloadApplied;
|
||||
}
|
||||
bool applied = Entities.TryApplyMotion(
|
||||
update,
|
||||
retainPayload,
|
||||
|
|
@ -790,6 +1021,35 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
out WorldSession.EntitySpawn accepted)
|
||||
{
|
||||
EnsureNotDisposed();
|
||||
if (TryGetPendingInitialResidence(
|
||||
update.Guid,
|
||||
out RuntimeEntityRecord pending,
|
||||
out RuntimeInitialCreateResidenceLease lease))
|
||||
{
|
||||
if (!IsFinite(update.Velocity)
|
||||
|| !IsFinite(update.Omega)
|
||||
|| !InitialCreateResidences.CanEnqueue(pending, lease))
|
||||
{
|
||||
accepted = pending.Snapshot;
|
||||
return false;
|
||||
}
|
||||
bool acceptedByGate = Entities.TryAcceptDeferredVector(
|
||||
update,
|
||||
out _);
|
||||
accepted = pending.Snapshot;
|
||||
if (!acceptedByGate)
|
||||
return false;
|
||||
EnqueueDormant(
|
||||
pending,
|
||||
lease,
|
||||
RuntimeInitialCreateContinuationKind.Vector,
|
||||
RuntimeAcceptedPositionSource.Unknown,
|
||||
new RuntimeInitialCreateTailAction(
|
||||
RuntimeInitialCreateTailActionKind.Vector,
|
||||
update.Guid,
|
||||
Vector: update));
|
||||
return true;
|
||||
}
|
||||
bool applied = Entities.TryApplyVector(update, out accepted);
|
||||
if (!applied
|
||||
|| !Entities.TryGetActive(
|
||||
|
|
@ -817,6 +1077,35 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
out RetailPhysicsStateTransition transition)
|
||||
{
|
||||
EnsureNotDisposed();
|
||||
if (TryGetPendingInitialResidence(
|
||||
update.Guid,
|
||||
out RuntimeEntityRecord pending,
|
||||
out RuntimeInitialCreateResidenceLease lease))
|
||||
{
|
||||
if (!InitialCreateResidences.CanEnqueue(pending, lease))
|
||||
{
|
||||
accepted = pending.Snapshot;
|
||||
transition = default;
|
||||
return false;
|
||||
}
|
||||
bool acceptedByGate = Entities.TryAcceptDeferredState(
|
||||
update,
|
||||
out _);
|
||||
accepted = pending.Snapshot;
|
||||
transition = default;
|
||||
if (!acceptedByGate)
|
||||
return false;
|
||||
EnqueueDormant(
|
||||
pending,
|
||||
lease,
|
||||
RuntimeInitialCreateContinuationKind.State,
|
||||
RuntimeAcceptedPositionSource.Unknown,
|
||||
new RuntimeInitialCreateTailAction(
|
||||
RuntimeInitialCreateTailActionKind.State,
|
||||
update.Guid,
|
||||
State: update));
|
||||
return true;
|
||||
}
|
||||
bool applied = Entities.TryApplyState(update, out accepted);
|
||||
transition = default;
|
||||
if (!applied
|
||||
|
|
@ -883,24 +1172,58 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
out AcceptedPhysicsTimestamps timestamps)
|
||||
{
|
||||
EnsureNotDisposed();
|
||||
RuntimeInitialCreateResidenceLease priorInitialResidence = default;
|
||||
bool hadPendingInitialResidence =
|
||||
Entities.TryGetActive(
|
||||
if (TryGetPendingInitialResidence(
|
||||
update.Guid,
|
||||
out RuntimeEntityRecord pendingCanonical)
|
||||
&& InitialCreateResidences.TryGetTransaction(
|
||||
pendingCanonical,
|
||||
out priorInitialResidence);
|
||||
if (hadPendingInitialResidence
|
||||
&& !InitialCreateResidences.CanEnqueueAcceptedPosition(
|
||||
pendingCanonical,
|
||||
priorInitialResidence,
|
||||
update))
|
||||
out RuntimeEntityRecord pendingCanonical,
|
||||
out RuntimeInitialCreateResidenceLease pendingLease))
|
||||
{
|
||||
disposition = PositionTimestampDisposition.Rejected;
|
||||
accepted = default;
|
||||
timestamps = default;
|
||||
return false;
|
||||
if (!RuntimeAuthoritativePositionRouteClassifier
|
||||
.IsValidCreateWirePosition(update.Position)
|
||||
|| update.Velocity is { } velocity
|
||||
&& !IsFinite(velocity)
|
||||
|| !InitialCreateResidences.CanEnqueue(
|
||||
pendingCanonical,
|
||||
pendingLease))
|
||||
{
|
||||
disposition = PositionTimestampDisposition.Rejected;
|
||||
accepted = default;
|
||||
timestamps = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool deferredKnown = Entities.TryAcceptDeferredPosition(
|
||||
update,
|
||||
isLocalPlayer,
|
||||
out disposition,
|
||||
out timestamps,
|
||||
out bool timestampMutation);
|
||||
accepted = pendingCanonical.Snapshot;
|
||||
if (!deferredKnown)
|
||||
return false;
|
||||
|
||||
if (disposition is PositionTimestampDisposition.Rejected
|
||||
&& !timestampMutation)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
EnqueueDormant(
|
||||
pendingCanonical,
|
||||
pendingLease,
|
||||
RuntimeInitialCreateContinuationKind.Position,
|
||||
RuntimeAcceptedPositionSource.PositionEvent,
|
||||
new RuntimeInitialCreateTailAction(
|
||||
RuntimeInitialCreateTailActionKind.Position,
|
||||
update.Guid,
|
||||
Position: update,
|
||||
PositionSource:
|
||||
RuntimeAcceptedPositionSource.PositionEvent,
|
||||
PositionDisposition: disposition,
|
||||
PreviousTeleportSequence:
|
||||
timestamps.PreviousTeleport,
|
||||
AcceptedTimestamps: timestamps,
|
||||
HasTimestampMutation: timestampMutation));
|
||||
return true;
|
||||
}
|
||||
bool hadCanonical = Entities.TryGetActive(
|
||||
update.Guid,
|
||||
|
|
@ -938,43 +1261,6 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
|| projectionRequiresTeleportHook,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// Initial CreateObject residence is one immutable transaction. A
|
||||
// Position packet accepted before its 0x11 placement completes is
|
||||
// retained in the transaction's ordered continuation batch; it must
|
||||
// not replace the initial mover operation, mutate the Runtime record
|
||||
// used to prepare that operation, or escape through a host callback.
|
||||
if (hadPendingInitialResidence)
|
||||
{
|
||||
if (!acceptedPosition)
|
||||
return true;
|
||||
if (!ReferenceEquals(canonical, pendingCanonical))
|
||||
throw FailInitialResidenceRegistration(
|
||||
canonical,
|
||||
publishDeleted: true);
|
||||
|
||||
RuntimeInitialCreateResidenceLease retained =
|
||||
InitialCreateResidences.EnqueueAcceptedPosition(
|
||||
canonical,
|
||||
priorInitialResidence,
|
||||
update,
|
||||
accepted,
|
||||
disposition,
|
||||
timestamps,
|
||||
isLocalPlayer,
|
||||
forcePositionRotation,
|
||||
currentLocalVelocity,
|
||||
projectionRequiresTeleportHook);
|
||||
if (!retained.IsValid)
|
||||
{
|
||||
throw FailInitialResidenceRegistration(
|
||||
canonical,
|
||||
publishDeleted: true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
RuntimePlacementCancellationReceipt cancellation = default;
|
||||
if (acceptedPosition)
|
||||
{
|
||||
|
|
@ -1118,6 +1404,15 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
out RuntimeEntityDeleteAcceptance acceptance)
|
||||
{
|
||||
EnsureNotDisposed();
|
||||
if (!isLocalPlayer)
|
||||
{
|
||||
// A child whose complete raw CreateObject is waiting on a missing
|
||||
// parent has no timestamp gate yet. Cancel its exact/older raw
|
||||
// generation before the normal known-object delete gate returns.
|
||||
Entities.ParentAttachments.CancelDeferredChildGeneration(
|
||||
delete.Guid,
|
||||
delete.InstanceSequence);
|
||||
}
|
||||
if (!Entities.TryDelete(delete, isLocalPlayer))
|
||||
{
|
||||
acceptance = null!;
|
||||
|
|
@ -1414,6 +1709,288 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
Entities.IsCurrent(canonical)
|
||||
&& matchesCommittedMutation();
|
||||
|
||||
private bool TryGetPendingInitialResidence(
|
||||
uint guid,
|
||||
out RuntimeEntityRecord canonical,
|
||||
out RuntimeInitialCreateResidenceLease lease)
|
||||
{
|
||||
if (Entities.TryGetActive(guid, out canonical)
|
||||
&& InitialCreateResidences.TryGetTransaction(
|
||||
canonical,
|
||||
out lease))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
canonical = null!;
|
||||
lease = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void EnqueueDormant(
|
||||
RuntimeEntityRecord canonical,
|
||||
in RuntimeInitialCreateResidenceLease prior,
|
||||
RuntimeInitialCreateContinuationKind kind,
|
||||
RuntimeAcceptedPositionSource positionSource,
|
||||
in RuntimeInitialCreateTailAction action)
|
||||
{
|
||||
RuntimeInitialCreateResidenceLease retained = InitialCreateResidences
|
||||
.EnqueueAccepted(
|
||||
canonical,
|
||||
prior,
|
||||
kind,
|
||||
positionSource,
|
||||
ImmutableArray.Create(action));
|
||||
if (!retained.IsValid)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Accepted {kind} for 0x{canonical.ServerGuid:X8}/{canonical.Incarnation} could not be retained by its initial-placement FIFO.");
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsFinite(System.Numerics.Vector3 value) =>
|
||||
float.IsFinite(value.X)
|
||||
&& float.IsFinite(value.Y)
|
||||
&& float.IsFinite(value.Z);
|
||||
|
||||
private static bool HasConsistentCreateIdentityAndParent(
|
||||
in WorldSession.EntitySpawn incoming)
|
||||
{
|
||||
if (incoming.Guid == 0u)
|
||||
return false;
|
||||
|
||||
bool hasTopParentGuid = incoming.ParentGuid is not null;
|
||||
bool hasTopParentLocation = incoming.ParentLocation is not null;
|
||||
if (hasTopParentGuid != hasTopParentLocation)
|
||||
return false;
|
||||
|
||||
if (incoming.Physics is not { } physics)
|
||||
{
|
||||
// These flattened values are parser projections of PhysicsDesc.
|
||||
// Without that source block, accepting any of them would create
|
||||
// contradictory admission authority before the raw create is
|
||||
// either queued for a parent or assigned a residence lease.
|
||||
return incoming.Position is null
|
||||
&& incoming.SetupTableId is null
|
||||
&& incoming.MotionState is null
|
||||
&& incoming.MotionTableId is null
|
||||
&& incoming.PhysicsState is null
|
||||
&& incoming.ObjScale is null
|
||||
&& incoming.Friction is null
|
||||
&& incoming.Elasticity is null
|
||||
&& incoming.InstanceSequence == 0
|
||||
&& incoming.MovementSequence == 0
|
||||
&& incoming.ServerControlSequence == 0
|
||||
&& incoming.PositionSequence == 0
|
||||
&& !hasTopParentGuid
|
||||
&& incoming.PlacementId is null;
|
||||
}
|
||||
if (physics.Timestamps.Instance != incoming.InstanceSequence
|
||||
|| physics.Timestamps.Position != incoming.PositionSequence
|
||||
|| physics.Timestamps.Movement != incoming.MovementSequence
|
||||
|| physics.Timestamps.ServerControlledMove
|
||||
!= incoming.ServerControlSequence
|
||||
|| physics.Position != incoming.Position)
|
||||
return false;
|
||||
|
||||
PhysicsAttachment? flattenedParent = incoming.ParentGuid is { } parentGuid
|
||||
&& incoming.ParentLocation is { } parentLocation
|
||||
? new PhysicsAttachment(parentGuid, parentLocation)
|
||||
: null;
|
||||
if (flattenedParent != physics.Parent
|
||||
|| incoming.PlacementId != physics.AnimationFrame)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsStructurallyValidDeferredCreate(
|
||||
in WorldSession.EntitySpawn incoming)
|
||||
{
|
||||
if (incoming.Guid == 0u)
|
||||
return false;
|
||||
if (incoming.Physics is not { } physics)
|
||||
return true;
|
||||
if (physics.Parent is null
|
||||
&& physics.Position is { LandblockId: not 0u } position
|
||||
&& !RuntimeAuthoritativePositionRouteClassifier
|
||||
.IsValidCreateWirePosition(position))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (physics.Velocity is { } velocity && !IsFinite(velocity)
|
||||
|| physics.Acceleration is { } acceleration
|
||||
&& !IsFinite(acceleration)
|
||||
|| physics.AngularVelocity is { } angularVelocity
|
||||
&& !IsFinite(angularVelocity)
|
||||
|| physics.Scale is { } scale && !float.IsFinite(scale)
|
||||
|| physics.Friction is { } friction && !float.IsFinite(friction)
|
||||
|| physics.Elasticity is { } elasticity
|
||||
&& !float.IsFinite(elasticity)
|
||||
|| physics.Translucency is { } translucency
|
||||
&& !float.IsFinite(translucency))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool AdmitSameGenerationCreate(
|
||||
RuntimeEntityRecord canonical,
|
||||
in RuntimeInitialCreateResidenceLease prior,
|
||||
in WorldSession.EntitySpawn incoming,
|
||||
in InboundCreateResult admitted,
|
||||
bool isLocalPlayer)
|
||||
{
|
||||
if (!ReferenceEquals(
|
||||
canonical,
|
||||
Entities.TryGetActive(
|
||||
incoming.Guid,
|
||||
out RuntimeEntityRecord current)
|
||||
? current
|
||||
: null)
|
||||
|| canonical.Incarnation != incoming.InstanceSequence
|
||||
|| admitted.Disposition
|
||||
is not CreateObjectTimestampDisposition.ExistingGeneration
|
||||
|| !InitialCreateResidences.CanEnqueue(canonical, prior))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var actions = ImmutableArray.CreateBuilder<
|
||||
RuntimeInitialCreateTailAction>();
|
||||
RuntimeAcceptedPositionSource positionSource =
|
||||
RuntimeAcceptedPositionSource.Unknown;
|
||||
|
||||
if (admitted.SameGenerationEvents is { } events)
|
||||
{
|
||||
actions.Add(new RuntimeInitialCreateTailAction(
|
||||
RuntimeInitialCreateTailActionKind
|
||||
.PreTailDescriptionAdaptation,
|
||||
incoming.Guid,
|
||||
Description: events.Description));
|
||||
|
||||
if (Entities.TryAcceptDeferredObjDesc(
|
||||
events.Appearance,
|
||||
out _))
|
||||
{
|
||||
actions.Add(new RuntimeInitialCreateTailAction(
|
||||
RuntimeInitialCreateTailActionKind.ObjDesc,
|
||||
incoming.Guid,
|
||||
ObjDesc: events.Appearance));
|
||||
}
|
||||
|
||||
if (events.Parent is { } parent)
|
||||
{
|
||||
if (Entities.TryAcceptDeferredCreateParent(
|
||||
parent,
|
||||
out _))
|
||||
{
|
||||
actions.Add(new RuntimeInitialCreateTailAction(
|
||||
RuntimeInitialCreateTailActionKind.CreateParent,
|
||||
incoming.Guid,
|
||||
CreateParent: parent));
|
||||
}
|
||||
}
|
||||
else if (events.Position is { } position)
|
||||
{
|
||||
if (!RuntimeAuthoritativePositionRouteClassifier
|
||||
.IsValidCreateWirePosition(position.Position)
|
||||
|| position.Velocity is { } velocity
|
||||
&& !IsFinite(velocity))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Entities.TryAcceptDeferredPosition(
|
||||
position,
|
||||
isLocalPlayer,
|
||||
out PositionTimestampDisposition disposition,
|
||||
out AcceptedPhysicsTimestamps timestamps,
|
||||
out bool timestampMutation))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (disposition is not PositionTimestampDisposition.Rejected
|
||||
|| timestampMutation)
|
||||
{
|
||||
positionSource = RuntimeAcceptedPositionSource
|
||||
.SameIncarnationCreate;
|
||||
actions.Add(new RuntimeInitialCreateTailAction(
|
||||
RuntimeInitialCreateTailActionKind.Position,
|
||||
incoming.Guid,
|
||||
Position: position,
|
||||
PositionSource: positionSource,
|
||||
PositionDisposition: disposition,
|
||||
PreviousTeleportSequence:
|
||||
timestamps.PreviousTeleport,
|
||||
AcceptedTimestamps: timestamps,
|
||||
HasTimestampMutation: timestampMutation));
|
||||
}
|
||||
}
|
||||
else if (events.Pickup is { } pickup
|
||||
&& Entities.TryAcceptDeferredPickup(pickup, out _))
|
||||
{
|
||||
actions.Add(new RuntimeInitialCreateTailAction(
|
||||
RuntimeInitialCreateTailActionKind.Pickup,
|
||||
incoming.Guid,
|
||||
Pickup: pickup));
|
||||
}
|
||||
|
||||
if (events.Movement is { } movement)
|
||||
{
|
||||
bool payloadApplied = Entities.TryAcceptDeferredMotion(
|
||||
movement,
|
||||
out AcceptedPhysicsTimestamps timestamps,
|
||||
out bool timestampMutation);
|
||||
if (payloadApplied || timestampMutation)
|
||||
{
|
||||
actions.Add(new RuntimeInitialCreateTailAction(
|
||||
RuntimeInitialCreateTailActionKind.Movement,
|
||||
incoming.Guid,
|
||||
Movement: movement,
|
||||
AcceptedTimestamps: timestamps,
|
||||
AppliesMovementPayload: payloadApplied,
|
||||
RetainMovementPayload: true,
|
||||
HasTimestampMutation: timestampMutation));
|
||||
}
|
||||
}
|
||||
|
||||
if (Entities.TryAcceptDeferredState(events.State, out _))
|
||||
{
|
||||
actions.Add(new RuntimeInitialCreateTailAction(
|
||||
RuntimeInitialCreateTailActionKind.State,
|
||||
incoming.Guid,
|
||||
State: events.State));
|
||||
}
|
||||
if (Entities.TryAcceptDeferredVector(events.Vector, out _))
|
||||
{
|
||||
actions.Add(new RuntimeInitialCreateTailAction(
|
||||
RuntimeInitialCreateTailActionKind.Vector,
|
||||
incoming.Guid,
|
||||
Vector: events.Vector));
|
||||
}
|
||||
}
|
||||
|
||||
actions.Add(new RuntimeInitialCreateTailAction(
|
||||
RuntimeInitialCreateTailActionKind.WeenieDescription,
|
||||
incoming.Guid,
|
||||
WeenieDescription: incoming));
|
||||
actions.Add(new RuntimeInitialCreateTailAction(
|
||||
RuntimeInitialCreateTailActionKind.ResidentCellCleanup,
|
||||
incoming.Guid));
|
||||
|
||||
RuntimeInitialCreateResidenceLease retained = InitialCreateResidences
|
||||
.EnqueueAccepted(
|
||||
canonical,
|
||||
prior,
|
||||
RuntimeInitialCreateContinuationKind.SameIncarnationCreate,
|
||||
positionSource,
|
||||
actions.ToImmutable());
|
||||
return retained.IsValid;
|
||||
}
|
||||
|
||||
private void EnsureNotDisposed() =>
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue