refactor(runtime): move session lifetime and ordered transport
Move the canonical WorldSession generation, connect/enter/tick/stop transaction, inbound subscription owner, and retryable teardown acknowledgements into AcDream.Runtime. Keep App as a borrowing graphical host with a single inertable command projection and no mirrored session state. Validated by 79 Runtime tests, 3,776 App tests with three existing skips, the Release solution build, and 8,428 complete Release tests with five existing skips. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
ecc4816c5a
commit
7593078774
37 changed files with 884 additions and 355 deletions
|
|
@ -4,6 +4,7 @@ using AcDream.App.Interaction;
|
|||
using AcDream.App.Net;
|
||||
using AcDream.Core.Selection;
|
||||
using AcDream.Runtime;
|
||||
using AcDream.Runtime.Session;
|
||||
using AcDream.UI.Abstractions;
|
||||
using AcDream.UI.Abstractions.Input;
|
||||
|
||||
|
|
@ -21,9 +22,9 @@ internal sealed class CurrentGameRuntimeCommandAdapter
|
|||
IRuntimeChatCommands,
|
||||
IRuntimePortalCommands
|
||||
{
|
||||
private readonly RuntimeOptions _options;
|
||||
private readonly LiveSessionController _session;
|
||||
private readonly LiveSessionHost _sessionHost;
|
||||
private readonly ICommandBus _commands;
|
||||
private readonly CurrentGameRuntimeViewAdapter _view;
|
||||
private readonly SelectionState _selectionState;
|
||||
private readonly SelectionInteractionController _selection;
|
||||
|
|
@ -32,9 +33,9 @@ internal sealed class CurrentGameRuntimeCommandAdapter
|
|||
private readonly ICurrentGameRuntimeEventSink _events;
|
||||
|
||||
public CurrentGameRuntimeCommandAdapter(
|
||||
RuntimeOptions options,
|
||||
LiveSessionController session,
|
||||
LiveSessionHost sessionHost,
|
||||
ICommandBus commands,
|
||||
CurrentGameRuntimeViewAdapter view,
|
||||
SelectionState selectionState,
|
||||
SelectionInteractionController selection,
|
||||
|
|
@ -42,9 +43,9 @@ internal sealed class CurrentGameRuntimeCommandAdapter
|
|||
ILiveCombatModeCommand combat,
|
||||
ICurrentGameRuntimeEventSink events)
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
_session = session ?? throw new ArgumentNullException(nameof(session));
|
||||
_sessionHost = sessionHost ?? throw new ArgumentNullException(nameof(sessionHost));
|
||||
_commands = commands ?? throw new ArgumentNullException(nameof(commands));
|
||||
_view = view ?? throw new ArgumentNullException(nameof(view));
|
||||
_selectionState = selectionState
|
||||
?? throw new ArgumentNullException(nameof(selectionState));
|
||||
|
|
@ -63,8 +64,8 @@ internal sealed class CurrentGameRuntimeCommandAdapter
|
|||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return RejectedStart(gate);
|
||||
|
||||
LiveSessionStartResult direct = _sessionHost.Start(_options);
|
||||
RuntimeSessionStartResult result = Convert(direct);
|
||||
RuntimeSessionStartResult result =
|
||||
_sessionHost.Start(expectedGeneration);
|
||||
_events.EmitCommand(
|
||||
RuntimeCommandDomain.Session,
|
||||
operation: 0,
|
||||
|
|
@ -83,8 +84,8 @@ internal sealed class CurrentGameRuntimeCommandAdapter
|
|||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return RejectedStart(gate);
|
||||
|
||||
LiveSessionStartResult direct = _sessionHost.Reconnect(_options);
|
||||
RuntimeSessionStartResult result = Convert(direct);
|
||||
RuntimeSessionStartResult result =
|
||||
_sessionHost.Reconnect(expectedGeneration);
|
||||
_events.EmitCommand(
|
||||
RuntimeCommandDomain.Session,
|
||||
operation: 1,
|
||||
|
|
@ -109,43 +110,15 @@ internal sealed class CurrentGameRuntimeCommandAdapter
|
|||
RuntimeTeardownStage.None);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_session.Stop();
|
||||
RuntimeGenerationToken current = _view.Generation;
|
||||
RuntimeTeardownStage completed =
|
||||
_session.CurrentSession is null && !_session.IsInWorld
|
||||
? RuntimeTeardownStage.Complete
|
||||
: RuntimeTeardownStage.CommandsInert
|
||||
| RuntimeTeardownStage.InboundDetached;
|
||||
var acknowledgement = new RuntimeTeardownAcknowledgement(
|
||||
expectedGeneration,
|
||||
current,
|
||||
RuntimeCommandStatus.Accepted,
|
||||
completed);
|
||||
_events.EmitCommand(
|
||||
RuntimeCommandDomain.Session,
|
||||
operation: 2,
|
||||
RuntimeCommandStatus.Accepted);
|
||||
_events.EmitLifecycle(previous);
|
||||
return acknowledgement;
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
var acknowledgement = new RuntimeTeardownAcknowledgement(
|
||||
expectedGeneration,
|
||||
_view.Generation,
|
||||
RuntimeCommandStatus.Rejected,
|
||||
RuntimeTeardownStage.None,
|
||||
error);
|
||||
_events.EmitCommand(
|
||||
RuntimeCommandDomain.Session,
|
||||
operation: 2,
|
||||
RuntimeCommandStatus.Rejected,
|
||||
text: error.GetType().Name);
|
||||
_events.EmitLifecycle(previous);
|
||||
return acknowledgement;
|
||||
}
|
||||
RuntimeTeardownAcknowledgement acknowledgement =
|
||||
_sessionHost.Stop(expectedGeneration);
|
||||
_events.EmitCommand(
|
||||
RuntimeCommandDomain.Session,
|
||||
operation: 2,
|
||||
acknowledgement.Status,
|
||||
text: acknowledgement.Error?.GetType().Name ?? string.Empty);
|
||||
_events.EmitLifecycle(previous);
|
||||
return acknowledgement;
|
||||
}
|
||||
|
||||
public RuntimeCommandResult Execute(
|
||||
|
|
@ -252,7 +225,7 @@ internal sealed class CurrentGameRuntimeCommandAdapter
|
|||
return Result(RuntimeCommandStatus.Unsupported);
|
||||
}
|
||||
|
||||
_session.Commands.Publish(new SendChatCmd(
|
||||
_commands.Publish(new SendChatCmd(
|
||||
channel,
|
||||
command.TargetName,
|
||||
command.Text));
|
||||
|
|
@ -293,7 +266,7 @@ internal sealed class CurrentGameRuntimeCommandAdapter
|
|||
return Result(RuntimeCommandStatus.Unsupported);
|
||||
}
|
||||
|
||||
_session.Commands.Publish(new ExecuteClientCommandCmd(
|
||||
_commands.Publish(new ExecuteClientCommandCmd(
|
||||
commandId.Value,
|
||||
string.Empty));
|
||||
_events.EmitCommand(
|
||||
|
|
@ -328,34 +301,6 @@ internal sealed class CurrentGameRuntimeCommandAdapter
|
|||
: RuntimeSessionStartStatus.Inactive,
|
||||
_view.Generation);
|
||||
|
||||
private RuntimeSessionStartResult Convert(LiveSessionStartResult result)
|
||||
{
|
||||
RuntimeSessionStartStatus status = result.Status switch
|
||||
{
|
||||
LiveSessionStartStatus.Disabled => RuntimeSessionStartStatus.Disabled,
|
||||
LiveSessionStartStatus.MissingCredentials =>
|
||||
RuntimeSessionStartStatus.MissingCredentials,
|
||||
LiveSessionStartStatus.NoCharacters =>
|
||||
RuntimeSessionStartStatus.NoCharacters,
|
||||
LiveSessionStartStatus.Connected =>
|
||||
RuntimeSessionStartStatus.Connected,
|
||||
LiveSessionStartStatus.Deferred =>
|
||||
RuntimeSessionStartStatus.Deferred,
|
||||
LiveSessionStartStatus.Failed =>
|
||||
RuntimeSessionStartStatus.Failed,
|
||||
_ => throw new ArgumentOutOfRangeException(
|
||||
nameof(result),
|
||||
result.Status,
|
||||
"Unknown live-session start result."),
|
||||
};
|
||||
return new RuntimeSessionStartResult(
|
||||
status,
|
||||
_view.Generation,
|
||||
result.Selection?.CharacterId ?? 0u,
|
||||
result.Selection?.CharacterName ?? string.Empty,
|
||||
result.Error);
|
||||
}
|
||||
|
||||
private static RuntimeCommandStatus ToCommandStatus(
|
||||
RuntimeSessionStartStatus status) =>
|
||||
status switch
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue