Started serializing bunches

This commit is contained in:
AeonLucid
2022-01-10 04:38:06 +01:00
parent 49e6e0f6f8
commit f3b0a31761
36 changed files with 2013 additions and 54 deletions
@@ -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; }
}
}