template <SIZE_T NumBits, typename SequenceType> classTSequenceNumber { static_assert(TIsSigned<SequenceType>::Value == false, "The base type for sequence numbers must be unsigned");
public: using SequenceT = SequenceType; using DifferenceT = int32;
static TAutoConsoleVariable<int32> CVarNetPacketOrderMaxMissingPackets(TEXT("net.PacketOrderMaxMissingPackets"), 3, TEXT("The maximum number of missed packet sequences that is allowed, before treating missing packets as lost."));
static TAutoConsoleVariable<int32> CVarNetPacketOrderMaxCachedPackets(TEXT("net.PacketOrderMaxCachedPackets"), 32, TEXT("(NOTE: Must be power of 2!) The maximum number of packets to cache while waiting for missing packet sequences, before treating missing packets as lost."))
// Cache the packet if we are already caching, and begin caching if we just encountered a missing sequence, within range if (bFillingPacketOrderCache || (bCheckForMissingSequence && MissingPacketCount > 0 && MissingPacketCount <= MaxMissingPackets)) { int32 LinearCacheIdx = PacketSequenceDelta - 1; int32 CacheCapacity = PacketOrderCache->Capacity(); bool bLastCacheEntry = LinearCacheIdx >= (CacheCapacity - 1);
// The last cache entry is only set, when we've reached capacity or when we receive a sequence which is out of bounds of the cache LinearCacheIdx = bLastCacheEntry ? (CacheCapacity - 1) : LinearCacheIdx;
// Reset the reader to its initial position, and cache the packet if (!CurCachePacket.IsValid()) { CurCachePacket = MakeUnique<FBitReader>(Reader); PacketOrderCacheCount++; ResetReaderMark.Pop(*CurCachePacket); } return; } } }
// Update incoming sequence data and deliver packet notifications // Packet is only accepted if both the incoming sequence number and incoming ack data are valid const int32 UpdatedPacketSequenceDelta = PacketNotify.Update(Header, HandlePacketNotification); }
// Update InAckSeqAck used to track the needed number of bits to transmit our ack history // Note: As we might reset sequence history we need to check if we already have advanced the InAckSeqAck const SequenceNumberT NewInAckSeqAck = UpdateInAckSeqAck(AckCount, NotificationData.AckedSeq); if (NewInAckSeqAck > InAckSeqAck) { InAckSeqAck = NewInAckSeqAck; } // ExpectedAck = OutAckSeq + 1 SequenceNumberT CurrentAck(OutAckSeq); ++CurrentAck;
// Make sure that we only look at the sequence history bit included in the notification data as the sequence history might have been reset, // in which case we might not receive the max size history even though the ack-count is bigger than the history const SequenceNumberT::DifferenceT HistoryBits = NotificationData.HistoryWordCount * SequenceHistoryT::BitsPerWord;
// Everything not found in the history buffer is treated as lost while (AckCount > HistoryBits) { --AckCount; InFunc(CurrentAck, false); ++CurrentAck; }
// For sequence numbers contained in the history we lookup the delivery status from the history while (AckCount > 0) { --AckCount; InFunc(CurrentAck, NotificationData.History.IsDelivered(AckCount)); ++CurrentAck; } OutAckSeq = NotificationData.AckedSeq; // Are we done waiting for an reset of the ack history? if (OutAckSeq > WaitingForFlushSeqAck) { WaitingForFlushSeqAck = OutAckSeq; } } }
FNetPacketNotify::SequenceNumberT::DifferenceT FNetPacketNotify::InternalUpdate(const FNotificationHeader& NotificationData, SequenceNumberT::DifferenceT InSeqDelta) { // We must check if we will overflow our outgoing ack-window, if we do and it contains processed data we must initiate a re-sync of ack-sequence history. // This is done by ignoring any new packets until we are in sync again. // This would typically only occur in situations where we would have had huge packet loss or spikes on the receiving end. if (!IsWaitingForSequenceHistoryFlush() && !WillSequenceFitInSequenceHistory(NotificationData.Seq)) { if (GetHasUnacknowledgedAcks()) { SetWaitForSequenceHistoryFlush(); } else { // We can reset if we have no previous acks and can then safely synthesize nacks on the receiving end const SequenceNumberT NewInAckSeqAck(NotificationData.Seq.Get() - 1); UE_LOG_PACKET_NOTIFY_WARNING(TEXT("FNetPacketNotify::Reset SequenceHistory - New InSeqDelta: %u Old: %u"), NewInAckSeqAck.Get(), InAckSeqAck.Get()); InAckSeqAck = NewInAckSeqAck; } }
if (!IsWaitingForSequenceHistoryFlush()) { // Just accept the incoming sequence, under normal circumstances NetConnection explicitly handles the acks. InSeq = NotificationData.Seq;
return InSeqDelta; } else { // Until we have flushed the history we treat incoming packets as lost while still advancing ack window as far as we can. SequenceNumberT NewInSeqToAck(NotificationData.Seq);
// Still waiting on flush, but we can fill up the history if (!WillSequenceFitInSequenceHistory(NotificationData.Seq) && GetHasUnacknowledgedAcks()) { // Mark everything we can as lost up until the end of the sequence history NewInSeqToAck = SequenceNumberT(InAckSeqAck.Get() + (MaxSequenceHistoryLength - GetCurrentSequenceHistoryLength())); }
template<class Functor> voidFNetPacketNotify::ProcessReceivedAcks(const FNotificationHeader& NotificationData, Functor&& InFunc) { // Are we done waiting for an reset of the ack history? if (OutAckSeq > WaitingForFlushSeqAck) { WaitingForFlushSeqAck = OutAckSeq; } }