diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs index 43d838d97..8bb9e5a9e 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -93,6 +93,7 @@ RequireInventoryProjectionSlotIdentity(root); RequireInventoryValidationUsesManagedTypedDocuments(root); RequireMenuDockingUsesManagedTypedSnapshot(root); RequireUnitySharedDocumentAccessorErgonomics(root); +RequireUnityViewportAndMapReadsUseManagedAccessors(root); RequireInventoryLoadoutSaveRequestAuthority(root); RequireInventoryLoadoutRestoreRequestAuthority(root); RequireDockedCurrentShipRequestAuthority(root); @@ -2407,7 +2408,7 @@ static void RequireDaemonRenderQueryAuthority(string root) "void LoadAsteroidBelt(AetheriaRuntimeZoneRenderAsteroidBeltPose beltPose)", "private void SyncDaemonEntityInstances()", ".Viewports", - ".Objects(ToViewportBounds(viewport))", + ".LatestObjects(ToViewportBounds(viewport))", "foreach (var entity in objects?.Objects ?? Array.Empty())", "_observedEntitySnapshotsByDaemonIndex.TryGetValue(entityIndex, out var entity)", "Loading entity {entity.Name} from daemon presentation query", @@ -2425,8 +2426,7 @@ static void RequireDaemonRenderQueryAuthority(string root) "beltPose.InstancePoses ?? Array.Empty()", "private void RefreshDaemonContactRows()", ".Aetheria()", - ".ZoneContacts", - ".LatestAsync()", + ".LatestZoneContacts()", "private void RefreshDaemonCompassMarkers()", "private void RefreshDaemonVisibleEntityInstances()", "PowerPulse(", @@ -2533,8 +2533,7 @@ static void RequireDaemonRenderQueryAuthority(string root) "ResolveCurrentEntityHudStatus()", "AetheriaUnityRuntimeClientProvider.ResolveClient(", ".Current", - ".Entity", - ".LatestAsync()", + ".LatestEntity()", "hud.OverrideShutdown", "hud.HeatsinksEnabled", "hud.Heatstroke", @@ -4492,10 +4491,9 @@ static void RequireSectorMapZoneDetailsUseEveSurface(string root) "ProjectZoneDetailsSurfaceState(", "AetheriaUnityRuntimeClientProvider.ResolveClient(", ".Aetheria()", - ".SectorMap", - ".LatestAsync()", + ".LatestSectorMap()", ".Details", - ".Zone(zoneIndex)", + ".LatestZone(zoneIndex)", ".Settings", ".LatestCatalog()", ".LatestPlayer()", @@ -6997,7 +6995,8 @@ static void RequireUnitySharedDocumentAccessorErgonomics(string root) Path.Combine(root, "Assets", "Scripts", "UI", "Menu", "InventoryPanel.cs"), Path.Combine(root, "Assets", "Scripts", "UI", "Menu", "TradeMenu.cs"), Path.Combine(root, "Assets", "Scripts", "UI", "Menu", "SectorRenderer.cs"), - Path.Combine(root, "Assets", "Scripts", "Zone Display", "ZoneRenderer.cs") + Path.Combine(root, "Assets", "Scripts", "Zone Display", "ZoneRenderer.cs"), + Path.Combine(root, "Assets", "Scripts", "Zone Display", "VolumeCloudRenderer.cs") }; var missingPaths = unityPaths.Where(path => !File.Exists(path)).ToArray(); @@ -7036,6 +7035,89 @@ static void RequireUnitySharedDocumentAccessorErgonomics(string root) Console.WriteLine("Shared document accessors: Unity shared catalog/settings reads use named managed Aetheria client accessors"); } +static void RequireUnityViewportAndMapReadsUseManagedAccessors(string root) +{ + var clientStatePath = Path.Combine( + root, + "Packages", + "org.gamecult.aetheria.state", + "Runtime", + "AetheriaClientState.cs"); + if (!File.Exists(clientStatePath)) + throw new InvalidOperationException("Cannot verify Unity viewport/map managed accessors; AetheriaClientState.cs is missing."); + + var clientState = File.ReadAllText(clientStatePath); + var requiredClientSymbols = new[] + { + "public AetheriaRuntimeSectorMapDocument LatestSectorMap()", + "public AetheriaRuntimeZoneContactsDocument LatestZoneContacts()", + "public AetheriaRuntimeCurrentZoneDocument LatestZone()", + "public AetheriaRuntimeCurrentEntityDocument LatestEntity()", + "public AetheriaRuntimeObjectsViewportDocument LatestObjects(AetheriaRuntimeRtsViewportBounds viewport)", + "public AetheriaRuntimeRenderSplatsViewportDocument LatestRenderSplats(AetheriaRuntimeRtsViewportBounds viewport)", + "public AetheriaRuntimeZoneDetailsDocument LatestZone(int zoneIndex)" + }; + var missingClientSymbols = requiredClientSymbols + .Where(symbol => !clientState.Contains(symbol, StringComparison.Ordinal)) + .ToArray(); + if (missingClientSymbols.Length > 0) + { + throw new InvalidOperationException( + "AetheriaClientState must expose named managed latest accessors for map, viewport, contacts, and current-entity documents: " + + string.Join(", ", missingClientSymbols)); + } + + var unityPaths = new[] + { + Path.Combine(root, "Assets", "Scripts", "Gameplay", "AetheriaUnityObservedTargetQuery.cs"), + Path.Combine(root, "Assets", "Scripts", "Gameplay", "AetheriaUnityRenderSplatViewportSource.cs"), + Path.Combine(root, "Assets", "Scripts", "UI", "HUD", "SchematicDisplay.cs"), + Path.Combine(root, "Assets", "Scripts", "UI", "MainMenu.cs"), + Path.Combine(root, "Assets", "Scripts", "UI", "Menu", "MapRenderer.cs"), + Path.Combine(root, "Assets", "Scripts", "UI", "Menu", "SectorMap.cs"), + Path.Combine(root, "Assets", "Scripts", "UI", "Menu", "SectorRenderer.cs"), + Path.Combine(root, "Assets", "Scripts", "Zone Display", "ZoneRenderer.cs") + }; + + var missingPaths = unityPaths.Where(path => !File.Exists(path)).ToArray(); + if (missingPaths.Length > 0) + { + throw new InvalidOperationException( + "Cannot verify Unity viewport/map managed accessors; missing sources: " + + string.Join(", ", missingPaths.Select(path => Path.GetRelativePath(root, path)))); + } + + var forbiddenCompactedSymbols = new[] + { + ".Aetheria().SectorMap.LatestAsync()", + ".Aetheria().ZoneContacts.LatestAsync()", + ".Aetheria().Current.Entity.LatestAsync()", + ".Aetheria().Viewports.Objects(", + ".Aetheria().Viewports.RenderSplats(", + ".Details.Zone(zoneIndex).LatestAsync()" + } + .Select(CompactSource) + .ToArray(); + var offenders = unityPaths + .Select(path => new + { + Path = path, + Compact = CompactSource(File.ReadAllText(path)) + }) + .Where(entry => forbiddenCompactedSymbols.Any(symbol => entry.Compact.Contains(symbol, StringComparison.Ordinal))) + .Select(entry => Path.GetRelativePath(root, entry.Path)) + .ToArray(); + + if (offenders.Length > 0) + { + throw new InvalidOperationException( + "Unity map, viewport, contact, and current-entity reads still walk CultMesh document handles instead of named managed accessors: " + + string.Join(", ", offenders)); + } + + Console.WriteLine("Viewport and map document accessors: Unity reads map, contact, viewport, and current-entity state through named managed accessors"); +} + static void RequireVerseHostSettingsAuthority(string root) { var settingsPath = Path.Combine(root, "Aetheria.State", "Documents", "AetheriaVerseHostSettings.cs"); @@ -7619,8 +7701,7 @@ static void RequireClientTargetBootAuthority(string root) "LatestSectorMap(AetheriaRuntimeStateBootReport stateBoot)", "AetheriaClient", ".Aetheria()", - ".SectorMap", - ".LatestAsync()", + ".LatestSectorMap()", "LatestVerseHostSettings(AetheriaRuntimeStateBootReport stateBoot)", "AetheriaState.At(AetheriaUnityRuntimePaths.GameDataDirectory)", ".ClientTarget", @@ -10095,7 +10176,7 @@ static void RequireAetheriaRuntimeVerseClientContract(string root) "Daemon.LatestFrame.LatestAsync()", "Daemon.Health.LatestAsync()", "Daemon.CommandBoundary.LatestAsync()", - "() => Catalog.Latest()", + "LatestCatalog", "AetheriaRuntimeStateRefResolver.CreateEveSurfaceCultMeshStateRefResolver(" }; var missingClientStateSymbols = requiredClientStateSymbols @@ -10922,8 +11003,7 @@ static void RequireMainMenuContinueRunState(string root) "LatestSectorMap", "AetheriaClient", ".Aetheria()", - ".SectorMap", - ".LatestAsync()", + ".LatestSectorMap()", "ContinueGame()", "AetheriaUnityObservedRunProjection.Project(", "SceneManager.LoadScene(\"ARPG\")" @@ -11255,8 +11335,7 @@ static void RequireMainMenuContinueRunState(string root) "AetheriaRuntimeZoneContactRow", "AetheriaRuntimeZoneTargetRow", ".Aetheria()", - ".ZoneContacts", - ".LatestAsync()", + ".LatestZoneContacts()", "_entityIndex.TryResolveEntityByDaemonIndex(targetEntityIndex, out var targetEntity)" }; var missingObservedTargetQuerySymbols = requiredObservedTargetQuerySymbols @@ -12214,8 +12293,7 @@ static void RequireUnityObserverDoesNotTickLocalSimulation(string root) "AetheriaRuntimeZoneContactRow", "AetheriaRuntimeZoneTargetRow", ".Aetheria()", - ".ZoneContacts", - ".LatestAsync()", + ".LatestZoneContacts()", "_entityIndex.TryResolveEntityByDaemonIndex(targetEntityIndex, out var targetEntity)" }; var missingObservedTargetQuerySymbols = requiredObservedTargetQuerySymbols @@ -12343,23 +12421,21 @@ static void RequireUnityObserverDoesNotTickLocalSimulation(string root) if (!mapRenderer.Contains("AetheriaUnityRuntimeClientProvider.ResolveClient(", StringComparison.Ordinal) || !mapRenderer.Contains(".Viewports", StringComparison.Ordinal) || - !mapRenderer.Contains(".Objects(viewport)", StringComparison.Ordinal) || - !mapRenderer.Contains(".RenderSplats(viewport)", StringComparison.Ordinal) || + !mapRenderer.Contains(".LatestObjects(viewport)", StringComparison.Ordinal) || + !mapRenderer.Contains(".LatestRenderSplats(viewport)", StringComparison.Ordinal) || !mapRenderer.Contains(".Settings", StringComparison.Ordinal) || !mapRenderer.Contains(".LatestPlayer()", StringComparison.Ordinal) || !sectorRenderer.Contains("AetheriaUnityRuntimeClientProvider.ResolveClient(", StringComparison.Ordinal) || !sectorRenderer.Contains(".Aetheria()", StringComparison.Ordinal) || - !sectorRenderer.Contains(".SectorMap", StringComparison.Ordinal) || - !sectorRenderer.Contains(".LatestAsync()", StringComparison.Ordinal) || + !sectorRenderer.Contains(".LatestSectorMap()", StringComparison.Ordinal) || !sectorRenderer.Contains(".Details", StringComparison.Ordinal) || - !sectorRenderer.Contains(".Zone(zoneIndex)", StringComparison.Ordinal) || + !sectorRenderer.Contains(".LatestZone(zoneIndex)", StringComparison.Ordinal) || !sectorRenderer.Contains(".LatestCatalog()", StringComparison.Ordinal) || !sectorRenderer.Contains(".Settings", StringComparison.Ordinal) || !sectorRenderer.Contains(".LatestPlayer()", StringComparison.Ordinal) || !sectorMap.Contains("AetheriaUnityRuntimeClientProvider.ResolveClient(", StringComparison.Ordinal) || !sectorMap.Contains(".Aetheria()", StringComparison.Ordinal) || - !sectorMap.Contains(".SectorMap", StringComparison.Ordinal) || - !sectorMap.Contains(".LatestAsync()", StringComparison.Ordinal) || + !sectorMap.Contains(".LatestSectorMap()", StringComparison.Ordinal) || !sectorRenderer.Contains("AetheriaRuntimeZoneDetailsSurfaceBuilder.ProjectDaemonZone(", StringComparison.Ordinal)) { throw new InvalidOperationException( @@ -13003,8 +13079,7 @@ static void RequireUnityObserverDoesNotTickLocalSimulation(string root) "TryStartDaemonObservedGame", "LatestSectorMap(stateBoot)", ".Aetheria()", - ".SectorMap", - ".LatestAsync()", + ".LatestSectorMap()", "AetheriaUnityObservedRunProjection.Project(", "sectorMap.FrameId", "sectorMap.IsTutorial", @@ -14213,9 +14288,9 @@ static void RequireRuntimeStateReaderOwnsUnityStateAcquisition(string root) !mainMenu.Contains("LatestSectorMap(AetheriaRuntimeStateBootReport stateBoot)", StringComparison.Ordinal) || !mainMenu.Contains("LatestRuntimeCatalog(AetheriaRuntimeStateBootReport stateBoot)", StringComparison.Ordinal) || !mainMenu.Contains(".Aetheria()", StringComparison.Ordinal) || - !mainMenu.Contains(".SectorMap", StringComparison.Ordinal) || + !mainMenu.Contains(".LatestSectorMap()", StringComparison.Ordinal) || !mainMenu.Contains(".LatestCatalog()", StringComparison.Ordinal) || - !mainMenu.Contains(".LatestAsync()", StringComparison.Ordinal)) + !mainMenu.Contains(".LatestPlayer()", StringComparison.Ordinal)) { throw new InvalidOperationException( "MainMenu no longer routes sector-map lookup through the shared Aetheria client facade."); diff --git a/Assets/Scripts/Gameplay/AetheriaUnityObservedTargetQuery.cs b/Assets/Scripts/Gameplay/AetheriaUnityObservedTargetQuery.cs index 5f6cdf70a..b35c90732 100644 --- a/Assets/Scripts/Gameplay/AetheriaUnityObservedTargetQuery.cs +++ b/Assets/Scripts/Gameplay/AetheriaUnityObservedTargetQuery.cs @@ -95,10 +95,7 @@ public sealed class AetheriaUnityObservedTargetQuery { return _resolveClient() ?.Aetheria() - .ZoneContacts - .LatestAsync() - .GetAwaiter() - .GetResult(); + .LatestZoneContacts(); } catch { diff --git a/Assets/Scripts/Gameplay/AetheriaUnityRenderSplatViewportSource.cs b/Assets/Scripts/Gameplay/AetheriaUnityRenderSplatViewportSource.cs index edf2bd0cf..c4d211337 100644 --- a/Assets/Scripts/Gameplay/AetheriaUnityRenderSplatViewportSource.cs +++ b/Assets/Scripts/Gameplay/AetheriaUnityRenderSplatViewportSource.cs @@ -107,10 +107,7 @@ public sealed class AetheriaUnityRenderSplatViewportSource : MonoBehaviour _document = ResolveClient() .Aetheria() .Viewports - .RenderSplats(ResolveViewportBounds()) - .LatestAsync() - .GetAwaiter() - .GetResult(); + .LatestRenderSplats(ResolveViewportBounds()); } catch (Exception ex) { diff --git a/Assets/Scripts/UI/HUD/SchematicDisplay.cs b/Assets/Scripts/UI/HUD/SchematicDisplay.cs index 12897a07d..88dd05bb8 100644 --- a/Assets/Scripts/UI/HUD/SchematicDisplay.cs +++ b/Assets/Scripts/UI/HUD/SchematicDisplay.cs @@ -367,10 +367,7 @@ public class SchematicDisplay : MonoBehaviour _currentEntityDocument = ResolveClient() .Aetheria() .Current - .Entity - .LatestAsync() - .GetAwaiter() - .GetResult(); + .LatestEntity(); } catch (Exception ex) { diff --git a/Assets/Scripts/UI/MainMenu.cs b/Assets/Scripts/UI/MainMenu.cs index c15a2fe50..8314ee43c 100644 --- a/Assets/Scripts/UI/MainMenu.cs +++ b/Assets/Scripts/UI/MainMenu.cs @@ -163,10 +163,7 @@ public class MainMenu : MonoBehaviour { return ResolveClient(stateBoot) .Aetheria() - .SectorMap - .LatestAsync() - .GetAwaiter() - .GetResult(); + .LatestSectorMap(); } catch (Exception ex) { diff --git a/Assets/Scripts/UI/Menu/MapRenderer.cs b/Assets/Scripts/UI/Menu/MapRenderer.cs index e0efbfd32..ab621dacc 100644 --- a/Assets/Scripts/UI/Menu/MapRenderer.cs +++ b/Assets/Scripts/UI/Menu/MapRenderer.cs @@ -127,17 +127,11 @@ public class MapRenderer : MonoBehaviour _objectsViewport = client .Aetheria() .Viewports - .Objects(viewport) - .LatestAsync() - .GetAwaiter() - .GetResult(); + .LatestObjects(viewport); _renderSplatsViewport = client .Aetheria() .Viewports - .RenderSplats(viewport) - .LatestAsync() - .GetAwaiter() - .GetResult(); + .LatestRenderSplats(viewport); var zoneName = string.IsNullOrWhiteSpace(_objectsViewport?.ZoneName) ? "Unknown" diff --git a/Assets/Scripts/UI/Menu/SectorMap.cs b/Assets/Scripts/UI/Menu/SectorMap.cs index 65db5f45b..4af400b2a 100644 --- a/Assets/Scripts/UI/Menu/SectorMap.cs +++ b/Assets/Scripts/UI/Menu/SectorMap.cs @@ -221,10 +221,7 @@ public class SectorMap : MonoBehaviour { _sectorMap = ResolveClient() .Aetheria() - .SectorMap - .LatestAsync() - .GetAwaiter() - .GetResult(); + .LatestSectorMap(); if (_sectorMapLoaded) return; diff --git a/Assets/Scripts/UI/Menu/SectorRenderer.cs b/Assets/Scripts/UI/Menu/SectorRenderer.cs index f07b96cce..48ecdc1f7 100644 --- a/Assets/Scripts/UI/Menu/SectorRenderer.cs +++ b/Assets/Scripts/UI/Menu/SectorRenderer.cs @@ -171,10 +171,7 @@ public class SectorRenderer : MonoBehaviour, IBeginDragHandler, IDragHandler, IS { var sectorMap = ResolveClient() .Aetheria() - .SectorMap - .LatestAsync() - .GetAwaiter() - .GetResult(); + .LatestSectorMap(); return (sectorMap?.Zones ?? Array.Empty()) .FirstOrDefault(zone => zone.ZoneIndex == zoneIndex); } @@ -200,10 +197,7 @@ public class SectorRenderer : MonoBehaviour, IBeginDragHandler, IDragHandler, IS return ResolveClient() .Aetheria() .Details - .Zone(zoneIndex) - .LatestAsync() - .GetAwaiter() - .GetResult(); + .LatestZone(zoneIndex); } catch (Exception ex) { @@ -309,10 +303,7 @@ public class SectorRenderer : MonoBehaviour, IBeginDragHandler, IDragHandler, IS return ResolveClient() .Aetheria() .Current - .Zone - .LatestAsync() - .GetAwaiter() - .GetResult(); + .LatestZone(); } catch (Exception ex) { diff --git a/Assets/Scripts/Zone Display/VolumeCloudRenderer.cs b/Assets/Scripts/Zone Display/VolumeCloudRenderer.cs index ee0a72911..3ac51baba 100644 --- a/Assets/Scripts/Zone Display/VolumeCloudRenderer.cs +++ b/Assets/Scripts/Zone Display/VolumeCloudRenderer.cs @@ -102,8 +102,7 @@ public class VolumeCloudRenderer : EffectBase _playerSettings = ResolveClient() .Aetheria() .Settings - .Player - .Latest(); + .LatestPlayer(); } catch (Exception ex) { diff --git a/Assets/Scripts/Zone Display/ZoneRenderer.cs b/Assets/Scripts/Zone Display/ZoneRenderer.cs index 341957cb1..22b0aacfc 100644 --- a/Assets/Scripts/Zone Display/ZoneRenderer.cs +++ b/Assets/Scripts/Zone Display/ZoneRenderer.cs @@ -895,10 +895,7 @@ public class ZoneRenderer : MonoBehaviour var objects = ResolveClient() .Aetheria() .Viewports - .Objects(ToViewportBounds(viewport)) - .LatestAsync() - .GetAwaiter() - .GetResult(); + .LatestObjects(ToViewportBounds(viewport)); foreach (var entity in objects?.Objects ?? Array.Empty()) { if (entity != null && entity.EntityIndex >= 0) @@ -943,10 +940,7 @@ public class ZoneRenderer : MonoBehaviour { var contacts = ResolveClient() .Aetheria() - .ZoneContacts - .LatestAsync() - .GetAwaiter() - .GetResult(); + .LatestZoneContacts(); foreach (var target in contacts?.Targets ?? Array.Empty()) { if (target != null && target.EntityIndex >= 0) diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaClientState.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaClientState.cs index 5ee7547e7..d60d7f12b 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaClientState.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaClientState.cs @@ -188,6 +188,26 @@ namespace GameCult.Aetheria.State.Verse return Latest(); } + public Task LatestSectorMapAsync() + { + return SectorMap.LatestAsync(); + } + + public AetheriaRuntimeSectorMapDocument LatestSectorMap() + { + return LatestSectorMapAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + } + + public Task LatestZoneContactsAsync() + { + return ZoneContacts.LatestAsync(); + } + + public AetheriaRuntimeZoneContactsDocument LatestZoneContacts() + { + return LatestZoneContactsAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + } + public Observable Watch() where TDocument : class { @@ -237,7 +257,7 @@ namespace GameCult.Aetheria.State.Verse frameTask.Result, healthTask.Result, commandBoundaryTask.Result, - () => Catalog.Latest()); + LatestCatalog); } } @@ -305,6 +325,26 @@ namespace GameCult.Aetheria.State.Verse public CultMeshDocumentHandle Entity { get; } public CultMeshDocumentHandle Docking { get; } + + public Task LatestZoneAsync() + { + return Zone.LatestAsync(); + } + + public AetheriaRuntimeCurrentZoneDocument LatestZone() + { + return LatestZoneAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + } + + public Task LatestEntityAsync() + { + return Entity.LatestAsync(); + } + + public AetheriaRuntimeCurrentEntityDocument LatestEntity() + { + return LatestEntityAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + } } public sealed class AetheriaClientSettingsState @@ -459,6 +499,16 @@ namespace GameCult.Aetheria.State.Verse return _objects(viewport ?? new AetheriaRuntimeRtsViewportBounds()); } + public Task LatestObjectsAsync(AetheriaRuntimeRtsViewportBounds viewport) + { + return Objects(viewport).LatestAsync(); + } + + public AetheriaRuntimeObjectsViewportDocument LatestObjects(AetheriaRuntimeRtsViewportBounds viewport) + { + return LatestObjectsAsync(viewport).ConfigureAwait(false).GetAwaiter().GetResult(); + } + public CultMeshDocumentHandle Gravity(AetheriaRuntimeRtsViewportBounds viewport) { return _gravity(viewport ?? new AetheriaRuntimeRtsViewportBounds()); @@ -468,6 +518,16 @@ namespace GameCult.Aetheria.State.Verse { return _renderSplats(viewport ?? new AetheriaRuntimeRtsViewportBounds()); } + + public Task LatestRenderSplatsAsync(AetheriaRuntimeRtsViewportBounds viewport) + { + return RenderSplats(viewport).LatestAsync(); + } + + public AetheriaRuntimeRenderSplatsViewportDocument LatestRenderSplats(AetheriaRuntimeRtsViewportBounds viewport) + { + return LatestRenderSplatsAsync(viewport).ConfigureAwait(false).GetAwaiter().GetResult(); + } } public sealed class AetheriaClientDetailState @@ -491,6 +551,16 @@ namespace GameCult.Aetheria.State.Verse return _zoneDetails(zoneIndex); } + public Task LatestZoneAsync(int zoneIndex) + { + return Zone(zoneIndex).LatestAsync(); + } + + public AetheriaRuntimeZoneDetailsDocument LatestZone(int zoneIndex) + { + return LatestZoneAsync(zoneIndex).ConfigureAwait(false).GetAwaiter().GetResult(); + } + public CultMeshDocumentHandle SelectedObject(int entityIndex) { return _selectedObject(entityIndex);