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:
Erik 2026-07-25 19:39:24 +02:00
parent ecc4816c5a
commit 7593078774
37 changed files with 884 additions and 355 deletions

View file

@ -23,6 +23,7 @@ using AcDream.Core.Social;
using AcDream.Core.Spells;
using AcDream.Core.World;
using AcDream.Content;
using AcDream.Runtime.Session;
using AcDream.UI.Abstractions.Panels.Chat;
using AcDream.UI.Abstractions.Panels.Vitals;
using DatReaderWriter;
@ -101,6 +102,7 @@ internal sealed class LiveSessionRuntimeFactory
private readonly LiveSessionUiRuntime _ui;
private readonly LiveSessionInteractionRuntime _interaction;
private readonly LiveSessionWorldRuntime _world;
private readonly LiveSessionCommandSurface _commands;
private readonly Action<string> _log;
public LiveSessionRuntimeFactory(
@ -109,6 +111,7 @@ internal sealed class LiveSessionRuntimeFactory
LiveSessionUiRuntime ui,
LiveSessionInteractionRuntime interaction,
LiveSessionWorldRuntime world,
LiveSessionCommandSurface commands,
Action<string> log)
{
_player = player ?? throw new ArgumentNullException(nameof(player));
@ -117,18 +120,22 @@ internal sealed class LiveSessionRuntimeFactory
_interaction = interaction
?? throw new ArgumentNullException(nameof(interaction));
_world = world ?? throw new ArgumentNullException(nameof(world));
_commands = commands ?? throw new ArgumentNullException(nameof(commands));
_log = log ?? throw new ArgumentNullException(nameof(log));
}
public LiveSessionHost Create(LiveSessionController controller)
public LiveSessionHost Create(
LiveSessionController controller,
LiveSessionConnectOptions connectOptions)
{
ArgumentNullException.ThrowIfNull(controller);
ArgumentNullException.ThrowIfNull(connectOptions);
return new LiveSessionHost(controller, new LiveSessionHostBindings(
Routing: new(
CreateEventRouter,
session => new LiveSessionCommandRouter(
CreateCommandBindings(session))),
Reset: CreateResetBindings(),
session => _commands.Attach(new LiveSessionCommandRouter(
CreateCommandBindings(session)))),
Reset: LiveSessionResetManifest.Create(CreateResetBindings()).Execute,
Selection: new(
SetPlayerIdentity: id => _player.Identity.ServerGuid = id,
SetVitalsIdentity: id => _ui.Vitals?.SetLocalPlayerGuid(id),
@ -149,7 +156,8 @@ internal sealed class LiveSessionRuntimeFactory
Connected: () =>
_domain.Chat.OnSystemMessage(
"connected — character list received",
chatType: 1)));
chatType: 1)),
connectOptions);
}
private LiveSessionResetBindings CreateResetBindings() => new()