diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs index b32a0dfe1..1e9c56d16 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -7189,6 +7189,7 @@ static void RequireUnitySharedDocumentAccessorErgonomics(string root) Path.Combine(root, "Assets", "Scripts", "Gameplay", "ActionBarSlot.cs"), Path.Combine(root, "Assets", "Scripts", "Gameplay", "AetheriaUnityGameplayBootShell.cs"), Path.Combine(root, "Assets", "Scripts", "Gameplay", "AetheriaUnityRuntimeClientProvider.cs"), + Path.Combine(root, "Assets", "Scripts", "Gameplay", "AetheriaUnityObservedTargetQuery.cs"), Path.Combine(root, "Assets", "Scripts", "UI", "HUD", "SchematicDisplay.cs"), Path.Combine(root, "Assets", "Scripts", "UI", "InputScreen", "InputDisplayLayout.cs"), Path.Combine(root, "Assets", "Scripts", "UI", "MainMenu.cs"), @@ -7311,6 +7312,66 @@ static void RequireUnitySharedDocumentAccessorErgonomics(string root) string.Join(", ", schematicDisplayHits)); } + var volumeCloudRenderer = File.ReadAllText(Path.Combine( + root, + "Assets", + "Scripts", + "Zone Display", + "VolumeCloudRenderer.cs")); + var requiredVolumeCloudSymbols = new[] + { + "AetheriaRuntimePlayerSettingsSession _playerSettings", + ".ObservePlayer()", + "_playerSettings?.Dispose()", + "_playerSettings?.Current" + }; + var missingVolumeCloudSymbols = requiredVolumeCloudSymbols + .Where(symbol => !volumeCloudRenderer.Contains(symbol, StringComparison.Ordinal)) + .ToArray(); + if (missingVolumeCloudSymbols.Length > 0) + { + throw new InvalidOperationException( + "VolumeCloudRenderer should read player settings through AetheriaRuntimePlayerSettingsSession: " + + string.Join(", ", missingVolumeCloudSymbols)); + } + + if (volumeCloudRenderer.Contains("CultMeshReactiveDocument _playerSettings", StringComparison.Ordinal) || + volumeCloudRenderer.Contains(".ReactivePlayer()", StringComparison.Ordinal)) + { + throw new InvalidOperationException( + "VolumeCloudRenderer still owns the raw player-settings CultMesh document instead of AetheriaRuntimePlayerSettingsSession."); + } + + var observedTargetQuery = File.ReadAllText(Path.Combine( + root, + "Assets", + "Scripts", + "Gameplay", + "AetheriaUnityObservedTargetQuery.cs")); + var requiredObservedTargetSymbols = new[] + { + "AetheriaRuntimeZoneContactsSession _zoneContacts", + ".ObserveZoneContacts()", + "_zoneContacts?.Dispose()", + "_zoneContacts?.Current" + }; + var missingObservedTargetSymbols = requiredObservedTargetSymbols + .Where(symbol => !observedTargetQuery.Contains(symbol, StringComparison.Ordinal)) + .ToArray(); + if (missingObservedTargetSymbols.Length > 0) + { + throw new InvalidOperationException( + "AetheriaUnityObservedTargetQuery should read contacts through AetheriaRuntimeZoneContactsSession: " + + string.Join(", ", missingObservedTargetSymbols)); + } + + if (observedTargetQuery.Contains("CultMeshReactiveDocument _zoneContacts", StringComparison.Ordinal) || + observedTargetQuery.Contains(".ReactiveZoneContacts()", StringComparison.Ordinal)) + { + throw new InvalidOperationException( + "AetheriaUnityObservedTargetQuery still owns the raw zone-contacts CultMesh document instead of AetheriaRuntimeZoneContactsSession."); + } + var mapRenderer = File.ReadAllText(Path.Combine( root, "Assets", @@ -7527,30 +7588,6 @@ static void RequireUnitySharedDocumentAccessorErgonomics(string root) string.Join(", ", missingZoneRendererSharedDocumentSymbols)); } - var volumeCloudRenderer = File.ReadAllText(Path.Combine( - root, - "Assets", - "Scripts", - "Zone Display", - "VolumeCloudRenderer.cs")); - var requiredVolumeCloudSymbols = new[] - { - "CultMeshReactiveDocument _playerSettings", - ".Settings", - ".ReactivePlayer()", - "_playerSettings?.Dispose()", - "_playerSettings?.Current" - }; - var missingVolumeCloudSymbols = requiredVolumeCloudSymbols - .Where(symbol => !volumeCloudRenderer.Contains(symbol, StringComparison.Ordinal)) - .ToArray(); - if (missingVolumeCloudSymbols.Length > 0) - { - throw new InvalidOperationException( - "VolumeCloudRenderer should bind player settings through the managed reactive Aetheria settings document: " + - string.Join(", ", missingVolumeCloudSymbols)); - } - Console.WriteLine("Shared document accessors: Unity shared catalog/settings reads use named managed Aetheria client accessors"); } @@ -7749,6 +7786,7 @@ static void RequireAetheriaManagedStateAccessorsCoverDomainDocuments(string root "public AetheriaRuntimeObservedDaemonState? LatestObservedDaemon()", "public AetheriaRuntimeObservedDockingState? CurrentDocking(", "public AetheriaRuntimeCatalogSession ObserveCatalog(", + "public AetheriaRuntimeZoneContactsSession ObserveZoneContacts(", "public AetheriaRuntimePlayerHudSession ObservePlayerHud(", "public AetheriaRuntimeLoadoutTemplatesDocument LatestLoadoutTemplates()", "public AetheriaRuntimeStationRefitDocument LatestStationRefit()", @@ -12324,7 +12362,7 @@ static void RequireMainMenuContinueRunState(string root) var requiredObservedTargetQuerySymbols = new[] { "public sealed class AetheriaUnityObservedTargetQuery : IDisposable", - "CultMeshReactiveDocument _zoneContacts", + "AetheriaRuntimeZoneContactsSession _zoneContacts", "public Entity GetObservedTarget(Entity observer)", "public float GetObservedInfoGathered(Entity observer, Entity target)", "public bool IsObservedHostileContact(Entity observer, Entity target)", @@ -12334,7 +12372,7 @@ static void RequireMainMenuContinueRunState(string root) "AetheriaRuntimeZoneContactRow", "AetheriaRuntimeZoneTargetRow", ".State", - ".ReactiveZoneContacts()", + ".ObserveZoneContacts()", "_zoneContacts?.Current", "_zoneContacts?.Dispose()", "_entityIndex.TryResolveEntityByDaemonIndex(targetEntityIndex, out var targetEntity)" @@ -12349,6 +12387,13 @@ static void RequireMainMenuContinueRunState(string root) string.Join(", ", missingObservedTargetQuerySymbols)); } + if (observedTargetQuery.Contains("CultMeshReactiveDocument _zoneContacts", StringComparison.Ordinal) || + observedTargetQuery.Contains(".ReactiveZoneContacts()", StringComparison.Ordinal)) + { + throw new InvalidOperationException( + "Observed Unity target/contact query still owns the raw zone-contacts CultMesh document instead of AetheriaRuntimeZoneContactsSession."); + } + var forbiddenManagerTargetQuerySymbols = new[] { "private bool TryQueryDaemonEntityContact(", @@ -13321,7 +13366,7 @@ static void RequireUnityObserverDoesNotTickLocalSimulation(string root) var requiredObservedTargetQuerySymbols = new[] { "public sealed class AetheriaUnityObservedTargetQuery : IDisposable", - "CultMeshReactiveDocument _zoneContacts", + "AetheriaRuntimeZoneContactsSession _zoneContacts", "public Entity GetObservedTarget(Entity observer)", "public float GetObservedInfoGathered(Entity observer, Entity target)", "public bool IsObservedHostileContact(Entity observer, Entity target)", @@ -13331,7 +13376,7 @@ static void RequireUnityObserverDoesNotTickLocalSimulation(string root) "AetheriaRuntimeZoneContactRow", "AetheriaRuntimeZoneTargetRow", ".State", - ".ReactiveZoneContacts()", + ".ObserveZoneContacts()", "_zoneContacts?.Current", "_zoneContacts?.Dispose()", "_entityIndex.TryResolveEntityByDaemonIndex(targetEntityIndex, out var targetEntity)" @@ -13346,6 +13391,13 @@ static void RequireUnityObserverDoesNotTickLocalSimulation(string root) string.Join(", ", missingObservedTargetQuerySymbols)); } + if (observedTargetQuery.Contains("CultMeshReactiveDocument _zoneContacts", StringComparison.Ordinal) || + observedTargetQuery.Contains(".ReactiveZoneContacts()", StringComparison.Ordinal)) + { + throw new InvalidOperationException( + "Unity observer target/contact projection still owns the raw zone-contacts CultMesh document instead of AetheriaRuntimeZoneContactsSession."); + } + var forbiddenManagerTargetQuerySymbols = new[] { "private bool TryQueryDaemonEntityContact(", @@ -15193,6 +15245,7 @@ static void RequireRuntimeStateReaderOwnsUnityStateAcquisition(string root) "public AetheriaRuntimeObservedDaemonState? CurrentObservedDaemon(", "public AetheriaRuntimeObservedDaemonSession ObserveDaemon(", "public AetheriaRuntimeCatalogSession ObserveCatalog(", + "public AetheriaRuntimeZoneContactsSession ObserveZoneContacts(", "public AetheriaRuntimePlayerSettingsSession ObservePlayer(" }; var missingManagedStateAccessSymbols = requiredManagedStateAccessSymbols diff --git a/Assets/Scripts/Gameplay/AetheriaUnityObservedTargetQuery.cs b/Assets/Scripts/Gameplay/AetheriaUnityObservedTargetQuery.cs index d68f73b9c..f69dae4c4 100644 --- a/Assets/Scripts/Gameplay/AetheriaUnityObservedTargetQuery.cs +++ b/Assets/Scripts/Gameplay/AetheriaUnityObservedTargetQuery.cs @@ -4,14 +4,13 @@ using System; using System.Linq; -using GameCult.Mesh; using GameCult.Aetheria.State.Verse; public sealed class AetheriaUnityObservedTargetQuery : IDisposable { private readonly Func _resolveClient; private readonly AetheriaUnityObservedEntityIndex _entityIndex; - private CultMeshReactiveDocument _zoneContacts; + private AetheriaRuntimeZoneContactsSession _zoneContacts; public AetheriaUnityObservedTargetQuery( Func resolveClient, @@ -100,7 +99,7 @@ public sealed class AetheriaUnityObservedTargetQuery : IDisposable { _zoneContacts = _resolveClient() ?.State - .ReactiveZoneContacts(); + .ObserveZoneContacts(); return _zoneContacts?.Current; } catch diff --git a/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs b/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs index 6837d8e53..ed1ec01e2 100644 --- a/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs +++ b/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs @@ -207,6 +207,7 @@ public class DaemonRuntimeDocumentTests var observedAuthoritativeFrame = client.State.LatestDaemonFrame(); using var catalogSession = client.State.ObserveCatalog(); using var playerSettingsSession = client.State.Settings.ObservePlayer(); + using var zoneContactsSession = client.State.ObserveZoneContacts(); using var playerHud = client.State.ObservePlayerHud(); var gameSurface = client.State.LatestGameSurface(); var gameTuiSurface = client.State.LatestGameTuiSurface(); @@ -286,6 +287,7 @@ public class DaemonRuntimeDocumentTests Assert.AreEqual(AetheriaRuntimePlayerSettingsDocument.SchemaId, playerSettingsSession.Current.Schema); Assert.AreEqual(AetheriaRuntimeVerseHostSettingsDocument.SchemaId, verseHostSettings.Schema); Assert.AreEqual(AetheriaRuntimeDaemonSchemas.ObjectsViewport, objectsViewport.Schema); + Assert.AreEqual(AetheriaRuntimeDaemonSchemas.ZoneContacts, zoneContactsSession.Current.Schema); Assert.AreEqual("Player", objectsViewport.Objects.FirstOrDefault()?.DisplayName); Assert.AreEqual(AetheriaRuntimeDaemonSchemas.ZoneDetails, zoneDetails.Schema); Assert.AreEqual(0, zoneDetails.ZoneIndex); diff --git a/Assets/Scripts/Zone Display/VolumeCloudRenderer.cs b/Assets/Scripts/Zone Display/VolumeCloudRenderer.cs index e593a8871..6292f5a7a 100644 --- a/Assets/Scripts/Zone Display/VolumeCloudRenderer.cs +++ b/Assets/Scripts/Zone Display/VolumeCloudRenderer.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Collections; using System.Collections.Generic; using GameCult.Aetheria.State.Verse; -using GameCult.Mesh; using UnityEngine; using UnityEngine.Rendering; @@ -54,7 +53,7 @@ public class VolumeCloudRenderer : EffectBase // The index of 4x4 pixels. private int frameIndex = 0; private bool firstFrame = true; - private CultMeshReactiveDocument _playerSettings; + private AetheriaRuntimePlayerSettingsSession _playerSettings; [SerializeField] private Shader cloudShader; @@ -104,7 +103,7 @@ public class VolumeCloudRenderer : EffectBase _playerSettings = ResolveClient() .State .Settings - .ReactivePlayer(); + .ObservePlayer(); } catch (Exception ex) { diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaClientState.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaClientState.cs index 48917f39b..b11744ba5 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaClientState.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaClientState.cs @@ -548,6 +548,12 @@ namespace GameCult.Aetheria.State.Verse return ZoneContacts.Reactive(options); } + public AetheriaRuntimeZoneContactsSession ObserveZoneContacts( + CultMeshReactiveDocumentOptions? options = null) + { + return new AetheriaRuntimeZoneContactsSession(ReactiveZoneContacts(options)); + } + public Task LatestStationRefitAsync() { return StationRefit.LatestAsync(); diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStateSessions.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStateSessions.cs index 818540515..297c23cb1 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStateSessions.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStateSessions.cs @@ -40,4 +40,22 @@ namespace GameCult.Aetheria.State.Verse _playerSettings.Dispose(); } } + + public sealed class AetheriaRuntimeZoneContactsSession : IDisposable + { + private readonly CultMeshReactiveDocument _zoneContacts; + + public AetheriaRuntimeZoneContactsSession( + CultMeshReactiveDocument zoneContacts) + { + _zoneContacts = zoneContacts ?? throw new ArgumentNullException(nameof(zoneContacts)); + } + + public AetheriaRuntimeZoneContactsDocument? Current => _zoneContacts.Current; + + public void Dispose() + { + _zoneContacts.Dispose(); + } + } }