diff --git a/src/Prospect.Unreal/Net/UNetConnection.cs b/src/Prospect.Unreal/Net/UNetConnection.cs
index 358070a..36674fc 100644
--- a/src/Prospect.Unreal/Net/UNetConnection.cs
+++ b/src/Prospect.Unreal/Net/UNetConnection.cs
@@ -267,6 +267,8 @@ public abstract class UNetConnection : UPlayer
///
/// Full incoming packet index.
///
+ private bool _bAdoptedInitialSequence;
+
public int InPacketId { get; private set; }
///
@@ -540,6 +542,25 @@ public abstract class UNetConnection : UPlayer
}
var packetSequenceDelta = PacketNotify.GetSequenceDelta(header);
+
+ // The Cycle client doesn't derive its initial packet sequences from the handshake
+ // cookie the way stock UE does, so our cookie-based InitSequence disagrees with it
+ // and every packet looks out-of-order. On the very first packet, adopt the client's
+ // announced sequences (Seq + AckedSeq) instead of the cookie-derived ones.
+ if (packetSequenceDelta <= 0 && !_bAdoptedInitialSequence && Driver != null && Driver.IsServer())
+ {
+ _bAdoptedInitialSequence = true;
+ var adoptIn = new SequenceNumber((ushort)(header.Seq.Value - 1));
+ var adoptOut = new SequenceNumber((ushort)(header.AckedSeq.Value + 1));
+ PacketNotify.Init(adoptIn, adoptOut);
+ InPacketId = header.Seq.Value - 1;
+ OutPacketId = header.AckedSeq.Value + 1;
+ OutAckPacketId = header.AckedSeq.Value;
+ LastNotifiedPacketId = OutAckPacketId;
+ Logger.Information("[HS-SEQ] Adopted client sequences: inSeq={In} outSeq={Out}", header.Seq.Value, header.AckedSeq.Value + 1);
+ packetSequenceDelta = PacketNotify.GetSequenceDelta(header);
+ }
+
if (packetSequenceDelta > 0)
{
var bPacketOrderCacheActive = !_bFlushingPacketOrderCache && _packetOrderCache != null;