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:
parent
7818494116
commit
a9a822f206
28 changed files with 2707 additions and 345 deletions
|
|
@ -58,7 +58,7 @@ public sealed record LiveSessionStartResult(
|
|||
public interface ILiveSessionLifecycleHost
|
||||
{
|
||||
LiveSessionBinding BindSession(WorldSession session);
|
||||
void ResetSessionState();
|
||||
void ResetSessionState(RuntimeGenerationToken retiringGeneration);
|
||||
void ReportConnecting(string host, int port, string user);
|
||||
void ReportConnected();
|
||||
void ApplySelectedCharacter(LiveSessionCharacterSelection selection);
|
||||
|
|
@ -185,12 +185,14 @@ public sealed class LiveSessionController
|
|||
{
|
||||
private sealed class SessionScope(
|
||||
WorldSession session,
|
||||
ILiveSessionLifecycleHost host)
|
||||
ILiveSessionLifecycleHost host,
|
||||
RuntimeGenerationToken generation)
|
||||
{
|
||||
private int _teardownStage;
|
||||
|
||||
public WorldSession Session { get; } = session;
|
||||
public ILiveSessionLifecycleHost Host { get; } = host;
|
||||
public RuntimeGenerationToken Generation { get; } = generation;
|
||||
public LiveSessionBinding? Binding { get; set; }
|
||||
public bool HostAttached { get; set; }
|
||||
public RuntimeTeardownStage CompletedStages { get; private set; }
|
||||
|
|
@ -226,7 +228,7 @@ public sealed class LiveSessionController
|
|||
}
|
||||
if (_teardownStage == 3)
|
||||
{
|
||||
Host.ResetSessionState();
|
||||
Host.ResetSessionState(Generation);
|
||||
CompletedStages |= RuntimeTeardownStage.HostReset;
|
||||
_teardownStage = 4;
|
||||
}
|
||||
|
|
@ -247,11 +249,15 @@ public sealed class LiveSessionController
|
|||
LiveSessionConnectOptions? Options = null,
|
||||
ILiveSessionLifecycleHost? Host = null);
|
||||
|
||||
private sealed record PendingHostReset(
|
||||
ILiveSessionLifecycleHost Host,
|
||||
RuntimeGenerationToken Generation);
|
||||
|
||||
private readonly object _gate = new();
|
||||
private readonly ILiveSessionOperations _operations;
|
||||
private SessionScope? _scope;
|
||||
private SessionScope? _retiredScope;
|
||||
private ILiveSessionLifecycleHost? _pendingInitialResetHost;
|
||||
private PendingHostReset? _pendingInitialReset;
|
||||
private PendingOperation? _pendingOperation;
|
||||
private int _operationDepth;
|
||||
private bool _inWorld;
|
||||
|
|
@ -306,7 +312,7 @@ public sealed class LiveSessionController
|
|||
_inWorld,
|
||||
_scope is not null,
|
||||
_retiredScope is not null,
|
||||
_pendingInitialResetHost is not null,
|
||||
_pendingInitialReset is not null,
|
||||
_pendingOperation is not null,
|
||||
_operationDepth,
|
||||
_generation,
|
||||
|
|
@ -460,7 +466,9 @@ public sealed class LiveSessionController
|
|||
ILiveSessionLifecycleHost host)
|
||||
{
|
||||
ILiveSessionLifecycleHost? oldHost =
|
||||
_scope?.Host ?? _retiredScope?.Host ?? _pendingInitialResetHost;
|
||||
_scope?.Host
|
||||
?? _retiredScope?.Host
|
||||
?? _pendingInitialReset?.Host;
|
||||
ulong generationBeforeStop = _generation;
|
||||
try
|
||||
{
|
||||
|
|
@ -480,6 +488,7 @@ public sealed class LiveSessionController
|
|||
ILiveSessionLifecycleHost host,
|
||||
bool resetHost)
|
||||
{
|
||||
RuntimeGenerationToken resetGeneration = new(_generation);
|
||||
ulong generation = ++_generation;
|
||||
try
|
||||
{
|
||||
|
|
@ -487,7 +496,12 @@ public sealed class LiveSessionController
|
|||
if (_generation != generation)
|
||||
return new LiveSessionStartResult(LiveSessionStartStatus.Deferred);
|
||||
if (resetHost)
|
||||
ResetHostBeforeStart(host, generation);
|
||||
{
|
||||
ResetHostBeforeStart(
|
||||
host,
|
||||
generation,
|
||||
resetGeneration);
|
||||
}
|
||||
if (_generation != generation)
|
||||
return new LiveSessionStartResult(LiveSessionStartStatus.Deferred);
|
||||
}
|
||||
|
|
@ -509,7 +523,10 @@ public sealed class LiveSessionController
|
|||
return new LiveSessionStartResult(LiveSessionStartStatus.Deferred);
|
||||
Console.WriteLine($"live: connecting to {endpoint} as {options.User}");
|
||||
WorldSession session = _operations.CreateSession(endpoint);
|
||||
scope = new SessionScope(session, host);
|
||||
scope = new SessionScope(
|
||||
session,
|
||||
host,
|
||||
new RuntimeGenerationToken(generation));
|
||||
_scope = scope;
|
||||
_inWorld = false;
|
||||
if (!IsCurrent(scope, generation))
|
||||
|
|
@ -614,7 +631,7 @@ public sealed class LiveSessionController
|
|||
}
|
||||
DrainRetiredScope();
|
||||
DrainPendingInitialReset();
|
||||
if (_retiredScope is null && _pendingInitialResetHost is null)
|
||||
if (_retiredScope is null && _pendingInitialReset is null)
|
||||
_lastTeardownStages = RuntimeTeardownStage.Complete;
|
||||
}
|
||||
|
||||
|
|
@ -632,35 +649,38 @@ public sealed class LiveSessionController
|
|||
|
||||
private void ResetHostBeforeStart(
|
||||
ILiveSessionLifecycleHost host,
|
||||
ulong generation)
|
||||
ulong generation,
|
||||
RuntimeGenerationToken resetGeneration)
|
||||
{
|
||||
bool requestedHostAlreadyReset = false;
|
||||
if (_pendingInitialResetHost is { } pending)
|
||||
if (_pendingInitialReset is { } pending)
|
||||
{
|
||||
pending.ResetSessionState();
|
||||
_pendingInitialResetHost = null;
|
||||
requestedHostAlreadyReset = ReferenceEquals(pending, host);
|
||||
pending.Host.ResetSessionState(pending.Generation);
|
||||
_pendingInitialReset = null;
|
||||
requestedHostAlreadyReset =
|
||||
ReferenceEquals(pending.Host, host);
|
||||
}
|
||||
if (requestedHostAlreadyReset || _generation != generation)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
host.ResetSessionState();
|
||||
host.ResetSessionState(resetGeneration);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_pendingInitialResetHost = host;
|
||||
_pendingInitialReset =
|
||||
new PendingHostReset(host, resetGeneration);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrainPendingInitialReset()
|
||||
{
|
||||
if (_pendingInitialResetHost is not { } host)
|
||||
if (_pendingInitialReset is not { } pending)
|
||||
return;
|
||||
host.ResetSessionState();
|
||||
_pendingInitialResetHost = null;
|
||||
pending.Host.ResetSessionState(pending.Generation);
|
||||
_pendingInitialReset = null;
|
||||
}
|
||||
|
||||
private void Schedule(PendingOperation operation)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue