Serve committed Eve state before restart tick

This commit is contained in:
Meta Vens 2026-07-20 02:07:48 +01:00
commit 02c004897c
4 changed files with 104 additions and 25 deletions

View file

@ -116,36 +116,63 @@ var nextApiPublicationUtc = DateTimeOffset.UtcNow;
var ingressState = new AetheriaDaemonIngressState();
var hotState = new AetheriaHotEntityPublicationState();
var reactiveSurfaceState = new AetheriaReactiveSurfacePublicationState();
var firstTick = await TickAsync(node, options, unityBundles, worldPhysics, latestFrame, ingressState, buildPublications: false).ConfigureAwait(false);
TraceStartup("first-tick");
var publishRestoredFrame = !options.Once &&
latestFrame?.Run != null &&
HasPlayableRun(latestFrame.Run) &&
HasPersistedClientBootstrap(node, latestFrame);
AetheriaRuntimeDaemonTickResult? initialPublication = null;
AetheriaPreparedPublication? initialPrepared = null;
Task publicationTask;
if (publishRestoredFrame)
{
await RefreshControlPlaneInputsAsync(node, ingressState).ConfigureAwait(false);
TraceStartup("restored-client-state");
}
else
{
var firstTick = await TickAsync(
node, options, unityBundles, worldPhysics, latestFrame, ingressState, buildPublications: false).ConfigureAwait(false);
TraceStartup("first-tick");
latestFrame = firstTick.Frame;
initialPrepared = PreparePublication(
node.StatePath,
options,
firstTick,
ingressState,
physicsPersistence);
initialPublication = initialPrepared.Publication;
}
ThrowIfClientHostFaulted(cultMeshClientHost);
latestFrame = firstTick.Frame;
var firstPublication = PreparePublication(
node.StatePath,
options,
firstTick,
ingressState,
physicsPersistence);
await PublishClientGameplayDocumentsAsync(
node,
options,
unityBundles,
firstPublication.Publication,
firstPublication.Catalog,
reactiveSurfaceState,
publishTopology: true).ConfigureAwait(false);
if (!publishRestoredFrame)
{
await PublishClientGameplayDocumentsAsync(
node,
options,
unityBundles,
initialPublication!,
ingressState.Catalog ?? throw new InvalidOperationException("Aetheria runtime catalog was not initialized."),
reactiveSurfaceState,
publishTopology: true).ConfigureAwait(false);
}
TraceStartup("client-bootstrap");
await PublishHotEntityStateAsync(
node, soaPublisher, hotState, firstTick.Frame, ingressState.Catalog, cultMeshClientHost).ConfigureAwait(false);
node, soaPublisher, hotState, latestFrame!, ingressState.Catalog, cultMeshClientHost).ConfigureAwait(false);
TraceStartup("hot-entity-state");
Task publicationTask = Task.Run(() => PersistPreparedDocumentsAsync(
node,
options,
physicsPersistence,
firstPublication,
initialTopology: true));
if (publishRestoredFrame)
{
publicationTask = Task.CompletedTask;
}
else
{
publicationTask = Task.Run(() => PersistPreparedDocumentsAsync(
node,
options,
physicsPersistence,
initialPrepared!,
initialTopology: true));
}
nextApiPublicationUtc = DateTimeOffset.UtcNow.Add(options.ApiPublicationInterval);
Console.WriteLine($"Aetheria Verse daemon published frame {firstTick.Frame.FrameId}.");
Console.WriteLine($"Aetheria Verse daemon published frame {latestFrame!.FrameId}.");
Console.WriteLine($"Aetheria daemon playable-world-ready in {startup.Elapsed.TotalMilliseconds:0.###}ms.");
if (options.Once)
@ -466,6 +493,22 @@ static AetheriaPreparedPublication PreparePublication(
ingressState.Catalog ?? throw new InvalidOperationException("Aetheria runtime catalog was not initialized."));
}
static bool HasPersistedClientBootstrap(
AetheriaStateNode node,
AetheriaRuntimeDaemonFrameDocument frame)
{
var input = node.Cache.Get<EveInputCapabilityDocument>(AetheriaRuntimeVerseRecordKeys.PilotInputCapability);
var game = node.Cache.Get<EveSurfaceDocument>(AetheriaRuntimeVerseRecordKeys.DaemonGameSurface);
var reactive = node.Cache.Get<EveSurfaceDocument>(AetheriaRuntimeVerseRecordKeys.DaemonGameReactiveSurface);
var map = node.Cache.Get<EveSurfaceDocument>(AetheriaRuntimeVerseRecordKeys.MapMenuSurface);
return node.Cache.Get<EveProviderAdvertisementDocument>(AetheriaRuntimeVerseRecordKeys.EveProviderAdvertisement) != null &&
node.Cache.Get<EveAssetCatalogDocument>(AetheriaRuntimeVerseRecordKeys.EveAssetCatalog) != null &&
input?.Version == frame.FrameId &&
game?.Version == frame.FrameId &&
reactive?.Version == frame.FrameId &&
map?.Version == frame.FrameId;
}
static async Task PersistPreparedDocumentsAsync(
AetheriaStateNode node,
AetheriaDaemonHostOptions options,

View file

@ -46,6 +46,7 @@ public static class AetheriaDocumentRegistry
typeof(AetheriaRuntimeDaemonProviderAdvertisementDocument),
typeof(AetheriaRuntimeDaemonHealthDocument),
typeof(AetheriaRuntimeDaemonCommandBoundaryDocument),
typeof(AetheriaRuntimeAssetManifestDocument),
typeof(AetheriaRuntimeDaemonFrameDocument),
typeof(AetheriaRuntimeGameViewportDocument),
typeof(AetheriaRuntimeObjectsViewportDocument),

View file

@ -127,6 +127,14 @@ public sealed class AetheriaStateNode : IAsyncDisposable, IDisposable
MainMenuStateKey.ToString(),
EveCommandAcceptanceStatusKey.ToString(),
AetheriaRuntimeVerseRecordKeys.DaemonFrameLatest.ToString(),
AetheriaRuntimeVerseRecordKeys.DaemonProviderAdvertisement.ToString(),
AetheriaRuntimeVerseRecordKeys.EveProviderAdvertisement.ToString(),
AetheriaRuntimeVerseRecordKeys.DaemonAssetManifest.ToString(),
AetheriaRuntimeVerseRecordKeys.EveAssetCatalog.ToString(),
AetheriaRuntimeVerseRecordKeys.PilotInputCapability.ToString(),
AetheriaRuntimeVerseRecordKeys.DaemonGameSurface.ToString(),
AetheriaRuntimeVerseRecordKeys.DaemonGameReactiveSurface.ToString(),
AetheriaRuntimeVerseRecordKeys.MapMenuSurface.ToString(),
AetheriaRuntimeVerseRecordKeys.VerseAuthorityPolicy.ToString(),
AetheriaRuntimeVerseRecordKeys.StarbridgeScenarioLatest.ToString(),
AetheriaRuntimeVerseRecordKeys.StarbridgeSessionLatest.ToString()

View file

@ -115,6 +115,33 @@ Historical journal pages remain durable and cold. Retention/garbage collection
of obsolete generations is separate from restart authority and must not be
smuggled into filtered hydration.
### Restored-frame client readiness
- **Owner:** the latest hard-flushed daemon frame owns restart world truth. Its
frame-versioned Eve input, pilot topology, reactive fragment, and map surface
are durable projections of that same boundary.
- **Inputs:** exact persisted Eve bootstrap record keys plus the latest frame;
the daemon boot filter hydrates those records without loading unrelated menu
surfaces or history.
- **Outputs:** on an ordinary restored daemon run, CultMesh can advertise the
committed Eve surfaces and publish a fresh hot SoA body before frame N+1 is
simulated. New stores and explicit `--once` runs retain the initialization
tick path.
- **Derived state:** provider advertisement and asset catalog are stable
bootstrap projections. Input and Eve surfaces qualify for the fast path only
when every record version exactly equals the durable frame id; missing or
stale records force a normal rebuild.
- **Forbidden writers:** startup must not advance simulation merely to make an
already committed frame presentable. It also must not pair a current frame
with stale client projections or let existence alone prove consistency.
- **Cut line:** restored readiness no longer calls the simulation tick,
`BuildPublications`, or the surface lowerers. Ordinary ticking resumes after
readiness and owns frame N+1.
- **Verification layer:** the restored artifact reached client bootstrap in
under one millisecond and playable readiness in 1.41–1.55 seconds, down from
2.19 seconds after Ymir compaction alone. The explicit `--once` witness still
executed and committed one new frame.
## Materialized Import And Publication Ownership
### Catalog import