refactor(net): own complete live session lifecycle

This commit is contained in:
Erik 2026-07-21 12:20:55 +02:00
parent 23b43d1859
commit d9ccf8a6b9
5 changed files with 1618 additions and 91 deletions

View file

@ -10,6 +10,29 @@ using AcDream.Core.Net.Packets;
namespace AcDream.Core.Net;
internal interface IWorldSessionTransport : IDisposable
{
void Send(ReadOnlySpan<byte> datagram);
void Send(IPEndPoint remote, ReadOnlySpan<byte> datagram);
byte[]? Receive(TimeSpan timeout, out IPEndPoint? from);
}
internal sealed class NetClientWorldSessionTransport(IPEndPoint remote)
: IWorldSessionTransport
{
private readonly NetClient _client = new(remote);
public void Send(ReadOnlySpan<byte> datagram) => _client.Send(datagram);
public void Send(IPEndPoint endpoint, ReadOnlySpan<byte> datagram) =>
_client.Send(endpoint, datagram);
public byte[]? Receive(TimeSpan timeout, out IPEndPoint? from) =>
_client.Receive(timeout, out from);
public void Dispose() => _client.Dispose();
}
/// <summary>
/// High-level AC client session: owns a <see cref="NetClient"/>, drives
/// the full handshake + character-enter-world flow, and converts the
@ -600,7 +623,7 @@ public sealed class WorldSession : IDisposable
public CharacterList.Parsed? Characters { get; private set; }
private readonly NetClient _net;
private readonly IWorldSessionTransport _net;
private long _lastInboundPacketTicks = Stopwatch.GetTimestamp();
private long _lastPingRequestTicks;
private long _lastPingRoundTripBits = BitConverter.DoubleToInt64Bits(double.NaN);
@ -676,10 +699,34 @@ public sealed class WorldSession : IDisposable
private uint _gameActionSequence;
public WorldSession(IPEndPoint serverLogin)
: this(
serverLogin,
static endpoint => new NetClientWorldSessionTransport(endpoint))
{
}
internal WorldSession(
IPEndPoint serverLogin,
IWorldSessionTransport transport)
: this(serverLogin, _ => transport)
{
}
internal WorldSession(
IPEndPoint serverLogin,
Func<IPEndPoint, IWorldSessionTransport> transportFactory)
{
ArgumentNullException.ThrowIfNull(serverLogin);
ArgumentNullException.ThrowIfNull(transportFactory);
if (serverLogin.Port == ushort.MaxValue)
throw new ArgumentOutOfRangeException(
nameof(serverLogin),
"The login endpoint must leave room for the adjacent connect port.");
_loginEndpoint = serverLogin;
_connectEndpoint = new IPEndPoint(serverLogin.Address, serverLogin.Port + 1);
_net = new NetClient(serverLogin);
_net = transportFactory(serverLogin)
?? throw new InvalidOperationException("The session transport factory returned null.");
// Phase I.6: SetTurbineChatChannels (0x0295) is a GameEvent
// sub-opcode of 0xF7B0, not a top-level opcode. Route it through