UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
ReplicationRecord.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "Containers/Array.h"
8
9namespace UE::Net::Private
10{
11
13{
14public:
15 // We should get away with 64k records in flight given a packet window of 256 and an average of 256 replicated object destroys, objects and subobjects per packet
18 // The MaxReplicationRecordCount should account for at least 256 packets with N records per replicated object or destroyed object. With lots of destroyed objects you could end up with maybe 300-400 records for a single packet.
20
21 static constexpr uint32 ObjectIndexBitCount = 20U;
23
24 // Circular queue, indexing into record infos
25 struct FRecord
26 {
28 };
29
40
41 // $IRIS: implement some some sort of low overhead chunked fifo array allocator for changemasks used for the replication record
42 // They tend to be relatively short lived and are always allocated and freed in the same order.
43 // for smaller changemasks we use inlined storage.
44
45 // We want to keep this state as small as possible as we will have many of them
47 {
48 FChangeMaskStorageOrPointer ChangeMaskOrPtr; // used for ChangeMask storage or a pointer to the changemask (could be repurposed to always be an index to save space)
49 uint32 Index : ObjectIndexBitCount; // Index in the ReplicationInfo array, effectively identifying the object
50 uint32 ReplicatedObjectState : ReplicatedObjectStateBitCount; // Encode EReplicatedObjectState using as few bits as we can
51 uint32 HasChangeMask : 1; // Do we have a changeMask?
52 uint32 HasAttachments : 1; // If this flag is set there's an associated AttachmentRecord
53 uint32 WroteTearOff : 1; // If this flag is set, we wrote TearOff
54 uint32 WroteDestroySubObject : 1; // If this flag is set, we wrote DestroySubObject
55 uint32 NewBaselineIndex : 2; // This is a new Baseline pending ack
56 uint32 HasSubObjectRecord : 1; // If this flag is set there's an associated SubObjectRecord
57 ReplicationRecordIndex NextIndex; // Points to next older record index
59 };
60
61 static_assert(sizeof(FRecordInfo) == 16, "Expected sizeof FRecordInfo to be 16 bytes");
62 static_assert(sizeof(ReplicationRecordIndex) == 2, "Need to remove or adjust Padding field in FRecordInfo");
63
64public:
65
66 // Simple index based linked list used to track in flight data
68 {
69 inline FRecordInfoList();
70
71 ReplicationRecordIndex LastRecordIndex; // Index of the last written index for this object, used to chain replication infos
72 ReplicationRecordIndex FirstRecordIndex; // First index in flight (oldest), used to quickly be able to iterate over all changes in flight
73 };
74
75 // Pop RecordInfo from record and remove it from the provided RecordInfoList
77
78 // Push RecordInfo to record and add it to the provided RecordInfoList
79 inline void PushInfoAndAddToList(FRecordInfoList& RecordList, const FRecordInfo& RecordInfo, uint64 AttachmentRecord = 0);
80
81 // Push RecordInfo to record and add it to the provided RecordInfoList. Call this when there's an associated SubObjectRecord.
83
84 // Reset a RecordInfoList
86
87public:
88 inline FReplicationRecord();
89
90 // Get the info for the provided index
93
94 // PeekInfo. If the info indicates there's an attachment record one must call DequeueAttachmentRecord(). If the info indicates there's a SubObjectRecord one must call DequeueSubObjectRecord().
95 inline const FRecordInfo& PeekInfo() const { return RecordInfos.Peek(); }
96 inline const FRecordInfo& PeekInfoAtOffset(uint32 Offset) const { return RecordInfos.PeekAtOffset(Offset); }
97 inline const uint32 GetInfoCount() const { return static_cast<uint32>(RecordInfos.Count()); }
98 inline const uint32 GetUnusedInfoCount() const { return static_cast<uint32>(MaxReplicationRecordCount) - static_cast<uint32>(RecordInfos.Count()); }
99
100 // If the info from PeekInfo() indicates there's an attachment record one needs to call this function as well.
102
103 // If the info from PeekInfo() indicates there's a subobject record one needs to call this function as well.
104 FSubObjectRecord DequeueSubObjectRecord();
105
106 // FrontIndex
107 inline ReplicationRecordIndex GetFrontIndex() const { return FrontIndex; }
108
109 // Push a record, currently the record is simply a count of how mane RecordInfos we stored for the record
110 inline void PushRecord(uint16 InfoCount);
111
112 // Pop a record
113 inline uint16 PopRecord();
114
115 inline const uint32 PeekRecordAtOffset(uint32 Offset) const { return Record.PeekAtOffset(Offset); }
116
117 // Num Records
118 inline const uint32 GetRecordCount() const { return static_cast<uint32>(Record.Count()); }
119
120private:
121
122 // Push Info to queue, the index of the info will be returned, as long as the info is valid it can be retrieved by the index
123 inline ReplicationRecordIndex PushInfo(const FRecordInfo& Info);
124
125 // PopInfo
126 inline void PopInfo();
127
128private:
129 // Storage for RecordInfos
131
132 // Storages for the Record for each packet
134
135 // Storage for attachment records
136 TResizableCircularQueue<uint64> AttachmentRecords;
137
138 // Storage for minimalistic subobject records, used by for example object destruction.
140
141 // Current Index at the oldest Record in the queue, this is used to do relative indexing into the queue when linking pushed records
142 ReplicationRecordIndex FrontIndex;
143};
144
146: RecordInfos(1024)
147, Record(256)
148, AttachmentRecords(64)
149, SubObjectRecords(128)
150, FrontIndex(0u)
151{
152}
153
155{
157 {
159 return &RecordInfos.PokeAtOffsetNoCheck(Offset);
160 }
161
162 return nullptr;
163}
164
166{
168 {
170 return &RecordInfos.PeekAtOffsetNoCheck(Offset);
171 }
172
173 return nullptr;
174}
175
177{
178 const uint64 AttachmentRecord = AttachmentRecords.Peek();
179 AttachmentRecords.Pop();
180 return AttachmentRecord;
181}
182
184{
185 FSubObjectRecord SubObjectRecord = MoveTemp(SubObjectRecords.Poke());
186 SubObjectRecords.Pop();
187 return SubObjectRecord;
188}
189
194
196{
197 check(Record.Count());
198
199 const uint16 InfoCount = Record.Peek();
200 Record.Pop();
201
202 return InfoCount;
203}
204
205FReplicationRecord::ReplicationRecordIndex FReplicationRecord::PushInfo(const FRecordInfo& Info)
206{
207 const SIZE_T CurrentInfoCount = RecordInfos.Count();
208
210
211 FRecordInfo& NewInfo = RecordInfos.EnqueueDefaulted_GetRef();
212 NewInfo = Info;
214
215 return (FrontIndex + CurrentInfoCount) % MaxReplicationRecordCount;
216}
217
218void FReplicationRecord::PopInfo()
219{
220 FrontIndex = (FrontIndex + 1U) % MaxReplicationRecordCount;
221 RecordInfos.Pop();
222};
223
225{
226 // This is the record at the front of the Record.
227 const ReplicationRecordIndex RecordIndex = GetFrontIndex();
229
230 // unlink
231 RecordList.FirstRecordIndex = RecordInfo.NextIndex;
232 RecordList.LastRecordIndex = (RecordList.LastRecordIndex == RecordIndex) ? InvalidReplicationRecordIndex : RecordList.LastRecordIndex;
233
234 PopInfo();
235}
236
238{
240 if (RecordInfo.HasAttachments)
241 {
242 AttachmentRecords.Enqueue(AttachmentRecord);
243 }
244
245 if (FRecordInfo* LastRecord = GetInfoForIndex(RecordList.LastRecordIndex))
246 {
247 // Link already in flight record to this newIndex
248 LastRecord->NextIndex = NewIndex;
249 RecordList.LastRecordIndex = NewIndex;
250 }
251 else
252 {
253 // If this is the FirstRecord we update it as well.
254 RecordList.FirstRecordIndex = NewIndex;
255 RecordList.LastRecordIndex = NewIndex;
256 }
257}
258
260{
262 if (RecordInfo.HasSubObjectRecord)
263 {
264 SubObjectRecords.Enqueue(SubObjectRecord);
265 }
266
267 if (FRecordInfo* LastRecord = GetInfoForIndex(RecordList.LastRecordIndex))
268 {
269 // Link already in flight record to this newIndex
270 LastRecord->NextIndex = NewIndex;
271 RecordList.LastRecordIndex = NewIndex;
272 }
273 else
274 {
275 // If this is the FirstRecord we update it as well.
276 RecordList.FirstRecordIndex = NewIndex;
277 RecordList.LastRecordIndex = NewIndex;
278 }
279}
280
286
288: LastRecordIndex(InvalidReplicationRecordIndex)
289, FirstRecordIndex(InvalidReplicationRecordIndex)
290{
291}
292
293}
#define check(expr)
Definition AssertionMacros.h:314
FPlatformTypes::SIZE_T SIZE_T
An unsigned integer the same size as a pointer, the same as UPTRINT.
Definition Platform.h:1150
FPlatformTypes::uint64 uint64
A 64-bit unsigned integer.
Definition Platform.h:1117
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
UE_INTRINSIC_CAST UE_REWRITE constexpr std::remove_reference_t< T > && MoveTemp(T &&Obj) noexcept
Definition UnrealTemplate.h:520
uint32 Offset
Definition VulkanMemory.cpp:4033
uint16_t uint16
Definition binka_ue_file_header.h:7
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition Array.h:670
Definition ResizableCircularQueue.h:21
const ElementT & PeekAtOffset(SIZE_T Offset=0) const
Definition ResizableCircularQueue.h:76
const ElementT & Peek() const
Definition ResizableCircularQueue.h:82
SIZE_T Count() const
Definition ResizableCircularQueue.h:34
void Enqueue(const ElementT &SrcData)
Definition ResizableCircularQueue.h:152
void Pop()
Definition ResizableCircularQueue.h:220
Definition ChangeMaskUtil.h:29
Definition ReplicationRecord.h:13
FSubObjectRecord DequeueSubObjectRecord()
Definition ReplicationRecord.h:183
static constexpr ReplicationRecordIndex InvalidReplicationRecordIndex
Definition ReplicationRecord.h:17
void PushRecord(uint16 InfoCount)
Definition ReplicationRecord.h:190
const FRecordInfo & PeekInfo() const
Definition ReplicationRecord.h:95
uint16 PopRecord()
Definition ReplicationRecord.h:195
const uint32 PeekRecordAtOffset(uint32 Offset) const
Definition ReplicationRecord.h:115
const uint32 GetUnusedInfoCount() const
Definition ReplicationRecord.h:98
static constexpr ReplicationRecordIndex MaxReplicationRecordCount
Definition ReplicationRecord.h:19
const uint32 GetInfoCount() const
Definition ReplicationRecord.h:97
void ResetList(FRecordInfoList &RecordList)
Definition ReplicationRecord.h:281
void PushInfoAndAddToList(FRecordInfoList &RecordList, const FRecordInfo &RecordInfo, uint64 AttachmentRecord=0)
Definition ReplicationRecord.h:237
FRecordInfo * GetInfoForIndex(ReplicationRecordIndex Index)
Definition ReplicationRecord.h:154
uint64 DequeueAttachmentRecord()
Definition ReplicationRecord.h:176
const FRecordInfo & PeekInfoAtOffset(uint32 Offset) const
Definition ReplicationRecord.h:96
uint16 ReplicationRecordIndex
Definition ReplicationRecord.h:16
static constexpr uint32 ReplicatedObjectStateBitCount
Definition ReplicationRecord.h:22
void PopInfoAndRemoveFromList(FRecordInfoList &RecordList)
Definition ReplicationRecord.h:224
const uint32 GetRecordCount() const
Definition ReplicationRecord.h:118
ReplicationRecordIndex GetFrontIndex() const
Definition ReplicationRecord.h:107
FReplicationRecord()
Definition ReplicationRecord.h:145
static constexpr uint32 ObjectIndexBitCount
Definition ReplicationRecord.h:21
Definition NetworkVersion.cpp:28
U16 Index
Definition radfft.cpp:71
ReplicationRecordIndex FirstRecordIndex
Definition ReplicationRecord.h:72
ReplicationRecordIndex LastRecordIndex
Definition ReplicationRecord.h:71
FRecordInfoList()
Definition ReplicationRecord.h:287
Definition ReplicationRecord.h:47
uint32 WroteTearOff
Definition ReplicationRecord.h:53
uint32 HasSubObjectRecord
Definition ReplicationRecord.h:56
uint32 NewBaselineIndex
Definition ReplicationRecord.h:55
uint32 ReplicatedObjectState
Definition ReplicationRecord.h:50
uint32 HasChangeMask
Definition ReplicationRecord.h:51
uint32 Index
Definition ReplicationRecord.h:49
uint32 HasAttachments
Definition ReplicationRecord.h:52
uint32 WroteDestroySubObject
Definition ReplicationRecord.h:54
ReplicationRecordIndex NextIndex
Definition ReplicationRecord.h:57
FChangeMaskStorageOrPointer ChangeMaskOrPtr
Definition ReplicationRecord.h:48
uint16 Padding
Definition ReplicationRecord.h:58
Definition ReplicationRecord.h:26
uint16 RecordCount
Definition ReplicationRecord.h:27
TArray< FSubObjectInfo > SubObjectInfos
Definition ReplicationRecord.h:38