feat(runtime): own deferred set-position residence
This commit is contained in:
parent
e84a388e6f
commit
4c02ac4259
18 changed files with 5557 additions and 34 deletions
|
|
@ -16,6 +16,7 @@ public sealed class ParentAttachmentState
|
|||
private readonly Dictionary<uint, ParentAttachmentRelation> _stagedByChild = new();
|
||||
private readonly Dictionary<uint, ParentAttachmentRelation> _recoveryByChild = new();
|
||||
private readonly Dictionary<uint, ParentAttachmentRelation> _lastAcceptedByChild = new();
|
||||
private readonly Dictionary<ParentIncarnation, List<uint>> _committedChildrenByParent = new();
|
||||
|
||||
public int UnresolvedRelationCount =>
|
||||
_unresolvedByChild.Values.Sum(queue => queue.Count);
|
||||
|
|
@ -148,6 +149,9 @@ public sealed class ParentAttachmentState
|
|||
out ParentAttachmentRelation committed)
|
||||
&& committed == relation;
|
||||
|
||||
public bool HasCommittedParent(uint childGuid) =>
|
||||
_lastAcceptedByChild.ContainsKey(childGuid);
|
||||
|
||||
public bool IsPending(
|
||||
ParentAttachmentRelation relation,
|
||||
ParentProjectionCandidateKind kind) =>
|
||||
|
|
@ -184,7 +188,20 @@ public sealed class ParentAttachmentState
|
|||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
RemoveCommittedChild(relation.ChildGuid);
|
||||
_lastAcceptedByChild[relation.ChildGuid] = relation;
|
||||
var parent = new ParentIncarnation(
|
||||
relation.ParentGuid,
|
||||
relation.ParentInstanceSequence);
|
||||
if (!_committedChildrenByParent.TryGetValue(
|
||||
parent,
|
||||
out List<uint>? children))
|
||||
{
|
||||
children = [];
|
||||
_committedChildrenByParent.Add(parent, children);
|
||||
}
|
||||
children.Add(relation.ChildGuid);
|
||||
_stagedByChild.Remove(relation.ChildGuid);
|
||||
_recoveryByChild[relation.ChildGuid] = relation;
|
||||
return true;
|
||||
|
|
@ -233,16 +250,34 @@ public sealed class ParentAttachmentState
|
|||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns only the exact direct children currently committed to one
|
||||
/// parent incarnation. Lost-cell destruction follows retail's live
|
||||
/// CHILDLIST and must not capture staged, unresolved, or future-generation
|
||||
/// relations that merely reuse the same parent GUID.
|
||||
/// </summary>
|
||||
public IReadOnlyList<uint> ChildrenAttachedToParent(
|
||||
uint parentGuid,
|
||||
ushort parentInstanceSequence)
|
||||
{
|
||||
var parent = new ParentIncarnation(parentGuid, parentInstanceSequence);
|
||||
return _committedChildrenByParent.TryGetValue(
|
||||
parent,
|
||||
out List<uint>? children)
|
||||
? children
|
||||
: Array.Empty<uint>();
|
||||
}
|
||||
|
||||
public void RemoveObject(uint guid)
|
||||
{
|
||||
_stagedByChild.Remove(guid);
|
||||
_recoveryByChild.Remove(guid);
|
||||
_lastAcceptedByChild.Remove(guid);
|
||||
RemoveCommittedChild(guid);
|
||||
_unresolvedByChild.Remove(guid);
|
||||
|
||||
RemoveParentReferences(_stagedByChild, guid);
|
||||
RemoveParentReferences(_recoveryByChild, guid);
|
||||
RemoveParentReferences(_lastAcceptedByChild, guid);
|
||||
RemoveCommittedParentReferences(guid);
|
||||
|
||||
uint[] children = _unresolvedByChild.Keys.ToArray();
|
||||
for (int i = 0; i < children.Length; i++)
|
||||
|
|
@ -268,10 +303,10 @@ public sealed class ParentAttachmentState
|
|||
relation => relation.WaitOwner is ParentAttachmentWaitOwner.Parent);
|
||||
_stagedByChild.Remove(guid);
|
||||
_recoveryByChild.Remove(guid);
|
||||
_lastAcceptedByChild.Remove(guid);
|
||||
RemoveCommittedChild(guid);
|
||||
RemoveParentReferences(_stagedByChild, guid);
|
||||
RemoveParentReferences(_recoveryByChild, guid);
|
||||
RemoveParentReferences(_lastAcceptedByChild, guid);
|
||||
RemoveCommittedParentReferences(guid);
|
||||
FilterParentCandidates(
|
||||
guid,
|
||||
relation => relation.ParentInstanceSequence == replacementGeneration
|
||||
|
|
@ -292,10 +327,10 @@ public sealed class ParentAttachmentState
|
|||
relation => relation.WaitOwner is ParentAttachmentWaitOwner.Parent);
|
||||
_stagedByChild.Remove(guid);
|
||||
_recoveryByChild.Remove(guid);
|
||||
_lastAcceptedByChild.Remove(guid);
|
||||
RemoveCommittedChild(guid);
|
||||
RemoveParentReferences(_stagedByChild, guid);
|
||||
RemoveParentReferences(_recoveryByChild, guid);
|
||||
RemoveParentReferences(_lastAcceptedByChild, guid);
|
||||
RemoveCommittedParentReferences(guid);
|
||||
FilterParentCandidates(
|
||||
guid,
|
||||
relation => PhysicsTimestampGate.IsNewer(
|
||||
|
|
@ -311,14 +346,14 @@ public sealed class ParentAttachmentState
|
|||
{
|
||||
_stagedByChild.Remove(childGuid);
|
||||
_recoveryByChild.Remove(childGuid);
|
||||
_lastAcceptedByChild.Remove(childGuid);
|
||||
RemoveCommittedChild(childGuid);
|
||||
}
|
||||
|
||||
public void RemoveChild(uint childGuid)
|
||||
{
|
||||
_stagedByChild.Remove(childGuid);
|
||||
_recoveryByChild.Remove(childGuid);
|
||||
_lastAcceptedByChild.Remove(childGuid);
|
||||
RemoveCommittedChild(childGuid);
|
||||
_unresolvedByChild.Remove(childGuid);
|
||||
}
|
||||
|
||||
|
|
@ -328,6 +363,50 @@ public sealed class ParentAttachmentState
|
|||
_stagedByChild.Clear();
|
||||
_recoveryByChild.Clear();
|
||||
_lastAcceptedByChild.Clear();
|
||||
foreach (List<uint> children in _committedChildrenByParent.Values)
|
||||
children.Clear();
|
||||
_committedChildrenByParent.Clear();
|
||||
}
|
||||
|
||||
private void RemoveCommittedChild(uint childGuid)
|
||||
{
|
||||
if (!_lastAcceptedByChild.Remove(
|
||||
childGuid,
|
||||
out ParentAttachmentRelation relation))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var parent = new ParentIncarnation(
|
||||
relation.ParentGuid,
|
||||
relation.ParentInstanceSequence);
|
||||
if (!_committedChildrenByParent.TryGetValue(
|
||||
parent,
|
||||
out List<uint>? children))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int childIndex = children.IndexOf(childGuid);
|
||||
if (childIndex >= 0)
|
||||
{
|
||||
int lastIndex = children.Count - 1;
|
||||
children[childIndex] = children[lastIndex];
|
||||
children.RemoveAt(lastIndex);
|
||||
}
|
||||
|
||||
if (children.Count == 0)
|
||||
_committedChildrenByParent.Remove(parent);
|
||||
}
|
||||
|
||||
private void RemoveCommittedParentReferences(uint parentGuid)
|
||||
{
|
||||
uint[] children = _lastAcceptedByChild
|
||||
.Where(pair => pair.Value.ParentGuid == parentGuid)
|
||||
.Select(pair => pair.Key)
|
||||
.ToArray();
|
||||
for (int i = 0; i < children.Length; i++)
|
||||
RemoveCommittedChild(children[i]);
|
||||
}
|
||||
|
||||
private static void RemoveParentReferences(
|
||||
|
|
@ -377,6 +456,10 @@ public sealed class ParentAttachmentState
|
|||
else
|
||||
_unresolvedByChild[childGuid] = retained;
|
||||
}
|
||||
|
||||
private readonly record struct ParentIncarnation(
|
||||
uint ServerGuid,
|
||||
ushort InstanceSequence);
|
||||
}
|
||||
|
||||
public readonly record struct ParentAttachmentRelation(
|
||||
|
|
|
|||
|
|
@ -254,6 +254,12 @@ public sealed class RuntimeEntityDirectory
|
|||
record.AdvanceMovementCommit();
|
||||
}
|
||||
|
||||
public void AdvancePlacementCommit(RuntimeEntityRecord record)
|
||||
{
|
||||
EnsureKnown(record);
|
||||
record.AdvancePlacementCommit();
|
||||
}
|
||||
|
||||
public void AdvanceParentCommit(RuntimeEntityRecord record)
|
||||
{
|
||||
EnsureKnown(record);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using AcDream.Core.Items;
|
||||
using AcDream.Runtime.Physics;
|
||||
|
||||
namespace AcDream.Runtime.Entities;
|
||||
|
||||
|
|
@ -14,6 +15,11 @@ public interface IRuntimeEntityObjectEventSource
|
|||
IDisposable Subscribe(IRuntimeEntityObjectObserver observer);
|
||||
}
|
||||
|
||||
public interface IRuntimePlacementObserver
|
||||
{
|
||||
void OnPlacement(in RuntimePlacementDelta delta);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// One synchronous, generation-stamped commit stream for the canonical entity
|
||||
/// directory and retained object table. It owns no cross-frame queue and
|
||||
|
|
@ -31,6 +37,7 @@ public sealed class RuntimeEntityObjectEventStream
|
|||
private readonly object _observerGate = new();
|
||||
private readonly List<PendingDispatch> _pendingDispatch = [];
|
||||
private IRuntimeEntityObjectObserver[] _observers = [];
|
||||
private IRuntimePlacementObserver[] _placementObservers = [];
|
||||
private Func<RuntimeGenerationToken> _generation = static () => default;
|
||||
private Func<ulong> _frameNumber = static () => 0UL;
|
||||
private bool _contextBound;
|
||||
|
|
@ -53,6 +60,8 @@ public sealed class RuntimeEntityObjectEventStream
|
|||
|
||||
public ulong LastSequence => _sequencer.LastSequence;
|
||||
public int SubscriberCount => Volatile.Read(ref _observers).Length;
|
||||
public int PlacementSubscriberCount =>
|
||||
Volatile.Read(ref _placementObservers).Length;
|
||||
public int PendingDispatchCount => _pendingDispatch.Count;
|
||||
public bool IsDispatching => _dispatching;
|
||||
public long DispatchFailureCount { get; private set; }
|
||||
|
|
@ -107,6 +116,26 @@ public sealed class RuntimeEntityObjectEventStream
|
|||
return new ObserverSubscription(this, observer);
|
||||
}
|
||||
|
||||
public IDisposable SubscribePlacement(IRuntimePlacementObserver observer)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(observer);
|
||||
lock (_observerGate)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
IRuntimePlacementObserver[] current = _placementObservers;
|
||||
if (Array.IndexOf(current, observer) >= 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The Runtime placement observer is already subscribed.");
|
||||
}
|
||||
var replacement = new IRuntimePlacementObserver[current.Length + 1];
|
||||
Array.Copy(current, replacement, current.Length);
|
||||
replacement[^1] = observer;
|
||||
Volatile.Write(ref _placementObservers, replacement);
|
||||
}
|
||||
return new PlacementObserverSubscription(this, observer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates the next stamp for another domain on the same Runtime event
|
||||
/// surface. J4–J6 will move those remaining domain publishers into
|
||||
|
|
@ -132,6 +161,15 @@ public sealed class RuntimeEntityObjectEventStream
|
|||
EnqueueAndDrain(PendingDispatch.ForEntity(delta));
|
||||
}
|
||||
|
||||
internal void PublishPlacement(
|
||||
in RuntimePlacementProjectionSnapshot placement)
|
||||
{
|
||||
var delta = new RuntimePlacementDelta(
|
||||
NextStamp(),
|
||||
placement);
|
||||
EnqueueAndDrain(PendingDispatch.ForPlacement(delta));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (_observerGate)
|
||||
|
|
@ -140,6 +178,7 @@ public sealed class RuntimeEntityObjectEventStream
|
|||
return;
|
||||
_disposed = true;
|
||||
Volatile.Write(ref _observers, []);
|
||||
Volatile.Write(ref _placementObservers, []);
|
||||
_objects.Cleared -= OnObjectsCleared;
|
||||
_objects.ObjectRemovalClassified -= OnObjectRemoved;
|
||||
_objects.ObjectMoved -= OnObjectMoved;
|
||||
|
|
@ -155,6 +194,7 @@ public sealed class RuntimeEntityObjectEventStream
|
|||
if (_disposed)
|
||||
return;
|
||||
Volatile.Write(ref _observers, []);
|
||||
Volatile.Write(ref _placementObservers, []);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -245,7 +285,7 @@ public sealed class RuntimeEntityObjectEventStream
|
|||
RuntimeEntityDelta delta = pending.Entity;
|
||||
observer.OnEntity(in delta);
|
||||
}
|
||||
else
|
||||
else if (pending.Kind is PendingDispatchKind.Inventory)
|
||||
{
|
||||
RuntimeInventoryDelta delta = pending.Inventory;
|
||||
observer.OnInventory(in delta);
|
||||
|
|
@ -256,6 +296,24 @@ public sealed class RuntimeEntityObjectEventStream
|
|||
RecordDispatchFailure(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (pending.Kind is PendingDispatchKind.Placement)
|
||||
{
|
||||
IRuntimePlacementObserver[] placementObservers =
|
||||
Volatile.Read(ref _placementObservers);
|
||||
foreach (IRuntimePlacementObserver observer in placementObservers)
|
||||
{
|
||||
try
|
||||
{
|
||||
RuntimePlacementDelta delta = pending.Placement;
|
||||
observer.OnPlacement(in delta);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
RecordDispatchFailure(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RecordDispatchFailure(Exception error)
|
||||
|
|
@ -295,24 +353,54 @@ public sealed class RuntimeEntityObjectEventStream
|
|||
}
|
||||
}
|
||||
|
||||
private void UnsubscribePlacement(IRuntimePlacementObserver observer)
|
||||
{
|
||||
lock (_observerGate)
|
||||
{
|
||||
IRuntimePlacementObserver[] current = _placementObservers;
|
||||
int index = Array.IndexOf(current, observer);
|
||||
if (index < 0)
|
||||
return;
|
||||
var replacement = new IRuntimePlacementObserver[current.Length - 1];
|
||||
if (index != 0)
|
||||
Array.Copy(current, 0, replacement, 0, index);
|
||||
if (index != current.Length - 1)
|
||||
{
|
||||
Array.Copy(
|
||||
current,
|
||||
index + 1,
|
||||
replacement,
|
||||
index,
|
||||
current.Length - index - 1);
|
||||
}
|
||||
Volatile.Write(ref _placementObservers, replacement);
|
||||
}
|
||||
}
|
||||
|
||||
private enum PendingDispatchKind : byte
|
||||
{
|
||||
Entity,
|
||||
Inventory,
|
||||
Placement,
|
||||
}
|
||||
|
||||
private readonly record struct PendingDispatch(
|
||||
PendingDispatchKind Kind,
|
||||
RuntimeEntityDelta Entity,
|
||||
RuntimeInventoryDelta Inventory)
|
||||
RuntimeInventoryDelta Inventory,
|
||||
RuntimePlacementDelta Placement)
|
||||
{
|
||||
public static PendingDispatch ForEntity(
|
||||
RuntimeEntityDelta entity) =>
|
||||
new(PendingDispatchKind.Entity, entity, default);
|
||||
new(PendingDispatchKind.Entity, entity, default, default);
|
||||
|
||||
public static PendingDispatch ForInventory(
|
||||
RuntimeInventoryDelta inventory) =>
|
||||
new(PendingDispatchKind.Inventory, default, inventory);
|
||||
new(PendingDispatchKind.Inventory, default, inventory, default);
|
||||
|
||||
public static PendingDispatch ForPlacement(
|
||||
RuntimePlacementDelta placement) =>
|
||||
new(PendingDispatchKind.Placement, default, default, placement);
|
||||
}
|
||||
|
||||
private sealed class ObserverSubscription(
|
||||
|
|
@ -325,4 +413,17 @@ public sealed class RuntimeEntityObjectEventStream
|
|||
public void Dispose() =>
|
||||
Interlocked.Exchange(ref _owner, null)?.Unsubscribe(observer);
|
||||
}
|
||||
|
||||
|
||||
private sealed class PlacementObserverSubscription(
|
||||
RuntimeEntityObjectEventStream owner,
|
||||
IRuntimePlacementObserver observer)
|
||||
: IDisposable
|
||||
{
|
||||
private RuntimeEntityObjectEventStream? _owner = owner;
|
||||
|
||||
public void Dispose() =>
|
||||
Interlocked.Exchange(ref _owner, null)?
|
||||
.UnsubscribePlacement(observer);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ public readonly record struct RuntimeEntityObjectOwnershipSnapshot(
|
|||
int EquipmentOwnerCount,
|
||||
int PendingMoveCount,
|
||||
int StreamSubscriberCount,
|
||||
int PlacementStreamSubscriberCount,
|
||||
long StreamDispatchFailureCount,
|
||||
bool HasLastStreamDispatchFailure,
|
||||
int PendingDispatchCount,
|
||||
|
|
@ -51,6 +52,7 @@ public readonly record struct RuntimeEntityObjectOwnershipSnapshot(
|
|||
&& EquipmentOwnerCount == 0
|
||||
&& PendingMoveCount == 0
|
||||
&& StreamSubscriberCount == 0
|
||||
&& PlacementStreamSubscriberCount == 0
|
||||
&& PendingDispatchCount == 0
|
||||
&& !IsDispatching
|
||||
&& !IsSessionClearInProgress;
|
||||
|
|
@ -95,10 +97,14 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
|
||||
public RuntimeEntityObjectLifetime(
|
||||
uint firstLocalEntityId = RuntimeEntityDirectory.FirstLocalEntityId,
|
||||
TimeProvider? timeProvider = null)
|
||||
TimeProvider? timeProvider = null,
|
||||
IGameRuntimeClock? gameClock = null)
|
||||
{
|
||||
Entities = new RuntimeEntityDirectory(firstLocalEntityId);
|
||||
Physics = new RuntimePhysicsState(Entities, timeProvider: timeProvider);
|
||||
Physics = new RuntimePhysicsState(
|
||||
Entities,
|
||||
timeProvider: timeProvider,
|
||||
gameClock: gameClock);
|
||||
Objects = new ClientObjectTable();
|
||||
// AP-129 (Campaign P Slice P4 review fix, 2026-07-30): the physics
|
||||
// entry-restriction gate (ObjectInfo.CheckEntryRestrictions) resolves
|
||||
|
|
@ -111,44 +117,51 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
EntityView = views.Entities;
|
||||
InventoryView = views.Inventory;
|
||||
Events = new RuntimeEntityObjectEventStream(Entities, Objects);
|
||||
Physics.SetPosition.BindEventStream(Events);
|
||||
}
|
||||
|
||||
internal RuntimeEntityObjectLifetime(
|
||||
PhysicsDataCache physicsDataCache,
|
||||
uint firstLocalEntityId = RuntimeEntityDirectory.FirstLocalEntityId,
|
||||
TimeProvider? timeProvider = null)
|
||||
TimeProvider? timeProvider = null,
|
||||
IGameRuntimeClock? gameClock = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(physicsDataCache);
|
||||
Entities = new RuntimeEntityDirectory(firstLocalEntityId);
|
||||
Physics = new RuntimePhysicsState(
|
||||
Entities,
|
||||
physicsDataCache,
|
||||
timeProvider);
|
||||
timeProvider,
|
||||
gameClock);
|
||||
Objects = new ClientObjectTable();
|
||||
Physics.Engine.Objects = Objects;
|
||||
var views = new RuntimeEntityObjectViews(Entities, Objects);
|
||||
EntityView = views.Entities;
|
||||
InventoryView = views.Inventory;
|
||||
Events = new RuntimeEntityObjectEventStream(Entities, Objects);
|
||||
Physics.SetPosition.BindEventStream(Events);
|
||||
}
|
||||
|
||||
internal RuntimeEntityObjectLifetime(
|
||||
PhysicsEngine physicsEngine,
|
||||
uint firstLocalEntityId = RuntimeEntityDirectory.FirstLocalEntityId,
|
||||
TimeProvider? timeProvider = null)
|
||||
TimeProvider? timeProvider = null,
|
||||
IGameRuntimeClock? gameClock = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(physicsEngine);
|
||||
Entities = new RuntimeEntityDirectory(firstLocalEntityId);
|
||||
Physics = new RuntimePhysicsState(
|
||||
Entities,
|
||||
physicsEngine,
|
||||
timeProvider);
|
||||
timeProvider,
|
||||
gameClock);
|
||||
Objects = new ClientObjectTable();
|
||||
Physics.Engine.Objects = Objects;
|
||||
var views = new RuntimeEntityObjectViews(Entities, Objects);
|
||||
EntityView = views.Entities;
|
||||
InventoryView = views.Inventory;
|
||||
Events = new RuntimeEntityObjectEventStream(Entities, Objects);
|
||||
Physics.SetPosition.BindEventStream(Events);
|
||||
}
|
||||
|
||||
public RuntimeEntityDirectory Entities { get; }
|
||||
|
|
@ -176,6 +189,7 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
Objects.EquipmentOwnerCount,
|
||||
Objects.PendingMoveCount,
|
||||
Events.SubscriberCount,
|
||||
Events.PlacementSubscriberCount,
|
||||
Events.DispatchFailureCount,
|
||||
Events.LastDispatchFailure is not null,
|
||||
Events.PendingDispatchCount,
|
||||
|
|
@ -421,6 +435,10 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
{
|
||||
EnsureNotDisposed();
|
||||
ArgumentNullException.ThrowIfNull(canonical);
|
||||
RuntimePlacementCancellationReceipt cancellation =
|
||||
Physics.SetPosition.Forget(
|
||||
canonical,
|
||||
releasePreparedMover: true);
|
||||
Physics.RemoveSpatialProjection(canonical);
|
||||
Entities.SetRemoteMotion(canonical, null);
|
||||
Entities.SetRemoteMotionBindingInProgress(canonical, false);
|
||||
|
|
@ -432,6 +450,7 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
Entities.SetPhysicsBodyAcquisitionInProgress(canonical, false);
|
||||
Entities.SetHasPartArray(canonical, false);
|
||||
Entities.ReleaseLocalId(canonical);
|
||||
Physics.SetPosition.PublishCancellation(cancellation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -510,6 +529,8 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
|
||||
Entities.RefreshSnapshot(canonical, accepted);
|
||||
Entities.AdvancePositionAuthority(canonical);
|
||||
RuntimePlacementCancellationReceipt cancellation =
|
||||
Physics.SetPosition.Forget(canonical);
|
||||
Entities.SuspendObjectClock(canonical);
|
||||
Entities.SetFullCell(canonical, 0u, 0u);
|
||||
Entities.ParentAttachments.EndChildProjection(update.Guid);
|
||||
|
|
@ -520,7 +541,8 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
() => acknowledgeProjection?.Invoke(canonical),
|
||||
RuntimeEntityChange.Withdrawn,
|
||||
() => canonical.PositionAuthorityVersion == positionVersion
|
||||
&& canonical.SpatialAuthorityVersion == spatialVersion);
|
||||
&& canonical.SpatialAuthorityVersion == spatialVersion,
|
||||
cancellation);
|
||||
}
|
||||
|
||||
public bool TryApplyCreateParent(
|
||||
|
|
@ -596,6 +618,8 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
return false;
|
||||
}
|
||||
|
||||
RuntimePlacementCancellationReceipt cancellation =
|
||||
Physics.SetPosition.Forget(canonical);
|
||||
Entities.SuspendObjectClock(canonical);
|
||||
Entities.SetFullCell(canonical, 0u, 0u);
|
||||
ulong spatialVersion = canonical.SpatialAuthorityVersion;
|
||||
|
|
@ -605,7 +629,8 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
RuntimeEntityChange.Withdrawn,
|
||||
() => canonical.PositionAuthorityVersion
|
||||
== positionAuthorityVersion
|
||||
&& canonical.SpatialAuthorityVersion == spatialVersion);
|
||||
&& canonical.SpatialAuthorityVersion == spatialVersion,
|
||||
cancellation);
|
||||
}
|
||||
|
||||
public bool TryApplyMotion(
|
||||
|
|
@ -764,6 +789,9 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
};
|
||||
}
|
||||
|
||||
RuntimePlacementCancellationReceipt cancellation = acceptedPosition
|
||||
? Physics.SetPosition.Forget(canonical)
|
||||
: default;
|
||||
Entities.RefreshSnapshot(
|
||||
canonical,
|
||||
snapshot,
|
||||
|
|
@ -793,7 +821,8 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
? RuntimeEntityChange.Rebucketed
|
||||
: RuntimeEntityChange.Updated,
|
||||
() => canonical.PositionAuthorityVersion == positionVersion
|
||||
&& canonical.SpatialAuthorityVersion == spatialVersion);
|
||||
&& canonical.SpatialAuthorityVersion == spatialVersion,
|
||||
cancellation);
|
||||
}
|
||||
|
||||
public bool CommitRebucket(
|
||||
|
|
@ -915,8 +944,13 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
&& active.Incarnation == delete.InstanceSequence
|
||||
&& Entities.RemoveActive(active))
|
||||
{
|
||||
RuntimePlacementCancellationReceipt cancellation =
|
||||
Physics.SetPosition.Forget(
|
||||
active,
|
||||
releasePreparedMover: true);
|
||||
retiredCanonical = active;
|
||||
Entities.RetainTeardown(active);
|
||||
Physics.SetPosition.PublishCancellation(cancellation);
|
||||
PublishEntity(
|
||||
removeRetainedObject
|
||||
? RuntimeEntityChange.Deleted
|
||||
|
|
@ -985,10 +1019,12 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
return Array.Empty<RuntimeEntityRecord>();
|
||||
|
||||
_sessionClearInProgress = true;
|
||||
Physics.SetPosition.ResetSession();
|
||||
Entities.BeginSessionClear();
|
||||
RuntimeEntityRecord[] active = Entities.ActiveRecords.ToArray();
|
||||
foreach (RuntimeEntityRecord canonical in active)
|
||||
{
|
||||
Physics.SetPosition.Forget(canonical);
|
||||
if (!Entities.RemoveActive(canonical))
|
||||
continue;
|
||||
Entities.RetainTeardown(canonical);
|
||||
|
|
@ -1119,6 +1155,8 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
|
||||
Entities.RefreshSnapshot(canonical, accepted);
|
||||
Entities.AdvancePositionAuthority(canonical);
|
||||
RuntimePlacementCancellationReceipt cancellation =
|
||||
Physics.SetPosition.Forget(canonical);
|
||||
ulong positionVersion = canonical.PositionAuthorityVersion;
|
||||
ulong spatialVersion = canonical.SpatialAuthorityVersion;
|
||||
return AcknowledgeProjectionAndPublish(
|
||||
|
|
@ -1126,7 +1164,8 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
() => acknowledgeProjection?.Invoke(canonical),
|
||||
RuntimeEntityChange.Updated,
|
||||
() => canonical.PositionAuthorityVersion == positionVersion
|
||||
&& canonical.SpatialAuthorityVersion == spatialVersion);
|
||||
&& canonical.SpatialAuthorityVersion == spatialVersion,
|
||||
cancellation);
|
||||
}
|
||||
|
||||
private void PublishEntity(
|
||||
|
|
@ -1138,8 +1177,12 @@ public sealed class RuntimeEntityObjectLifetime : IDisposable
|
|||
RuntimeEntityRecord canonical,
|
||||
Action acknowledgeProjection,
|
||||
RuntimeEntityChange change,
|
||||
Func<bool> matchesCommittedMutation)
|
||||
Func<bool> matchesCommittedMutation,
|
||||
RuntimePlacementCancellationReceipt cancellation = default)
|
||||
{
|
||||
Physics.SetPosition.PublishCancellation(cancellation);
|
||||
if (!IsExpectedCanonical(canonical, matchesCommittedMutation))
|
||||
return false;
|
||||
try
|
||||
{
|
||||
acknowledgeProjection();
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ public sealed class RuntimeEntityRecord
|
|||
public uint RawPhysicsState { get; internal set; }
|
||||
public PhysicsStateFlags FinalPhysicsState { get; internal set; }
|
||||
public ulong SpatialAuthorityVersion { get; private set; }
|
||||
public ulong PlacementCommitVersion { get; private set; }
|
||||
public ulong PhysicsStateMutationVersion { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -136,6 +137,8 @@ public sealed class RuntimeEntityRecord
|
|||
|
||||
internal void AdvanceMovementCommit() => MovementCommitVersion++;
|
||||
|
||||
internal void AdvancePlacementCommit() => PlacementCommitVersion++;
|
||||
|
||||
internal void AdvanceParentCommit() => ParentCommitVersion++;
|
||||
|
||||
internal void AdvanceObjDescAuthority() => ObjDescAuthorityVersion++;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue