diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs index c53499f75..43d838d97 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -92,6 +92,7 @@ RequireTradePurchaseRequestAuthority(root); RequireInventoryProjectionSlotIdentity(root); RequireInventoryValidationUsesManagedTypedDocuments(root); RequireMenuDockingUsesManagedTypedSnapshot(root); +RequireUnitySharedDocumentAccessorErgonomics(root); RequireInventoryLoadoutSaveRequestAuthority(root); RequireInventoryLoadoutRestoreRequestAuthority(root); RequireDockedCurrentShipRequestAuthority(root); @@ -4167,8 +4168,7 @@ static void RequireRuntimeInputScreenUsesEveSurface(string root) "AetheriaUnityRuntimeClientProvider.ResolveClient(", ".Aetheria()", ".Settings", - ".Player", - ".Latest()", + ".LatestPlayer()", "action.ApplyBindingOverride", "new InputAction(\"Aetheria Input Capture\")", "AetheriaRuntimeInputSettingsSurfaceBuilder.IsSupportedCapturePath(", @@ -4496,10 +4496,9 @@ static void RequireSectorMapZoneDetailsUseEveSurface(string root) ".LatestAsync()", ".Details", ".Zone(zoneIndex)", - ".Catalog", ".Settings", - ".Player", - ".Latest()", + ".LatestCatalog()", + ".LatestPlayer()", "AetheriaClient", "AetheriaRuntimeZoneDetailsSurfaceCommands.TryRead(request, out var command)", "AetheriaRuntimeZoneDetailsCommandKind.Close" @@ -6177,7 +6176,7 @@ static void RequireInventoryDropdownUseEveSurface(string root) "TryResolveObservedDockingIndex(out var dockingIndex)", "dockingIndex.TryResolveCurrentDockingBay(out var resolvedDockingBay)", ".DockingState", - ".Latest()", + ".TryLatest(", "LoadoutRestoreOptions" }; @@ -6915,7 +6914,8 @@ static void RequireMenuDockingUsesManagedTypedSnapshot(string root) Path.Combine(root, "Assets", "Scripts", "UI", "Menu", "InventoryPanel.cs"), Path.Combine(root, "Assets", "Scripts", "UI", "Menu", "LocalMenu.cs"), Path.Combine(root, "Assets", "Scripts", "UI", "Menu", "MenuPanel.cs"), - Path.Combine(root, "Assets", "Scripts", "UI", "Menu", "TradeMenu.cs") + Path.Combine(root, "Assets", "Scripts", "UI", "Menu", "TradeMenu.cs"), + Path.Combine(root, "Assets", "Scripts", "Gameplay", "AetheriaUnityObservedDockingIndex.cs") }; var missingPaths = menuPaths.Where(path => !File.Exists(path)).ToArray(); @@ -6954,6 +6954,88 @@ static void RequireMenuDockingUsesManagedTypedSnapshot(string root) Console.WriteLine("Menu docking state: Unity menus read managed typed docking snapshots through AetheriaClientDockingState.TryLatest"); } +static void RequireUnitySharedDocumentAccessorErgonomics(string root) +{ + var clientStatePath = Path.Combine( + root, + "Packages", + "org.gamecult.aetheria.state", + "Runtime", + "AetheriaClientState.cs"); + if (!File.Exists(clientStatePath)) + throw new InvalidOperationException("Cannot verify shared document accessor ergonomics; AetheriaClientState.cs is missing."); + + var clientState = File.ReadAllText(clientStatePath); + var requiredClientSymbols = new[] + { + "public AetheriaRuntimeCatalogSnapshot LatestCatalog()", + "public Task LatestPlayerAsync()", + "public AetheriaRuntimePlayerSettingsDocument LatestPlayer()", + "public Task LatestVerseHostAsync()", + "public AetheriaRuntimeVerseHostSettingsDocument LatestVerseHost()" + }; + var missingClientSymbols = requiredClientSymbols + .Where(symbol => !clientState.Contains(symbol, StringComparison.Ordinal)) + .ToArray(); + if (missingClientSymbols.Length > 0) + { + throw new InvalidOperationException( + "AetheriaClientState must expose named managed latest accessors for shared Unity documents: " + + string.Join(", ", missingClientSymbols)); + } + + var unityPaths = new[] + { + Path.Combine(root, "Assets", "Scripts", "Gameplay", "ActionBarSlot.cs"), + Path.Combine(root, "Assets", "Scripts", "Gameplay", "AetheriaUnityGameplayBootShell.cs"), + Path.Combine(root, "Assets", "Scripts", "Gameplay", "AetheriaUnityRuntimeClientProvider.cs"), + Path.Combine(root, "Assets", "Scripts", "UI", "HUD", "SchematicDisplay.cs"), + Path.Combine(root, "Assets", "Scripts", "UI", "InputScreen", "InputDisplayLayout.cs"), + Path.Combine(root, "Assets", "Scripts", "UI", "MainMenu.cs"), + Path.Combine(root, "Assets", "Scripts", "UI", "Menu", "MapRenderer.cs"), + Path.Combine(root, "Assets", "Scripts", "UI", "Menu", "InventoryMenu.cs"), + Path.Combine(root, "Assets", "Scripts", "UI", "Menu", "InventoryPanel.cs"), + Path.Combine(root, "Assets", "Scripts", "UI", "Menu", "TradeMenu.cs"), + Path.Combine(root, "Assets", "Scripts", "UI", "Menu", "SectorRenderer.cs"), + Path.Combine(root, "Assets", "Scripts", "Zone Display", "ZoneRenderer.cs") + }; + + var missingPaths = unityPaths.Where(path => !File.Exists(path)).ToArray(); + if (missingPaths.Length > 0) + { + throw new InvalidOperationException( + "Cannot verify shared Unity document accessor ergonomics; missing sources: " + + string.Join(", ", missingPaths.Select(path => Path.GetRelativePath(root, path)))); + } + + var forbiddenCompactedSymbols = new[] + { + ".Aetheria().Catalog.Latest()", + ".Aetheria().Settings.Player.Latest()", + ".Aetheria().Settings.VerseHost.Latest()" + } + .Select(CompactSource) + .ToArray(); + var offenders = unityPaths + .Select(path => new + { + Path = path, + Compact = CompactSource(File.ReadAllText(path)) + }) + .Where(entry => forbiddenCompactedSymbols.Any(symbol => entry.Compact.Contains(symbol, StringComparison.Ordinal))) + .Select(entry => Path.GetRelativePath(root, entry.Path)) + .ToArray(); + + if (offenders.Length > 0) + { + throw new InvalidOperationException( + "Unity presentation code still walks shared CultMesh document handles instead of named managed accessors: " + + string.Join(", ", offenders)); + } + + Console.WriteLine("Shared document accessors: Unity shared catalog/settings reads use named managed Aetheria client accessors"); +} + static void RequireVerseHostSettingsAuthority(string root) { var settingsPath = Path.Combine(root, "Aetheria.State", "Documents", "AetheriaVerseHostSettings.cs"); @@ -7487,8 +7569,7 @@ static void RequireClientTargetBootAuthority(string root) "stateBoot.StateFileExists", "AetheriaUnityRuntimeClientProvider.ResolveClient(stateBoot.StateFilePath, stateBoot.RuntimeId)", ".Aetheria()", - ".Catalog", - ".Latest()", + ".LatestCatalog()", "new ItemManager(", "new AetheriaUnityLoadoutItemProjector(itemManager, runtimeCatalog)", "ZoneRenderer.SetDroppedPickupItemProjector(loadoutItemProjector.CreateLoadoutItem)", @@ -10715,8 +10796,7 @@ static void RequireMainMenuVerseHostProjection(string root) { "LatestVerseHostSettings(AetheriaRuntimeStateBootReport stateBoot)", ".Settings", - ".VerseHost", - ".Latest()", + ".LatestVerseHost()", "AetheriaRuntimeMainMenuSurfaceBuilder.ProjectRoot(", "AetheriaRuntimeMainMenuSurfaceBuilder.BuildRoot(" }; @@ -11214,7 +11294,7 @@ static void RequireMainMenuContinueRunState(string root) "public sealed class AetheriaUnityObservedDockingIndex", ".Aetheria()", ".DockingState", - ".Latest()", + ".TryLatest(", "public bool IsEntityUndocked(Entity entity)", "public bool TryResolveDockingBay(", "out AetheriaRuntimeCurrentDockingDocument docking", @@ -12266,17 +12346,16 @@ static void RequireUnityObserverDoesNotTickLocalSimulation(string root) !mapRenderer.Contains(".Objects(viewport)", StringComparison.Ordinal) || !mapRenderer.Contains(".RenderSplats(viewport)", StringComparison.Ordinal) || !mapRenderer.Contains(".Settings", StringComparison.Ordinal) || - !mapRenderer.Contains(".Player", StringComparison.Ordinal) || + !mapRenderer.Contains(".LatestPlayer()", StringComparison.Ordinal) || !sectorRenderer.Contains("AetheriaUnityRuntimeClientProvider.ResolveClient(", StringComparison.Ordinal) || !sectorRenderer.Contains(".Aetheria()", StringComparison.Ordinal) || !sectorRenderer.Contains(".SectorMap", StringComparison.Ordinal) || !sectorRenderer.Contains(".LatestAsync()", StringComparison.Ordinal) || !sectorRenderer.Contains(".Details", StringComparison.Ordinal) || !sectorRenderer.Contains(".Zone(zoneIndex)", StringComparison.Ordinal) || - !sectorRenderer.Contains(".Catalog", StringComparison.Ordinal) || - !sectorRenderer.Contains(".Latest()", StringComparison.Ordinal) || + !sectorRenderer.Contains(".LatestCatalog()", StringComparison.Ordinal) || !sectorRenderer.Contains(".Settings", StringComparison.Ordinal) || - !sectorRenderer.Contains(".Player", StringComparison.Ordinal) || + !sectorRenderer.Contains(".LatestPlayer()", StringComparison.Ordinal) || !sectorMap.Contains("AetheriaUnityRuntimeClientProvider.ResolveClient(", StringComparison.Ordinal) || !sectorMap.Contains(".Aetheria()", StringComparison.Ordinal) || !sectorMap.Contains(".SectorMap", StringComparison.Ordinal) || @@ -13810,8 +13889,7 @@ static void RequireRuntimeStateReaderOwnsUnityStateAcquisition(string root) "AetheriaRuntimeStateBoot.Inspect(AetheriaUnityRuntimePaths.GameDataDirectory)", "AetheriaUnityRuntimeClientProvider.ResolveClient(stateBoot.StateFilePath, stateBoot.RuntimeId)", ".Aetheria()", - ".Catalog", - ".Latest()", + ".LatestCatalog()", "new ItemManager(", "new AetheriaUnityLoadoutItemProjector(itemManager, runtimeCatalog)", "ZoneRenderer.SetDroppedPickupItemProjector(loadoutItemProjector.CreateLoadoutItem)", @@ -13846,8 +13924,7 @@ static void RequireRuntimeStateReaderOwnsUnityStateAcquisition(string root) "AetheriaClient", ".Aetheria()", ".Settings", - ".Player", - ".Latest()", + ".LatestPlayer()", "OpenAsync(", "pullOnOpen: true", "ApplyPlayerSettings(settings, stored)" @@ -14137,7 +14214,7 @@ static void RequireRuntimeStateReaderOwnsUnityStateAcquisition(string root) !mainMenu.Contains("LatestRuntimeCatalog(AetheriaRuntimeStateBootReport stateBoot)", StringComparison.Ordinal) || !mainMenu.Contains(".Aetheria()", StringComparison.Ordinal) || !mainMenu.Contains(".SectorMap", StringComparison.Ordinal) || - !mainMenu.Contains(".Catalog.Latest()", StringComparison.Ordinal) || + !mainMenu.Contains(".LatestCatalog()", StringComparison.Ordinal) || !mainMenu.Contains(".LatestAsync()", StringComparison.Ordinal)) { throw new InvalidOperationException( diff --git a/Assets/Scripts/Gameplay/ActionBarSlot.cs b/Assets/Scripts/Gameplay/ActionBarSlot.cs index a74bdf7d0..c5226469d 100644 --- a/Assets/Scripts/Gameplay/ActionBarSlot.cs +++ b/Assets/Scripts/Gameplay/ActionBarSlot.cs @@ -85,7 +85,7 @@ public abstract class ActionBarBinding try { - _catalog ??= Client.Aetheria().Catalog.Latest(); + _catalog ??= Client.Aetheria().LatestCatalog(); return _catalog?.FindItem(item.ItemKey); } catch (Exception ex) diff --git a/Assets/Scripts/Gameplay/AetheriaUnityGameplayBootShell.cs b/Assets/Scripts/Gameplay/AetheriaUnityGameplayBootShell.cs index 465ab480f..c7097e10d 100644 --- a/Assets/Scripts/Gameplay/AetheriaUnityGameplayBootShell.cs +++ b/Assets/Scripts/Gameplay/AetheriaUnityGameplayBootShell.cs @@ -36,8 +36,7 @@ public sealed class AetheriaUnityGameplayBootShell var runtimeCatalog = AetheriaUnityRuntimeClientProvider.ResolveClient(stateBoot.StateFilePath, stateBoot.RuntimeId) .Aetheria() - .Catalog - .Latest(); + .LatestCatalog(); if (runtimeCatalog == null) throw new InvalidOperationException("Aetheria typed runtime catalog is required before gameplay boot."); diff --git a/Assets/Scripts/Gameplay/AetheriaUnityObservedDockingIndex.cs b/Assets/Scripts/Gameplay/AetheriaUnityObservedDockingIndex.cs index 235ec18b2..cddcfb0ad 100644 --- a/Assets/Scripts/Gameplay/AetheriaUnityObservedDockingIndex.cs +++ b/Assets/Scripts/Gameplay/AetheriaUnityObservedDockingIndex.cs @@ -163,11 +163,8 @@ public sealed class AetheriaUnityObservedDockingIndex snapshot = null; try { - snapshot = _resolveClient() - ?.Aetheria() - .DockingState - .Latest(); - return snapshot != null; + var client = _resolveClient(); + return client?.Aetheria().DockingState.TryLatest(out snapshot) == true; } catch { diff --git a/Assets/Scripts/Gameplay/AetheriaUnityRuntimeClientProvider.cs b/Assets/Scripts/Gameplay/AetheriaUnityRuntimeClientProvider.cs index 9e40d4a89..3659d9ae9 100644 --- a/Assets/Scripts/Gameplay/AetheriaUnityRuntimeClientProvider.cs +++ b/Assets/Scripts/Gameplay/AetheriaUnityRuntimeClientProvider.cs @@ -83,8 +83,7 @@ public static class AetheriaUnityRuntimeClientProvider var stored = ResolveClient(AetheriaUnityRuntimePaths.RuntimeStateFilePath) .Aetheria() .Settings - .Player - .Latest(); + .LatestPlayer(); if (stored == null) return settings; diff --git a/Assets/Scripts/UI/HUD/SchematicDisplay.cs b/Assets/Scripts/UI/HUD/SchematicDisplay.cs index 9e3cbb032..12897a07d 100644 --- a/Assets/Scripts/UI/HUD/SchematicDisplay.cs +++ b/Assets/Scripts/UI/HUD/SchematicDisplay.cs @@ -327,7 +327,7 @@ public class SchematicDisplay : MonoBehaviour try { - _catalog = ResolveClient().Aetheria().Catalog.Latest(); + _catalog = ResolveClient().Aetheria().LatestCatalog(); } catch (Exception ex) { @@ -347,8 +347,7 @@ public class SchematicDisplay : MonoBehaviour _playerSettings = ResolveClient() .Aetheria() .Settings - .Player - .Latest(); + .LatestPlayer(); } catch (Exception ex) { diff --git a/Assets/Scripts/UI/InputScreen/InputDisplayLayout.cs b/Assets/Scripts/UI/InputScreen/InputDisplayLayout.cs index 49abbff1b..ab63714fe 100644 --- a/Assets/Scripts/UI/InputScreen/InputDisplayLayout.cs +++ b/Assets/Scripts/UI/InputScreen/InputDisplayLayout.cs @@ -391,8 +391,7 @@ public class InputDisplayLayout : MonoBehaviour _playerSettings = ResolveClient() .Aetheria() .Settings - .Player - .Latest(); + .LatestPlayer(); } catch (Exception ex) { diff --git a/Assets/Scripts/UI/MainMenu.cs b/Assets/Scripts/UI/MainMenu.cs index a80763a20..c15a2fe50 100644 --- a/Assets/Scripts/UI/MainMenu.cs +++ b/Assets/Scripts/UI/MainMenu.cs @@ -185,8 +185,7 @@ public class MainMenu : MonoBehaviour return ResolveClient(stateBoot) .Aetheria() .Settings - .Player - .Latest(); + .LatestPlayer(); } catch (Exception ex) { @@ -205,8 +204,7 @@ public class MainMenu : MonoBehaviour return ResolveClient(stateBoot) .Aetheria() .Settings - .VerseHost - .Latest(); + .LatestVerseHost(); } catch (Exception ex) { @@ -225,7 +223,7 @@ public class MainMenu : MonoBehaviour try { - _catalog = ResolveClient(stateBoot).Aetheria().Catalog.Latest(); + _catalog = ResolveClient(stateBoot).Aetheria().LatestCatalog(); return _catalog; } catch (Exception ex) diff --git a/Assets/Scripts/UI/Menu/InventoryMenu.cs b/Assets/Scripts/UI/Menu/InventoryMenu.cs index ec37e5335..462c68ad0 100644 --- a/Assets/Scripts/UI/Menu/InventoryMenu.cs +++ b/Assets/Scripts/UI/Menu/InventoryMenu.cs @@ -999,7 +999,7 @@ public class InventoryMenu : MonoBehaviour try { - _catalog = ResolveClient().Aetheria().Catalog.Latest(); + _catalog = ResolveClient().Aetheria().LatestCatalog(); } catch (Exception ex) { @@ -1019,8 +1019,7 @@ public class InventoryMenu : MonoBehaviour _playerSettings = ResolveClient() .Aetheria() .Settings - .Player - .Latest(); + .LatestPlayer(); } catch (Exception ex) { diff --git a/Assets/Scripts/UI/Menu/InventoryPanel.cs b/Assets/Scripts/UI/Menu/InventoryPanel.cs index f40038e69..ec8c7e558 100644 --- a/Assets/Scripts/UI/Menu/InventoryPanel.cs +++ b/Assets/Scripts/UI/Menu/InventoryPanel.cs @@ -1496,7 +1496,7 @@ private void Update() try { - _catalog = ResolveClient().Aetheria().Catalog.Latest(); + _catalog = ResolveClient().Aetheria().LatestCatalog(); } catch (Exception ex) { @@ -1516,8 +1516,7 @@ private void Update() _playerSettings = ResolveClient() .Aetheria() .Settings - .Player - .Latest(); + .LatestPlayer(); } catch (Exception ex) { diff --git a/Assets/Scripts/UI/Menu/MapRenderer.cs b/Assets/Scripts/UI/Menu/MapRenderer.cs index fc111b51e..e0efbfd32 100644 --- a/Assets/Scripts/UI/Menu/MapRenderer.cs +++ b/Assets/Scripts/UI/Menu/MapRenderer.cs @@ -320,8 +320,7 @@ public class MapRenderer : MonoBehaviour return ResolveClient() .Aetheria() .Settings - .Player - .Latest() + .LatestPlayer() ?.ShowAsteroidsInMinimap ?? false; } catch (Exception ex) diff --git a/Assets/Scripts/UI/Menu/SectorRenderer.cs b/Assets/Scripts/UI/Menu/SectorRenderer.cs index 0ecdc1505..f07b96cce 100644 --- a/Assets/Scripts/UI/Menu/SectorRenderer.cs +++ b/Assets/Scripts/UI/Menu/SectorRenderer.cs @@ -225,7 +225,7 @@ public class SectorRenderer : MonoBehaviour, IBeginDragHandler, IDragHandler, IS try { - _catalog = ResolveClient().Aetheria().Catalog.Latest(); + _catalog = ResolveClient().Aetheria().LatestCatalog(); } catch (Exception ex) { @@ -245,8 +245,7 @@ public class SectorRenderer : MonoBehaviour, IBeginDragHandler, IDragHandler, IS _playerSettings = ResolveClient() .Aetheria() .Settings - .Player - .Latest(); + .LatestPlayer(); } catch (Exception ex) { diff --git a/Assets/Scripts/UI/Menu/TradeMenu.cs b/Assets/Scripts/UI/Menu/TradeMenu.cs index e0f31de8e..91a3ddf62 100644 --- a/Assets/Scripts/UI/Menu/TradeMenu.cs +++ b/Assets/Scripts/UI/Menu/TradeMenu.cs @@ -628,7 +628,7 @@ public class TradeMenu : MonoBehaviour try { - _catalog = ResolveClient().Aetheria().Catalog.Latest(); + _catalog = ResolveClient().Aetheria().LatestCatalog(); } catch (Exception ex) { @@ -648,8 +648,7 @@ public class TradeMenu : MonoBehaviour _playerSettings = ResolveClient() .Aetheria() .Settings - .Player - .Latest(); + .LatestPlayer(); } catch (Exception ex) { diff --git a/Assets/Scripts/Zone Display/ZoneRenderer.cs b/Assets/Scripts/Zone Display/ZoneRenderer.cs index 8fc0f6aa5..341957cb1 100644 --- a/Assets/Scripts/Zone Display/ZoneRenderer.cs +++ b/Assets/Scripts/Zone Display/ZoneRenderer.cs @@ -1179,7 +1179,7 @@ public class ZoneRenderer : MonoBehaviour try { - _catalog = ResolveClient().Aetheria().Catalog.Latest(); + _catalog = ResolveClient().Aetheria().LatestCatalog(); } catch (Exception ex) { diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaClientState.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaClientState.cs index 2a1a3b846..5ee7547e7 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaClientState.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaClientState.cs @@ -183,6 +183,11 @@ namespace GameCult.Aetheria.State.Verse return Document().LatestAsync().ConfigureAwait(false).GetAwaiter().GetResult(); } + public AetheriaRuntimeCatalogSnapshot LatestCatalog() + { + return Latest(); + } + public Observable Watch() where TDocument : class { @@ -315,6 +320,26 @@ namespace GameCult.Aetheria.State.Verse public CultMeshDocumentHandle Player { get; } public CultMeshDocumentHandle VerseHost { get; } + + public Task LatestPlayerAsync() + { + return Player.LatestAsync(); + } + + public AetheriaRuntimePlayerSettingsDocument LatestPlayer() + { + return LatestPlayerAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + } + + public Task LatestVerseHostAsync() + { + return VerseHost.LatestAsync(); + } + + public AetheriaRuntimeVerseHostSettingsDocument LatestVerseHost() + { + return LatestVerseHostAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + } } public sealed class AetheriaClientDockingState