diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs index e931cd1f5..54e4a4a20 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -3577,7 +3577,7 @@ static void RequireMainMenuSettingsCommands(string root) { "AetheriaRuntimeMainMenuSurfaceBuilder.BuildPlayerSettingsShell(", "AetheriaRuntimeMainMenuSurfaceBuilder.ProjectPlayerSettings(", - "LatestPlayerSettings(CurrentStateBoot())", + "ResolvePlayerSettings(CurrentStateBoot())", "AetheriaEveUnitySurfaceHost.RenderRuntime(", "AetheriaRuntimeMainMenuCommandKind.PlayerSettingsCommand", "SendKnownAetheriaEveCommand(request, \"player-settings\")" @@ -3685,7 +3685,7 @@ static void RequireMainMenuSettingsShellUsesEveSurface(string root) "AetheriaRuntimeMainMenuCommandKind.BackToMain", "AetheriaRuntimeMainMenuCommandKind.BackToSettings", "AetheriaRuntimeMainMenuCommandKind.OpenRuntimeInputScreen", - "LatestPlayerSettings(", + "ResolvePlayerSettings(", "AetheriaEveUnitySurfaceHost.RenderRuntime(", "AetheriaEveUnitySurfaceHost.Hide(_menuSurfaceDocument)" }; @@ -3812,9 +3812,9 @@ static void RequireMainMenuRootUsesEveSurface(string root) "AetheriaRuntimeMainMenuCommandKind.ShowSettings", "AetheriaRuntimeMainMenuCommandKind.Quit", "HandleMainSurfaceCommand(", - "LatestSectorMap(stateBoot)", - "LatestVerseHostSettings(stateBoot)", - "LatestPlayerSettings(stateBoot)", + "ResolveSectorMap(stateBoot)", + "ResolveVerseHostSettings(stateBoot)", + "ResolvePlayerSettings(stateBoot)", "HideMenuSurface();" }; @@ -6990,7 +6990,17 @@ static void RequireUnitySharedDocumentAccessorErgonomics(string root) "public Task LatestVerseHostAsync()", "public AetheriaRuntimeVerseHostSettingsDocument LatestVerseHost()", "public Task> ReactiveVerseHostAsync(", - "public CultMeshReactiveDocument ReactiveVerseHost(" + "public CultMeshReactiveDocument ReactiveVerseHost(", + "public Task> ReactiveLoadoutTemplatesAsync(", + "public CultMeshReactiveDocument ReactiveLoadoutTemplates(", + "public Task> ReactiveSectorMapAsync(", + "public CultMeshReactiveDocument ReactiveSectorMap(", + "public Task> ReactiveZoneContactsAsync(", + "public CultMeshReactiveDocument ReactiveZoneContacts(", + "public Task> ReactiveStationRefitAsync(", + "public CultMeshReactiveDocument ReactiveStationRefit(", + "public Task> ReactiveZoneRenderAsync(", + "public CultMeshReactiveDocument ReactiveZoneRender(" }; var missingClientSymbols = requiredClientSymbols .Where(symbol => !clientState.Contains(symbol, StringComparison.Ordinal)) @@ -7141,11 +7151,19 @@ static void RequireUnitySharedDocumentAccessorErgonomics(string root) "MainMenu.cs")); var requiredMainMenuSharedDocumentSymbols = new[] { + "CultMeshReactiveDocument _sectorMap", "CultMeshReactiveDocument _playerSettings", + "CultMeshReactiveDocument _verseHostSettings", + ".ReactiveSectorMap()", ".Settings", ".ReactivePlayer()", + ".ReactiveVerseHost()", + "_sectorMap?.Dispose()", "_playerSettings?.Dispose()", + "_verseHostSettings?.Dispose()", + "_sectorMap?.Current", "_playerSettings?.Current", + "_verseHostSettings?.Current", "private void OnDestroy()" }; var missingMainMenuSharedDocumentSymbols = requiredMainMenuSharedDocumentSymbols @@ -7154,7 +7172,7 @@ static void RequireUnitySharedDocumentAccessorErgonomics(string root) if (missingMainMenuSharedDocumentSymbols.Length > 0) { throw new InvalidOperationException( - "MainMenu should bind player settings through the managed reactive Aetheria settings document: " + + "MainMenu should bind sector, player, and Verse host state through managed reactive Aetheria documents: " + string.Join(", ", missingMainMenuSharedDocumentSymbols)); } @@ -8131,16 +8149,16 @@ static void RequireClientTargetBootAuthority(string root) var requiredMainMenuSymbols = new[] { "CurrentStateBoot()", - "LatestSectorMap(AetheriaRuntimeStateBootReport stateBoot)", + "ResolveSectorMap(AetheriaRuntimeStateBootReport stateBoot)", "AetheriaClient", ".Aetheria()", - ".LatestSectorMap()", - "LatestVerseHostSettings(AetheriaRuntimeStateBootReport stateBoot)", + ".ReactiveSectorMap()", + "ResolveVerseHostSettings(AetheriaRuntimeStateBootReport stateBoot)", "AetheriaState.At(AetheriaUnityRuntimePaths.GameDataDirectory)", ".ClientTarget", "RequestClientTargetCommand(request)", "AetheriaRuntimeClientTargetSurfaceCommands.TryRequest(", - "LatestPlayerSettings(AetheriaRuntimeStateBootReport stateBoot)", + "ResolvePlayerSettings(AetheriaRuntimeStateBootReport stateBoot)", "AetheriaRuntimeMainMenuSurfaceBuilder.ProjectRoot(", "AetheriaRuntimeMainMenuSurfaceBuilder.ProjectVerseSettings(", "AetheriaRuntimeMainMenuSurfaceBuilder.BuildRoot(", @@ -11340,9 +11358,9 @@ static void RequireMainMenuVerseHostProjection(string root) var requiredMainMenuSymbols = new[] { - "LatestVerseHostSettings(AetheriaRuntimeStateBootReport stateBoot)", + "ResolveVerseHostSettings(AetheriaRuntimeStateBootReport stateBoot)", ".Settings", - ".LatestVerseHost()", + ".ReactiveVerseHost()", "AetheriaRuntimeMainMenuSurfaceBuilder.ProjectRoot(", "AetheriaRuntimeMainMenuSurfaceBuilder.BuildRoot(" }; @@ -11465,10 +11483,10 @@ static void RequireMainMenuContinueRunState(string root) var requiredMenuSymbols = new[] { - "LatestSectorMap", + "ResolveSectorMap", "AetheriaClient", ".Aetheria()", - ".LatestSectorMap()", + ".ReactiveSectorMap()", "ContinueGame()", "SceneManager.LoadScene(\"ARPG\")" }; @@ -13547,9 +13565,9 @@ static void RequireUnityObserverDoesNotTickLocalSimulation(string root) var requiredMenuSymbols = new[] { "TryStartDaemonObservedGame", - "LatestSectorMap(stateBoot)", + "ResolveSectorMap(stateBoot)", ".Aetheria()", - ".LatestSectorMap()", + ".ReactiveSectorMap()", "sectorMap.FrameId", "SceneManager.LoadScene(\"ARPG\")" }; @@ -14751,9 +14769,9 @@ static void RequireRuntimeStateReaderOwnsUnityStateAcquisition(string root) } if (!mainMenu.Contains("AetheriaClient", StringComparison.Ordinal) || - !mainMenu.Contains("LatestSectorMap(AetheriaRuntimeStateBootReport stateBoot)", StringComparison.Ordinal) || + !mainMenu.Contains("ResolveSectorMap(AetheriaRuntimeStateBootReport stateBoot)", StringComparison.Ordinal) || !mainMenu.Contains(".Aetheria()", StringComparison.Ordinal) || - !mainMenu.Contains(".LatestSectorMap()", StringComparison.Ordinal) || + !mainMenu.Contains(".ReactiveSectorMap()", StringComparison.Ordinal) || !mainMenu.Contains(".ReactivePlayer()", StringComparison.Ordinal)) { throw new InvalidOperationException( diff --git a/Assets/Scripts/UI/MainMenu.cs b/Assets/Scripts/UI/MainMenu.cs index 9b61fc7f8..edea6dcee 100644 --- a/Assets/Scripts/UI/MainMenu.cs +++ b/Assets/Scripts/UI/MainMenu.cs @@ -19,7 +19,9 @@ public class MainMenu : MonoBehaviour private UIDocument _menuSurfaceDocument; private string _clientStatePath; + private CultMeshReactiveDocument _sectorMap; private CultMeshReactiveDocument _playerSettings; + private CultMeshReactiveDocument _verseHostSettings; private Func _canOpenRuntimeInputScreen; private Action _openRuntimeInputScreen; private readonly AetheriaEveUnitySurfaceChrome _menuSurfaceChrome = new AetheriaEveUnitySurfaceChrome @@ -51,9 +53,9 @@ public class MainMenu : MonoBehaviour private void ShowMain() { var stateBoot = CurrentStateBoot(); - var sectorMap = LatestSectorMap(stateBoot); - var verseHost = LatestVerseHostSettings(stateBoot); - var playerSettings = LatestPlayerSettings(stateBoot); + var sectorMap = ResolveSectorMap(stateBoot); + var verseHost = ResolveVerseHostSettings(stateBoot); + var playerSettings = ResolvePlayerSettings(stateBoot); RenderMenuSurface( AetheriaRuntimeMainMenuSurfaceBuilder.BuildRoot( AetheriaRuntimeMainMenuSurfaceBuilder.ProjectRoot( @@ -78,7 +80,7 @@ public class MainMenu : MonoBehaviour switch (command.Kind) { case AetheriaRuntimeMainMenuCommandKind.ContinueRun: - if (LatestSectorMap(CurrentStateBoot()) == null) + if (ResolveSectorMap(CurrentStateBoot()) == null) { Debug.LogWarning("Main-menu Continue requested without a typed Aetheria sector-map projection."); ShowMain(); @@ -113,7 +115,7 @@ public class MainMenu : MonoBehaviour private bool TryStartDaemonObservedGame(string title) { var stateBoot = CurrentStateBoot(); - var sectorMap = LatestSectorMap(stateBoot); + var sectorMap = ResolveSectorMap(stateBoot); if (sectorMap == null) { Debug.LogWarning($"Cannot start Aetheria observer scene without a typed Aetheria sector-map projection in {stateBoot.StateFilePath}."); @@ -139,7 +141,7 @@ public class MainMenu : MonoBehaviour return AetheriaRuntimeStateBoot.Inspect(AetheriaUnityRuntimePaths.GameDataDirectory); } - private AetheriaRuntimeSectorMapDocument LatestSectorMap(AetheriaRuntimeStateBootReport stateBoot) + private AetheriaRuntimeSectorMapDocument ResolveSectorMap(AetheriaRuntimeStateBootReport stateBoot) { if (!stateBoot.SupportsLocalStateFileRead || !stateBoot.StateFileExists) { @@ -148,18 +150,19 @@ public class MainMenu : MonoBehaviour try { - return ResolveClient(stateBoot) + _sectorMap ??= ResolveClient(stateBoot) .Aetheria() - .LatestSectorMap(); + .ReactiveSectorMap(); + return _sectorMap?.Current; } catch (Exception ex) { - Debug.LogError($"Failed to read typed Aetheria sector-map state for the main menu: {ex}"); + Debug.LogError($"Failed to bind typed Aetheria sector-map state for the main menu: {ex}"); return null; } } - private AetheriaRuntimePlayerSettingsDocument LatestPlayerSettings(AetheriaRuntimeStateBootReport stateBoot) + private AetheriaRuntimePlayerSettingsDocument ResolvePlayerSettings(AetheriaRuntimeStateBootReport stateBoot) { if (!stateBoot.SupportsLocalStateFileRead || !stateBoot.StateFileExists) return null; @@ -179,21 +182,22 @@ public class MainMenu : MonoBehaviour } } - private AetheriaRuntimeVerseHostSettingsDocument LatestVerseHostSettings(AetheriaRuntimeStateBootReport stateBoot) + private AetheriaRuntimeVerseHostSettingsDocument ResolveVerseHostSettings(AetheriaRuntimeStateBootReport stateBoot) { if (!stateBoot.SupportsLocalStateFileRead || !stateBoot.StateFileExists) return null; try { - return ResolveClient(stateBoot) + _verseHostSettings ??= ResolveClient(stateBoot) .Aetheria() .Settings - .LatestVerseHost(); + .ReactiveVerseHost(); + return _verseHostSettings?.Current; } catch (Exception ex) { - Debug.LogError($"Failed to read typed Aetheria Verse host settings for the main menu: {ex}"); + Debug.LogError($"Failed to bind typed Aetheria Verse host settings for the main menu: {ex}"); return null; } } @@ -220,7 +224,7 @@ public class MainMenu : MonoBehaviour AetheriaRuntimeMainMenuSurfaceBuilder.BuildInputSettings( AetheriaRuntimeMainMenuSurfaceBuilder.ProjectRoot( stateBoot, - LatestPlayerSettings(stateBoot), + ResolvePlayerSettings(stateBoot), CanOpenRuntimeInputScreen(), InGame, DateTime.UtcNow.ToString("O"))), @@ -232,7 +236,7 @@ public class MainMenu : MonoBehaviour RenderMenuSurface( AetheriaRuntimeMainMenuSurfaceBuilder.BuildPlayerSettingsShell( AetheriaRuntimeMainMenuSurfaceBuilder.ProjectPlayerSettings( - LatestPlayerSettings(CurrentStateBoot()), + ResolvePlayerSettings(CurrentStateBoot()), DateTime.UtcNow.ToString("O"))), HandlePlayerSettingsSurfaceCommand); } @@ -244,7 +248,7 @@ public class MainMenu : MonoBehaviour AetheriaRuntimeMainMenuSurfaceBuilder.BuildVerseSettingsShell( AetheriaRuntimeMainMenuSurfaceBuilder.ProjectVerseSettings( stateBoot, - LatestVerseHostSettings(stateBoot), + ResolveVerseHostSettings(stateBoot), DateTime.UtcNow.ToString("O"))), HandleVerseSettingsSurfaceCommand); } @@ -469,8 +473,12 @@ public class MainMenu : MonoBehaviour private void ClearClientCaches() { + _sectorMap?.Dispose(); _playerSettings?.Dispose(); + _verseHostSettings?.Dispose(); + _sectorMap = null; _playerSettings = null; + _verseHostSettings = null; } private void OnDestroy() diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaClientState.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaClientState.cs index f1719e590..5f7b02c5b 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaClientState.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaClientState.cs @@ -230,6 +230,18 @@ namespace GameCult.Aetheria.State.Verse return LatestLoadoutTemplatesAsync().ConfigureAwait(false).GetAwaiter().GetResult(); } + public Task> ReactiveLoadoutTemplatesAsync( + CultMeshReactiveDocumentOptions? options = null) + { + return LoadoutTemplates.ReactiveAsync(options); + } + + public CultMeshReactiveDocument ReactiveLoadoutTemplates( + CultMeshReactiveDocumentOptions? options = null) + { + return LoadoutTemplates.Reactive(options); + } + public Task LatestSectorMapAsync() { return SectorMap.LatestAsync(); @@ -240,6 +252,18 @@ namespace GameCult.Aetheria.State.Verse return LatestSectorMapAsync().ConfigureAwait(false).GetAwaiter().GetResult(); } + public Task> ReactiveSectorMapAsync( + CultMeshReactiveDocumentOptions? options = null) + { + return SectorMap.ReactiveAsync(options); + } + + public CultMeshReactiveDocument ReactiveSectorMap( + CultMeshReactiveDocumentOptions? options = null) + { + return SectorMap.Reactive(options); + } + public Task LatestZoneContactsAsync() { return ZoneContacts.LatestAsync(); @@ -250,6 +274,18 @@ namespace GameCult.Aetheria.State.Verse return LatestZoneContactsAsync().ConfigureAwait(false).GetAwaiter().GetResult(); } + public Task> ReactiveZoneContactsAsync( + CultMeshReactiveDocumentOptions? options = null) + { + return ZoneContacts.ReactiveAsync(options); + } + + public CultMeshReactiveDocument ReactiveZoneContacts( + CultMeshReactiveDocumentOptions? options = null) + { + return ZoneContacts.Reactive(options); + } + public Task LatestStationRefitAsync() { return StationRefit.LatestAsync(); @@ -260,6 +296,18 @@ namespace GameCult.Aetheria.State.Verse return LatestStationRefitAsync().ConfigureAwait(false).GetAwaiter().GetResult(); } + public Task> ReactiveStationRefitAsync( + CultMeshReactiveDocumentOptions? options = null) + { + return StationRefit.ReactiveAsync(options); + } + + public CultMeshReactiveDocument ReactiveStationRefit( + CultMeshReactiveDocumentOptions? options = null) + { + return StationRefit.Reactive(options); + } + public Task LatestZoneRenderAsync() { return ZoneRender.LatestAsync(); @@ -270,6 +318,18 @@ namespace GameCult.Aetheria.State.Verse return LatestZoneRenderAsync().ConfigureAwait(false).GetAwaiter().GetResult(); } + public Task> ReactiveZoneRenderAsync( + CultMeshReactiveDocumentOptions? options = null) + { + return ZoneRender.ReactiveAsync(options); + } + + public CultMeshReactiveDocument ReactiveZoneRender( + CultMeshReactiveDocumentOptions? options = null) + { + return ZoneRender.Reactive(options); + } + public Observable Watch() where TDocument : class {