feat(runtime): commit dormant SetPosition activation
This commit is contained in:
parent
99f867f053
commit
5785a07b3e
13 changed files with 4674 additions and 111 deletions
|
|
@ -79,6 +79,10 @@ public sealed class ShadowObjectRegistry
|
|||
private readonly HashSet<uint> _prefixScratch = new();
|
||||
private readonly List<uint> _removedPrefixScratch = new();
|
||||
private ulong _mutationRevision;
|
||||
private ulong _nextPreparedSetPositionCommitId;
|
||||
private ulong _lastAppliedSetPositionCommitId;
|
||||
private readonly HashSet<ulong> _pendingSetPositionDispatches = [];
|
||||
private long _setPositionDispatchFailureCount;
|
||||
internal event Action<uint, ulong>? OwnerMutated;
|
||||
internal event Action<uint, uint>? OwnerPrefixMembershipChanged;
|
||||
|
||||
|
|
@ -783,6 +787,553 @@ public sealed class ShadowObjectRegistry
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Immutable owner-local shadow transaction prepared before Runtime's
|
||||
/// SetPosition publication tail. The prepared rows are built against an
|
||||
/// isolated registry; applying them never re-runs the flood oracle.
|
||||
/// </summary>
|
||||
internal sealed record PreparedSetPositionShadowCommit(
|
||||
ulong CommitId,
|
||||
uint EntityId,
|
||||
ulong ExpectedMutationRevision,
|
||||
ulong ExpectedOwnerVersion,
|
||||
ulong FinalMutationRevision,
|
||||
ulong FinalOwnerVersion,
|
||||
bool ProvenShapeless,
|
||||
PreparedShadowOwnerState? OwnerState,
|
||||
PreparedShadowCellReplacement[] CellReplacements,
|
||||
PreparedShadowPrefixReplacement[] PrefixReplacements,
|
||||
HashSet<uint>? OwnerPrefixes,
|
||||
uint[] ChangedPrefixes);
|
||||
|
||||
internal sealed record PreparedShadowCellReplacement(
|
||||
uint CellId,
|
||||
List<ShadowEntry> Entries);
|
||||
|
||||
internal sealed record PreparedShadowPrefixReplacement(
|
||||
uint Prefix,
|
||||
bool Remove,
|
||||
List<uint>? Slots,
|
||||
Dictionary<uint, int>? Indices,
|
||||
Stack<int>? FreeSlots);
|
||||
|
||||
internal readonly record struct SetPositionShadowCommitReceipt(
|
||||
ulong CommitId,
|
||||
uint EntityId,
|
||||
ulong OwnerVersion,
|
||||
uint[] ChangedPrefixes,
|
||||
bool Mutated)
|
||||
{
|
||||
internal bool IsValid => CommitId != 0UL && EntityId != 0u;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepares the complete owner-row replacement without touching the active
|
||||
/// collision world. A missing registration is accepted only when the
|
||||
/// caller carries an explicit proven-shapeless disposition; absence alone
|
||||
/// is not evidence because an authored BSP payload may still be pending.
|
||||
/// </summary>
|
||||
internal bool TryPrepareSetPosition(
|
||||
uint entityId,
|
||||
Vector3 worldPosition,
|
||||
Quaternion worldRotation,
|
||||
uint seedCellId,
|
||||
float worldOffsetX,
|
||||
float worldOffsetY,
|
||||
PhysicsShadowCommitAction action,
|
||||
System.Collections.Immutable.ImmutableArray<uint> crossCellIds,
|
||||
bool provenShapeless,
|
||||
bool suspendOwner,
|
||||
out PreparedSetPositionShadowCommit? prepared)
|
||||
{
|
||||
prepared = null;
|
||||
ulong expectedMutation = _mutationRevision;
|
||||
ulong expectedOwner = GetOwnerVersion(entityId);
|
||||
bool hasOwner = TryCaptureOwnerState(
|
||||
entityId,
|
||||
out PreparedShadowOwnerState? source);
|
||||
if (!hasOwner)
|
||||
{
|
||||
if (!provenShapeless)
|
||||
return false;
|
||||
_pendingSetPositionDispatches.EnsureCapacity(
|
||||
_pendingSetPositionDispatches.Count + 1);
|
||||
prepared = new PreparedSetPositionShadowCommit(
|
||||
checked(++_nextPreparedSetPositionCommitId),
|
||||
entityId,
|
||||
expectedMutation,
|
||||
expectedOwner,
|
||||
expectedMutation,
|
||||
expectedOwner,
|
||||
ProvenShapeless: true,
|
||||
OwnerState: null,
|
||||
CellReplacements: [],
|
||||
PrefixReplacements: [],
|
||||
OwnerPrefixes: null,
|
||||
ChangedPrefixes: Array.Empty<uint>());
|
||||
return _mutationRevision == expectedMutation
|
||||
&& GetOwnerVersion(entityId) == expectedOwner
|
||||
&& !HasLogicalOwner(entityId);
|
||||
}
|
||||
if (provenShapeless || source is null)
|
||||
return false;
|
||||
|
||||
var staging = new ShadowObjectRegistry
|
||||
{
|
||||
DataCache = DataCache,
|
||||
};
|
||||
staging.InstallOwnerState(source);
|
||||
if (suspendOwner)
|
||||
{
|
||||
if (!staging.Suspend(entityId))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
staging.CommitSetPosition(
|
||||
entityId,
|
||||
worldPosition,
|
||||
worldRotation,
|
||||
seedCellId,
|
||||
worldOffsetX,
|
||||
worldOffsetY,
|
||||
action,
|
||||
crossCellIds);
|
||||
}
|
||||
if (!staging.TryCaptureOwnerState(
|
||||
entityId,
|
||||
out PreparedShadowOwnerState? replacement)
|
||||
|| replacement is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint[] changedPrefixes = CaptureChangedPrefixes(source, replacement);
|
||||
PreparedShadowCellReplacement[] cellReplacements =
|
||||
PrepareCellReplacements(entityId, source, replacement);
|
||||
HashSet<uint> replacementPrefixes = CapturePrefixes(replacement);
|
||||
PreparedShadowPrefixReplacement[] prefixReplacements =
|
||||
PreparePrefixReplacements(
|
||||
entityId,
|
||||
CapturePrefixes(source),
|
||||
replacementPrefixes,
|
||||
changedPrefixes);
|
||||
ulong finalMutation = checked(expectedMutation + 1UL);
|
||||
ulong finalOwner = checked(expectedOwner + 1UL);
|
||||
// Reserve dictionary capacity before the non-fallible publication
|
||||
// suffix. Row/list payloads themselves were allocated in staging.
|
||||
_cells.EnsureCapacity(_cells.Count + replacement.Rows.Count);
|
||||
_entityToCells.EnsureCapacity(_entityToCells.Count + 1);
|
||||
_entityReg.EnsureCapacity(_entityReg.Count + 1);
|
||||
_entityShapes.EnsureCapacity(_entityShapes.Count + 1);
|
||||
_suspendedEntityCells.EnsureCapacity(_suspendedEntityCells.Count + 1);
|
||||
_withdrawnPrefixesByOwner.EnsureCapacity(
|
||||
_withdrawnPrefixesByOwner.Count + 1);
|
||||
_ownerVersions.EnsureCapacity(_ownerVersions.Count + 1);
|
||||
_ownerPrefixes.EnsureCapacity(_ownerPrefixes.Count + 1);
|
||||
_prefixOwnerSlots.EnsureCapacity(
|
||||
_prefixOwnerSlots.Count + changedPrefixes.Length);
|
||||
_prefixOwnerIndices.EnsureCapacity(
|
||||
_prefixOwnerIndices.Count + changedPrefixes.Length);
|
||||
_prefixFreeSlots.EnsureCapacity(
|
||||
_prefixFreeSlots.Count + changedPrefixes.Length);
|
||||
_suspendedEntities.EnsureCapacity(_suspendedEntities.Count + 1);
|
||||
_pendingSetPositionDispatches.EnsureCapacity(
|
||||
_pendingSetPositionDispatches.Count + 1);
|
||||
|
||||
prepared = new PreparedSetPositionShadowCommit(
|
||||
checked(++_nextPreparedSetPositionCommitId),
|
||||
entityId,
|
||||
expectedMutation,
|
||||
expectedOwner,
|
||||
finalMutation,
|
||||
finalOwner,
|
||||
ProvenShapeless: false,
|
||||
replacement,
|
||||
cellReplacements,
|
||||
prefixReplacements,
|
||||
replacementPrefixes,
|
||||
changedPrefixes);
|
||||
return _mutationRevision == expectedMutation
|
||||
&& GetOwnerVersion(entityId) == expectedOwner
|
||||
&& HasLogicalOwner(entityId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a previously prepared owner-local row swap with callbacks
|
||||
/// suppressed. Runtime dispatches the returned exact notification only
|
||||
/// after the complete SetPosition state suffix is visible.
|
||||
/// </summary>
|
||||
internal bool TryApplySetPosition(
|
||||
PreparedSetPositionShadowCommit prepared,
|
||||
out SetPositionShadowCommitReceipt receipt)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(prepared);
|
||||
receipt = default;
|
||||
if (prepared.CommitId <= _lastAppliedSetPositionCommitId
|
||||
|| _mutationRevision != prepared.ExpectedMutationRevision
|
||||
|| GetOwnerVersion(prepared.EntityId)
|
||||
!= prepared.ExpectedOwnerVersion
|
||||
|| HasLogicalOwner(prepared.EntityId)
|
||||
== prepared.ProvenShapeless)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (prepared.ProvenShapeless)
|
||||
{
|
||||
receipt = new SetPositionShadowCommitReceipt(
|
||||
prepared.CommitId,
|
||||
prepared.EntityId,
|
||||
prepared.ExpectedOwnerVersion,
|
||||
Array.Empty<uint>(),
|
||||
Mutated: false);
|
||||
_lastAppliedSetPositionCommitId = prepared.CommitId;
|
||||
_pendingSetPositionDispatches.Add(prepared.CommitId);
|
||||
return true;
|
||||
}
|
||||
if (prepared.OwnerState is null)
|
||||
return false;
|
||||
|
||||
for (int index = 0; index < prepared.CellReplacements.Length; index++)
|
||||
{
|
||||
PreparedShadowCellReplacement replacement =
|
||||
prepared.CellReplacements[index];
|
||||
_cells[replacement.CellId] = replacement.Entries;
|
||||
}
|
||||
PreparedShadowOwnerState state = prepared.OwnerState;
|
||||
_entityReg[prepared.EntityId] = state.Registration;
|
||||
ReplaceOwnerValue(_entityShapes, prepared.EntityId, state.Shapes);
|
||||
if (state.Suspended)
|
||||
_suspendedEntities.Add(prepared.EntityId);
|
||||
else
|
||||
_suspendedEntities.Remove(prepared.EntityId);
|
||||
ReplaceOwnerValue(
|
||||
_suspendedEntityCells,
|
||||
prepared.EntityId,
|
||||
state.SuspendedCellIds);
|
||||
ReplaceOwnerValue(
|
||||
_withdrawnPrefixesByOwner,
|
||||
prepared.EntityId,
|
||||
state.WithdrawnPrefixes);
|
||||
ReplaceOwnerValue(
|
||||
_entityToCells,
|
||||
prepared.EntityId,
|
||||
state.CellIds);
|
||||
if (prepared.OwnerPrefixes is not null)
|
||||
_ownerPrefixes[prepared.EntityId] = prepared.OwnerPrefixes;
|
||||
for (int index = 0; index < prepared.PrefixReplacements.Length; index++)
|
||||
{
|
||||
PreparedShadowPrefixReplacement replacement =
|
||||
prepared.PrefixReplacements[index];
|
||||
if (replacement.Remove)
|
||||
{
|
||||
_prefixOwnerSlots.Remove(replacement.Prefix);
|
||||
_prefixOwnerIndices.Remove(replacement.Prefix);
|
||||
_prefixFreeSlots.Remove(replacement.Prefix);
|
||||
continue;
|
||||
}
|
||||
_prefixOwnerSlots[replacement.Prefix] = replacement.Slots!;
|
||||
_prefixOwnerIndices[replacement.Prefix] = replacement.Indices!;
|
||||
_prefixFreeSlots[replacement.Prefix] = replacement.FreeSlots!;
|
||||
}
|
||||
_mutationRevision = prepared.FinalMutationRevision;
|
||||
_ownerVersions[prepared.EntityId] = prepared.FinalOwnerVersion;
|
||||
_lastAppliedSetPositionCommitId = prepared.CommitId;
|
||||
_pendingSetPositionDispatches.Add(prepared.CommitId);
|
||||
receipt = new SetPositionShadowCommitReceipt(
|
||||
prepared.CommitId,
|
||||
prepared.EntityId,
|
||||
prepared.FinalOwnerVersion,
|
||||
prepared.ChangedPrefixes,
|
||||
Mutated: true);
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool IsPreparedSetPositionCurrent(
|
||||
PreparedSetPositionShadowCommit prepared)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(prepared);
|
||||
return prepared.CommitId > _lastAppliedSetPositionCommitId
|
||||
&& _mutationRevision == prepared.ExpectedMutationRevision
|
||||
&& GetOwnerVersion(prepared.EntityId)
|
||||
== prepared.ExpectedOwnerVersion
|
||||
&& HasLogicalOwner(prepared.EntityId)
|
||||
!= prepared.ProvenShapeless;
|
||||
}
|
||||
|
||||
internal void DispatchSetPositionCommit(
|
||||
in SetPositionShadowCommitReceipt receipt)
|
||||
{
|
||||
if (!receipt.IsValid
|
||||
|| receipt.CommitId > _lastAppliedSetPositionCommitId
|
||||
|| !_pendingSetPositionDispatches.Remove(receipt.CommitId))
|
||||
return;
|
||||
if (!receipt.Mutated)
|
||||
return;
|
||||
ulong currentOwnerVersion = GetOwnerVersion(receipt.EntityId);
|
||||
if (!HasLogicalOwner(receipt.EntityId)
|
||||
|| currentOwnerVersion != receipt.OwnerVersion)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (int index = 0; index < receipt.ChangedPrefixes.Length; index++)
|
||||
{
|
||||
if (!HasLogicalOwner(receipt.EntityId)
|
||||
|| GetOwnerVersion(receipt.EntityId) != receipt.OwnerVersion)
|
||||
{
|
||||
return;
|
||||
}
|
||||
DispatchSetPositionPrefixObservers(
|
||||
receipt.EntityId,
|
||||
receipt.ChangedPrefixes[index]);
|
||||
}
|
||||
if (!HasLogicalOwner(receipt.EntityId))
|
||||
return;
|
||||
currentOwnerVersion = GetOwnerVersion(receipt.EntityId);
|
||||
if (currentOwnerVersion != receipt.OwnerVersion)
|
||||
return;
|
||||
DispatchSetPositionOwnerObservers(
|
||||
receipt.EntityId,
|
||||
currentOwnerVersion);
|
||||
}
|
||||
|
||||
internal bool DiscardSetPositionCommit(
|
||||
in SetPositionShadowCommitReceipt receipt) =>
|
||||
receipt.IsValid
|
||||
&& _pendingSetPositionDispatches.Remove(receipt.CommitId);
|
||||
|
||||
internal int PendingSetPositionDispatchCount =>
|
||||
_pendingSetPositionDispatches.Count;
|
||||
|
||||
internal long SetPositionDispatchFailureCount =>
|
||||
_setPositionDispatchFailureCount;
|
||||
|
||||
private void DispatchSetPositionPrefixObservers(uint owner, uint prefix)
|
||||
{
|
||||
Action<uint, uint>? observers = OwnerPrefixMembershipChanged;
|
||||
if (observers is null)
|
||||
return;
|
||||
foreach (Action<uint, uint> observer in observers.GetInvocationList())
|
||||
{
|
||||
try
|
||||
{
|
||||
observer(owner, prefix);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_setPositionDispatchFailureCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DispatchSetPositionOwnerObservers(uint owner, ulong version)
|
||||
{
|
||||
Action<uint, ulong>? observers = OwnerMutated;
|
||||
if (observers is null)
|
||||
return;
|
||||
foreach (Action<uint, ulong> observer in observers.GetInvocationList())
|
||||
{
|
||||
try
|
||||
{
|
||||
observer(owner, version);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_setPositionDispatchFailureCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static uint[] CaptureChangedPrefixes(
|
||||
PreparedShadowOwnerState before,
|
||||
PreparedShadowOwnerState after)
|
||||
{
|
||||
HashSet<uint> oldPrefixes = CapturePrefixes(before);
|
||||
HashSet<uint> newPrefixes = CapturePrefixes(after);
|
||||
var changed = new List<uint>();
|
||||
foreach (uint prefix in oldPrefixes)
|
||||
{
|
||||
if (!newPrefixes.Contains(prefix))
|
||||
changed.Add(prefix);
|
||||
}
|
||||
foreach (uint prefix in newPrefixes)
|
||||
{
|
||||
if (!oldPrefixes.Contains(prefix))
|
||||
changed.Add(prefix);
|
||||
}
|
||||
changed.Sort();
|
||||
return changed.ToArray();
|
||||
}
|
||||
|
||||
private static HashSet<uint> CapturePrefixes(
|
||||
PreparedShadowOwnerState state)
|
||||
{
|
||||
var prefixes = new HashSet<uint>
|
||||
{
|
||||
state.Registration.SeedCellId & 0xFFFF0000u,
|
||||
};
|
||||
if (state.CellIds is not null)
|
||||
{
|
||||
for (int index = 0; index < state.CellIds.Count; index++)
|
||||
prefixes.Add(state.CellIds[index] & 0xFFFF0000u);
|
||||
}
|
||||
if (state.WithdrawnPrefixes is not null)
|
||||
{
|
||||
foreach (uint prefix in state.WithdrawnPrefixes)
|
||||
prefixes.Add(prefix & 0xFFFF0000u);
|
||||
}
|
||||
return prefixes;
|
||||
}
|
||||
|
||||
private PreparedShadowCellReplacement[] PrepareCellReplacements(
|
||||
uint entityId,
|
||||
PreparedShadowOwnerState before,
|
||||
PreparedShadowOwnerState after)
|
||||
{
|
||||
var touched = new HashSet<uint>();
|
||||
AddCells(touched, before.CellIds);
|
||||
AddCells(touched, after.CellIds);
|
||||
var afterRows = new Dictionary<uint, ShadowEntry[]>();
|
||||
for (int index = 0; index < after.Rows.Count; index++)
|
||||
{
|
||||
PreparedShadowCellRows row = after.Rows[index];
|
||||
touched.Add(row.CellId);
|
||||
afterRows[row.CellId] = row.Entries;
|
||||
}
|
||||
for (int index = 0; index < before.Rows.Count; index++)
|
||||
touched.Add(before.Rows[index].CellId);
|
||||
|
||||
uint[] ordered = touched.ToArray();
|
||||
Array.Sort(ordered);
|
||||
var result = new PreparedShadowCellReplacement[ordered.Length];
|
||||
for (int index = 0; index < ordered.Length; index++)
|
||||
{
|
||||
uint cellId = ordered[index];
|
||||
_cells.TryGetValue(cellId, out List<ShadowEntry>? active);
|
||||
afterRows.TryGetValue(cellId, out ShadowEntry[]? ownerRows);
|
||||
int retainedCount = 0;
|
||||
if (active is not null)
|
||||
{
|
||||
for (int row = 0; row < active.Count; row++)
|
||||
{
|
||||
if (active[row].EntityId != entityId)
|
||||
retainedCount++;
|
||||
}
|
||||
}
|
||||
var replacement = new List<ShadowEntry>(
|
||||
retainedCount + (ownerRows?.Length ?? 0));
|
||||
if (active is not null)
|
||||
{
|
||||
for (int row = 0; row < active.Count; row++)
|
||||
{
|
||||
if (active[row].EntityId != entityId)
|
||||
replacement.Add(active[row]);
|
||||
}
|
||||
}
|
||||
if (ownerRows is not null)
|
||||
replacement.AddRange(ownerRows);
|
||||
result[index] = new PreparedShadowCellReplacement(
|
||||
cellId,
|
||||
replacement);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private PreparedShadowPrefixReplacement[] PreparePrefixReplacements(
|
||||
uint entityId,
|
||||
HashSet<uint> before,
|
||||
HashSet<uint> after,
|
||||
uint[] changedPrefixes)
|
||||
{
|
||||
var result = new PreparedShadowPrefixReplacement[
|
||||
changedPrefixes.Length];
|
||||
for (int index = 0; index < changedPrefixes.Length; index++)
|
||||
{
|
||||
uint prefix = changedPrefixes[index];
|
||||
bool removeOwner = before.Contains(prefix)
|
||||
&& !after.Contains(prefix);
|
||||
_prefixOwnerSlots.TryGetValue(prefix, out List<uint>? oldSlots);
|
||||
_prefixOwnerIndices.TryGetValue(
|
||||
prefix,
|
||||
out Dictionary<uint, int>? oldIndices);
|
||||
_prefixFreeSlots.TryGetValue(prefix, out Stack<int>? oldFree);
|
||||
var slots = oldSlots is null ? [] : new List<uint>(oldSlots);
|
||||
var indices = oldIndices is null
|
||||
? new Dictionary<uint, int>()
|
||||
: new Dictionary<uint, int>(oldIndices);
|
||||
Stack<int> free = CloneStack(oldFree);
|
||||
if (removeOwner)
|
||||
{
|
||||
if (indices.Remove(entityId, out int ownerSlot))
|
||||
{
|
||||
slots[ownerSlot] = 0u;
|
||||
free.Push(ownerSlot);
|
||||
}
|
||||
result[index] = indices.Count == 0
|
||||
? new PreparedShadowPrefixReplacement(
|
||||
prefix,
|
||||
Remove: true,
|
||||
Slots: null,
|
||||
Indices: null,
|
||||
FreeSlots: null)
|
||||
: new PreparedShadowPrefixReplacement(
|
||||
prefix,
|
||||
Remove: false,
|
||||
slots,
|
||||
indices,
|
||||
free);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!indices.ContainsKey(entityId))
|
||||
{
|
||||
if (free.TryPop(out int freeIndex))
|
||||
{
|
||||
slots[freeIndex] = entityId;
|
||||
indices[entityId] = freeIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
indices[entityId] = slots.Count;
|
||||
slots.Add(entityId);
|
||||
}
|
||||
}
|
||||
result[index] = new PreparedShadowPrefixReplacement(
|
||||
prefix,
|
||||
Remove: false,
|
||||
slots,
|
||||
indices,
|
||||
free);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Stack<int> CloneStack(Stack<int>? source) =>
|
||||
source is null
|
||||
? new Stack<int>()
|
||||
: new Stack<int>(source.Reverse());
|
||||
|
||||
private static void AddCells(HashSet<uint> destination, List<uint>? cells)
|
||||
{
|
||||
if (cells is null)
|
||||
return;
|
||||
for (int index = 0; index < cells.Count; index++)
|
||||
destination.Add(cells[index]);
|
||||
}
|
||||
|
||||
private static void ReplaceOwnerValue<T>(
|
||||
Dictionary<uint, T> destination,
|
||||
uint entityId,
|
||||
T? value)
|
||||
where T : class
|
||||
{
|
||||
if (value is null)
|
||||
destination.Remove(entityId);
|
||||
else
|
||||
destination[entityId] = value;
|
||||
}
|
||||
|
||||
private void RefreshPositionRows(
|
||||
uint entityId,
|
||||
RegistrationRecord registration,
|
||||
|
|
@ -1896,7 +2447,9 @@ public sealed class ShadowObjectRegistry
|
|||
|| _entityToCells.Count != 0
|
||||
|| _entityReg.Count != 0
|
||||
|| _suspendedEntities.Count != 0
|
||||
|| _suspendedEntityCells.Count != 0;
|
||||
|| _suspendedEntityCells.Count != 0
|
||||
|| _nextPreparedSetPositionCommitId
|
||||
!= _lastAppliedSetPositionCommitId;
|
||||
if (mutated)
|
||||
AdvanceMutationRevision();
|
||||
_cells.Clear();
|
||||
|
|
@ -1916,6 +2469,7 @@ public sealed class ShadowObjectRegistry
|
|||
_ownerFreeSlots.Clear();
|
||||
_prefixScratch.Clear();
|
||||
_removedPrefixScratch.Clear();
|
||||
_pendingSetPositionDispatches.Clear();
|
||||
_fallback = null;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue