perf(net): borrow inbound packet storage

Decode headers, optional fields, fragments, and single-fragment messages directly over pooled datagrams. Copy only fragment state that crosses a datagram lifetime, preserve synchronous dispatch and ACK ordering, and lock the path to the owned decoder with differential and zero-allocation tests.
This commit is contained in:
Erik 2026-07-25 05:58:55 +02:00
parent 7211bb1bf7
commit e928c5dd02
18 changed files with 1329 additions and 66 deletions

View file

@ -801,8 +801,11 @@ public sealed class WorldSession : IDisposable
_net.Send(PacketCodec.Encode(loginHeader, loginPayload, null));
// Step 2: wait for ConnectRequest
Packet? cr = null;
while (DateTime.UtcNow < deadline && cr is null)
bool connectRequestReceived = false;
BorrowedOptionalHeader connectRequest = default;
ushort connectRequestIteration = 0;
while (DateTime.UtcNow < deadline
&& !connectRequestReceived)
{
PooledInboundDatagram? received =
ReceiveBlocking(deadline - DateTime.UtcNow);
@ -812,14 +815,23 @@ public sealed class WorldSession : IDisposable
PooledInboundDatagram datagram = received.Value;
try
{
var dec = PacketCodec.TryDecode(
datagram.Memory.Span,
BorrowedPacketDecodeResult dec =
PacketCodec.TryDecodeBorrowed(
datagram.Memory,
inboundIsaac: null);
if (dec.IsOk
&& dec.Packet!.Header.HasFlag(
&& dec.Packet.Header.HasFlag(
PacketHeaderFlags.ConnectRequest))
{
cr = dec.Packet;
connectRequest = dec.Packet.Optional with
{
RawBytes = ReadOnlyMemory<byte>.Empty,
RetransmitRequestBytes =
ReadOnlyMemory<byte>.Empty,
};
connectRequestIteration =
dec.Packet.Header.Iteration;
connectRequestReceived = true;
}
}
finally
@ -827,10 +839,15 @@ public sealed class WorldSession : IDisposable
ReturnInboundDatagram(datagram);
}
}
if (cr is null) { Transition(State.Failed); throw new TimeoutException("ConnectRequest not received"); }
if (!connectRequestReceived)
{
Transition(State.Failed);
throw new TimeoutException(
"ConnectRequest not received");
}
// Step 3: seed ISAAC, send ConnectResponse to port+1, with 200ms race delay
var opt = cr.Optional;
BorrowedOptionalHeader opt = connectRequest;
// Phase G.1: server's initial PortalYearTicks (r12 §1.3) lives
// in the ConnectRequest optional section. Publish it to
@ -846,7 +863,7 @@ public sealed class WorldSession : IDisposable
// SharedNet::SendOptionalHeader @ 0x00543160 copies this ReceiverData
// generation into connection-level control packets, including the
// final disconnect. ACE currently emits iteration 1.
_sessionIteration = cr.Header.Iteration;
_sessionIteration = connectRequestIteration;
_transportNegotiated = true;
_clientPacketSequence = 2;
@ -979,7 +996,7 @@ public sealed class WorldSession : IDisposable
{
try
{
ProcessDatagram(datagram.Memory.Span);
ProcessDatagram(datagram.Memory);
}
finally
{
@ -1142,7 +1159,7 @@ public sealed class WorldSession : IDisposable
try
{
ProcessDatagram(
datagram.Memory.Span,
datagram.Memory,
opcodesThisCall);
return true;
}
@ -1153,11 +1170,14 @@ public sealed class WorldSession : IDisposable
}
private void ProcessDatagram(
ReadOnlySpan<byte> bytes,
ReadOnlyMemory<byte> bytes,
List<uint>? opcodesOut = null,
bool dispatchWorldEvents = true)
{
var dec = PacketCodec.TryDecode(bytes, _inboundIsaac);
BorrowedPacketDecodeResult dec =
PacketCodec.TryDecodeBorrowed(
bytes,
_inboundIsaac);
if (!dec.IsOk) return;
// Retail LinkStatusHolder::OnHeartbeat @ 0x004113D0 updates its
@ -1172,7 +1192,7 @@ public sealed class WorldSession : IDisposable
// with "Network Timeout" because it sees no acks coming back —
// which surfaces in other clients' views as the player rendering
// as a stationary purple haze (loading state).
var serverHeader = dec.Packet!.Header;
PacketHeader serverHeader = dec.Packet.Header;
if (serverHeader.Sequence > 0
&& (serverHeader.Flags & PacketHeaderFlags.AckSequence) == 0)
{
@ -1184,7 +1204,7 @@ public sealed class WorldSession : IDisposable
// periodically — no explicit opcode, just the header flag.
if ((serverHeader.Flags & PacketHeaderFlags.TimeSync) != 0)
{
double t = dec.Packet!.Optional.TimeSync;
double t = dec.Packet.Optional.TimeSync;
if (t > 0)
{
LastServerTimeTicks = t;
@ -1192,11 +1212,21 @@ public sealed class WorldSession : IDisposable
}
}
foreach (var frag in dec.Packet!.Fragments)
foreach (BorrowedMessageFragment frag
in dec.Packet.Fragments)
{
var body = _assembler.Ingest(frag, out _);
if (body is null || body.Length < 4) continue;
uint op = BinaryPrimitives.ReadUInt32LittleEndian(body);
if (!_assembler.TryIngest(
frag,
out ReadOnlyMemory<byte> bodyMemory,
out _)
|| bodyMemory.Length < 4)
{
continue;
}
ReadOnlySpan<byte> body = bodyMemory.Span;
uint op = BinaryPrimitives.ReadUInt32LittleEndian(
body);
opcodesOut?.Add(op);
// Retail waits for the server's opcode-only CharacterLogOff echo
@ -1335,7 +1365,9 @@ public sealed class WorldSession : IDisposable
&& !_setStateHexDumped)
{
_setStateHexDumped = true;
var hex = string.Join(" ", body.Take(Math.Min(body.Length, 32))
var hex = string.Join(" ", body
.Slice(0, Math.Min(body.Length, 32))
.ToArray()
.Select(b => b.ToString("X2")));
Console.WriteLine($"[setstate-hex] body.len={body.Length} first-{Math.Min(body.Length, 32)}-bytes: {hex}");
}
@ -1460,7 +1492,8 @@ public sealed class WorldSession : IDisposable
// header (guid + sequence + eventType) and dispatch to the
// registered handler for that sub-opcode. Unregistered
// types get counted for diagnostic overlays.
var env = GameEventEnvelope.TryParse(body);
var env = GameEventEnvelope.TryParseBorrowed(
bodyMemory);
if (env is not null) GameEvents.Dispatch(env.Value);
}
else if (op == 0xEA60u) // AdminEnvirons — server pushes a fog preset or sound cue
@ -1472,7 +1505,7 @@ public sealed class WorldSession : IDisposable
if (body.Length >= 8)
{
uint envType = System.Buffers.Binary.BinaryPrimitives
.ReadUInt32LittleEndian(body.AsSpan(4, 4));
.ReadUInt32LittleEndian(body.Slice(4, 4));
EnvironChanged?.Invoke(envType);
}
}
@ -1502,7 +1535,7 @@ public sealed class WorldSession : IDisposable
if (body.Length >= 6)
{
ushort sequence = System.Buffers.Binary.BinaryPrimitives.ReadUInt16LittleEndian(
body.AsSpan(4, 2));
body.Slice(4, 2));
TeleportStarted?.Invoke(sequence);
}
}
@ -2344,7 +2377,7 @@ public sealed class WorldSession : IDisposable
datagram =>
{
ProcessDatagram(
datagram.Memory.Span,
datagram.Memory,
dispatchWorldEvents: false);
return Volatile.Read(ref _characterLogOffConfirmed) != 0;
},