refactor(app): key live projections by runtime identity

Move materialized live-object sidecars and presentation worksets to exact RuntimeEntityKey ownership. Runtime remains the only GUID/incarnation/local-ID authority while hydration, animation, effects, lights, equipped children, renderer resources, visibility, liveness, and teardown resolve exact projection identities. Preserve synchronous callbacks, local-ID allocation order, and current rendering behavior.
This commit is contained in:
Erik 2026-07-25 21:50:58 +02:00
parent e18df84437
commit 420e5eea70
73 changed files with 2939 additions and 1715 deletions

View file

@ -65,11 +65,6 @@ internal sealed class LiveEntityNetworkUpdateController
private EntityPhysicsHost? _playerHost => _playerHostSource.Host;
private uint _playerServerGuid => _playerIdentity.ServerGuid;
private double _physicsScriptGameTime => _gameTime.CurrentScriptTime;
private IReadOnlyDictionary<uint, WorldEntity> _entitiesByServerGuid =>
_liveEntities.MaterializedWorldEntities;
private IReadOnlyDictionary<uint, WorldEntity> _visibleEntitiesByServerGuid =>
_liveEntities.WorldEntities;
internal uint? LastLivePlayerLandblockId =>
_authorityGate.LastLivePlayerLandblockId;
@ -262,7 +257,7 @@ internal sealed class LiveEntityNetworkUpdateController
ulong acceptedMovementVelocityAuthorityVersion =
accepted.VelocityAuthorityVersion;
if (!_entitiesByServerGuid.TryGetValue(update.Guid, out var entity)) return;
if (!_liveEntities.TryGetWorldEntity(update.Guid, out var entity)) return;
if (!_animatedEntities.TryGetValue(entity.Id, out var ae))
{
DispatchRemoteInboundMotion(
@ -566,8 +561,14 @@ internal sealed class LiveEntityNetworkUpdateController
// window from this transition. Without this, the entity
// starts its run animation and is instantly interrupted.
var refreshedTime = System.DateTime.UtcNow;
if (_remoteMovementObservations.TryGetValue(update.Guid, out var prev))
_remoteMovementObservations[update.Guid] = (prev.Pos, refreshedTime);
if (acceptedMotionRecord.ProjectionKey is { } motionKey
&& _remoteMovementObservations.TryGetValue(
motionKey,
out var prev))
{
_remoteMovementObservations[motionKey] =
(prev.Pos, refreshedTime);
}
if (_remoteDeadReckon.TryGetValue(update.Guid, out var dr))
dr.LastServerPosTime = (refreshedTime - System.DateTime.UnixEpoch).TotalSeconds;
}
@ -817,7 +818,7 @@ internal sealed class LiveEntityNetworkUpdateController
ulong acceptedVectorAuthorityVersion,
ulong acceptedVectorVelocityAuthorityVersion)
{
if (!_entitiesByServerGuid.ContainsKey(update.Guid)) return;
if (!_liveEntities.ContainsWorldEntity(update.Guid)) return;
if (update.Guid == _playerServerGuid) return; // local jump uses our own physics
if (!_remoteDeadReckon.TryGetValue(update.Guid, out var rm)) return;
@ -863,7 +864,7 @@ internal sealed class LiveEntityNetworkUpdateController
// state-derived velocity write (adaptation note: retail's
// equivalence comes from the per-tick transition-sweep order —
// R6 scope).
if (_entitiesByServerGuid.TryGetValue(update.Guid, out var ent)
if (_liveEntities.TryGetWorldEntity(update.Guid, out var ent)
&& _animatedEntities.TryGetValue(ent.Id, out var ae)
&& ae.Sequencer is not null)
{
@ -940,12 +941,12 @@ internal sealed class LiveEntityNetworkUpdateController
if (parsed.Guid == _playerServerGuid)
_playerController?.ApplyPhysicsState(record.FinalPhysicsState);
if (!_entitiesByServerGuid.TryGetValue(parsed.Guid, out var entity)) return;
if (!_liveEntities.TryGetWorldEntity(parsed.Guid, out var entity)) return;
// L.2g slice 1c (2026-05-13): the server addresses entities by
// ServerGuid (parsed.Guid, e.g. 0x7A9B4015), but
// ShadowObjectRegistry's cell index is keyed by local entity.Id
// (e.g. 0x000F4245). Translate via _entitiesByServerGuid before
// (e.g. 0x000F4245). Translate through the canonical Runtime directory before
// mutating the registry — otherwise the lookup misses and the
// state flip silently no-ops, leaving doors blocked even though
// ACE flipped the ETHEREAL bit.
@ -1035,7 +1036,7 @@ internal sealed class LiveEntityNetworkUpdateController
_playerController)))
return;
if (!_entitiesByServerGuid.ContainsKey(update.Guid))
if (!_liveEntities.ContainsWorldEntity(update.Guid))
{
if (!IsCurrentPositionOwner())
return;
@ -1068,7 +1069,7 @@ internal sealed class LiveEntityNetworkUpdateController
}
}
if (!_entitiesByServerGuid.TryGetValue(update.Guid, out var entity)) return;
if (!_liveEntities.TryGetWorldEntity(update.Guid, out var entity)) return;
if (!IsCurrentPositionOwner(entity))
return;
_entityEffects?.MarkLiveOwnerPoseDirty(update.Guid);
@ -1223,16 +1224,20 @@ internal sealed class LiveEntityNetworkUpdateController
if (update.Guid != _playerServerGuid)
{
var now = System.DateTime.UtcNow;
if (_remoteMovementObservations.TryGetValue(update.Guid, out var prev))
RuntimeEntityKey positionKey = positionRecord.ProjectionKey
?? throw new InvalidOperationException(
$"Position owner 0x{update.Guid:X8}/" +
$"{positionRecord.Generation} has no exact projection key.");
if (_remoteMovementObservations.TryGetValue(positionKey, out var prev))
{
float moveDist = System.Numerics.Vector3.Distance(prev.Pos, worldPos);
if (moveDist > 0.05f)
_remoteMovementObservations[update.Guid] = (worldPos, now);
_remoteMovementObservations[positionKey] = (worldPos, now);
// else: leave old entry so "Time" = last real movement time
}
else
{
_remoteMovementObservations[update.Guid] = (worldPos, now);
_remoteMovementObservations[positionKey] = (worldPos, now);
}
// Retail-faithful hard-snap on UpdatePosition.