From aafdd9a9678c99e0a158dbd6202278fbfeda6149 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Mon, 29 Jun 2026 03:21:58 +0100 Subject: [PATCH] Read catalog surface as managed document --- .../Aetheria.State.Unity.Smoke.csproj | 1 - Aetheria.State.Unity.Smoke/Program.cs | 20 +-- .../AetheriaRuntimeCatalogClient.cs | 70 +------- Aetheria.State.Verify/Program.cs | 22 ++- Aetheria.State/AetheriaStateNode.cs | 30 ++++ .../Runtime/AetheriaRuntimeCatalogStore.cs | 167 ------------------ 6 files changed, 58 insertions(+), 252 deletions(-) diff --git a/Aetheria.State.Unity.Smoke/Aetheria.State.Unity.Smoke.csproj b/Aetheria.State.Unity.Smoke/Aetheria.State.Unity.Smoke.csproj index 169606e45..de6a3899a 100644 --- a/Aetheria.State.Unity.Smoke/Aetheria.State.Unity.Smoke.csproj +++ b/Aetheria.State.Unity.Smoke/Aetheria.State.Unity.Smoke.csproj @@ -10,6 +10,5 @@ - diff --git a/Aetheria.State.Unity.Smoke/Program.cs b/Aetheria.State.Unity.Smoke/Program.cs index 6b70614ba..d9cc4565c 100644 --- a/Aetheria.State.Unity.Smoke/Program.cs +++ b/Aetheria.State.Unity.Smoke/Program.cs @@ -12,8 +12,7 @@ var statePath = args.Length > 1 await using var client = await AetheriaRuntimeCatalogClient.OpenAsync(statePath); var catalog = client.ReadCatalog(); var packageCatalog = AetheriaRuntimeCatalogStore.OpenReadOnly(statePath); -var packageSurfaces = AetheriaRuntimeCatalogStore.ReadEveSurfaces(statePath); -var surface = await client.ReadCatalogSurfaceAsync(); +var surface = client.ReadCatalogSurface(); var commitSmokeDirectory = Path.Combine(Path.GetTempPath(), "aetheria-state-unity-smoke", Guid.NewGuid().ToString("N")); var commitSmokeStatePath = Path.Combine(commitSmokeDirectory, "aetheria-world.cc"); Directory.CreateDirectory(commitSmokeDirectory); @@ -158,15 +157,14 @@ if (surface?.Schema != "gamecult.eve.surface.v1" || throw new InvalidOperationException("Runtime catalog client did not read the typed Eve surface."); } -var packageSurface = packageSurfaces.FirstOrDefault(candidate => candidate.Surface.Id == AetheriaCatalogSurfaceProjector.SurfaceId); -if (packageSurface == null || - packageSurface.Schema != "gamecult.eve.surface.v1" || - packageSurface.ProviderId != "aetheria" || - packageSurface.Surface.Root.Kind != "surface" || - packageSurface.Surface.Root.Children.Count == 0 || - packageSurface.Commands.All(command => command.Command != AetheriaRuntimeCatalogCommands.Refresh)) +if (surface == null || + surface.Schema != "gamecult.eve.surface.v1" || + surface.ProviderId != "aetheria" || + surface.Surface.Root.Kind != "surface" || + surface.Surface.Root.Children.Length == 0 || + surface.Commands.All(command => command.Command != AetheriaRuntimeCatalogCommands.Refresh)) { - throw new InvalidOperationException("Package runtime store did not read the typed Eve surface contract from CultCache."); + throw new InvalidOperationException("Unity runtime catalog client did not read the typed Eve surface contract from the managed document."); } try @@ -229,7 +227,7 @@ Console.WriteLine($"Interior/hardpoint sample: {interior.Name} {interior.Interio Console.WriteLine($"Behavior payload sample: {behaviorHost.Name} {behaviorPayload.Kind}/{behaviorPayload.Fields.Count}"); Console.WriteLine($"Behavior sample: {behaviorKind}"); Console.WriteLine($"Eve surface: {surface.Surface.Id}"); -Console.WriteLine($"Package Eve surfaces: {packageSurfaces.Count}"); +Console.WriteLine($"Catalog Eve surface document: {surface.Surface.Id}"); Console.WriteLine("Runtime Eve command smoke: surface command document is a typed state record"); static int CountRequiredBehaviorItemRefsMissingItemKeys(AetheriaRuntimeBehaviorPayload payload) diff --git a/Aetheria.State.Unity/AetheriaRuntimeCatalogClient.cs b/Aetheria.State.Unity/AetheriaRuntimeCatalogClient.cs index 717d9fecb..899dd3d08 100644 --- a/Aetheria.State.Unity/AetheriaRuntimeCatalogClient.cs +++ b/Aetheria.State.Unity/AetheriaRuntimeCatalogClient.cs @@ -26,75 +26,9 @@ public sealed class AetheriaRuntimeCatalogClient : IAsyncDisposable, IDisposable return _node.RuntimeCatalog().Latest(); } - public async Task ReadCatalogSurfaceAsync() + public EveSurfaceState? ReadCatalogSurface() { - var surface = AetheriaRuntimeCatalogStore - .ReadEveSurfaces(_node.StatePath) - .FirstOrDefault(candidate => candidate.Surface.Id == AetheriaCatalogSurfaceProjector.SurfaceId); - return surface == null ? null : ToState(surface); - } - - private static EveSurfaceState ToState(global::GameCult.Eve.Surface.EveSurfaceDocument document) - { - return new EveSurfaceState - { - Type = document.Type, - Schema = document.Schema, - ProviderId = document.ProviderId, - ProviderKind = document.ProviderKind, - Title = document.Title, - Version = document.Version, - UpdatedAtUtc = document.UpdatedAtUtc, - Surface = new EveSurface - { - Id = document.Surface.Id, - Root = ToState(document.Surface.Root), - Styles = document.Surface.Styles - .Select(style => new EveStyleToken { Name = style.Name, Value = style.Value }) - .ToArray() - }, - Commands = document.Commands - .Select(command => - { - var record = CultMesh.OperationBindingRecord(command.Operation); - return new EveCommandTemplate - { - Command = record.OperationId, - Label = record.Label, - Transport = record.RouteDescription, - SchemaId = record.SchemaId, - RouteKind = record.RouteKind, - RouteDescription = record.RouteDescription - }; - }) - .ToArray() - }; - } - - private static EveSurfaceComponent ToState(global::GameCult.Eve.Surface.EveSurfaceComponent component) - { - return new EveSurfaceComponent - { - Id = component.Id, - Kind = component.Kind, - Props = new Dictionary(component.Props, StringComparer.Ordinal), - Children = component.Children.Select(ToState).ToArray(), - StateBindings = component.StateBindings - .Select(binding => - { - var record = CultMesh.StateBindingRecord(binding); - return new EveSurfaceStateBinding - { - TargetProp = record.TargetProp, - PointerId = record.PointerId, - SourceId = record.SourceId, - SchemaId = record.SchemaId, - RouteKind = record.RouteKind, - RouteDescription = record.RouteDescription - }; - }) - .ToArray() - }; + return _node.CatalogSurface().Latest(); } public ValueTask DisposeAsync() diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs index e1be105c1..479b110a5 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -11615,11 +11615,8 @@ static void RequireUnityRuntimeCatalogClientUsesManagedDocument(string root) "enableDurableShardLogs: false", "public AetheriaRuntimeCatalogSnapshot ReadCatalog()", "return _node.RuntimeCatalog().Latest();", - "public async Task ReadCatalogSurfaceAsync()", - "AetheriaRuntimeCatalogStore", - ".ReadEveSurfaces(_node.StatePath)", - "candidate.Surface.Id == AetheriaCatalogSurfaceProjector.SurfaceId", - "private static EveSurfaceState ToState(global::GameCult.Eve.Surface.EveSurfaceDocument document)" + "public EveSurfaceState? ReadCatalogSurface()", + "return _node.CatalogSurface().Latest();" }; var missingSymbols = requiredSymbols .Where(symbol => !client.Contains(symbol, StringComparison.Ordinal)) @@ -11635,6 +11632,10 @@ static void RequireUnityRuntimeCatalogClientUsesManagedDocument(string root) { "_node.ReadCatalogSnapshot()", "_node.GetCatalogSurfaceAsync()", + "ReadCatalogSurfaceAsync(", + "AetheriaRuntimeCatalogStore.ReadEveSurfaces", + "AetheriaRuntimeCatalogStore", + "ToState(global::GameCult.Eve.Surface.EveSurfaceDocument", "AetheriaRuntimeCatalogSnapshotMapper.FromCatalog(", "AetheriaCatalogSnapshot" }; @@ -15767,6 +15768,17 @@ static void RequireRuntimeStateReaderOwnsUnityStateAcquisition(string root) "Runtime catalog store still exposes file-backed run/zone snapshot projection; managed daemon checkpoint and zone-render documents own runtime state lowering."); } + if (runtimeCatalogStore.Contains("ReadEveSurfaces", StringComparison.Ordinal) || + runtimeCatalogStore.Contains("ReadEveSurface(", StringComparison.Ordinal) || + runtimeCatalogStore.Contains("ProjectStatRecipeSurfaceDocument", StringComparison.Ordinal) || + runtimeCatalogStore.Contains("ProjectTradeValuePolicySurfaceDocument", StringComparison.Ordinal) || + runtimeCatalogStore.Contains("EveSurfaceSchema", StringComparison.Ordinal) || + runtimeCatalogStore.Contains("ReadFieldEveSurface", StringComparison.Ordinal)) + { + throw new InvalidOperationException( + "Runtime catalog store still exposes Eve-surface parsing/projection helpers; surface access must flow through managed EveSurfaceState documents."); + } + if (!unityPackageProject.Contains("AetheriaRuntimeStateRefResolver.cs", StringComparison.Ordinal) || unityPackageProject.Contains("AetheriaRuntimeStateReader.cs", StringComparison.Ordinal)) { diff --git a/Aetheria.State/AetheriaStateNode.cs b/Aetheria.State/AetheriaStateNode.cs index 1328ea978..f43eb1073 100644 --- a/Aetheria.State/AetheriaStateNode.cs +++ b/Aetheria.State/AetheriaStateNode.cs @@ -15,6 +15,7 @@ public sealed class AetheriaStateNode : IAsyncDisposable, IDisposable { private readonly CultMeshNode _node; private CultMeshDocumentHandle? _runtimeCatalog; + private CultMeshDocumentHandle? _catalogSurface; private AetheriaStateNode(string statePath, string runtimeId, CultMeshNode node) { @@ -116,6 +117,35 @@ public sealed class AetheriaStateNode : IAsyncDisposable, IDisposable routeHint: new CultMeshRouteHint(CultMeshLocalityKind.SharedMemory, "Aetheria typed catalog state")); } + public CultMeshDocumentHandle CatalogSurface() + { + var catalog = RuntimeCatalog(); + return _catalogSurface ??= CultMesh.Document( + "aetheria.catalog.surface", + CultMesh.Verse("aetheria.local", RuntimeId), + _ => Task.FromResult(AetheriaCatalogSurfaceProjector.Build( + catalog.Latest(), + DateTimeOffset.UtcNow.ToString("O"))), + _ => Database.WatchRecord( + AetheriaRuntimeVerseRecordKeys.DaemonFrameLatest) + .Where(change => change.Document != null) + .Select(_ => AetheriaCatalogSurfaceProjector.Build( + catalog.Latest(), + DateTimeOffset.UtcNow.ToString("O"))), + sources: new[] + { + CultMesh.ProjectionSource( + "aetheria.catalog.runtime", + AetheriaRuntimeCatalogSnapshot.SchemaId, + "managed Aetheria runtime catalog document"), + CultMesh.ProjectionSource( + CatalogSurfaceKey.ToString(), + "gamecult.eve.surface.v1", + "managed Aetheria catalog Eve surface document") + }, + routeHint: new CultMeshRouteHint(CultMeshLocalityKind.SharedMemory, "Aetheria typed catalog Eve surface")); + } + public Task> SubmitDaemonCommandAsync( AetheriaRuntimeDaemonCommandDocument command) { diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs index 7155bea5c..f148d5747 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs @@ -3,8 +3,6 @@ using System.Buffers; using System.Collections.Generic; using System.IO; using System.Linq; -using GameCult.Eve.Surface; -using GameCult.Mesh; using MessagePack; #nullable enable @@ -16,7 +14,6 @@ namespace GameCult.Aetheria.State.Verse private const string ItemDefinitionSchema = "aetheria.item_definition"; private const string CorporationSchema = "aetheria.corporation"; private const string NameFileSchema = "aetheria.name_file"; - private const string EveSurfaceSchema = "gamecult.eve.surface"; private const string TradeValuePolicySchema = "aetheria.trade_value_policy"; private const string PlayerSettingsSchema = "aetheria.player_settings"; private const string VerseHostSettingsSchema = "aetheria.verse_host_settings"; @@ -58,51 +55,6 @@ namespace GameCult.Aetheria.State.Verse tradeValueSettings); } - public static IReadOnlyList ReadEveSurfaces(string stateFilePath) - { - if (string.IsNullOrWhiteSpace(stateFilePath)) - throw new ArgumentException("State file path must be non-empty.", nameof(stateFilePath)); - - var catalog = ReadSchemaCatalog(stateFilePath); - var surfaces = new List(); - foreach (var record in ReadRecords(stateFilePath)) - { - if (!catalog.TryGetValue(record.SchemaId, out var schemaName) || schemaName != EveSurfaceSchema) - continue; - - surfaces.Add(ReadEveSurface(record.Payload)); - } - - var catalogSnapshot = OpenReadOnly(stateFilePath); - surfaces.Add(AetheriaRuntimeEveSurfaceAdapter.ToEveSurfaceDocument( - ProjectStatRecipeSurfaceDocument(catalogSnapshot))); - surfaces.Add(AetheriaRuntimeEveSurfaceAdapter.ToEveSurfaceDocument( - ProjectTradeValuePolicySurfaceDocument(catalogSnapshot))); - return surfaces; - } - - public static AetheriaRuntimeSurfaceDocument ProjectStatRecipeSurfaceDocument(string stateFilePath) - { - return ProjectStatRecipeSurfaceDocument(OpenReadOnly(stateFilePath)); - } - - public static AetheriaRuntimeSurfaceDocument ProjectStatRecipeSurfaceDocument( - AetheriaRuntimeCatalogSnapshot? catalog) - { - return AetheriaRuntimeStatRecipeSurfaceBuilder.BuildFromCatalog(catalog); - } - - public static AetheriaRuntimeSurfaceDocument ProjectTradeValuePolicySurfaceDocument(string stateFilePath) - { - return ProjectTradeValuePolicySurfaceDocument(OpenReadOnly(stateFilePath)); - } - - public static AetheriaRuntimeSurfaceDocument ProjectTradeValuePolicySurfaceDocument( - AetheriaRuntimeCatalogSnapshot? catalog) - { - return AetheriaRuntimeTradeValuePolicySurfaceBuilder.BuildFromCatalog(catalog); - } - private static AetheriaRuntimeStatRecipe? ReadBehaviorStatRecipe(AetheriaRuntimeBehaviorValue? value) { if (value?.Children == null || value.Children.Count <= 5) @@ -883,23 +835,6 @@ namespace GameCult.Aetheria.State.Verse return new AetheriaRuntimeNameFile(NameFileKey(legacyId), name, nameCount, sampleNames, names); } - private static EveSurfaceDocument ReadEveSurface(byte[] payload) - { - var reader = new MessagePackReader(payload); - var fields = reader.ReadArrayHeader(); - var type = ReadFieldString(ref reader, fields, 0); - var schema = ReadFieldString(ref reader, fields, 1); - var providerId = ReadFieldString(ref reader, fields, 2); - var providerKind = ReadFieldString(ref reader, fields, 3); - var title = ReadFieldString(ref reader, fields, 4); - var version = ReadFieldInt64(ref reader, fields, 5); - var updatedAtUtc = ReadFieldString(ref reader, fields, 6); - var surface = ReadFieldEveSurfaceTree(ref reader, fields, 7); - var commands = ReadFieldEveCommandTemplates(ref reader, fields, 8); - SkipRemaining(ref reader, fields, 9); - return new EveSurfaceDocument(type, schema, providerId, providerKind, title, version, updatedAtUtc, surface, commands); - } - private static AetheriaRuntimePlayerSettingsSnapshot ReadPlayerSettingsPayload(byte[] payload) { var reader = new MessagePackReader(payload); @@ -1671,108 +1606,6 @@ namespace GameCult.Aetheria.State.Verse return allegiances; } - private static EveSurfaceTree ReadFieldEveSurfaceTree(ref MessagePackReader reader, int fields, int index) - { - if (index >= fields) - return new EveSurfaceTree("", EmptyEveComponent(), Array.Empty()); - - var surfaceFields = reader.ReadArrayHeader(); - var id = ReadFieldString(ref reader, surfaceFields, 0); - var root = ReadFieldEveComponent(ref reader, surfaceFields, 1); - var styles = ReadFieldEveStyleTokens(ref reader, surfaceFields, 2); - SkipRemaining(ref reader, surfaceFields, 3); - return new EveSurfaceTree(id, root, styles); - } - - private static EveSurfaceComponent ReadFieldEveComponent(ref MessagePackReader reader, int fields, int index) - { - if (index >= fields) - return EmptyEveComponent(); - - var componentFields = reader.ReadArrayHeader(); - var id = ReadFieldString(ref reader, componentFields, 0); - var kind = ReadFieldString(ref reader, componentFields, 1); - var props = ReadFieldStringMap(ref reader, componentFields, 2); - var children = ReadFieldEveComponents(ref reader, componentFields, 3); - SkipRemaining(ref reader, componentFields, 4); - return new EveSurfaceComponent(id, kind, props, children); - } - - private static IReadOnlyList ReadFieldEveComponents(ref MessagePackReader reader, int fields, int index) - { - if (index >= fields) return Array.Empty(); - var count = reader.ReadArrayHeader(); - var components = new EveSurfaceComponent[count]; - for (var component = 0; component < count; component++) - components[component] = ReadFieldEveComponent(ref reader, 1, 0); - return components; - } - - private static IReadOnlyDictionary ReadFieldStringMap(ref MessagePackReader reader, int fields, int index) - { - if (index >= fields) return EmptyStringMap(); - var count = reader.ReadMapHeader(); - if (count == 0) return EmptyStringMap(); - var map = new Dictionary(count, StringComparer.Ordinal); - for (var entry = 0; entry < count; entry++) - { - var key = ReadString(ref reader); - var value = ReadString(ref reader); - if (!string.IsNullOrWhiteSpace(key)) - map[key] = value; - } - - return map; - } - - private static IReadOnlyList ReadFieldEveStyleTokens(ref MessagePackReader reader, int fields, int index) - { - if (index >= fields) return Array.Empty(); - var count = reader.ReadArrayHeader(); - var tokens = new EveStyleToken[count]; - for (var token = 0; token < count; token++) - { - var tokenFields = reader.ReadArrayHeader(); - var name = ReadFieldString(ref reader, tokenFields, 0); - var value = ReadFieldString(ref reader, tokenFields, 1); - SkipRemaining(ref reader, tokenFields, 2); - tokens[token] = new EveStyleToken(name, value); - } - - return tokens; - } - - private static IReadOnlyList ReadFieldEveCommandTemplates(ref MessagePackReader reader, int fields, int index) - { - if (index >= fields) return Array.Empty(); - var count = reader.ReadArrayHeader(); - var commands = new EveCommandTemplate[count]; - for (var command = 0; command < count; command++) - { - var commandFields = reader.ReadArrayHeader(); - var name = ReadFieldString(ref reader, commandFields, 0); - var label = ReadFieldString(ref reader, commandFields, 1); - var transport = ReadFieldString(ref reader, commandFields, 2); - SkipRemaining(ref reader, commandFields, 3); - commands[command] = new EveCommandTemplate(CultMesh.OperationBinding( - name, - label, - routeHint: new CultMeshRouteHint(CultMeshLocalityKind.Automatic, transport))); - } - - return commands; - } - - private static EveSurfaceComponent EmptyEveComponent() - { - return new EveSurfaceComponent("", "", EmptyStringMap(), Array.Empty()); - } - - private static IReadOnlyDictionary EmptyStringMap() - { - return new Dictionary(0, StringComparer.Ordinal); - } - private static IReadOnlyList ReadShapeCells(ref MessagePackReader reader) { var count = reader.ReadArrayHeader();