From 783ea271cce99dac5187844c4146e2d12d7cfb33 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 28 Jun 2026 07:16:21 +0100 Subject: [PATCH] Route SoA latest view through managed state --- Aetheria.State.Freeze/Program.cs | 33 +++++++++++--- Aetheria.State.Verify/Program.cs | 29 ++++++------ .../Tests/DaemonRuntimeDocumentTests.cs | 44 ++++++++++++------- GameCult.Aetheria.State.Unity.csproj | 1 - .../AetheriaRuntimeDaemonSoaFramePublisher.cs | 3 +- .../AetheriaRuntimeDaemonSoaViewStore.cs | 19 -------- .../AetheriaRuntimeDaemonSoaViewStore.cs.meta | 2 - .../AetheriaRuntimeDaemonTickRunner.cs | 2 +- 8 files changed, 70 insertions(+), 63 deletions(-) delete mode 100644 Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonSoaViewStore.cs delete mode 100644 Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonSoaViewStore.cs.meta diff --git a/Aetheria.State.Freeze/Program.cs b/Aetheria.State.Freeze/Program.cs index 6066cbbd0..c9b6e691e 100644 --- a/Aetheria.State.Freeze/Program.cs +++ b/Aetheria.State.Freeze/Program.cs @@ -83,25 +83,31 @@ static async Task ExportFixtureSetAsync( if (commands.Count > 0) WriteArtifact(outputDirectory, manifest, "commands.seeded", AetheriaRuntimeDaemonSchemas.Command + "[]", commands); - var frame = AetheriaRuntimeDaemonFrameStore.ReadFrame(statePath); + await using var node = await AetheriaStateNode + .OpenAsync(statePath, runtimeId: "aetheria-freeze", startServer: false) + .ConfigureAwait(false); + + var frame = await node.LatestFrame().ReadAsync().ConfigureAwait(false); + if (frame == null) + throw new InvalidOperationException("Freeze export cannot continue because the daemon latest frame is missing."); WriteArtifact(outputDirectory, manifest, "daemon-frame.latest", AetheriaRuntimeDaemonSchemas.Frame, frame); - if (AetheriaRuntimeDaemonPublicationStore.TryReadHealth(statePath, out var health)) + if (await TryReadAsync(() => node.Health().ReadAsync()).ConfigureAwait(false) is { } health) WriteArtifact(outputDirectory, manifest, "daemon-health.latest", AetheriaRuntimeDaemonSchemas.Health, health); - if (AetheriaRuntimeDaemonPublicationStore.TryReadProviderAdvertisement(statePath, out var provider)) + if (await TryReadAsync(() => node.ProviderAdvertisement().ReadAsync()).ConfigureAwait(false) is { } provider) WriteArtifact(outputDirectory, manifest, "daemon-provider.latest", AetheriaRuntimeDaemonSchemas.ProviderAdvertisement, provider); - if (AetheriaRuntimeDaemonPublicationStore.TryReadCommandBoundary(statePath, out var commandBoundary)) + if (await TryReadAsync(() => node.CommandBoundary().ReadAsync()).ConfigureAwait(false) is { } commandBoundary) WriteArtifact(outputDirectory, manifest, "daemon-command-boundary.latest", AetheriaRuntimeDaemonSchemas.CommandBoundary, commandBoundary); - if (AetheriaRuntimeDaemonPublicationStore.TryReadVerseAuthorityPolicy(statePath, out var authorityPolicy)) + if (await TryReadAsync(() => node.VerseAuthorityPolicy().ReadAsync()).ConfigureAwait(false) is { } authorityPolicy) WriteArtifact(outputDirectory, manifest, "verse-authority-policy.latest", AetheriaRuntimeVerseAuthoritySchemas.Policy, authorityPolicy); - if (AetheriaRuntimeDaemonPublicationStore.TryReadStarbridgeSessionSummary(statePath, out var starbridgeSummary)) + if (await TryReadAsync(() => node.StarbridgeSessionSummary().ReadAsync()).ConfigureAwait(false) is { } starbridgeSummary) WriteArtifact(outputDirectory, manifest, "starbridge-session-summary.latest", AetheriaRuntimeDaemonSchemas.StarbridgeSessionSummary, starbridgeSummary); - if (AetheriaRuntimeDaemonSoaViewStore.TryReadView(statePath, out var soaView)) + if (await TryReadAsync(() => node.LatestSoaView().ReadAsync()).ConfigureAwait(false) is { } soaView) WriteArtifact(outputDirectory, manifest, "daemon-soa-view.latest", AetheriaRuntimeDaemonSchemas.SoaView, soaView); var viewport = new AetheriaRuntimeRtsViewportBounds @@ -142,6 +148,19 @@ static void ExportAuthorityDecisionFixtures(string outputDirectory) WriteJson(Path.Combine(outputDirectory, "manifest.json"), manifest); } +static async Task TryReadAsync(Func> read) + where T : class +{ + try + { + return await read().ConfigureAwait(false); + } + catch (KeyNotFoundException) + { + return null; + } +} + static void ExportYmirQueryFixtures(string outputDirectory) { Directory.CreateDirectory(outputDirectory); diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs index 107d61eb5..afbeaa086 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -8675,7 +8675,6 @@ static void RequireDaemonVersePublication(string root) var daemonFrameStorePath = Path.Combine(root, "Packages", "org.gamecult.aetheria.state", "Runtime", "AetheriaRuntimeDaemonFrameStore.cs"); var daemonPublicationStorePath = Path.Combine(root, "Packages", "org.gamecult.aetheria.state", "Runtime", "AetheriaRuntimeDaemonPublicationStore.cs"); var daemonSoaDocumentsPath = Path.Combine(root, "Packages", "org.gamecult.aetheria.state", "Runtime", "AetheriaRuntimeDaemonSoaDocuments.cs"); - var daemonSoaViewStorePath = Path.Combine(root, "Packages", "org.gamecult.aetheria.state", "Runtime", "AetheriaRuntimeDaemonSoaViewStore.cs"); var daemonSoaFramePublisherPath = Path.Combine(root, "Packages", "org.gamecult.aetheria.state", "Runtime", "AetheriaRuntimeDaemonSoaFramePublisher.cs"); var daemonStateRefsPath = Path.Combine(root, "Packages", "org.gamecult.aetheria.state", "Runtime", "AetheriaRuntimeDaemonStateRefs.cs"); var daemonGameSurfaceBuilderPath = Path.Combine(root, "Packages", "org.gamecult.aetheria.state", "Runtime", "AetheriaRuntimeDaemonGameSurfaceBuilder.cs"); @@ -8701,7 +8700,6 @@ static void RequireDaemonVersePublication(string root) daemonFrameStorePath, daemonPublicationStorePath, daemonSoaDocumentsPath, - daemonSoaViewStorePath, daemonSoaFramePublisherPath, daemonStateRefsPath, daemonGameSurfaceBuilderPath, @@ -8736,7 +8734,6 @@ static void RequireDaemonVersePublication(string root) var daemonFrameStore = File.ReadAllText(daemonFrameStorePath); var daemonPublicationStore = File.ReadAllText(daemonPublicationStorePath); var daemonSoaDocuments = File.ReadAllText(daemonSoaDocumentsPath); - var daemonSoaViewStore = File.ReadAllText(daemonSoaViewStorePath); var daemonSoaFramePublisher = File.ReadAllText(daemonSoaFramePublisherPath); var daemonStateRefs = File.ReadAllText(daemonStateRefsPath); var daemonGameSurfaceBuilder = File.ReadAllText(daemonGameSurfaceBuilderPath); @@ -8822,7 +8819,7 @@ static void RequireDaemonVersePublication(string root) var requiredSoaFramePublisherSymbols = new[] { "public static class AetheriaRuntimeDaemonSoaFramePublisher", - "PublishCurrentZoneEntities(", + "BuildCurrentZoneEntities(", "AetheriaRuntimeDaemonSoaBackends.MemoryMappedFile", "MemoryMappedFile.CreateOrOpen(", "ObserverWritable = false", @@ -8831,8 +8828,7 @@ static void RequireDaemonVersePublication(string root) "AetheriaRuntimeDaemonSoaColumnKinds.Velocity", "AetheriaRuntimeDaemonSoaColumnKinds.PhysicsBodyRadius", "AetheriaRuntimeDaemonSoaColumnKinds.RenderVisibility", - "AetheriaRuntimeDaemonSoaColumnKinds.RenderGroupId", - "AetheriaRuntimeDaemonSoaViewStore.PublishView(stateFilePath, view)" + "AetheriaRuntimeDaemonSoaColumnKinds.RenderGroupId" }; var missingSoaFramePublisherSymbols = requiredSoaFramePublisherSymbols .Where(symbol => !daemonSoaFramePublisher.Contains(symbol, StringComparison.Ordinal)) @@ -8930,13 +8926,18 @@ static void RequireDaemonVersePublication(string root) string.Join(", ", publicationStoreReaderHits)); } - if (daemonFrameStore.Contains("TryReadFrame(", StringComparison.Ordinal) || - daemonFrameStore.Contains("ReadFrame(", StringComparison.Ordinal) || - daemonSoaViewStore.Contains("TryReadView(", StringComparison.Ordinal) || - daemonSoaViewStore.Contains("ReadView(", StringComparison.Ordinal)) + var daemonSoaViewStorePath = Path.Combine(root, "Packages", "org.gamecult.aetheria.state", "Runtime", "AetheriaRuntimeDaemonSoaViewStore.cs"); + if (File.Exists(daemonSoaViewStorePath)) { throw new InvalidOperationException( - "Daemon frame/SoA stores still expose file-backed readers; use tick-result documents or AetheriaClient managed observation."); + "Daemon SoA latest-view publication must stay on managed CultMesh pointers; delete AetheriaRuntimeDaemonSoaViewStore.cs instead of reviving sidecar path writes."); + } + + if (daemonFrameStore.Contains("TryReadFrame(", StringComparison.Ordinal) || + daemonFrameStore.Contains("ReadFrame(", StringComparison.Ordinal)) + { + throw new InvalidOperationException( + "Daemon frame store still exposes file-backed readers; use tick-result documents or AetheriaClient managed observation."); } var requiredDaemonRegistrySymbols = new[] @@ -9111,7 +9112,7 @@ static void RequireDaemonVersePublication(string root) "VerseId", "CultMeshAddress", "AetheriaRuntimeDaemonCommandBoundaryDocument.Create", - "AetheriaRuntimeDaemonSoaFramePublisher.PublishCurrentZoneEntities(stateFilePath, frame)", + "AetheriaRuntimeDaemonSoaFramePublisher.BuildCurrentZoneEntities(stateFilePath, frame)", "AetheriaRuntimeDaemonProviderAdvertisementDocument.Create", "AetheriaRuntimeDaemonPublicationStore.PublishCommandBoundary", "AetheriaRuntimeDaemonPublicationStore.PublishProviderAdvertisement", @@ -9227,7 +9228,7 @@ static void RequireDaemonVersePublication(string root) ? daemonHostSource.Substring(apiPublicationStart, apiPublicationEnd - apiPublicationStart) : ""; if (apiPublicationBlock.Contains("AetheriaRuntimeDaemonPublicationStore.TryRead", StringComparison.Ordinal) || - apiPublicationBlock.Contains("AetheriaRuntimeDaemonSoaViewStore.TryRead", StringComparison.Ordinal)) + apiPublicationBlock.Contains("SoaViewStore.TryRead", StringComparison.Ordinal)) { throw new InvalidOperationException( "Daemon API document publication must use typed tick result documents instead of reopening witness files."); @@ -13857,7 +13858,7 @@ static void RequireRuntimeStateReaderOwnsUnityStateAcquisition(string root) "FramePath", "SoaViewPath", "AetheriaRuntimeDaemonFrameStore.GetFramePath", - "AetheriaRuntimeDaemonSoaViewStore.GetViewPath" + "SoaViewStore.GetViewPath" }; var observedDaemonStateBypassHits = forbiddenObservedDaemonStateSymbols .Where(symbol => observedDaemonState.Contains(symbol, StringComparison.Ordinal)) diff --git a/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs b/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs index a9b7d89a6..fd23e4492 100644 --- a/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs +++ b/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs @@ -1941,11 +1941,11 @@ public class DaemonRuntimeDocumentTests } [Test] - public void SoaViewStorePublishesLatestDaemonView() + public void ManagedLatestSoaViewPublishesLatestDaemonView() { var statePath = Path.Combine( Path.GetTempPath(), - "aetheria-daemon-soa-view-store-tests", + "aetheria-managed-soa-view-tests", Path.GetRandomFileName(), "state.cc"); var view = AetheriaRuntimeDaemonSoaViewDocument.Create( @@ -1991,22 +1991,32 @@ public class DaemonRuntimeDocumentTests } }); - var viewPath = AetheriaRuntimeDaemonSoaViewStore.PublishView(statePath, view); + PublishLatestSoaViewThroughVerseClient(statePath, view); - Assert.AreEqual(AetheriaRuntimeStateBoundary.GetDaemonSoaViewPath(statePath), viewPath); + using var client = AetheriaRuntimeVerseClient + .OpenAsync(statePath, "daemon-soa-store-read-test", startServer: false, pullOnOpen: true) + .GetAwaiter() + .GetResult(); + var published = client.LatestSoaView() + .ReadAsync() + .GetAwaiter() + .GetResult(); + + Assert.IsNotNull(published); Assert.AreEqual(AetheriaRuntimeDaemonSchemas.SoaView, view.Schema); - Assert.AreEqual("aetheria-daemon", view.DaemonId); - Assert.AreEqual("session-soa-store", view.SessionId); - Assert.AreEqual(123, view.FrameId); - Assert.AreEqual(456, view.Generation); - Assert.IsTrue(view.IsAuthoritative); - Assert.AreEqual(1, view.Buffers.Count); - Assert.AreEqual("entity-hot-0", view.Buffers[0].BufferId); - Assert.IsFalse(view.Buffers[0].ObserverWritable); - Assert.AreEqual(1, view.Columns.Count); - Assert.AreEqual(AetheriaRuntimeDaemonSoaColumnKinds.Heat, view.Columns[0].Kind); - Assert.AreEqual(1, view.DirtyRanges.Count); - Assert.AreEqual(256, view.DirtyRanges[0].Count); + Assert.AreEqual(AetheriaRuntimeDaemonSchemas.SoaView, published.Schema); + Assert.AreEqual("aetheria-daemon", published.DaemonId); + Assert.AreEqual("session-soa-store", published.SessionId); + Assert.AreEqual(123, published.FrameId); + Assert.AreEqual(456, published.Generation); + Assert.IsTrue(published.IsAuthoritative); + Assert.AreEqual(1, published.Buffers.Count); + Assert.AreEqual("entity-hot-0", published.Buffers[0].BufferId); + Assert.IsFalse(published.Buffers[0].ObserverWritable); + Assert.AreEqual(1, published.Columns.Count); + Assert.AreEqual(AetheriaRuntimeDaemonSoaColumnKinds.Heat, published.Columns[0].Kind); + Assert.AreEqual(1, published.DirtyRanges.Count); + Assert.AreEqual(256, published.DirtyRanges[0].Count); } [Test] @@ -2072,7 +2082,7 @@ public class DaemonRuntimeDocumentTests 1.0, 0.02); - var view = AetheriaRuntimeDaemonSoaFramePublisher.PublishCurrentZoneEntities(statePath, frame); + var view = AetheriaRuntimeDaemonSoaFramePublisher.BuildCurrentZoneEntities(statePath, frame); Assert.AreEqual(AetheriaRuntimeDaemonSoaBackends.MemoryMappedFile, view.Backend); Assert.AreEqual(1, view.Buffers.Count); Assert.IsFalse(view.Buffers[0].ObserverWritable); diff --git a/GameCult.Aetheria.State.Unity.csproj b/GameCult.Aetheria.State.Unity.csproj index 8d685aa7b..202b795fb 100644 --- a/GameCult.Aetheria.State.Unity.csproj +++ b/GameCult.Aetheria.State.Unity.csproj @@ -71,7 +71,6 @@ - diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonSoaFramePublisher.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonSoaFramePublisher.cs index 2febd559b..e7e747b09 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonSoaFramePublisher.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonSoaFramePublisher.cs @@ -24,7 +24,7 @@ namespace GameCult.Aetheria.State.Verse new Dictionary(StringComparer.Ordinal); private static long RetainedBufferUseSequence; - public static AetheriaRuntimeDaemonSoaViewDocument PublishCurrentZoneEntities( + public static AetheriaRuntimeDaemonSoaViewDocument BuildCurrentZoneEntities( string stateFilePath, AetheriaRuntimeDaemonFrameDocument frame) { @@ -74,7 +74,6 @@ namespace GameCult.Aetheria.State.Verse synchronizationMode: AetheriaRuntimeDaemonSoaSynchronizationModes.ImmutableFrame, renderGroups: CreateRenderGroups(entities)); - AetheriaRuntimeDaemonSoaViewStore.PublishView(stateFilePath, view); return view; } diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonSoaViewStore.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonSoaViewStore.cs deleted file mode 100644 index 11b6a4f70..000000000 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonSoaViewStore.cs +++ /dev/null @@ -1,19 +0,0 @@ -#nullable enable - -namespace GameCult.Aetheria.State.Verse -{ - public static class AetheriaRuntimeDaemonSoaViewStore - { - public static string GetViewPath(string stateFilePath) - { - return AetheriaRuntimeStateBoundary.GetDaemonSoaViewPath(stateFilePath); - } - - public static string PublishView(string stateFilePath, AetheriaRuntimeDaemonSoaViewDocument view) - { - var path = GetViewPath(stateFilePath); - AetheriaRuntimeCultCacheDocumentStore.WriteDaemonSoaView(path, view); - return path; - } - } -} diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonSoaViewStore.cs.meta b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonSoaViewStore.cs.meta deleted file mode 100644 index 4ac81fa8c..000000000 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonSoaViewStore.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: e7b9b2f5fbcc6fd44b0a7984041a6330 \ No newline at end of file diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonTickRunner.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonTickRunner.cs index 997f5c78f..998a77f22 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonTickRunner.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonTickRunner.cs @@ -210,7 +210,7 @@ namespace GameCult.Aetheria.State.Verse } var framePath = AetheriaRuntimeDaemonFrameStore.PublishFrame(stateFilePath, frame); - var soaView = AetheriaRuntimeDaemonSoaFramePublisher.PublishCurrentZoneEntities(stateFilePath, frame); + var soaView = AetheriaRuntimeDaemonSoaFramePublisher.BuildCurrentZoneEntities(stateFilePath, frame); var commandBoundary = AetheriaRuntimeDaemonCommandBoundaryDocument.Create(options.DaemonId); var commandBoundaryPath = AetheriaRuntimeDaemonPublicationStore.PublishCommandBoundary( stateFilePath,