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();