From df41d11ef1ea81c4f831f33822befe4bc0e810c4 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 28 Jun 2026 10:27:43 +0100 Subject: [PATCH] Sample reactive projection inputs in Verse client --- Aetheria.State.Verify/Program.cs | 40 +++++++++-- .../Runtime/AetheriaRuntimeVerseClient.cs | 71 +++++++++++++++---- 2 files changed, 94 insertions(+), 17 deletions(-) diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs index f252aa5ef..bc629115b 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -10717,10 +10717,23 @@ static void RequireAetheriaRuntimeVerseClientContract(string root) "WatchDaemonEditorTuiSurfaces()", "CultMeshMutableStatePointer", "private AetheriaClientState? _aetheriaState", + "private AetheriaRuntimeReactiveProjectionInputs? _projectionInputs", + "_projectionInputs?.Dispose()", "return _aetheriaState ??= CreateAetheriaState();", "AetheriaRuntimeLoadoutTemplatesDocument", "ProjectStarbridgeSummaryAsync", - "var catalog = await catalogDocument.LatestAsync().ConfigureAwait(false);", + "new AetheriaRuntimeReactiveProjectionInputs(", + "catalogDocument.Reactive()", + "loadoutTemplatesDocument.Reactive()", + "starbridgeScenarioDocument.Reactive()", + "starbridgeSessionDocument.Reactive()", + "BootstrapRuntimeCatalogSnapshot(),", + "BootstrapLoadoutTemplatesDocument())", + "projectionInputs.Catalog", + "projectionInputs.LoadoutTemplates.Templates", + "projectionInputs.StarbridgeScenario", + "projectionInputs.StarbridgeSession", + "private sealed class AetheriaRuntimeReactiveProjectionInputs : IDisposable", "BootstrapCatalogDocument(", "CatalogBootstrapSource(", "BootstrapRuntimeCatalogSnapshot()", @@ -10744,6 +10757,22 @@ static void RequireAetheriaRuntimeVerseClientContract(string root) string.Join(", ", missingClientSymbols)); } + if (client.Contains("ProjectStationRefitAsync", StringComparison.Ordinal) && + client.Contains("loadoutTemplatesDocument.LatestAsync()", StringComparison.Ordinal)) + { + throw new InvalidOperationException( + "Aetheria runtime Verse client station-refit projection still polls loadout templates instead of sampling managed reactive projection inputs."); + } + + if (client.Contains("ProjectStarbridgeSummaryAsync", StringComparison.Ordinal) && + (client.Contains("starbridgeScenarioDocument.LatestAsync()", StringComparison.Ordinal) || + client.Contains("starbridgeSessionDocument.LatestAsync()", StringComparison.Ordinal) || + client.Contains("catalogDocument.LatestAsync()", StringComparison.Ordinal))) + { + throw new InvalidOperationException( + "Aetheria runtime Verse client Starbridge projection still polls sibling documents instead of sampling managed reactive projection inputs."); + } + var requiredClientStateSymbols = new[] { "public Func CreateEveSurfaceStateRefResolver()", @@ -10825,7 +10854,7 @@ static void RequireAetheriaRuntimeVerseClientContract(string root) } var starbridgeSummaryStart = client.IndexOf( - "async Task ProjectStarbridgeSummaryAsync", + "Task ProjectStarbridgeSummaryAsync", StringComparison.Ordinal); if (starbridgeSummaryStart < 0) { @@ -10837,7 +10866,8 @@ static void RequireAetheriaRuntimeVerseClientContract(string root) var starbridgeSummaryBlock = indexedDocumentStart > starbridgeSummaryStart ? client.Substring(starbridgeSummaryStart, indexedDocumentStart - starbridgeSummaryStart) : client.Substring(starbridgeSummaryStart); - if (!starbridgeSummaryBlock.Contains("var catalog = await catalogDocument.LatestAsync().ConfigureAwait(false);", StringComparison.Ordinal) || + if (!starbridgeSummaryBlock.Contains("projectionInputs.Catalog", StringComparison.Ordinal) || + starbridgeSummaryBlock.Contains("catalogDocument.LatestAsync()", StringComparison.Ordinal) || starbridgeSummaryBlock.Contains("BootstrapRuntimeCatalogSnapshot()", StringComparison.Ordinal) || starbridgeSummaryBlock.Contains("AetheriaRuntimeCatalogStore.OpenReadOnly", StringComparison.Ordinal)) { @@ -16619,8 +16649,8 @@ static void RequireInventoryLoadoutRestoreRequestAuthority(string root) !rtsProjection.Contains("AetheriaRuntimeDaemonTradeItemQueries.TryProjectLoadoutTemplatePrice(", StringComparison.Ordinal) || !rtsProjection.Contains("credits >= price", StringComparison.Ordinal) || !clientState.Contains("public CultMeshDocumentHandle LoadoutTemplates", StringComparison.Ordinal) || - !verseClient.Contains("var loadoutTemplates = await loadoutTemplatesDocument.LatestAsync()", StringComparison.Ordinal) || - !verseClient.Contains("ProjectStationRefit(frame, loadoutTemplates.Templates, catalog)", StringComparison.Ordinal)) + !verseClient.Contains("projectionInputs.LoadoutTemplates.Templates", StringComparison.Ordinal) || + !verseClient.Contains("ProjectStationRefit(", StringComparison.Ordinal)) { throw new InvalidOperationException( "StationRefitAsync must publish typed loadout restore options with shared runtime pricing and daemon-target identity."); diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeVerseClient.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeVerseClient.cs index d7b773cd8..d71a86e2a 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeVerseClient.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeVerseClient.cs @@ -144,6 +144,7 @@ namespace GameCult.Aetheria.State.Verse private readonly CultMeshNode _node; private AetheriaClientState? _aetheriaState; + private AetheriaRuntimeReactiveProjectionInputs? _projectionInputs; private bool _disposed; private AetheriaRuntimeVerseClient(string statePath, string runtimeId, CultMeshNode node) @@ -514,6 +515,7 @@ namespace GameCult.Aetheria.State.Verse return; _disposed = true; + _projectionInputs?.Dispose(); _node.Dispose(); } @@ -542,6 +544,14 @@ namespace GameCult.Aetheria.State.Verse AetheriaRuntimeVerseRecordKeys.StarbridgeScenarioLatest); var starbridgeSessionDocument = Document( AetheriaRuntimeVerseRecordKeys.StarbridgeSessionLatest); + var projectionInputs = new AetheriaRuntimeReactiveProjectionInputs( + catalogDocument.Reactive(), + loadoutTemplatesDocument.Reactive(), + starbridgeScenarioDocument.Reactive(), + starbridgeSessionDocument.Reactive(), + BootstrapRuntimeCatalogSnapshot(), + BootstrapLoadoutTemplatesDocument()); + _projectionInputs = projectionInputs; return new AetheriaClientState( Document( @@ -699,25 +709,23 @@ namespace GameCult.Aetheria.State.Verse routeHint: new CultMeshRouteHint(CultMeshLocalityKind.SharedMemory, "Aetheria managed catalog state")); } - async Task ProjectStationRefitAsync( + Task ProjectStationRefitAsync( AetheriaRuntimeDaemonFrameDocument frame) { - var loadoutTemplates = await loadoutTemplatesDocument.LatestAsync().ConfigureAwait(false); - var catalog = await catalogDocument.LatestAsync().ConfigureAwait(false); - return AetheriaRuntimeRtsProjection.ProjectStationRefit(frame, loadoutTemplates.Templates, catalog); + return Task.FromResult(AetheriaRuntimeRtsProjection.ProjectStationRefit( + frame, + projectionInputs.LoadoutTemplates.Templates, + projectionInputs.Catalog)); } - async Task ProjectStarbridgeSummaryAsync( + Task ProjectStarbridgeSummaryAsync( AetheriaRuntimeDaemonFrameDocument frame) { - var scenario = await starbridgeScenarioDocument.LatestAsync().ConfigureAwait(false); - var session = await starbridgeSessionDocument.LatestAsync().ConfigureAwait(false); - var catalog = await catalogDocument.LatestAsync().ConfigureAwait(false); - return AetheriaRuntimeStarbridgeProjection.ProjectSessionSummary( + return Task.FromResult(AetheriaRuntimeStarbridgeProjection.ProjectSessionSummary( frame, - scenario, - session, - catalog); + projectionInputs.StarbridgeScenario, + projectionInputs.StarbridgeSession, + projectionInputs.Catalog)); } static string IndexedDocumentId(string prefix, int index) @@ -776,6 +784,45 @@ namespace GameCult.Aetheria.State.Verse description: "legacy catalog store bootstrap seed for managed Aetheria state"); } + private sealed class AetheriaRuntimeReactiveProjectionInputs : IDisposable + { + private readonly CultMeshReactiveDocument _catalog; + private readonly CultMeshReactiveDocument _loadoutTemplates; + private readonly CultMeshReactiveDocument _starbridgeScenario; + private readonly CultMeshReactiveDocument _starbridgeSession; + private readonly AetheriaRuntimeCatalogSnapshot _fallbackCatalog; + private readonly AetheriaRuntimeLoadoutTemplatesDocument _fallbackLoadoutTemplates; + + public AetheriaRuntimeReactiveProjectionInputs( + CultMeshReactiveDocument catalog, + CultMeshReactiveDocument loadoutTemplates, + CultMeshReactiveDocument starbridgeScenario, + CultMeshReactiveDocument starbridgeSession, + AetheriaRuntimeCatalogSnapshot fallbackCatalog, + AetheriaRuntimeLoadoutTemplatesDocument fallbackLoadoutTemplates) + { + _catalog = catalog ?? throw new ArgumentNullException(nameof(catalog)); + _loadoutTemplates = loadoutTemplates ?? throw new ArgumentNullException(nameof(loadoutTemplates)); + _starbridgeScenario = starbridgeScenario ?? throw new ArgumentNullException(nameof(starbridgeScenario)); + _starbridgeSession = starbridgeSession ?? throw new ArgumentNullException(nameof(starbridgeSession)); + _fallbackCatalog = fallbackCatalog ?? throw new ArgumentNullException(nameof(fallbackCatalog)); + _fallbackLoadoutTemplates = fallbackLoadoutTemplates ?? throw new ArgumentNullException(nameof(fallbackLoadoutTemplates)); + } + + public AetheriaRuntimeCatalogSnapshot Catalog => _catalog.Current ?? _fallbackCatalog; + public AetheriaRuntimeLoadoutTemplatesDocument LoadoutTemplates => _loadoutTemplates.Current ?? _fallbackLoadoutTemplates; + public AetheriaRuntimeStarbridgeScenarioDocument? StarbridgeScenario => _starbridgeScenario.Current; + public AetheriaRuntimeStarbridgeSessionDocument? StarbridgeSession => _starbridgeSession.Current; + + public void Dispose() + { + _catalog.Dispose(); + _loadoutTemplates.Dispose(); + _starbridgeScenario.Dispose(); + _starbridgeSession.Dispose(); + } + } + private AetheriaRuntimeCatalogSnapshot BootstrapRuntimeCatalogSnapshot() { return AetheriaRuntimeCatalogStore.OpenReadOnly(StatePath);