Route viewport projections through managed documents

This commit is contained in:
Meta Vens 2026-06-28 01:37:43 +01:00
commit e0acc8a1b5
11 changed files with 251 additions and 26 deletions

View file

@ -2395,7 +2395,8 @@ static void RequireDaemonRenderQueryAuthority(string root)
"LoadAsteroidBelt(beltPose)",
"void LoadAsteroidBelt(AetheriaRuntimeZoneRenderAsteroidBeltPose beltPose)",
"private void SyncDaemonEntityInstances()",
"ObjectsViewportAsync(ToViewportBounds(viewport))",
".Viewports",
".Objects(ToViewportBounds(viewport))",
"foreach (var entity in objects?.Objects ?? Array.Empty<AetheriaRuntimeRtsViewportObject>())",
"_observedEntityFacadesByDaemonIndex.TryGetValue(entityIndex, out var entity)",
"Loading entity {entity.Name} from daemon presentation query",
@ -4482,7 +4483,8 @@ static void RequireSectorMapZoneDetailsUseEveSurface(string root)
".Aetheria()",
".SectorMap",
".LatestAsync()",
"ZoneDetailsAsync(zoneIndex)",
".Details",
".Zone(zoneIndex)",
"OpenRuntimeCatalog()",
"PlayerSettingsAsync()",
"AetheriaClient",
@ -11406,13 +11408,16 @@ static void RequireUnityObserverDoesNotTickLocalSimulation(string root)
}
if (!mapRenderer.Contains("private AetheriaClient _client", StringComparison.Ordinal) ||
!mapRenderer.Contains("ObjectsViewportAsync", StringComparison.Ordinal) ||
!mapRenderer.Contains(".Viewports", StringComparison.Ordinal) ||
!mapRenderer.Contains(".Objects(viewport)", StringComparison.Ordinal) ||
!mapRenderer.Contains(".RenderSplats(viewport)", StringComparison.Ordinal) ||
!mapRenderer.Contains("PlayerSettingsAsync()", StringComparison.Ordinal) ||
!sectorRenderer.Contains("private AetheriaClient _client", StringComparison.Ordinal) ||
!sectorRenderer.Contains(".Aetheria()", StringComparison.Ordinal) ||
!sectorRenderer.Contains(".SectorMap", StringComparison.Ordinal) ||
!sectorRenderer.Contains(".LatestAsync()", StringComparison.Ordinal) ||
!sectorRenderer.Contains("ZoneDetailsAsync(zoneIndex)", StringComparison.Ordinal) ||
!sectorRenderer.Contains(".Details", StringComparison.Ordinal) ||
!sectorRenderer.Contains(".Zone(zoneIndex)", StringComparison.Ordinal) ||
!sectorRenderer.Contains("OpenRuntimeCatalog()", StringComparison.Ordinal) ||
!sectorRenderer.Contains("PlayerSettingsAsync()", StringComparison.Ordinal) ||
!sectorMap.Contains("private AetheriaClient _client", StringComparison.Ordinal) ||
@ -13741,7 +13746,8 @@ static void RequireInventoryDoubleClickTransferRequestAuthority(string root)
"TryResolveTypedInventoryRows(",
"TryValidateTypedCargoSlot(",
"TryValidateTypedEquipmentSlot(",
"InventoryAsync(entityIndex)",
".Details",
".Inventory(entityIndex)",
"origin.Cargo.TryGetValue(item, out var originPosition)",
"SourceIndex == cargoIndex",
"SourceIndex == equipmentIndex",

View file

@ -108,7 +108,10 @@ public sealed class AetheriaUnityRenderSplatViewportSource : MonoBehaviour
try
{
_document = ResolveClient()
.RenderSplatsViewportAsync(ResolveViewportBounds())
.Aetheria()
.Viewports
.RenderSplats(ResolveViewportBounds())
.LatestAsync()
.GetAwaiter()
.GetResult();
}

View file

@ -176,6 +176,31 @@ public class DaemonRuntimeDocumentTests
.LatestAsync<AetheriaRuntimeDaemonFrameDocument>()
.GetAwaiter()
.GetResult();
var viewport = new AetheriaRuntimeRtsViewportBounds
{
MinX = -100,
MinY = -100,
MaxX = 100,
MaxY = 100
};
var objectsViewport = client.Aetheria()
.Viewports
.Objects(viewport)
.LatestAsync()
.GetAwaiter()
.GetResult();
var zoneDetails = client.Aetheria()
.Details
.Zone(0)
.LatestAsync()
.GetAwaiter()
.GetResult();
var inventory = client.Aetheria()
.Details
.Inventory(0)
.LatestAsync()
.GetAwaiter()
.GetResult();
var currentEntityFromClientType = client
.LatestAsync<AetheriaRuntimeCurrentEntityDocument>()
.GetAwaiter()
@ -211,6 +236,12 @@ public class DaemonRuntimeDocumentTests
Assert.AreEqual(AetheriaRuntimeDaemonSchemas.Frame, latestFrame.Schema);
Assert.AreEqual(frame.FrameId, latestFrame.FrameId);
Assert.AreEqual(frame.FrameId, latestFrameByType.FrameId);
Assert.AreEqual(AetheriaRuntimeDaemonSchemas.ObjectsViewport, objectsViewport.Schema);
Assert.AreEqual("Player", objectsViewport.Objects.FirstOrDefault()?.DisplayName);
Assert.AreEqual(AetheriaRuntimeDaemonSchemas.ZoneDetails, zoneDetails.Schema);
Assert.AreEqual(0, zoneDetails.ZoneIndex);
Assert.AreEqual(AetheriaRuntimeDaemonSchemas.Inventory, inventory.Schema);
Assert.AreEqual("zone.0.entity.0", inventory.EntityKey);
Assert.AreEqual("zone.0.entity.0", currentEntity.EntityKey);
Assert.AreEqual(0, currentEntity.EntityIndex);
Assert.AreEqual("Player", currentEntity.Entity?.DisplayName);

View file

@ -870,7 +870,10 @@ public class InventoryMenu : MonoBehaviour
return false;
var inventory = ResolveClient()
.InventoryAsync(entityIndex)
.Aetheria()
.Details
.Inventory(entityIndex)
.LatestAsync()
.GetAwaiter()
.GetResult();
if (inventory == null || !string.Equals(inventory.EntityKey, entityKey, StringComparison.Ordinal))

View file

@ -1289,7 +1289,10 @@ private void Update()
return false;
var inventory = ResolveClient()
.InventoryAsync(entityIndex)
.Aetheria()
.Details
.Inventory(entityIndex)
.LatestAsync()
.GetAwaiter()
.GetResult();
if (inventory == null || !string.Equals(inventory.EntityKey, entityKey, StringComparison.Ordinal))

View file

@ -133,11 +133,17 @@ public class MapRenderer : MonoBehaviour
var client = ResolveClient();
var viewport = ResolveViewportBounds();
_objectsViewport = client
.ObjectsViewportAsync(viewport)
.Aetheria()
.Viewports
.Objects(viewport)
.LatestAsync()
.GetAwaiter()
.GetResult();
_renderSplatsViewport = client
.RenderSplatsViewportAsync(viewport)
.Aetheria()
.Viewports
.RenderSplats(viewport)
.LatestAsync()
.GetAwaiter()
.GetResult();

View file

@ -200,7 +200,10 @@ public class SectorRenderer : MonoBehaviour, IBeginDragHandler, IDragHandler, IS
try
{
return ResolveClient()
.ZoneDetailsAsync(zoneIndex)
.Aetheria()
.Details
.Zone(zoneIndex)
.LatestAsync()
.GetAwaiter()
.GetResult();
}

View file

@ -896,7 +896,10 @@ public class ZoneRenderer : MonoBehaviour
try
{
var objects = ResolveClient()
.ObjectsViewportAsync(ToViewportBounds(viewport))
.Aetheria()
.Viewports
.Objects(ToViewportBounds(viewport))
.LatestAsync()
.GetAwaiter()
.GetResult();
foreach (var entity in objects?.Objects ?? Array.Empty<AetheriaRuntimeRtsViewportObject>())

View file

@ -177,32 +177,28 @@ namespace GameCult.Aetheria.State.Verse
AetheriaRuntimeRtsViewportBounds viewport)
{
ThrowIfDisposed();
var frame = await RequireFrameAsync().ConfigureAwait(false);
return AetheriaRuntimeRtsProjection.ProjectViewport(frame, viewport);
return await State.Viewports.Map(viewport).LatestAsync().ConfigureAwait(false);
}
public async Task<AetheriaRuntimeObjectsViewportDocument> ObjectsViewportAsync(
AetheriaRuntimeRtsViewportBounds viewport)
{
ThrowIfDisposed();
var frame = await RequireFrameAsync().ConfigureAwait(false);
return AetheriaRuntimeRtsProjection.ProjectObjectsViewport(frame, viewport);
return await State.Viewports.Objects(viewport).LatestAsync().ConfigureAwait(false);
}
public async Task<AetheriaRuntimeGravityViewportDocument> GravityViewportAsync(
AetheriaRuntimeRtsViewportBounds viewport)
{
ThrowIfDisposed();
var frame = await RequireFrameAsync().ConfigureAwait(false);
return AetheriaRuntimeRtsProjection.ProjectGravityViewport(frame, viewport);
return await State.Viewports.Gravity(viewport).LatestAsync().ConfigureAwait(false);
}
public async Task<AetheriaRuntimeRenderSplatsViewportDocument> RenderSplatsViewportAsync(
AetheriaRuntimeRtsViewportBounds viewport)
{
ThrowIfDisposed();
var frame = await RequireFrameAsync().ConfigureAwait(false);
return AetheriaRuntimeRtsProjection.ProjectRenderSplatsViewport(frame, viewport);
return await State.Viewports.RenderSplats(viewport).LatestAsync().ConfigureAwait(false);
}
public async Task<AetheriaRuntimeAssetManifestDocument> AssetManifestAsync()
@ -253,8 +249,7 @@ namespace GameCult.Aetheria.State.Verse
public async Task<AetheriaRuntimeZoneDetailsDocument> ZoneDetailsAsync(int zoneIndex)
{
ThrowIfDisposed();
var frame = await RequireFrameAsync().ConfigureAwait(false);
return AetheriaRuntimeRtsProjection.ProjectZoneDetails(frame, zoneIndex);
return await State.Details.Zone(zoneIndex).LatestAsync().ConfigureAwait(false);
}
public async Task<AetheriaRuntimeZoneRenderDocument> ZoneRenderAsync()
@ -268,6 +263,9 @@ namespace GameCult.Aetheria.State.Verse
AetheriaRuntimeStarbridgeSessionDocument? session = null)
{
ThrowIfDisposed();
if (scenario == null && session == null)
return await State.Starbridge.Summary.LatestAsync().ConfigureAwait(false);
var frame = await RequireFrameAsync().ConfigureAwait(false);
scenario ??= await _verse.GetStarbridgeScenarioAsync().ConfigureAwait(false);
session ??= await _verse.GetStarbridgeSessionAsync().ConfigureAwait(false);
@ -281,15 +279,13 @@ namespace GameCult.Aetheria.State.Verse
public async Task<AetheriaRuntimeSelectedObjectDocument> SelectedObjectAsync(int entityIndex)
{
ThrowIfDisposed();
var frame = await RequireFrameAsync().ConfigureAwait(false);
return AetheriaRuntimeRtsProjection.ProjectSelectedObject(frame, entityIndex);
return await State.Details.SelectedObject(entityIndex).LatestAsync().ConfigureAwait(false);
}
public async Task<AetheriaRuntimeInventoryDocument> InventoryAsync(int entityIndex)
{
ThrowIfDisposed();
var frame = await RequireFrameAsync().ConfigureAwait(false);
return AetheriaRuntimeRtsProjection.ProjectInventory(frame, entityIndex);
return await State.Details.Inventory(entityIndex).LatestAsync().ConfigureAwait(false);
}
public Task<AetheriaRuntimeDaemonHealthDocument?> DaemonHealthAsync()

View file

@ -22,8 +22,16 @@ namespace GameCult.Aetheria.State.Verse
CultMeshDocumentHandle<AetheriaRuntimeStationRefitDocument> stationRefit,
CultMeshDocumentHandle<AetheriaRuntimeSectorMapDocument> sectorMap,
CultMeshDocumentHandle<AetheriaRuntimeZoneRenderDocument> zoneRender,
Func<AetheriaRuntimeRtsViewportBounds, CultMeshDocumentHandle<AetheriaRuntimeRtsViewportDocument>> mapViewport,
Func<AetheriaRuntimeRtsViewportBounds, CultMeshDocumentHandle<AetheriaRuntimeObjectsViewportDocument>> objectsViewport,
Func<AetheriaRuntimeRtsViewportBounds, CultMeshDocumentHandle<AetheriaRuntimeGravityViewportDocument>> gravityViewport,
Func<AetheriaRuntimeRtsViewportBounds, CultMeshDocumentHandle<AetheriaRuntimeRenderSplatsViewportDocument>> renderSplatsViewport,
Func<int, CultMeshDocumentHandle<AetheriaRuntimeZoneDetailsDocument>> zoneDetails,
Func<int, CultMeshDocumentHandle<AetheriaRuntimeSelectedObjectDocument>> selectedObject,
Func<int, CultMeshDocumentHandle<AetheriaRuntimeInventoryDocument>> inventory,
CultMeshDocumentHandle<AetheriaRuntimeStarbridgeScenarioDocument> starbridgeScenario,
CultMeshDocumentHandle<AetheriaRuntimeStarbridgeSessionDocument> starbridgeSession,
CultMeshDocumentHandle<AetheriaRuntimeStarbridgeSessionSummaryDocument> starbridgeSummary,
Func<string, CultMeshDocumentHandle<AetheriaRuntimeStarbridgePlayerSeatDocument>> starbridgePlayerSeat)
{
LatestFrame = latestFrame ?? throw new ArgumentNullException(nameof(latestFrame));
@ -33,10 +41,20 @@ namespace GameCult.Aetheria.State.Verse
StationRefit = stationRefit ?? throw new ArgumentNullException(nameof(stationRefit));
SectorMap = sectorMap ?? throw new ArgumentNullException(nameof(sectorMap));
ZoneRender = zoneRender ?? throw new ArgumentNullException(nameof(zoneRender));
Viewports = new AetheriaClientViewportState(
mapViewport,
objectsViewport,
gravityViewport,
renderSplatsViewport);
Details = new AetheriaClientDetailState(
zoneDetails,
selectedObject,
inventory);
DockingState = new AetheriaClientDockingState(Current.Entity, Current.Docking, StationRefit);
Starbridge = new AetheriaClientStarbridgeState(
starbridgeScenario,
starbridgeSession,
starbridgeSummary,
starbridgePlayerSeat);
_documents = CultMesh.Documents(
LatestFrame,
@ -60,6 +78,10 @@ namespace GameCult.Aetheria.State.Verse
public AetheriaClientDockingState DockingState { get; }
public AetheriaClientViewportState Viewports { get; }
public AetheriaClientDetailState Details { get; }
public AetheriaClientStarbridgeState Starbridge { get; }
public CultMeshDocumentHandle<AetheriaRuntimeZoneContactsDocument> ZoneContacts { get; }
@ -232,6 +254,78 @@ namespace GameCult.Aetheria.State.Verse
public bool IsDocked => StationRefit?.IsDocked == true && CurrentDockingBay != null;
}
public sealed class AetheriaClientViewportState
{
private readonly Func<AetheriaRuntimeRtsViewportBounds, CultMeshDocumentHandle<AetheriaRuntimeRtsViewportDocument>> _map;
private readonly Func<AetheriaRuntimeRtsViewportBounds, CultMeshDocumentHandle<AetheriaRuntimeObjectsViewportDocument>> _objects;
private readonly Func<AetheriaRuntimeRtsViewportBounds, CultMeshDocumentHandle<AetheriaRuntimeGravityViewportDocument>> _gravity;
private readonly Func<AetheriaRuntimeRtsViewportBounds, CultMeshDocumentHandle<AetheriaRuntimeRenderSplatsViewportDocument>> _renderSplats;
internal AetheriaClientViewportState(
Func<AetheriaRuntimeRtsViewportBounds, CultMeshDocumentHandle<AetheriaRuntimeRtsViewportDocument>> map,
Func<AetheriaRuntimeRtsViewportBounds, CultMeshDocumentHandle<AetheriaRuntimeObjectsViewportDocument>> objects,
Func<AetheriaRuntimeRtsViewportBounds, CultMeshDocumentHandle<AetheriaRuntimeGravityViewportDocument>> gravity,
Func<AetheriaRuntimeRtsViewportBounds, CultMeshDocumentHandle<AetheriaRuntimeRenderSplatsViewportDocument>> renderSplats)
{
_map = map ?? throw new ArgumentNullException(nameof(map));
_objects = objects ?? throw new ArgumentNullException(nameof(objects));
_gravity = gravity ?? throw new ArgumentNullException(nameof(gravity));
_renderSplats = renderSplats ?? throw new ArgumentNullException(nameof(renderSplats));
}
public CultMeshDocumentHandle<AetheriaRuntimeRtsViewportDocument> Map(AetheriaRuntimeRtsViewportBounds viewport)
{
return _map(viewport ?? new AetheriaRuntimeRtsViewportBounds());
}
public CultMeshDocumentHandle<AetheriaRuntimeObjectsViewportDocument> Objects(AetheriaRuntimeRtsViewportBounds viewport)
{
return _objects(viewport ?? new AetheriaRuntimeRtsViewportBounds());
}
public CultMeshDocumentHandle<AetheriaRuntimeGravityViewportDocument> Gravity(AetheriaRuntimeRtsViewportBounds viewport)
{
return _gravity(viewport ?? new AetheriaRuntimeRtsViewportBounds());
}
public CultMeshDocumentHandle<AetheriaRuntimeRenderSplatsViewportDocument> RenderSplats(AetheriaRuntimeRtsViewportBounds viewport)
{
return _renderSplats(viewport ?? new AetheriaRuntimeRtsViewportBounds());
}
}
public sealed class AetheriaClientDetailState
{
private readonly Func<int, CultMeshDocumentHandle<AetheriaRuntimeZoneDetailsDocument>> _zoneDetails;
private readonly Func<int, CultMeshDocumentHandle<AetheriaRuntimeSelectedObjectDocument>> _selectedObject;
private readonly Func<int, CultMeshDocumentHandle<AetheriaRuntimeInventoryDocument>> _inventory;
internal AetheriaClientDetailState(
Func<int, CultMeshDocumentHandle<AetheriaRuntimeZoneDetailsDocument>> zoneDetails,
Func<int, CultMeshDocumentHandle<AetheriaRuntimeSelectedObjectDocument>> selectedObject,
Func<int, CultMeshDocumentHandle<AetheriaRuntimeInventoryDocument>> inventory)
{
_zoneDetails = zoneDetails ?? throw new ArgumentNullException(nameof(zoneDetails));
_selectedObject = selectedObject ?? throw new ArgumentNullException(nameof(selectedObject));
_inventory = inventory ?? throw new ArgumentNullException(nameof(inventory));
}
public CultMeshDocumentHandle<AetheriaRuntimeZoneDetailsDocument> Zone(int zoneIndex)
{
return _zoneDetails(zoneIndex);
}
public CultMeshDocumentHandle<AetheriaRuntimeSelectedObjectDocument> SelectedObject(int entityIndex)
{
return _selectedObject(entityIndex);
}
public CultMeshDocumentHandle<AetheriaRuntimeInventoryDocument> Inventory(int entityIndex)
{
return _inventory(entityIndex);
}
}
public sealed class AetheriaClientStarbridgeState
{
private readonly Func<string, CultMeshDocumentHandle<AetheriaRuntimeStarbridgePlayerSeatDocument>> _playerSeat;
@ -239,10 +333,12 @@ namespace GameCult.Aetheria.State.Verse
internal AetheriaClientStarbridgeState(
CultMeshDocumentHandle<AetheriaRuntimeStarbridgeScenarioDocument> scenario,
CultMeshDocumentHandle<AetheriaRuntimeStarbridgeSessionDocument> session,
CultMeshDocumentHandle<AetheriaRuntimeStarbridgeSessionSummaryDocument> summary,
Func<string, CultMeshDocumentHandle<AetheriaRuntimeStarbridgePlayerSeatDocument>> playerSeat)
{
Scenario = scenario ?? throw new ArgumentNullException(nameof(scenario));
Session = session ?? throw new ArgumentNullException(nameof(session));
Summary = summary ?? throw new ArgumentNullException(nameof(summary));
_playerSeat = playerSeat ?? throw new ArgumentNullException(nameof(playerSeat));
}
@ -250,6 +346,8 @@ namespace GameCult.Aetheria.State.Verse
public CultMeshDocumentHandle<AetheriaRuntimeStarbridgeSessionDocument> Session { get; }
public CultMeshDocumentHandle<AetheriaRuntimeStarbridgeSessionSummaryDocument> Summary { get; }
public CultMeshDocumentHandle<AetheriaRuntimeStarbridgePlayerSeatDocument> PlayerSeat(string seatId)
{
if (string.IsNullOrWhiteSpace(seatId))

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
@ -761,10 +762,45 @@ namespace GameCult.Aetheria.State.Verse
"aetheria.zone.render",
frame => Task.FromResult(AetheriaRuntimeRtsProjection.ProjectZoneRender(frame)),
AetheriaRuntimeDaemonSchemas.ZoneRender),
viewport => ProjectedDocument(
ViewportDocumentId("aetheria.viewport.map", viewport),
frame => Task.FromResult(AetheriaRuntimeRtsProjection.ProjectViewport(frame, viewport)),
AetheriaRuntimeDaemonSchemas.RtsViewport),
viewport => ProjectedDocument(
ViewportDocumentId("aetheria.viewport.objects", viewport),
frame => Task.FromResult(AetheriaRuntimeRtsProjection.ProjectObjectsViewport(frame, viewport)),
AetheriaRuntimeDaemonSchemas.ObjectsViewport),
viewport => ProjectedDocument(
ViewportDocumentId("aetheria.viewport.gravity", viewport),
frame => Task.FromResult(AetheriaRuntimeRtsProjection.ProjectGravityViewport(frame, viewport)),
AetheriaRuntimeDaemonSchemas.GravityViewport),
viewport => ProjectedDocument(
ViewportDocumentId("aetheria.viewport.render_splats", viewport),
frame => Task.FromResult(AetheriaRuntimeRtsProjection.ProjectRenderSplatsViewport(frame, viewport)),
AetheriaRuntimeDaemonSchemas.RenderSplatsViewport),
zoneIndex => ProjectedDocument(
IndexedDocumentId("aetheria.zone.details", zoneIndex),
frame => Task.FromResult(AetheriaRuntimeRtsProjection.ProjectZoneDetails(frame, zoneIndex)),
AetheriaRuntimeDaemonSchemas.ZoneDetails),
entityIndex => ProjectedDocument(
IndexedDocumentId("aetheria.object.selected", entityIndex),
frame => Task.FromResult(AetheriaRuntimeRtsProjection.ProjectSelectedObject(frame, entityIndex)),
AetheriaRuntimeDaemonSchemas.SelectedObject),
entityIndex => ProjectedDocument(
IndexedDocumentId("aetheria.inventory", entityIndex),
frame => Task.FromResult(AetheriaRuntimeRtsProjection.ProjectInventory(frame, entityIndex)),
AetheriaRuntimeDaemonSchemas.Inventory),
Document<AetheriaRuntimeStarbridgeScenarioDocument>(
AetheriaRuntimeVerseRecordKeys.StarbridgeScenarioLatest),
Document<AetheriaRuntimeStarbridgeSessionDocument>(
AetheriaRuntimeVerseRecordKeys.StarbridgeSessionLatest),
ProjectedDocument(
"aetheria.starbridge.summary",
ProjectStarbridgeSummaryAsync,
AetheriaRuntimeDaemonSchemas.StarbridgeSessionSummary,
CultMesh.ProjectionSource(AetheriaRuntimeVerseRecordKeys.StarbridgeScenarioLatest.ToString()),
CultMesh.ProjectionSource(AetheriaRuntimeVerseRecordKeys.StarbridgeSessionLatest.ToString()),
CultMesh.ProjectionSource("catalog:aetheria.runtime")),
seatId => Document<AetheriaRuntimeStarbridgePlayerSeatDocument>(
AetheriaRuntimeVerseRecordKeys.StarbridgePlayerSeat(seatId)));
@ -805,6 +841,43 @@ namespace GameCult.Aetheria.State.Verse
var catalog = OpenRuntimeCatalog();
return AetheriaRuntimeRtsProjection.ProjectStationRefit(frame, loadoutTemplates, catalog);
}
async Task<AetheriaRuntimeStarbridgeSessionSummaryDocument> ProjectStarbridgeSummaryAsync(
AetheriaRuntimeDaemonFrameDocument frame)
{
var scenario = await GetStarbridgeScenarioAsync().ConfigureAwait(false);
var session = await GetStarbridgeSessionAsync().ConfigureAwait(false);
return AetheriaRuntimeStarbridgeProjection.ProjectSessionSummary(
frame,
scenario,
session,
OpenRuntimeCatalog());
}
static string IndexedDocumentId(string prefix, int index)
{
return $"{prefix}.{index.ToString(CultureInfo.InvariantCulture)}";
}
static string ViewportDocumentId(string prefix, AetheriaRuntimeRtsViewportBounds viewport)
{
var normalized = AetheriaRuntimeRtsProjection.Normalize(viewport);
return string.Join(
".",
prefix,
ViewportToken(normalized.MinX),
ViewportToken(normalized.MinY),
ViewportToken(normalized.MaxX),
ViewportToken(normalized.MaxY));
}
static string ViewportToken(double value)
{
return value
.ToString("0.###", CultureInfo.InvariantCulture)
.Replace('-', 'n')
.Replace('.', 'p');
}
}
private async Task<AetheriaRuntimeDaemonFrameDocument> RequireFrameAsync()