refactor(net): cut GameWindow over to session owner

This commit is contained in:
Erik 2026-07-21 12:37:22 +02:00
parent 783ef1d6db
commit 6a5d9e2e6a
5 changed files with 363 additions and 268 deletions

View file

@ -216,7 +216,6 @@ public sealed class LiveSessionController : IDisposable
private SessionScope? _retiredScope;
private ILiveSessionLifecycleHost? _pendingInitialResetHost;
private PendingOperation? _pendingOperation;
private WorldSession? _legacySession;
private int _operationDepth;
private bool _inWorld;
private bool _disposeRequested;
@ -236,12 +235,9 @@ public sealed class LiveSessionController : IDisposable
public WorldSession? CurrentSession
{
get { lock (_gate) return _scope?.Session ?? _legacySession; }
get { lock (_gate) return _scope?.Session; }
}
/// <summary>Temporary Slice-3 compatibility alias; removed at GameWindow cutover.</summary>
public WorldSession? Session => CurrentSession;
public ICommandBus Commands
{
get
@ -319,11 +315,6 @@ public sealed class LiveSessionController : IDisposable
{
if (_disposed)
return;
if (_legacySession is not null && _scope is null)
{
_legacySession.Tick();
return;
}
if (!_inWorld || _scope is null || _operationDepth != 0)
return;
@ -629,11 +620,6 @@ public sealed class LiveSessionController : IDisposable
private void DisposeCore()
{
StopCore();
if (_legacySession is { } legacy)
{
_operations.DisposeSession(legacy);
_legacySession = null;
}
_disposed = true;
}
@ -649,40 +635,4 @@ public sealed class LiveSessionController : IDisposable
throw new ObjectDisposedException(nameof(LiveSessionController));
}
// Temporary compatibility surface for Slice 3 Commit E. The complete
// controller above is exercised independently before GameWindow cuts over.
public WorldSession? CreateAndWire(RuntimeOptions options, Action<WorldSession> wireEvents)
{
ArgumentNullException.ThrowIfNull(options);
ArgumentNullException.ThrowIfNull(wireEvents);
lock (_gate)
{
ThrowIfDisposing();
if (!options.LiveMode)
return null;
if (string.IsNullOrEmpty(options.LiveUser) || string.IsNullOrEmpty(options.LivePass))
{
Console.WriteLine(
"live: ACDREAM_LIVE set but TEST_USER/TEST_PASS missing; skipping");
return null;
}
try
{
IPEndPoint endpoint = _operations.ResolveEndpoint(options.LiveHost, options.LivePort);
Console.WriteLine($"live: connecting to {endpoint} as {options.LiveUser}");
_legacySession = _operations.CreateSession(endpoint);
wireEvents(_legacySession);
return _legacySession;
}
catch (Exception error)
{
Console.WriteLine($"live: session setup failed: {error.Message}");
if (_legacySession is not null)
_operations.DisposeSession(_legacySession);
_legacySession = null;
return null;
}
}
}
}

View file

@ -0,0 +1,65 @@
using AcDream.Core.Net;
namespace AcDream.App.Net;
internal sealed record LiveSessionLifecycleBindings(
Func<WorldSession, LiveSessionBinding> Bind,
Action Reset,
Action<string, int, string> Connecting,
Action Connected,
Action<LiveSessionCharacterSelection> Selected,
Action<LiveSessionCharacterSelection> Entered);
/// <summary>
/// Focused adapter between the session lifetime owner and App composition.
/// It tracks only the exact borrowed session attached to the host; domain and
/// presentation state remain behind the supplied lifecycle callbacks.
/// </summary>
internal sealed class LiveSessionLifecycleHost : ILiveSessionLifecycleHost
{
private readonly LiveSessionLifecycleBindings _bindings;
private WorldSession? _boundSession;
public LiveSessionLifecycleHost(LiveSessionLifecycleBindings bindings)
{
_bindings = bindings ?? throw new ArgumentNullException(nameof(bindings));
ArgumentNullException.ThrowIfNull(bindings.Bind);
ArgumentNullException.ThrowIfNull(bindings.Reset);
ArgumentNullException.ThrowIfNull(bindings.Connecting);
ArgumentNullException.ThrowIfNull(bindings.Connected);
ArgumentNullException.ThrowIfNull(bindings.Selected);
ArgumentNullException.ThrowIfNull(bindings.Entered);
}
public LiveSessionBinding BindSession(WorldSession session)
{
ArgumentNullException.ThrowIfNull(session);
if (_boundSession is not null)
throw new InvalidOperationException("A live session is already attached to this host.");
LiveSessionBinding binding = _bindings.Bind(session);
_boundSession = session;
return binding;
}
public void ResetSessionState() => _bindings.Reset();
public void ReportConnecting(string host, int port, string user) =>
_bindings.Connecting(host, port, user);
public void ReportConnected() => _bindings.Connected();
public void ApplySelectedCharacter(LiveSessionCharacterSelection selection) =>
_bindings.Selected(selection);
public void ApplyEnteredWorld(LiveSessionCharacterSelection selection) =>
_bindings.Entered(selection);
public void DetachSession(WorldSession session)
{
if (!ReferenceEquals(_boundSession, session))
throw new InvalidOperationException(
"The live-session controller attempted to detach a session that is not bound.");
_boundSession = null;
}
}