refactor(runtime): unify generation reset for direct hosts

Move canonical per-session teardown into one retryable Runtime transaction, reduce App reset to projection acknowledgements, and prove the same GameRuntime graph through deterministic no-window lifecycle, gameplay, portal, fault, reconnect, and isolation gates.\n\nCo-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Erik 2026-07-27 00:43:26 +02:00
parent 7818494116
commit a9a822f206
28 changed files with 2707 additions and 345 deletions

View file

@ -58,6 +58,7 @@ public readonly record struct GameRuntimeOwnershipSnapshot(
RuntimeSimulationOwnershipSnapshot Simulation,
RuntimeWorldEnvironmentOwnershipSnapshot Environment,
RuntimeWorldTransitOwnershipSnapshot Transit,
RuntimeGenerationResetSnapshot GenerationReset,
GameRuntimeEventOwnershipSnapshot Events)
{
public bool IsConverged =>
@ -70,6 +71,7 @@ public readonly record struct GameRuntimeOwnershipSnapshot(
&& PlayerIdentity.IsConverged
&& Simulation.IsConverged
&& Transit.IsSessionIdle
&& GenerationReset.IsConverged
&& Events.IsConverged;
}
@ -241,8 +243,19 @@ public sealed class GameRuntime
context,
faultInjection);
var generationReset = new RuntimeGenerationReset(
transit,
context.Communication,
context.Inventory,
context.Actions,
context.Movement,
context.EntityObjects,
context.Character,
context.PlayerIdentity);
context.EntityObjects.BindEventContext(
() => context.Session.Generation,
() => generationReset.ActiveRetiringGeneration
?? context.Session.Generation,
() => clock.FrameNumber);
context.Events = new GameRuntimeEventHub(
context.EntityObjects,
@ -264,6 +277,7 @@ public sealed class GameRuntime
ActionOwner = context.Actions;
EnvironmentOwner = environment;
TransitOwner = transit;
GenerationReset = generationReset;
_events = context.Events;
construction.Complete();
}
@ -285,6 +299,7 @@ public sealed class GameRuntime
public RuntimeLocalPlayerMovementState MovementOwner { get; }
public RuntimeWorldEnvironmentState EnvironmentOwner { get; }
public RuntimeWorldTransitState TransitOwner { get; }
public RuntimeGenerationReset GenerationReset { get; }
public RuntimeGenerationToken Generation => Session.Generation;
@ -347,6 +362,11 @@ public sealed class GameRuntime
Portal.Snapshot,
Portal.Ownership);
public void ResetGeneration(
RuntimeGenerationToken retiringGeneration,
IRuntimeGenerationResetHost host) =>
GenerationReset.Reset(retiringGeneration, host);
public IDisposable Subscribe(IRuntimeEventObserver observer)
{
lock (_lifetimeGate)
@ -393,6 +413,7 @@ public sealed class GameRuntime
MovementOwner),
EnvironmentOwner.CaptureOwnership(),
TransitOwner.CaptureOwnership(),
GenerationReset.CaptureSnapshot(),
_events.CaptureOwnership());
}
}
@ -534,6 +555,7 @@ public sealed class GameRuntime
StopSession();
return Session.CaptureOwnership().IsConverged;
case 3:
GenerationReset.DrainPending();
TransitOwner.ResetSession();
return TransitOwner.CaptureOwnership().IsSessionIdle;
case 4: