From 90630cbc31944b95ec6ef71270cf049c51add6e7 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 28 Jun 2026 14:14:50 +0100 Subject: [PATCH] Centralize sector map state sessions --- Aetheria.State.Verify/Program.cs | 72 +++++++++++++++---- .../Tests/DaemonRuntimeDocumentTests.cs | 4 ++ Assets/Scripts/UI/Menu/SectorMap.cs | 5 +- Assets/Scripts/UI/Menu/SectorRenderer.cs | 21 +++--- .../Runtime/AetheriaClientState.cs | 13 ++++ .../Runtime/AetheriaRuntimeStateSessions.cs | 36 ++++++++++ 6 files changed, 122 insertions(+), 29 deletions(-) diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs index 9c365ee6d..1a1d7f3e2 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -4546,13 +4546,13 @@ static void RequireSectorMapZoneDetailsUseEveSurface(string root) "ProjectZoneDetailsSurfaceState(", "AetheriaUnityRuntimeClientProvider.ResolveClient(", ".State", - ".ReactiveSectorMap()", + ".ObserveSectorMap()", ".Details", - ".ReactiveZone(zoneIndex)", + ".ObserveZone(zoneIndex)", "_zoneDetails?.Current", ".Settings", - ".ReactiveCatalog()", - ".ReactivePlayer()", + ".ObserveCatalog()", + ".ObservePlayer()", "AetheriaClient", "AetheriaRuntimeZoneDetailsSurfaceCommands.TryRead(request, out var command)", "AetheriaRuntimeZoneDetailsCommandKind.Close" @@ -7594,13 +7594,22 @@ static void RequireUnitySharedDocumentAccessorErgonomics(string root) "SectorRenderer.cs")); var requiredSectorRendererSharedDocumentSymbols = new[] { - "CultMeshReactiveDocument _catalog", - "CultMeshReactiveDocument _playerSettings", - "ResolveClient().State.ReactiveCatalog()", + "AetheriaRuntimeCatalogSession _catalog", + "AetheriaRuntimePlayerSettingsSession _playerSettings", + "AetheriaRuntimeSectorMapSession _sectorMap", + "AetheriaRuntimeCurrentZoneSession _currentZone", + "AetheriaRuntimeZoneDetailsSession _zoneDetails", + "ResolveClient().State.ObserveCatalog()", + ".ObserveSectorMap()", + ".Current", + ".ObserveZone(zoneIndex)", ".Settings", - ".ReactivePlayer()", + ".ObservePlayer()", "_catalog?.Dispose()", "_playerSettings?.Dispose()", + "_sectorMap?.Dispose()", + "_currentZone?.Dispose()", + "_zoneDetails?.Dispose()", "_catalog?.Current", "_playerSettings?.Current", "private void OnDestroy()" @@ -7615,6 +7624,28 @@ static void RequireUnitySharedDocumentAccessorErgonomics(string root) string.Join(", ", missingSectorRendererSharedDocumentSymbols)); } + var forbiddenSectorRendererSharedDocumentSymbols = new[] + { + "CultMeshReactiveDocument _catalog", + "CultMeshReactiveDocument _playerSettings", + "CultMeshReactiveDocument _sectorMap", + "CultMeshReactiveDocument _currentZone", + "CultMeshReactiveDocument _zoneDetails", + "ResolveClient().State.ReactiveCatalog()", + ".ReactiveSectorMap()", + ".ReactiveZone(zoneIndex)", + ".ReactivePlayer()" + }; + var sectorRendererRawDocumentHits = forbiddenSectorRendererSharedDocumentSymbols + .Where(symbol => sectorRenderer.Contains(symbol, StringComparison.Ordinal)) + .ToArray(); + if (sectorRendererRawDocumentHits.Length > 0) + { + throw new InvalidOperationException( + "SectorRenderer still owns raw map/catalog/settings CultMesh documents instead of managed sessions: " + + string.Join(", ", sectorRendererRawDocumentHits)); + } + var zoneRenderer = File.ReadAllText(Path.Combine( root, "Assets", @@ -7663,6 +7694,7 @@ static void RequireUnityViewportAndMapReadsUseManagedAccessors(string root) "public AetheriaRuntimeSectorMapDocument LatestSectorMap()", "public AetheriaRuntimeZoneContactsDocument LatestZoneContacts()", "public AetheriaRuntimeCurrentZoneDocument LatestZone()", + "public AetheriaRuntimeCurrentZoneSession ObserveZone(", "public AetheriaRuntimeCurrentEntityDocument LatestEntity()", "public AetheriaRuntimeObjectsViewportDocument LatestObjects(AetheriaRuntimeRtsViewportBounds viewport)", "public CultMeshReactiveDocument ReactiveObjects(", @@ -7670,7 +7702,8 @@ static void RequireUnityViewportAndMapReadsUseManagedAccessors(string root) "public AetheriaRuntimeRenderSplatsViewportDocument LatestRenderSplats(AetheriaRuntimeRtsViewportBounds viewport)", "public CultMeshReactiveDocument ReactiveRenderSplats(", "public AetheriaRuntimeRenderSplatsViewportSession ObserveRenderSplats(", - "public AetheriaRuntimeZoneDetailsDocument LatestZone(int zoneIndex)" + "public AetheriaRuntimeZoneDetailsDocument LatestZone(int zoneIndex)", + "public AetheriaRuntimeZoneDetailsSession ObserveZone(" }; var missingClientSymbols = requiredClientSymbols .Where(symbol => !clientState.Contains(symbol, StringComparison.Ordinal)) @@ -13584,16 +13617,16 @@ static void RequireUnityObserverDoesNotTickLocalSimulation(string root) !mapRenderer.Contains(".ObservePlayer()", StringComparison.Ordinal) || !sectorRenderer.Contains("AetheriaUnityRuntimeClientProvider.ResolveClient(", StringComparison.Ordinal) || !sectorRenderer.Contains(".State", StringComparison.Ordinal) || - !sectorRenderer.Contains(".ReactiveSectorMap()", StringComparison.Ordinal) || + !sectorRenderer.Contains(".ObserveSectorMap()", StringComparison.Ordinal) || !sectorRenderer.Contains(".Details", StringComparison.Ordinal) || - !sectorRenderer.Contains(".ReactiveZone(zoneIndex)", StringComparison.Ordinal) || + !sectorRenderer.Contains(".ObserveZone(zoneIndex)", StringComparison.Ordinal) || !sectorRenderer.Contains(".Current", StringComparison.Ordinal) || !sectorRenderer.Contains(".Settings", StringComparison.Ordinal) || - !sectorRenderer.Contains(".ReactiveCatalog()", StringComparison.Ordinal) || - !sectorRenderer.Contains(".ReactivePlayer()", StringComparison.Ordinal) || + !sectorRenderer.Contains(".ObserveCatalog()", StringComparison.Ordinal) || + !sectorRenderer.Contains(".ObservePlayer()", StringComparison.Ordinal) || !sectorMap.Contains("AetheriaUnityRuntimeClientProvider.ResolveClient(", StringComparison.Ordinal) || !sectorMap.Contains(".State", StringComparison.Ordinal) || - !sectorMap.Contains(".ReactiveSectorMap()", StringComparison.Ordinal) || + !sectorMap.Contains(".ObserveSectorMap()", StringComparison.Ordinal) || !sectorRenderer.Contains("AetheriaRuntimeZoneDetailsSurfaceBuilder.ProjectDaemonZone(", StringComparison.Ordinal)) { throw new InvalidOperationException( @@ -13607,6 +13640,13 @@ static void RequireUnityObserverDoesNotTickLocalSimulation(string root) "Map and sector UI must not read the raw ObservedGalaxy projection directly."); } + if (sectorMap.Contains("CultMeshReactiveDocument _sectorMapDocument", StringComparison.Ordinal) || + sectorMap.Contains(".ReactiveSectorMap()", StringComparison.Ordinal)) + { + throw new InvalidOperationException( + "SectorMap still owns a raw sector-map CultMesh document instead of AetheriaRuntimeSectorMapSession."); + } + var requiredDaemonControlValidationSymbols = new[] { "case AetheriaRuntimeDaemonCommandKinds.SetTractorPower:", @@ -15315,7 +15355,9 @@ static void RequireRuntimeStateReaderOwnsUnityStateAcquisition(string root) "public AetheriaRuntimeVerseHostSettingsSession ObserveVerseHost(", "public AetheriaRuntimeSectorMapSession ObserveSectorMap(", "public AetheriaRuntimeObjectsViewportSession ObserveObjects(", - "public AetheriaRuntimeRenderSplatsViewportSession ObserveRenderSplats(" + "public AetheriaRuntimeRenderSplatsViewportSession ObserveRenderSplats(", + "public AetheriaRuntimeCurrentZoneSession ObserveZone(", + "public AetheriaRuntimeZoneDetailsSession ObserveZone(" }; var missingManagedStateAccessSymbols = requiredManagedStateAccessSymbols .Where(symbol => !aetheriaClientState.Contains(symbol, StringComparison.Ordinal)) diff --git a/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs b/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs index cc69ac200..6d2613238 100644 --- a/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs +++ b/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs @@ -210,6 +210,8 @@ public class DaemonRuntimeDocumentTests using var playerSettingsSession = client.State.Settings.ObservePlayer(); using var verseHostSettingsSession = client.State.Settings.ObserveVerseHost(); using var zoneContactsSession = client.State.ObserveZoneContacts(); + using var currentZoneSession = client.State.Current.ObserveZone(); + using var zoneDetailsSession = client.State.Details.ObserveZone(0); using var objectsViewportSession = client.State.Viewports.ObserveObjects(viewport); using var renderSplatsViewportSession = client.State.Viewports.ObserveRenderSplats(viewport); using var playerHud = client.State.ObservePlayerHud(); @@ -296,8 +298,10 @@ public class DaemonRuntimeDocumentTests Assert.AreEqual(AetheriaRuntimeDaemonSchemas.ObjectsViewport, objectsViewportSession.Current.Schema); Assert.AreEqual(AetheriaRuntimeDaemonSchemas.RenderSplatsViewport, renderSplatsViewportSession.Current.Schema); Assert.AreEqual(AetheriaRuntimeDaemonSchemas.ZoneContacts, zoneContactsSession.Current.Schema); + Assert.AreEqual(AetheriaRuntimeDaemonSchemas.CurrentZone, currentZoneSession.Current.Schema); Assert.AreEqual("Player", objectsViewport.Objects.FirstOrDefault()?.DisplayName); Assert.AreEqual(AetheriaRuntimeDaemonSchemas.ZoneDetails, zoneDetails.Schema); + Assert.AreEqual(AetheriaRuntimeDaemonSchemas.ZoneDetails, zoneDetailsSession.Current.Schema); Assert.AreEqual(0, zoneDetails.ZoneIndex); Assert.AreEqual(AetheriaRuntimeDaemonSchemas.Inventory, inventory.Schema); Assert.AreEqual("zone.0.entity.0", inventory.EntityKey); diff --git a/Assets/Scripts/UI/Menu/SectorMap.cs b/Assets/Scripts/UI/Menu/SectorMap.cs index 8b313fb78..8c62d62f7 100644 --- a/Assets/Scripts/UI/Menu/SectorMap.cs +++ b/Assets/Scripts/UI/Menu/SectorMap.cs @@ -2,7 +2,6 @@ using System; using System.Collections; using System.Collections.Generic; using System.Linq; -using GameCult.Mesh; using GameCult.Aetheria.State.Verse; using TMPro; using UniRx; @@ -53,7 +52,7 @@ public class SectorMap : MonoBehaviour private readonly Queue> _queuedZoneReveals = new Queue>(); private readonly Dictionary _zonesByIndex = new Dictionary(); - private CultMeshReactiveDocument _sectorMapDocument; + private AetheriaRuntimeSectorMapSession _sectorMapDocument; private AetheriaRuntimeSectorMapDocument _sectorMap; private bool _sectorMapLoaded; @@ -223,7 +222,7 @@ public class SectorMap : MonoBehaviour { _sectorMapDocument ??= ResolveClient() .State - .ReactiveSectorMap(); + .ObserveSectorMap(); _sectorMap = _sectorMapDocument.Current; if (_sectorMapLoaded) diff --git a/Assets/Scripts/UI/Menu/SectorRenderer.cs b/Assets/Scripts/UI/Menu/SectorRenderer.cs index 37f9afae1..56e2a1180 100644 --- a/Assets/Scripts/UI/Menu/SectorRenderer.cs +++ b/Assets/Scripts/UI/Menu/SectorRenderer.cs @@ -3,7 +3,6 @@ using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; -using GameCult.Mesh; using GameCult.Aetheria.EveRuntime; using GameCult.Aetheria.State.Verse; using GameCult.Eve.Surface; @@ -47,12 +46,12 @@ public class SectorRenderer : MonoBehaviour, IBeginDragHandler, IDragHandler, IS private float _sectorCameraDepth; private UIDocument _zoneDetailsSurfaceDocument; private string _clientStatePath = ""; - private CultMeshReactiveDocument _catalog; - private CultMeshReactiveDocument _playerSettings; - private CultMeshReactiveDocument _sectorMap; - private CultMeshReactiveDocument _currentZone; + private AetheriaRuntimeCatalogSession _catalog; + private AetheriaRuntimePlayerSettingsSession _playerSettings; + private AetheriaRuntimeSectorMapSession _sectorMap; + private AetheriaRuntimeCurrentZoneSession _currentZone; private int _zoneDetailsIndex = -1; - private CultMeshReactiveDocument _zoneDetails; + private AetheriaRuntimeZoneDetailsSession _zoneDetails; private readonly AetheriaEveUnitySurfaceChrome _zoneDetailsSurfaceChrome = new AetheriaEveUnitySurfaceChrome { RootAlignItems = Align.FlexEnd, @@ -176,7 +175,7 @@ public class SectorRenderer : MonoBehaviour, IBeginDragHandler, IDragHandler, IS { _sectorMap ??= ResolveClient() .State - .ReactiveSectorMap(); + .ObserveSectorMap(); var sectorMap = _sectorMap.Current; return (sectorMap?.Zones ?? Array.Empty()) .FirstOrDefault(zone => zone.ZoneIndex == zoneIndex); @@ -206,7 +205,7 @@ public class SectorRenderer : MonoBehaviour, IBeginDragHandler, IDragHandler, IS var nextZoneDetails = ResolveClient() .State .Details - .ReactiveZone(zoneIndex); + .ObserveZone(zoneIndex); _zoneDetails?.Dispose(); _zoneDetailsIndex = zoneIndex; _zoneDetails = nextZoneDetails; @@ -232,7 +231,7 @@ public class SectorRenderer : MonoBehaviour, IBeginDragHandler, IDragHandler, IS try { - _catalog = ResolveClient().State.ReactiveCatalog(); + _catalog = ResolveClient().State.ObserveCatalog(); } catch (Exception ex) { @@ -252,7 +251,7 @@ public class SectorRenderer : MonoBehaviour, IBeginDragHandler, IDragHandler, IS _playerSettings = ResolveClient() .State .Settings - .ReactivePlayer(); + .ObservePlayer(); } catch (Exception ex) { @@ -325,7 +324,7 @@ public class SectorRenderer : MonoBehaviour, IBeginDragHandler, IDragHandler, IS _currentZone ??= ResolveClient() .State .Current - .ReactiveZone(); + .ObserveZone(); } catch (Exception ex) { diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaClientState.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaClientState.cs index 20860c751..452d04dbc 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaClientState.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaClientState.cs @@ -892,6 +892,12 @@ namespace GameCult.Aetheria.State.Verse return Zone.Reactive(options); } + public AetheriaRuntimeCurrentZoneSession ObserveZone( + CultMeshReactiveDocumentOptions? options = null) + { + return new AetheriaRuntimeCurrentZoneSession(ReactiveZone(options)); + } + public Task LatestEntityAsync() { return Entity.LatestAsync(); @@ -1193,6 +1199,13 @@ namespace GameCult.Aetheria.State.Verse return Zone(zoneIndex).Reactive(options); } + public AetheriaRuntimeZoneDetailsSession ObserveZone( + int zoneIndex, + CultMeshReactiveDocumentOptions? options = null) + { + return new AetheriaRuntimeZoneDetailsSession(ReactiveZone(zoneIndex, options)); + } + public CultMeshDocumentHandle SelectedObject(int entityIndex) { return _selectedObject(entityIndex); diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStateSessions.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStateSessions.cs index fd9cfa7b5..4cbaedb07 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStateSessions.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStateSessions.cs @@ -95,6 +95,42 @@ namespace GameCult.Aetheria.State.Verse } } + public sealed class AetheriaRuntimeCurrentZoneSession : IDisposable + { + private readonly CultMeshReactiveDocument _currentZone; + + public AetheriaRuntimeCurrentZoneSession( + CultMeshReactiveDocument currentZone) + { + _currentZone = currentZone ?? throw new ArgumentNullException(nameof(currentZone)); + } + + public AetheriaRuntimeCurrentZoneDocument? Current => _currentZone.Current; + + public void Dispose() + { + _currentZone.Dispose(); + } + } + + public sealed class AetheriaRuntimeZoneDetailsSession : IDisposable + { + private readonly CultMeshReactiveDocument _zoneDetails; + + public AetheriaRuntimeZoneDetailsSession( + CultMeshReactiveDocument zoneDetails) + { + _zoneDetails = zoneDetails ?? throw new ArgumentNullException(nameof(zoneDetails)); + } + + public AetheriaRuntimeZoneDetailsDocument? Current => _zoneDetails.Current; + + public void Dispose() + { + _zoneDetails.Dispose(); + } + } + public sealed class AetheriaRuntimeObjectsViewportSession : IDisposable { private readonly CultMeshReactiveDocument _objectsViewport;