Started serializing bunches
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
using Prospect.Unreal.Net.Packets.Header.Sequence;
|
||||
using Prospect.Unreal.Serialization;
|
||||
using Serilog;
|
||||
|
||||
namespace Prospect.Unreal.Net.Packets.Header;
|
||||
|
||||
public delegate void HandlePacketNotification(SequenceNumber ackedSequence, bool delivered);
|
||||
|
||||
public class FNetPacketNotify
|
||||
{
|
||||
private static readonly ILogger Logger = Log.ForContext<FNetPacketNotify>();
|
||||
|
||||
public const int SequenceNumberBits = 14;
|
||||
public const int MaxSequenceHistoryLength = 256;
|
||||
|
||||
private readonly Queue<FSentAckData> _ackRecord = new Queue<FSentAckData>();
|
||||
|
||||
private readonly SequenceHistory _inSeqHistory = new SequenceHistory();
|
||||
private SequenceNumber _inSeq;
|
||||
private SequenceNumber _inAckSeq;
|
||||
private SequenceNumber _inAckSeqAck;
|
||||
|
||||
private SequenceNumber _outSeq;
|
||||
private SequenceNumber _outAckSeq;
|
||||
|
||||
public void Init(SequenceNumber initialInSeq, SequenceNumber initialOutSeq)
|
||||
{
|
||||
_inSeqHistory.Reset();
|
||||
_inSeq = initialInSeq;
|
||||
_inAckSeq = initialInSeq;
|
||||
_inAckSeqAck = initialInSeq;
|
||||
_outSeq = initialOutSeq;
|
||||
_outAckSeq = new SequenceNumber((ushort)(initialOutSeq.Value - 1));
|
||||
}
|
||||
|
||||
public bool ReadHeader(ref FNotificationHeader data, FBitReader reader)
|
||||
{
|
||||
var packedHeader = reader.ReadUInt32();
|
||||
|
||||
data.Seq = FPackedHeader.GetSeq(packedHeader);
|
||||
data.AckedSeq = FPackedHeader.GetAckedSeq(packedHeader);
|
||||
data.HistoryWordCount = FPackedHeader.GetHistoryWordCount(packedHeader) + 1;
|
||||
data.History = new SequenceHistory();
|
||||
data.History.Read(reader, data.HistoryWordCount);
|
||||
|
||||
return !reader.IsError();
|
||||
}
|
||||
|
||||
public int GetSequenceDelta(FNotificationHeader notificationData)
|
||||
{
|
||||
if (!notificationData.Seq.Greater(_inSeq))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!notificationData.AckedSeq.GreaterEq(_outAckSeq))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_outSeq.Greater(notificationData.AckedSeq))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return SequenceNumber.Diff(notificationData.Seq, _inSeq);
|
||||
}
|
||||
|
||||
public int Update(FNotificationHeader notificationData, HandlePacketNotification func)
|
||||
{
|
||||
var inSeqDelta = GetSequenceDelta(notificationData);
|
||||
if (inSeqDelta > 0)
|
||||
{
|
||||
Logger.Verbose("FNetPacketNotify::Update - Seq {Seq}, InSeq {InSeq}", notificationData.Seq.Value, _inSeq.Value);
|
||||
|
||||
ProcessReceivedAcks(notificationData, func);
|
||||
|
||||
_inSeq = notificationData.Seq;
|
||||
|
||||
return inSeqDelta;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void ProcessReceivedAcks(FNotificationHeader notificationData, HandlePacketNotification func)
|
||||
{
|
||||
if (notificationData.AckedSeq.Greater(_outAckSeq))
|
||||
{
|
||||
Logger.Verbose("ProcessReceivedAcks - AckedSeq {AckedSeq}, OutAckSeq {OutAckSeq}", notificationData.AckedSeq.Value, _outAckSeq.Value);
|
||||
|
||||
var ackCount = SequenceNumber.Diff(notificationData.AckedSeq, _outAckSeq);
|
||||
|
||||
// Update InAckSeqAck used to track the needed number of bits to transmit our ack history
|
||||
_inAckSeqAck = UpdateInAckSeqAck(ackCount, notificationData.AckedSeq);
|
||||
|
||||
// ExpectedAck = OutAckSeq + 1
|
||||
var currentAck = new SequenceNumber(_outAckSeq.Value).IncrementAndGet();
|
||||
|
||||
if (ackCount > SequenceHistory.Size)
|
||||
{
|
||||
Logger.Warning("ProcessReceivedAcks - Missed Acks");
|
||||
}
|
||||
|
||||
// Everything not found in the history buffer is treated as lost
|
||||
while (ackCount > SequenceHistory.Size)
|
||||
{
|
||||
--ackCount;
|
||||
func(currentAck, false);
|
||||
currentAck = currentAck.IncrementAndGet();
|
||||
}
|
||||
|
||||
// For sequence numbers contained in the history we lookup the delivery status from the history
|
||||
while (ackCount > 0)
|
||||
{
|
||||
--ackCount;
|
||||
func(currentAck, notificationData.History.IsDelivered(ackCount));
|
||||
currentAck = currentAck.IncrementAndGet();
|
||||
}
|
||||
|
||||
_outAckSeq = notificationData.AckedSeq;
|
||||
}
|
||||
}
|
||||
|
||||
private SequenceNumber UpdateInAckSeqAck(int ackCount, SequenceNumber ackedSeq)
|
||||
{
|
||||
if (ackCount <= _ackRecord.Count)
|
||||
{
|
||||
if (ackCount > 1)
|
||||
{
|
||||
for (var i = 0; i < ackCount - 1; i++)
|
||||
{
|
||||
_ackRecord.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
var ackData = _ackRecord.Dequeue();
|
||||
if (ackData.OutSeq.Equals(ackedSeq))
|
||||
{
|
||||
return ackData.InAckSeq;
|
||||
}
|
||||
}
|
||||
|
||||
return new SequenceNumber((ushort)(ackedSeq.Value - MaxSequenceHistoryLength));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Prospect.Unreal.Net.Packets.Header.Sequence;
|
||||
|
||||
namespace Prospect.Unreal.Net.Packets.Header;
|
||||
|
||||
public ref struct FNotificationHeader
|
||||
{
|
||||
public SequenceHistory History;
|
||||
public uint HistoryWordCount;
|
||||
public SequenceNumber Seq;
|
||||
public SequenceNumber AckedSeq;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using Prospect.Unreal.Net.Packets.Header.Sequence;
|
||||
|
||||
namespace Prospect.Unreal.Net.Packets.Header;
|
||||
|
||||
public class FPackedHeader
|
||||
{
|
||||
public const int HistoryWordCountBits = 4;
|
||||
public const int SeqMask = (1 << FNetPacketNotify.SequenceNumberBits) - 1;
|
||||
public const int HistoryWordCountMask = (1 << HistoryWordCountBits) - 1;
|
||||
public const int AckSeqShift = HistoryWordCountBits;
|
||||
public const int SeqShift = AckSeqShift + FNetPacketNotify.SequenceNumberBits;
|
||||
|
||||
public static SequenceNumber GetSeq(uint packed)
|
||||
{
|
||||
return new SequenceNumber((ushort)(packed >> SeqShift & SeqMask));
|
||||
}
|
||||
|
||||
public static SequenceNumber GetAckedSeq(uint packed)
|
||||
{
|
||||
return new SequenceNumber((ushort)(packed >> AckSeqShift & SeqMask));
|
||||
}
|
||||
|
||||
public static uint GetHistoryWordCount(uint packed)
|
||||
{
|
||||
return packed & HistoryWordCountMask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Prospect.Unreal.Net.Packets.Header.Sequence;
|
||||
|
||||
public readonly struct FSentAckData
|
||||
{
|
||||
public FSentAckData(SequenceNumber outSeq, SequenceNumber inAckSeq)
|
||||
{
|
||||
OutSeq = outSeq;
|
||||
InAckSeq = inAckSeq;
|
||||
}
|
||||
|
||||
public SequenceNumber OutSeq { get; }
|
||||
public SequenceNumber InAckSeq { get; }
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using Prospect.Unreal.Serialization;
|
||||
|
||||
namespace Prospect.Unreal.Net.Packets.Header.Sequence;
|
||||
|
||||
public readonly struct SequenceHistory
|
||||
{
|
||||
// Hardcoded values for FNetPacketNotify
|
||||
public const uint HistorySize = FNetPacketNotify.MaxSequenceHistoryLength;
|
||||
|
||||
public const uint BitsPerWord = sizeof(uint) * 8;
|
||||
public const uint WordCount = HistorySize / BitsPerWord;
|
||||
public const uint MaxSizeInBits = WordCount * BitsPerWord;
|
||||
public const uint Size = HistorySize;
|
||||
|
||||
private readonly uint[] _storage;
|
||||
|
||||
public SequenceHistory()
|
||||
{
|
||||
_storage = new uint[WordCount];
|
||||
}
|
||||
|
||||
public void Read(FBitReader reader, uint numWords)
|
||||
{
|
||||
numWords = Math.Min(numWords, WordCount);
|
||||
for (var i = 0; i < numWords; i++)
|
||||
{
|
||||
_storage[i] = reader.ReadUInt32();
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
for (var i = 0; i < _storage.Length; i++)
|
||||
{
|
||||
_storage[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsDelivered(int index)
|
||||
{
|
||||
var wordIndex = (int)(index / BitsPerWord);
|
||||
var wordMask = 1 << (int)(index & (BitsPerWord - 1));
|
||||
|
||||
return (_storage[wordIndex] & wordMask) != 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
namespace Prospect.Unreal.Net.Packets.Header.Sequence;
|
||||
|
||||
public readonly struct SequenceNumber
|
||||
{
|
||||
// Hardcoded values for FNetPacketNotify
|
||||
public const int NumBits = FNetPacketNotify.SequenceNumberBits;
|
||||
|
||||
public const int SeqNumberBits = NumBits;
|
||||
public const ushort SeqNumberCount = 1 << NumBits;
|
||||
public const ushort SeqNumberHalf = 1 << (NumBits - 1);
|
||||
public const ushort SeqNumberMax = SeqNumberCount - 1;
|
||||
public const ushort SeqNumberMask = SeqNumberMax;
|
||||
|
||||
public SequenceNumber(ushort value)
|
||||
{
|
||||
Value = (ushort)(value & SeqNumberMask);
|
||||
}
|
||||
|
||||
public ushort Value { get; }
|
||||
|
||||
public bool Greater(SequenceNumber other)
|
||||
{
|
||||
return (Value != other.Value) && (((Value - other.Value) & SeqNumberMask) < SeqNumberHalf);
|
||||
}
|
||||
|
||||
public bool GreaterEq(SequenceNumber other)
|
||||
{
|
||||
return ((Value - other.Value) & SeqNumberMask) < SeqNumberHalf;
|
||||
}
|
||||
|
||||
public bool Equals(SequenceNumber other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is SequenceNumber other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
|
||||
public static int Diff(SequenceNumber a, SequenceNumber b)
|
||||
{
|
||||
const int shiftValue = sizeof(int) * 8 - NumBits;
|
||||
|
||||
var valueA = a.Value;
|
||||
var valueB = b.Value;
|
||||
|
||||
return ((valueA - valueB) << shiftValue) >> shiftValue;
|
||||
}
|
||||
|
||||
public SequenceNumber IncrementAndGet()
|
||||
{
|
||||
return new SequenceNumber((ushort)(Value + 1));
|
||||
}
|
||||
|
||||
public readonly struct Difference
|
||||
{
|
||||
public Difference(int value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public int Value { get; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user