fix(streaming): preserve exact live projection identity
Carry RuntimeEntityKey through spatial residency, visibility transitions, rebucketing, quiescence, landblock retirement, and origin recentering. This prevents stale incarnation edges from mutating a replacement projection while retaining the static DAT-entity path. Validated by 104 focused ownership/streaming tests, the Release solution build, and 8,444 complete Release tests with five existing skips. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
420e5eea70
commit
e937cc36df
8 changed files with 338 additions and 120 deletions
|
|
@ -951,7 +951,10 @@ public sealed class LiveEntityRuntime : ILiveEntityRadarSource
|
|||
{
|
||||
try
|
||||
{
|
||||
_spatial.RebucketLiveEntity(entity, spatialCellOrLandblockId);
|
||||
_spatial.RebucketLiveEntity(
|
||||
key,
|
||||
entity,
|
||||
spatialCellOrLandblockId);
|
||||
}
|
||||
catch (AggregateException error)
|
||||
{
|
||||
|
|
@ -974,7 +977,7 @@ public sealed class LiveEntityRuntime : ILiveEntityRadarSource
|
|||
runtimeNotificationFailure: null);
|
||||
return false;
|
||||
}
|
||||
bool visible = _spatial.IsLiveEntityProjectionResident(entity.Id);
|
||||
bool visible = _spatial.IsLiveEntityProjectionResident(key);
|
||||
record.IsSpatiallyVisible = visible;
|
||||
RefreshPresentation(record);
|
||||
if ((spatialCellOrLandblockId & 0xFFFFu) != 0xFFFFu)
|
||||
|
|
@ -1483,6 +1486,23 @@ public sealed class LiveEntityRuntime : ILiveEntityRadarSource
|
|||
return false;
|
||||
}
|
||||
|
||||
internal bool TryGetProjectionKey(
|
||||
uint serverGuid,
|
||||
out RuntimeEntityKey key)
|
||||
{
|
||||
if (_projections.TryGetCurrent(
|
||||
serverGuid,
|
||||
out LiveEntityRecord? record)
|
||||
&& record.ProjectionKey is { } found)
|
||||
{
|
||||
key = found;
|
||||
return true;
|
||||
}
|
||||
|
||||
key = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool ClearAnimationRuntime(uint serverGuid)
|
||||
{
|
||||
if (!_projections.TryGetCurrent(serverGuid, out LiveEntityRecord? record)
|
||||
|
|
@ -2942,25 +2962,26 @@ public sealed class LiveEntityRuntime : ILiveEntityRadarSource
|
|||
}
|
||||
}
|
||||
|
||||
private void OnSpatialVisibilityChanged(uint localEntityId, bool visible)
|
||||
private void OnSpatialVisibilityChanged(RuntimeEntityKey key, bool visible)
|
||||
{
|
||||
// GpuWorldState serializes re-entrant visibility callbacks. A callback
|
||||
// may delete an incarnation and materialize the same server GUID before
|
||||
// an older queued edge drains. Apply an edge only while it still
|
||||
// matches current spatial truth; otherwise it belongs to the displaced
|
||||
// projection and must not mutate the replacement generation.
|
||||
if (_spatial.IsLiveEntityProjectionResident(localEntityId) != visible)
|
||||
if (_spatial.IsLiveEntityProjectionResident(key) != visible)
|
||||
return;
|
||||
if (!_projections.TryGetByLocalId(
|
||||
localEntityId,
|
||||
if (!_projections.TryGet(
|
||||
key,
|
||||
out LiveEntityRecord? record)
|
||||
|| record.WorldEntity is not { } entity
|
||||
|| entity.Id != localEntityId)
|
||||
|| entity.Id != key.LocalEntityId)
|
||||
return;
|
||||
|
||||
uint serverGuid = record.ServerGuid;
|
||||
bool wasVisible = record.IsSpatiallyVisible;
|
||||
RuntimeEntityKey key = RequireProjectionKey(record);
|
||||
if (RequireProjectionKey(record) != key)
|
||||
return;
|
||||
bool wasOrdinaryRoot = _spatialRootObjects.TryGetValue(
|
||||
key,
|
||||
out LiveEntityRecord? indexedRoot)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue