fix(net): honor retail graceful logout handshake
Send the active character id, drain until the authoritative server confirmation, then emit retail's zero-sequence connection disconnect with the negotiated receiver iteration. The connected gate now waits for ACE to remove the exact UDP session before reconnecting, eliminating fixed-delay races.
This commit is contained in:
parent
b03371c03d
commit
68578fa5fa
10 changed files with 372 additions and 41 deletions
31
src/AcDream.Core.Net/Packets/TransportDisconnect.cs
Normal file
31
src/AcDream.Core.Net/Packets/TransportDisconnect.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
namespace AcDream.Core.Net.Packets;
|
||||
|
||||
/// <summary>
|
||||
/// Builds retail's connection-level disconnect packet.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <c>ClientNet::LogOffServer</c> at <c>0x00543EF0</c> creates the
|
||||
/// <c>0x8000</c> optional header and sends it through
|
||||
/// <c>SharedNet::SendOptionalHeader</c> at <c>0x00543160</c>. That path
|
||||
/// zero-initializes the packet sequence and copies the receiver's network id
|
||||
/// and iteration into the fixed header. The disconnect is cleartext and has
|
||||
/// no body.
|
||||
/// </remarks>
|
||||
public static class TransportDisconnect
|
||||
{
|
||||
public static byte[] Build(ushort networkId, ushort iteration)
|
||||
{
|
||||
var header = new PacketHeader
|
||||
{
|
||||
Sequence = 0,
|
||||
Flags = PacketHeaderFlags.Disconnect,
|
||||
Id = networkId,
|
||||
Iteration = iteration,
|
||||
};
|
||||
|
||||
return PacketCodec.Encode(
|
||||
header,
|
||||
ReadOnlySpan<byte>.Empty,
|
||||
outboundIsaac: null);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue