diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs index 3c711d1d2..86a04f1e8 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -2521,6 +2521,7 @@ static void RequireDaemonRenderQueryAuthority(string root) { "private AetheriaRuntimeCurrentEntityDocument _currentEntityDocument;", "ResolveCurrentEntityHudStatus()", + "AetheriaUnityRuntimeClientProvider.ResolveClient(", ".Current", ".Entity", ".LatestAsync()", @@ -4484,7 +4485,7 @@ static void RequireSectorMapZoneDetailsUseEveSurface(string root) "AetheriaRuntimeZoneDetailsSurfaceBuilder.ProjectDaemonZone(", "AetheriaRuntimeZoneDetailsSurfaceBuilder.Project(", "ProjectZoneDetailsSurfaceState(", - "private AetheriaClient _client", + "AetheriaUnityRuntimeClientProvider.ResolveClient(", ".Aetheria()", ".SectorMap", ".LatestAsync()", @@ -4495,7 +4496,6 @@ static void RequireSectorMapZoneDetailsUseEveSurface(string root) ".Player", ".Latest()", "AetheriaClient", - ".OpenLocalAsync(", "AetheriaRuntimeZoneDetailsSurfaceCommands.TryRead(request, out var command)", "AetheriaRuntimeZoneDetailsCommandKind.Close" }; @@ -11433,7 +11433,7 @@ static void RequireUnityObserverDoesNotTickLocalSimulation(string root) !mapRenderer.Contains(".RenderSplats(viewport)", StringComparison.Ordinal) || !mapRenderer.Contains(".Settings", StringComparison.Ordinal) || !mapRenderer.Contains(".Player", StringComparison.Ordinal) || - !sectorRenderer.Contains("private AetheriaClient _client", StringComparison.Ordinal) || + !sectorRenderer.Contains("AetheriaUnityRuntimeClientProvider.ResolveClient(", StringComparison.Ordinal) || !sectorRenderer.Contains(".Aetheria()", StringComparison.Ordinal) || !sectorRenderer.Contains(".SectorMap", StringComparison.Ordinal) || !sectorRenderer.Contains(".LatestAsync()", StringComparison.Ordinal) || @@ -12840,6 +12840,10 @@ static void RequireRuntimeStateReaderOwnsUnityStateAcquisition(string root) var renderSplatViewportSource = File.Exists(renderSplatViewportSourcePath) ? File.ReadAllText(renderSplatViewportSourcePath) : throw new InvalidOperationException("Cannot verify daemon state acquisition; AetheriaUnityRenderSplatViewportSource.cs is missing."); + var schematicDisplayPath = Path.Combine(root, "Assets", "Scripts", "UI", "HUD", "SchematicDisplay.cs"); + var schematicDisplay = File.Exists(schematicDisplayPath) + ? File.ReadAllText(schematicDisplayPath) + : throw new InvalidOperationException("Cannot verify daemon state acquisition; SchematicDisplay.cs is missing."); var menuPanelPath = Path.Combine(root, "Assets", "Scripts", "UI", "Menu", "MenuPanel.cs"); var menuPanel = File.Exists(menuPanelPath) ? File.ReadAllText(menuPanelPath) @@ -12856,6 +12860,10 @@ static void RequireRuntimeStateReaderOwnsUnityStateAcquisition(string root) var localMenu = File.Exists(localMenuPath) ? File.ReadAllText(localMenuPath) : throw new InvalidOperationException("Cannot verify daemon state acquisition; LocalMenu.cs is missing."); + var sectorRendererPath = Path.Combine(root, "Assets", "Scripts", "UI", "Menu", "SectorRenderer.cs"); + var sectorRenderer = File.Exists(sectorRendererPath) + ? File.ReadAllText(sectorRendererPath) + : throw new InvalidOperationException("Cannot verify daemon state acquisition; SectorRenderer.cs is missing."); var gameplayBootShellPath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "AetheriaUnityGameplayBootShell.cs"); var gameplayBootShell = File.Exists(gameplayBootShellPath) ? File.ReadAllText(gameplayBootShellPath) @@ -12931,6 +12939,9 @@ static void RequireRuntimeStateReaderOwnsUnityStateAcquisition(string root) "public static AetheriaClient ResolveClient(string stateFilePath, string runtimeId = \"\")", "public static AetheriaClient ResolveClient(AetheriaRuntimeStateBootReport stateBoot, string runtimeId = \"\")", "public static AetheriaClient CurrentClientForStateFile(string stateFilePath)", + "private static readonly Dictionary RuntimeClients", + "RuntimeClients.TryGetValue(cacheKey, out var runtimeClient)", + "RuntimeClients[cacheKey] = runtimeClient", "AetheriaClient", ".Aetheria()", ".Settings", @@ -12959,7 +12970,9 @@ static void RequireRuntimeStateReaderOwnsUnityStateAcquisition(string root) ["Assets/Scripts/UI/Menu/MenuPanel.cs"] = menuPanel, ["Assets/Scripts/UI/Menu/MapRenderer.cs"] = mapRenderer, ["Assets/Scripts/UI/Menu/SectorMap.cs"] = sectorMap, - ["Assets/Scripts/UI/Menu/LocalMenu.cs"] = localMenu + ["Assets/Scripts/UI/Menu/LocalMenu.cs"] = localMenu, + ["Assets/Scripts/UI/Menu/SectorRenderer.cs"] = sectorRenderer, + ["Assets/Scripts/UI/HUD/SchematicDisplay.cs"] = schematicDisplay }; var directClientOpenHits = providerOwnedClientAccessSources .Where(pair => diff --git a/Assets/Scripts/Gameplay/AetheriaUnityRuntimeClientProvider.cs b/Assets/Scripts/Gameplay/AetheriaUnityRuntimeClientProvider.cs index 914d50f59..9e40d4a89 100644 --- a/Assets/Scripts/Gameplay/AetheriaUnityRuntimeClientProvider.cs +++ b/Assets/Scripts/Gameplay/AetheriaUnityRuntimeClientProvider.cs @@ -3,15 +3,15 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ using System; +using System.Collections.Generic; using System.IO; using GameCult.Aetheria.State.Verse; using UnityEngine; public static class AetheriaUnityRuntimeClientProvider { - private static AetheriaClient _runtimeClient; - private static string _runtimeClientStatePath; - private static string _runtimeClientRuntimeId; + private static readonly Dictionary RuntimeClients = + new Dictionary(StringComparer.Ordinal); private static RuntimePlayerSettings _runtimePlayerSettings; public static RuntimePlayerSettings PlayerSettings => @@ -20,15 +20,11 @@ public static class AetheriaUnityRuntimeClientProvider public static AetheriaClient ResolveClient(string stateFilePath, string runtimeId = "") { var effectiveRuntimeId = string.IsNullOrWhiteSpace(runtimeId) ? "raven-unity" : runtimeId; - if (_runtimeClient != null && - string.Equals(_runtimeClientStatePath, stateFilePath, StringComparison.Ordinal) && - string.Equals(_runtimeClientRuntimeId, effectiveRuntimeId, StringComparison.Ordinal)) - { - return _runtimeClient; - } + var cacheKey = CacheKey(stateFilePath, effectiveRuntimeId); + if (RuntimeClients.TryGetValue(cacheKey, out var runtimeClient)) + return runtimeClient; - Dispose(); - _runtimeClient = AetheriaClient + runtimeClient = AetheriaClient .OpenAsync( stateFilePath, effectiveRuntimeId, @@ -37,9 +33,8 @@ public static class AetheriaUnityRuntimeClientProvider pullOnOpen: true) .GetAwaiter() .GetResult(); - _runtimeClientStatePath = stateFilePath; - _runtimeClientRuntimeId = effectiveRuntimeId; - return _runtimeClient; + RuntimeClients[cacheKey] = runtimeClient; + return runtimeClient; } public static AetheriaClient ResolveClient(AetheriaRuntimeStateBootReport stateBoot, string runtimeId = "") @@ -54,18 +49,30 @@ public static class AetheriaUnityRuntimeClientProvider public static AetheriaClient CurrentClientForStateFile(string stateFilePath) { - return _runtimeClient != null && - string.Equals(_runtimeClientStatePath, stateFilePath, StringComparison.Ordinal) - ? _runtimeClient - : null; + foreach (var pair in RuntimeClients) + { + if (pair.Key.StartsWith(StatePathCachePrefix(stateFilePath), StringComparison.Ordinal)) + return pair.Value; + } + + return null; } public static void Dispose() { - _runtimeClient?.Dispose(); - _runtimeClient = null; - _runtimeClientStatePath = null; - _runtimeClientRuntimeId = null; + foreach (var client in RuntimeClients.Values) + client.Dispose(); + RuntimeClients.Clear(); + } + + private static string CacheKey(string stateFilePath, string runtimeId) + { + return StatePathCachePrefix(stateFilePath) + runtimeId; + } + + private static string StatePathCachePrefix(string stateFilePath) + { + return (stateFilePath ?? "") + "\n"; } private static RuntimePlayerSettings LoadPlayerSettings() diff --git a/Assets/Scripts/UI/HUD/SchematicDisplay.cs b/Assets/Scripts/UI/HUD/SchematicDisplay.cs index 363d47a7f..9e3cbb032 100644 --- a/Assets/Scripts/UI/HUD/SchematicDisplay.cs +++ b/Assets/Scripts/UI/HUD/SchematicDisplay.cs @@ -2,7 +2,6 @@ using System; using System.Collections; using System.Collections.Generic; using System.Globalization; -using System.IO; using System.Linq; using GameCult.Aetheria.State.Verse; using TMPro; @@ -69,7 +68,6 @@ public class SchematicDisplay : MonoBehaviour private EquippedCargoBay[] _cargoBays; private SchematicDisplayItem[] _schematicItems; private AetherDrive _aetherDrive; - private AetheriaClient _client; private string _clientStatePath = ""; private AetheriaRuntimeCatalogSnapshot _catalog; private AetheriaRuntimePlayerSettingsDocument _playerSettings; @@ -388,22 +386,14 @@ public class SchematicDisplay : MonoBehaviour private AetheriaClient ResolveClient() { - var gameDataDirectory = new DirectoryInfo(Path.Combine(Application.dataPath, "..", "GameData")); - var stateBoot = AetheriaRuntimeStateBoot.Inspect(gameDataDirectory); - if (_client != null && string.Equals(_clientStatePath, stateBoot.StateFilePath, StringComparison.Ordinal)) - return _client; + var stateBoot = AetheriaRuntimeStateBoot.Inspect(AetheriaUnityRuntimePaths.GameDataDirectory); + if (!string.Equals(_clientStatePath, stateBoot.StateFilePath, StringComparison.Ordinal)) + { + _clientStatePath = stateBoot.StateFilePath; + ClearClientCaches(); + } - DisposeClient(); - _client = AetheriaClient - .OpenLocalAsync( - gameDataDirectory, - "unity-schematic-display", - "local", - pullOnOpen: true) - .GetAwaiter() - .GetResult(); - _clientStatePath = stateBoot.StateFilePath; - return _client; + return AetheriaUnityRuntimeClientProvider.ResolveClient(stateBoot, "unity-schematic-display"); } private string FormatValue(float value) @@ -434,19 +424,11 @@ public class SchematicDisplay : MonoBehaviour return $"{FormatValue(value - 273.15f)} C"; } - private void DisposeClient() + private void ClearClientCaches() { - _client?.Dispose(); - _client = null; - _clientStatePath = ""; _catalog = null; _playerSettings = null; _currentEntityDocument = null; _currentEntityDocumentReadTime = float.NegativeInfinity; } - - private void OnDestroy() - { - DisposeClient(); - } } diff --git a/Assets/Scripts/UI/Menu/SectorRenderer.cs b/Assets/Scripts/UI/Menu/SectorRenderer.cs index 92b303bb4..0ecdc1505 100644 --- a/Assets/Scripts/UI/Menu/SectorRenderer.cs +++ b/Assets/Scripts/UI/Menu/SectorRenderer.cs @@ -2,7 +2,6 @@ using System; using System.Collections; using System.Collections.Generic; using System.Globalization; -using System.IO; using System.Linq; using GameCult.Aetheria.EveRuntime; using GameCult.Aetheria.State.Verse; @@ -46,7 +45,6 @@ public class SectorRenderer : MonoBehaviour, IBeginDragHandler, IDragHandler, IS private float _sectorBackgroundDepth; private float _sectorCameraDepth; private UIDocument _zoneDetailsSurfaceDocument; - private AetheriaClient _client; private string _clientStatePath = ""; private AetheriaRuntimeCatalogSnapshot _catalog; private AetheriaRuntimePlayerSettingsDocument _playerSettings; @@ -275,31 +273,18 @@ public class SectorRenderer : MonoBehaviour, IBeginDragHandler, IDragHandler, IS private AetheriaClient ResolveClient() { - var gameDataDirectory = new DirectoryInfo(Path.Combine(Application.dataPath, "..", "GameData")); - var stateBoot = AetheriaRuntimeStateBoot.Inspect(gameDataDirectory); - if (_client != null && string.Equals(_clientStatePath, stateBoot.StateFilePath, StringComparison.Ordinal)) - return _client; + var stateBoot = AetheriaRuntimeStateBoot.Inspect(AetheriaUnityRuntimePaths.GameDataDirectory); + if (!string.Equals(_clientStatePath, stateBoot.StateFilePath, StringComparison.Ordinal)) + { + _clientStatePath = stateBoot.StateFilePath; + ClearClientCaches(); + } - DisposeClient(); - _client = AetheriaClient - .OpenLocalAsync( - gameDataDirectory, - "unity-sector-renderer", - "local", - pullOnOpen: true) - .GetAwaiter() - .GetResult(); - _clientStatePath = stateBoot.StateFilePath; - _catalog = null; - _playerSettings = null; - return _client; + return AetheriaUnityRuntimeClientProvider.ResolveClient(stateBoot, "unity-sector-renderer"); } - private void DisposeClient() + private void ClearClientCaches() { - _client?.Dispose(); - _client = null; - _clientStatePath = ""; _catalog = null; _playerSettings = null; } @@ -350,8 +335,6 @@ public class SectorRenderer : MonoBehaviour, IBeginDragHandler, IDragHandler, IS private void OnDestroy() { - DisposeClient(); - if (_zoneDetailsSurfaceDocument != null) { AetheriaEveUnitySurfaceHost.DestroyDocument(_zoneDetailsSurfaceDocument);