Move daemon checkpoint state onto managed pointers

This commit is contained in:
Meta Vens 2026-06-28 06:24:11 +01:00
commit d2baa6c4b4
4 changed files with 63 additions and 27 deletions

View file

@ -937,15 +937,16 @@ static async Task PublishRuntimeSessionAsync(
static async Task EnsureWorldDocumentAsync(AetheriaStateNode node)
{
var now = DateTimeOffset.UtcNow.ToString("O");
var existing = await node.GetWorldAsync().ConfigureAwait(false);
var world = node.World();
var existing = await world.ReadAsync().ConfigureAwait(false);
if (existing != null)
{
existing.UpdatedAtUtc = now;
await node.PutWorldAsync(existing).ConfigureAwait(false);
await world.ReplaceAsync(existing).ConfigureAwait(false);
return;
}
await node.PutWorldAsync(new AetheriaWorldState
await world.ReplaceAsync(new AetheriaWorldState
{
Name = "Aetheria",
WorldId = "aetheria",
@ -957,11 +958,12 @@ static async Task EnsureWorldDocumentAsync(AetheriaStateNode node)
static async Task EnsureTradeValuePolicyAsync(AetheriaStateNode node, string now)
{
var existing = await node.GetTradeValuePolicyAsync().ConfigureAwait(false);
var tradeValuePolicy = node.TradeValuePolicy();
var existing = await tradeValuePolicy.ReadAsync().ConfigureAwait(false);
if (existing != null)
return;
await node.PutTradeValuePolicyAsync(
await tradeValuePolicy.ReplaceAsync(
AetheriaRuntimeStateMapper.ToTradeValuePolicy(
AetheriaRuntimeTradeValueSettings.Default,
now)).ConfigureAwait(false);
@ -993,7 +995,7 @@ static async Task EnsurePlayableRunDocumentsAsync(AetheriaStateNode node, string
.ReplaceAsync(settings)
.ConfigureAwait(false);
await node.PutRunStateAsync(runKey, new AetheriaRunState
await node.RunState(runKey).ReplaceAsync(new AetheriaRunState
{
RunId = runId,
EntranceZoneIndex = 0,
@ -1006,7 +1008,7 @@ static async Task EnsurePlayableRunDocumentsAsync(AetheriaStateNode node, string
UpdatedAtUtc = now
}).ConfigureAwait(false);
await node.PutZoneStateAsync(zoneKey, new AetheriaZoneState
await node.ZoneState(zoneKey).ReplaceAsync(new AetheriaZoneState
{
Name = "Daemon Local Testbed",
Position = Vec2(0, 0),
@ -1084,7 +1086,7 @@ static async Task EnsurePlayableRunDocumentsAsync(AetheriaStateNode node, string
GravityTerrainWaveFrequency = 0.6
}).ConfigureAwait(false);
await node.PutEntitySnapshotAsync(new CultRecordKey(entityKeys[0]), SeedEntity(
await node.EntitySnapshot(new CultRecordKey(entityKeys[0])).ReplaceAsync(SeedEntity(
"Anchor Station",
"station",
Vec3(-220, 0, -90),
@ -1103,7 +1105,7 @@ static async Task EnsurePlayableRunDocumentsAsync(AetheriaStateNode node, string
Contact(entityKeys[7], 0.6, false)
],
["repair-parts", "reactor-fuel", "drone-core"])).ConfigureAwait(false);
await node.PutEntitySnapshotAsync(new CultRecordKey(entityKeys[1]), SeedEntity(
await node.EntitySnapshot(new CultRecordKey(entityKeys[1])).ReplaceAsync(SeedEntity(
"Vanguard One",
"ship",
Vec3(-60, 0, -40),
@ -1120,7 +1122,7 @@ static async Task EnsurePlayableRunDocumentsAsync(AetheriaStateNode node, string
Contact(entityKeys[5], 0.85, true)
],
["coilgun-ammo", "field-rations"])).ConfigureAwait(false);
await node.PutEntitySnapshotAsync(new CultRecordKey(entityKeys[2]), SeedEntity(
await node.EntitySnapshot(new CultRecordKey(entityKeys[2])).ReplaceAsync(SeedEntity(
"Wing Two",
"ship",
Vec3(180, 0, 120),
@ -1137,7 +1139,7 @@ static async Task EnsurePlayableRunDocumentsAsync(AetheriaStateNode node, string
Contact(entityKeys[6], 0.6, true)
],
["sensor-buoy", "shield-cell"])).ConfigureAwait(false);
await node.PutEntitySnapshotAsync(new CultRecordKey(entityKeys[3]), SeedEntity(
await node.EntitySnapshot(new CultRecordKey(entityKeys[3])).ReplaceAsync(SeedEntity(
"Torch Three",
"ship",
Vec3(-160, 0, 210),
@ -1153,7 +1155,7 @@ static async Task EnsurePlayableRunDocumentsAsync(AetheriaStateNode node, string
Contact(entityKeys[5], 0.9, true)
],
["micro-missile", "coolant-pack"])).ConfigureAwait(false);
await node.PutEntitySnapshotAsync(new CultRecordKey(entityKeys[4]), SeedEntity(
await node.EntitySnapshot(new CultRecordKey(entityKeys[4])).ReplaceAsync(SeedEntity(
"Ash Raider",
"ship",
Vec3(420, 0, 180),
@ -1169,7 +1171,7 @@ static async Task EnsurePlayableRunDocumentsAsync(AetheriaStateNode node, string
Contact(entityKeys[2], 0.5, true)
],
["scrap-metal", "stolen-capacitor"])).ConfigureAwait(false);
await node.PutEntitySnapshotAsync(new CultRecordKey(entityKeys[5]), SeedEntity(
await node.EntitySnapshot(new CultRecordKey(entityKeys[5])).ReplaceAsync(SeedEntity(
"Cinder Knife",
"ship",
Vec3(560, 0, -110),
@ -1184,7 +1186,7 @@ static async Task EnsurePlayableRunDocumentsAsync(AetheriaStateNode node, string
Contact(entityKeys[3], 1, true)
],
["volatile-fuel"])).ConfigureAwait(false);
await node.PutEntitySnapshotAsync(new CultRecordKey(entityKeys[6]), SeedEntity(
await node.EntitySnapshot(new CultRecordKey(entityKeys[6])).ReplaceAsync(SeedEntity(
"Blackwake",
"ship",
Vec3(680, 0, 260),
@ -1198,7 +1200,7 @@ static async Task EnsurePlayableRunDocumentsAsync(AetheriaStateNode node, string
Contact(entityKeys[2], 0.8, true)
],
["ore-cache", "burned-relay-core"])).ConfigureAwait(false);
await node.PutEntitySnapshotAsync(new CultRecordKey(entityKeys[7]), SeedEntity(
await node.EntitySnapshot(new CultRecordKey(entityKeys[7])).ReplaceAsync(SeedEntity(
"Derelict Relay",
"station",
Vec3(-260, 0, 260),
@ -1378,7 +1380,7 @@ static async Task<AetheriaRuntimeRunCheckpointCommit?> ReadRuntimeRunCheckpointA
if (string.IsNullOrWhiteSpace(settings?.ActiveRunKey))
return null;
var run = await node.GetRunStateAsync(new CultRecordKey(settings.ActiveRunKey)).ConfigureAwait(false);
var run = await node.RunState(new CultRecordKey(settings.ActiveRunKey)).ReadAsync().ConfigureAwait(false);
if (run == null)
return null;
@ -1386,7 +1388,7 @@ static async Task<AetheriaRuntimeRunCheckpointCommit?> ReadRuntimeRunCheckpointA
var zoneKeys = run.ZoneKeys ?? Array.Empty<string>();
for (var zoneIndex = 0; zoneIndex < zoneKeys.Length; zoneIndex++)
{
var zone = await node.GetZoneStateAsync(new CultRecordKey(zoneKeys[zoneIndex])).ConfigureAwait(false);
var zone = await node.ZoneState(new CultRecordKey(zoneKeys[zoneIndex])).ReadAsync().ConfigureAwait(false);
if (zone == null)
continue;
@ -1429,7 +1431,7 @@ static async Task<AetheriaRuntimeZoneSnapshotCommit> ToRuntimeZoneAsync(
var entities = new List<AetheriaRuntimeEntitySnapshotCommit>();
for (var entityIndex = 0; entityIndex < entityKeys.Length; entityIndex++)
{
var entity = await node.GetEntitySnapshotAsync(new CultRecordKey(entityKeys[entityIndex])).ConfigureAwait(false);
var entity = await node.EntitySnapshot(new CultRecordKey(entityKeys[entityIndex])).ReadAsync().ConfigureAwait(false);
if (entity != null)
entities.Add(ToRuntimeEntity(entity, entityIndex, entityIndices));
}

View file

@ -111,7 +111,7 @@ var corporations = node.Cache.GetAll<AetheriaCorporation>().ToArray();
var nameFiles = node.Cache.GetAll<AetheriaNameFile>().ToArray();
var catalog = await node.RuntimeCatalog().LatestAsync().ConfigureAwait(false);
var surface = AetheriaCatalogSurfaceProjector.Build(catalog, DateTimeOffset.UtcNow.ToString("O"));
var tradeValuePolicy = await node.GetTradeValuePolicyAsync()
var tradeValuePolicy = await node.TradeValuePolicy().ReadAsync()
?? throw new InvalidOperationException("Missing authored trade value policy document.");
await RequireTradeValuePolicyEveCommandPersistsAsync();
var runtimeCatalog = catalog;
@ -9945,7 +9945,7 @@ static void RequireTypedEveCommandBodies(string root)
"command.TradeValuePolicy.Value",
"command.TradeValuePolicy.TierIndex",
"ExecuteTradeValuePolicyCommandAsync(",
"PutTradeValuePolicyAsync(",
"TradeValuePolicy()",
"SubmitInputSettingsCommandAsync(",
"SubmitLoadoutTemplateCommandAsync(",
"SubmitKnownSurfaceCommandAsync(",
@ -12826,7 +12826,9 @@ static async Task RequireTradeValuePolicyEveCommandPersistsAsync()
var policy = AetheriaRuntimeStateMapper.ToTradeValuePolicy(
AetheriaRuntimeTradeValueSettings.Default,
DateTimeOffset.UtcNow.ToString("O"));
await commandNode.PutTradeValuePolicyAsync(policy).ConfigureAwait(false);
await commandNode.TradeValuePolicy()
.ReplaceAsync(policy)
.ConfigureAwait(false);
var originalMinimum = policy.QualityPriceModifier?.Minimum ?? 0;
var editedMinimum = originalMinimum + 0.125;
@ -12847,7 +12849,7 @@ static async Task RequireTradeValuePolicyEveCommandPersistsAsync()
"Trade value policy Eve command was not accepted by the typed command bridge.");
}
var editedPolicy = await commandNode.GetTradeValuePolicyAsync().ConfigureAwait(false)
var editedPolicy = await commandNode.TradeValuePolicy().ReadAsync().ConfigureAwait(false)
?? throw new InvalidOperationException("Trade value policy disappeared after typed Eve command.");
var actualMinimum = editedPolicy.QualityPriceModifier?.Minimum ?? 0;
if (Math.Abs(actualMinimum - editedMinimum) > 0.000001)

View file

@ -298,16 +298,17 @@ public static class AetheriaEveCommandBridge
var commit = command.LoadoutTemplate ?? throw new InvalidOperationException(
$"Loadout template command '{command.CommandId}' is missing its typed payload.");
var loadout = AetheriaRuntimeStateMapper.ToLoadoutTemplate(commit, command.IssuedAtUtc);
await node.PutLoadoutTemplateAsync(
AetheriaRuntimeStateMapper.LoadoutKey(loadout.Name),
loadout).ConfigureAwait(false);
await node.LoadoutTemplate(AetheriaRuntimeStateMapper.LoadoutKey(loadout.Name))
.ReplaceAsync(loadout)
.ConfigureAwait(false);
}
private static async Task ExecuteTradeValuePolicyCommandAsync(
AetheriaStateNode node,
AetheriaRuntimeEveCommandDocument command)
{
var policy = await node.GetTradeValuePolicyAsync().ConfigureAwait(false) ??
var tradeValuePolicy = node.TradeValuePolicy();
var policy = await tradeValuePolicy.ReadAsync().ConfigureAwait(false) ??
AetheriaRuntimeStateMapper.ToTradeValuePolicy(
AetheriaRuntimeTradeValueSettings.Default,
command.IssuedAtUtc);
@ -343,7 +344,7 @@ public static class AetheriaEveCommandBridge
if (persistPolicy)
{
policy.UpdatedAtUtc = command.IssuedAtUtc;
await node.PutTradeValuePolicyAsync(policy).ConfigureAwait(false);
await tradeValuePolicy.ReplaceAsync(policy).ConfigureAwait(false);
}
}

View file

@ -225,6 +225,37 @@ public sealed class AetheriaStateNode : IAsyncDisposable, IDisposable
return MutableDocumentPointer<AetheriaEveCommandAcceptanceStatus>(EveCommandAcceptanceStatusKey);
}
public CultMeshMutableStatePointer<AetheriaWorldState> World()
{
return MutableDocumentPointer<AetheriaWorldState>(new CultRecordKey("global:aetheria.world_state.v1"));
}
public CultMeshMutableStatePointer<AetheriaTradeValuePolicy> TradeValuePolicy()
{
return MutableDocumentPointer<AetheriaTradeValuePolicy>(
new CultRecordKey(AetheriaTradeValuePolicy.RecordKey));
}
public CultMeshMutableStatePointer<AetheriaLoadoutTemplate> LoadoutTemplate(CultRecordKey key)
{
return MutableDocumentPointer<AetheriaLoadoutTemplate>(key);
}
public CultMeshMutableStatePointer<AetheriaRunState> RunState(CultRecordKey key)
{
return MutableDocumentPointer<AetheriaRunState>(key);
}
public CultMeshMutableStatePointer<AetheriaZoneState> ZoneState(CultRecordKey key)
{
return MutableDocumentPointer<AetheriaZoneState>(key);
}
public CultMeshMutableStatePointer<AetheriaEntitySnapshot> EntitySnapshot(CultRecordKey key)
{
return MutableDocumentPointer<AetheriaEntitySnapshot>(key);
}
public Task<CultRecordHandle<AetheriaWorldState>> PutWorldAsync(AetheriaWorldState world)
{
return Database.PutAsync(new CultRecordKey("global:aetheria.world_state.v1"), world);