Sample reactive projection inputs in Verse client

This commit is contained in:
Meta Vens 2026-06-28 10:27:43 +01:00
commit df41d11ef1
2 changed files with 94 additions and 17 deletions

View file

@ -10717,10 +10717,23 @@ static void RequireAetheriaRuntimeVerseClientContract(string root)
"WatchDaemonEditorTuiSurfaces()",
"CultMeshMutableStatePointer<EveSurfaceState>",
"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<string, string> CreateEveSurfaceStateRefResolver()",
@ -10825,7 +10854,7 @@ static void RequireAetheriaRuntimeVerseClientContract(string root)
}
var starbridgeSummaryStart = client.IndexOf(
"async Task<AetheriaRuntimeStarbridgeSessionSummaryDocument> ProjectStarbridgeSummaryAsync",
"Task<AetheriaRuntimeStarbridgeSessionSummaryDocument> 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<AetheriaRuntimeLoadoutTemplatesDocument> 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.");

View file

@ -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<AetheriaRuntimeStarbridgeSessionDocument>(
AetheriaRuntimeVerseRecordKeys.StarbridgeSessionLatest);
var projectionInputs = new AetheriaRuntimeReactiveProjectionInputs(
catalogDocument.Reactive(),
loadoutTemplatesDocument.Reactive(),
starbridgeScenarioDocument.Reactive(),
starbridgeSessionDocument.Reactive(),
BootstrapRuntimeCatalogSnapshot(),
BootstrapLoadoutTemplatesDocument());
_projectionInputs = projectionInputs;
return new AetheriaClientState(
Document<AetheriaRuntimeDaemonProviderAdvertisementDocument>(
@ -699,25 +709,23 @@ namespace GameCult.Aetheria.State.Verse
routeHint: new CultMeshRouteHint(CultMeshLocalityKind.SharedMemory, "Aetheria managed catalog state"));
}
async Task<AetheriaRuntimeStationRefitDocument> ProjectStationRefitAsync(
Task<AetheriaRuntimeStationRefitDocument> 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<AetheriaRuntimeStarbridgeSessionSummaryDocument> ProjectStarbridgeSummaryAsync(
Task<AetheriaRuntimeStarbridgeSessionSummaryDocument> 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<AetheriaRuntimeCatalogSnapshot> _catalog;
private readonly CultMeshReactiveDocument<AetheriaRuntimeLoadoutTemplatesDocument> _loadoutTemplates;
private readonly CultMeshReactiveDocument<AetheriaRuntimeStarbridgeScenarioDocument> _starbridgeScenario;
private readonly CultMeshReactiveDocument<AetheriaRuntimeStarbridgeSessionDocument> _starbridgeSession;
private readonly AetheriaRuntimeCatalogSnapshot _fallbackCatalog;
private readonly AetheriaRuntimeLoadoutTemplatesDocument _fallbackLoadoutTemplates;
public AetheriaRuntimeReactiveProjectionInputs(
CultMeshReactiveDocument<AetheriaRuntimeCatalogSnapshot> catalog,
CultMeshReactiveDocument<AetheriaRuntimeLoadoutTemplatesDocument> loadoutTemplates,
CultMeshReactiveDocument<AetheriaRuntimeStarbridgeScenarioDocument> starbridgeScenario,
CultMeshReactiveDocument<AetheriaRuntimeStarbridgeSessionDocument> 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);