diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs index 9f10a2772..47cd5fed1 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -1978,6 +1978,12 @@ static void RequireDaemonRenderQueryAuthority(string root) "ZoneRenderer must reconcile rendered entities from daemon frame snapshots instead of subscribing to mirrored Unity entity collection mutations."); } + if (zoneRenderer.Contains("foreach (var entity in legacyEntityFacadeZone.Entities)", StringComparison.Ordinal)) + { + throw new InvalidOperationException( + "ZoneRenderer must use the daemon-indexed observed facade projection supplied by gameplay instead of enumerating Unity Zone.Entities."); + } + if (zoneRenderer.Contains("PerspectiveEntity.EntityInfoGathered", StringComparison.Ordinal)) { throw new InvalidOperationException( @@ -2015,6 +2021,7 @@ static void RequireDaemonRenderQueryAuthority(string root) "private Zone _legacyEntityFacadeZone;", "public void LoadDaemonZoneView(", "Zone legacyEntityFacadeZone,", + "IReadOnlyDictionary observedEntityFacadesByDaemonIndex,", "AetheriaRuntimeRunCheckpointCommit daemonRun = null", "public void ApplyDaemonFrame(", "AetheriaRuntimeZoneSnapshotCommit daemonZone,", @@ -2033,7 +2040,7 @@ static void RequireDaemonRenderQueryAuthority(string root) "LoadAsteroidBelt(beltPose)", "void LoadAsteroidBelt(AetheriaRuntimeDaemonAsteroidBeltPose beltPose)", "foreach (var entitySnapshot in daemonZone?.Entities ?? Array.Empty())", - "entitiesByDaemonIndex.TryGetValue(entitySnapshot.EntityIndex, out var entity)", + "observedEntityFacadesByDaemonIndex.TryGetValue(entitySnapshot.EntityIndex, out var entity)", "private readonly Dictionary _entityInstancesByDaemonIndex", "public IReadOnlyDictionary DaemonEntityInstances => _entityInstancesByDaemonIndex;", "public bool TryGetEntityInstance(int daemonEntityIndex, out EntityInstance instance)", @@ -7755,6 +7762,9 @@ static void RequireMainMenuContinueRunState(string root) "GetObservedInfoGathered(CurrentEntity, target)", "IsObservedHostileContact(CurrentEntity, observedTarget)", "_observedEntityFacadesByRecordKey", + "_observedEntityFacadesByDaemonIndex", + "RebuildObservedEntityFacadeIndex();", + "ZoneRenderer.LoadDaemonZoneView(Zone, _observedEntityFacadesByDaemonIndex, daemonZone, daemonRun)", "entity.RestoreStatGrids(entitySnapshot.StatGrids)", "RestoreThermalExposure((float)entitySnapshot.Heatstroke, (float)entitySnapshot.Hypothermia)", "entity.HeatsinksEnabled = entitySnapshot.HeatsinksEnabled", @@ -8012,7 +8022,7 @@ static void RequireUnityObserverDoesNotTickLocalSimulation(string root) "observed.IsAuthoritative", "TryRestoreEntityGraphFromDaemonRun(observed.Run)", "CreateDaemonZoneConstructionBlueprint(daemonZone)", - "ZoneRenderer.LoadDaemonZoneView(Zone, daemonZone, daemonRun)", + "ZoneRenderer.LoadDaemonZoneView(Zone, _observedEntityFacadesByDaemonIndex, daemonZone, daemonRun)", "ZoneRenderer?.ApplyDaemonFrame(daemonZone, run)", "CreateDaemonEntitySnapshots(runId, daemonZone)", "FindCurrentDaemonZoneSnapshot()", diff --git a/Assets/Scripts/Gameplay/ActionGameManager.cs b/Assets/Scripts/Gameplay/ActionGameManager.cs index e7344de8c..2efc96c19 100644 --- a/Assets/Scripts/Gameplay/ActionGameManager.cs +++ b/Assets/Scripts/Gameplay/ActionGameManager.cs @@ -67,6 +67,7 @@ public class ActionGameManager : MonoBehaviour private string _lastAppliedAuthoritativeDaemonRunId = ""; private int _lastAppliedAuthoritativeDaemonZoneIndex = -1; private readonly Dictionary _observedEntityFacadesByRecordKey = new Dictionary(StringComparer.Ordinal); + private readonly Dictionary _observedEntityFacadesByDaemonIndex = new Dictionary(); private static DirectoryInfo _gameDataDirectory; public static DirectoryInfo GameDataDirectory { @@ -2097,7 +2098,7 @@ public class ActionGameManager : MonoBehaviour Zone.Log = s => Debug.Log($"Zone: {s}"); - ZoneRenderer.LoadDaemonZoneView(Zone, daemonZone, daemonRun); + ZoneRenderer.LoadDaemonZoneView(Zone, _observedEntityFacadesByDaemonIndex, daemonZone, daemonRun); if (CurrentEntity != null) { @@ -2708,6 +2709,7 @@ public class ActionGameManager : MonoBehaviour entity.RestoreThermalExposure((float)entitySnapshot.Heatstroke, (float)entitySnapshot.Hypothermia); RestoreRuntimeBehaviorStateFromTypedSnapshot(entity, entitySnapshot, _observedEntityFacadesByRecordKey); } + RebuildObservedEntityFacadeIndex(); foreach (var entity in _observedEntityFacadesByRecordKey.Values) { @@ -2795,11 +2797,22 @@ public class ActionGameManager : MonoBehaviour _observedEntityFacadesByRecordKey.Clear(); foreach (var restoredEntity in restoredEntities) _observedEntityFacadesByRecordKey[restoredEntity.Key] = restoredEntity.Value; + RebuildObservedEntityFacadeIndex(); if (restoredEntities.TryGetValue(currentEntityKey, out var currentEntity)) RestoreCurrentEntityBinding(currentEntity, actionBarBindings); } + private void RebuildObservedEntityFacadeIndex() + { + _observedEntityFacadesByDaemonIndex.Clear(); + foreach (var entity in _observedEntityFacadesByRecordKey.Values) + { + if (entity != null && entity.DaemonEntityIndex >= 0) + _observedEntityFacadesByDaemonIndex[entity.DaemonEntityIndex] = entity; + } + } + private void RestoreChildAndDockingRelationships( IReadOnlyList entitySnapshots, IReadOnlyDictionary restoredEntities) @@ -3266,17 +3279,7 @@ public class ActionGameManager : MonoBehaviour private bool TryGetObservedEntityFacade(int daemonEntityIndex, out Entity entity) { - foreach (var observedEntity in _observedEntityFacadesByRecordKey.Values) - { - if (observedEntity != null && observedEntity.DaemonEntityIndex == daemonEntityIndex) - { - entity = observedEntity; - return true; - } - } - - entity = null; - return false; + return _observedEntityFacadesByDaemonIndex.TryGetValue(daemonEntityIndex, out entity); } public void RequestTowToStation() diff --git a/Assets/Scripts/Zone Display/ZoneRenderer.cs b/Assets/Scripts/Zone Display/ZoneRenderer.cs index d64ff6bf3..e06dc6c8a 100644 --- a/Assets/Scripts/Zone Display/ZoneRenderer.cs +++ b/Assets/Scripts/Zone Display/ZoneRenderer.cs @@ -216,6 +216,7 @@ public class ZoneRenderer : MonoBehaviour public void LoadDaemonZoneView( Zone legacyEntityFacadeZone, + IReadOnlyDictionary observedEntityFacadesByDaemonIndex, AetheriaRuntimeZoneSnapshotCommit daemonZone = null, AetheriaRuntimeRunCheckpointCommit daemonRun = null) { @@ -245,16 +246,11 @@ public class ZoneRenderer : MonoBehaviour _suns = _bodyViewsByBodyKey.Values.Where(p => p is SunObject).ToArray(); - var entitiesByDaemonIndex = new Dictionary(); - foreach (var entity in legacyEntityFacadeZone.Entities) - { - if (entity != null && entity.DaemonEntityIndex >= 0) - entitiesByDaemonIndex[entity.DaemonEntityIndex] = entity; - } foreach (var entitySnapshot in daemonZone?.Entities ?? Array.Empty()) { if (entitySnapshot != null && - entitiesByDaemonIndex.TryGetValue(entitySnapshot.EntityIndex, out var entity)) + observedEntityFacadesByDaemonIndex != null && + observedEntityFacadesByDaemonIndex.TryGetValue(entitySnapshot.EntityIndex, out var entity)) { Debug.Log($"Loading entity {entity.Name} from daemon entity snapshot {entitySnapshot.EntityIndex}"); LoadEntity(entity);