文章目录
- 类型
- 同步初始化
- 创建 FObjectReplicator
- 创建 FRepLayout、Cmd、ShadowOffset
- 创建 FRepChangedPropertyTracker、FRepState
- 创建 FReplicationChangelistMgr、FRepChangelistState、ShadowBuffer
- 属性同步
- 属性变化检测
- 查找变化属性,写入ShadowMemory
- 发送数据
- 接收数据
- UObject同步
- 发送对象和属性变化
- 接收对象和属性变化
- UObject指针同步
- AActor同步
- 发送新的Actor
- 接受新的Actor
- TODO
类型
// 网络驱动,World唯一
UWorld::Driver -> UNetDriver
// 网络连接,OwningActor是PlayerController,服务器会存在多个ClientConnections
UNetDriver::ServerConnection、ClientConnections -> UNetConnection
// 交换、处理Actor数据,无论属性同步还是RPC,都需要经过Actor对应的ActorChannel
UNetConnection::ActorChannels -> UActorChannel
// Actor可能有多个同步的SubObject,每个都会有一个FObjectReplicator(包括Actor)进行同步的处理
UActorChannel::ReplicationMap -> FObjectReplicator
// 包含了一个类的同步信息,主要是各个属性的信息
UNetDriver::RepLayoutMap -> FRepLayout
// 类的每个同步的属性,内部有属性的地址偏移(FProperty* InProperty, int32 InIndex)
UClass::ClassReps -> FRepRecord
// 每个同步属性对应的同步信息,例如在Object内存内的Offset、在ShadowData内的Offset、子Cmd的下标范围、同步和OnRep函数的条件
FRepLayout::Parents -> FRepParentCmd
// UE::Net::Private::FNetPropertyConditionManager::Get()::PropertyTrackerMap
// -> TMap<FObjectKey, TSharedPtr<FRepChangedPropertyTracker>>
// 记录具体对象的哪些属性是ActiveForRep的,以及同步的DynamicCondition
FRepChangedPropertyTracker(FCustomPropertyConditionState)
// 存了对象的同步状态,例如历史修改、过程中处理的共享数据、ShadowBuffer
UNetDriver::ReplicationChangeListMap -> TMap< UObject*, FReplicationChangelistMgrWrapper >
::FReplicationChangelistMgr::FRepChangelistState
// GUID、GUIDCache等信息
UNetConnection::PackageMap -> UPackageMapClient(UPackageMap)
// 内部的GUID信息表
UPackageMapClient::GuidCache -> FNetGUIDCache
同步初始化
创建 FObjectReplicator
创建 UActorChannel:
UNetConnection* Connection = Actor->GetNetConnection();
UActorChannel* Ch = Connection->FindActorChannelRef(Actor);
if (Ch == nullptr)
Ch = (UActorChannel *)Connection->CreateChannelByName( NAME_Actor, EChannelCreateFlags::OpenedLocally );
Ch->SetChannelActor(Actor, ESetChannelActorFlags::None);
ActorReplicator = FindOrCreateReplicator(Actor);
创建 FObjectReplicator:
TSharedRef<FObjectReplicator>& UActorChannel::CreateReplicator(UObject* Obj)
TSharedRef<FObjectReplicator>& UActorChannel::CreateReplicator(UObject* Obj, bool bCheckDormantReplicators)
NewReplicator = Connection->CreateReplicatorForNewActorChannel(Obj);
NewReplicator->InitWithObject( Object, this, true );
// 创建 FReplicationChangelistMgr、FRepChangelistState、ShadowBuffer
NewRef->StartReplicating(this);
void FObjectReplicator::InitWithObject(UObject* InObject, UNetConnection* InConnection, bool bUseDefaultState)
// 创建 FRepLayout
RepLayout = Connection->Driver->GetObjectClassRepLayout(ObjectClass);
// 创建 FRepChangedPropertyTracker、FRepState
InitRecentProperties(Source);
创建 FRepLayout、Cmd、ShadowOffset
// Create RepLayout
TSharedPtr<FRepLayout> UNetDriver::GetObjectClassRepLayout( UClass * Class )
TSharedPtr<FRepLayout>* RepLayoutPtr = RepLayoutMap.Find(Class);
if (!RepLayoutPtr)
RepLayoutPtr = &RepLayoutMap.Add(Class, FRepLayout::CreateFromClass(Class, ServerConnection, Flags));
RepLayout->InitFromClass(InClass, ServerConnection, CreateFlags);
void FRepLayout::InitFromClass(UClass* InObjectClass, const UNetConnection* ServerConnection, const ECreateRepLayoutFlags CreateFlags)
// 初始化数组(网络相关属性、函数)
InObjectClass->SetUpRuntimeReplicationData();
for (int32 i = 0; i < InObjectClass->ClassReps.Num(); i++)
// 添加ParentCmd(对应每个UProperty,ArrayDim不为1时一个UProperty会对应多个)
const int32 ParentHandle = AddParentProperty(Parents, Property, ArrayIdx);
// 当前属性内部偏移(数组)
const int32 ParentOffset = Property->ElementSize * ArrayIdx;
// 击落当前UProperty对应的Cmd数组位置起点
Parents[ParentHandle].CmdStart = Cmds.Num();
// 处理对应的Cmd
RelativeHandle = InitFromProperty_r<ERepBuildType::Class>(SharedParams, StackParams);
// CmdEnd = 终点+1
Parents[ParentHandle].CmdEnd = Cmds.Num();
// 增加一个Return的特殊Cmd
AddReturnCmd(Cmds);
TArray<FLifetimeProperty> LifetimeProps;
UObject* Object = InObjectClass->GetDefaultObject();
// 调用CDO的GetLifetimeReplicatedProps,收集FLifetimeProperty到LifetimeProps
Object->GetLifetimeReplicatedProps(LifetimeProps);
PushModelProperties.Init(false, Parents.Num());
for (int32 i = 0; i < LifetimeProps.Num(); i++)
const int32 ParentIndex = LifetimeProps[i].RepIndex;
Parents[ParentIndex].Condition = LifetimeProps[i].Condition;
Parents[ParentIndex].RepNotifyCondition = LifetimeProps[i].RepNotifyCondition;
// 记录RepFunc
if (UFunction* RepNotifyFunc = InObjectClass->FindFunctionByName(Parents[ParentIndex].Property->RepNotifyFunc))
Parents[ParentIndex].RepNotifyNumParams = RepNotifyFunc->NumParms;
if (bIsPushModelEnabled && LifetimeProps[i].bIsPushBased)
PushModelProperties[ParentIndex] = true;
BuildHandleToCmdIndexTable_r(0, Cmds.Num() - 1, BaseHandleToCmdIndex);
for (int32 CmdIndex = CmdStart; CmdIndex < CmdEnd; CmdIndex++)
const int32 Index = HandleToCmdIndex.Add(FHandleToCmdIndex(CmdIndex));
if (Cmd.Type == ERepLayoutCmdType::DynamicArray)
HandleToCmdIndex[Index].HandleToCmdIndex = TUniquePtr<TArray<FHandleToCmdIndex>>(new TArray<FHandleToCmdIndex>());
BuildHandleToCmdIndexTable_r(CmdIndex + 1, Cmd.EndCmd - 1, ArrayHandleToCmdIndex);
BuildShadowOffsets<ERepBuildType::Class>(InObjectClass, Parents, Cmds, ShadowDataBufferSize);
处理类内部的同步字段:
// --- Init ClassReps(List of replication records), NetFields(List of network relevant fields (functions))
void UClass::SetUpRuntimeReplicationData()
// 根据CLASS_ReplicationDataIsSetUp判断是否已经设置
// NetFields根据名字排序,ClassReps根据地址偏移排序
// ClassReps内的FRepRecord内存有一个int值,如果一个属性的ArrayDim(c数组,一般不会这样使用所以都是1)大于1,则会记录多次({p,0}{p,1}{p,2})
if ((Prop->PropertyFlags & CPF_Net)&& Prop->GetOwner<UObject>() == this)
NetProperties.Add(Prop);
if ((Func->FunctionFlags&FUNC_Net) && !Func->GetSuperFunction())
NetFields.Add(Func);
...
// UProperty记录在ClassReps数组中的下标
NetProperties[i]->RepIndex = (uint16)ClassReps.Num();
for (int32 j = 0; j < NetProperties[i]->ArrayDim; j++)
new(ClassReps)FRepRecord(NetProperties[i], j);
处理类型信息Cmd:
// --- Init Cmds
template<ERepBuildType BuildType>
static int32 InitFromProperty_r(FInitFromPropertySharedParams& SharedParams, FInitFromPropertyStackParams StackParams)
if (FArrayProperty* ArrayProp = CastField<FArrayProperty>(StackParams.Property))
++StackParams.RelativeHandle;
StackParams.Offset += GetOffsetForProperty<BuildType>(*ArrayProp);
// 增加一个DynamicArray的特殊Cmd
const uint32 ArrayChecksum = AddArrayCmd(SharedParams, StackParams);
FRepLayoutCmd& Cmd = SharedParams.Cmds.AddZeroed_GetRef();
Cmd.Type = ERepLayoutCmdType::DynamicArray;
InitFromProperty_r<BuildType>(SharedParams, NewStackParams);
// 增加一个Return的特殊Cmd
AddReturnCmd(SharedParams.Cmds);
Cmds.AddZeroed_GetRef().Type = ERepLayoutCmdType::Return;
SharedParams.Cmds[CmdStart].EndCmd = CmdEnd;
else if (FStructProperty* StructProp = CastField<FStructProperty>(StackParams.Property))
UScriptStruct* Struct = StructProp->Struct;
StackParams.Offset += GetOffsetForProperty<BuildType>(*StructProp);
// 如果自己定义了NetSerialize,就不需要知道内部的结构了,所以只增加一个Cmd
if (EnumHasAnyFlags(Struct->StructFlags, STRUCT_NetSerializeNative))
SharedParams.bHasNetSerializeProperties = true;
++StackParams.RelativeHandle;
AddPropertyCmd(SharedParams, StackParams);
return StackParams.RelativeHandle;
// 和UClass::SetUpRuntimeReplicationData类似,递归增加Cmd
return InitFromStructProperty<BuildType>(SharedParams, StackParams, StructProp, Struct);
TArray<FProperty*> NetProperties;
NetProperties.Add(*It);
Sort(NetProperties.GetData(), NetProperties.Num(), FCompareUFieldOffsets());
const uint32 StructChecksum = GetRepLayoutCmdCompatibleChecksum(SharedParams, StackParams);
for (int32 i = 0; i < NetProperties.Num(); i++)
for (int32 j = 0; j < NetProperties[i]->ArrayDim; j++)
const int32 ArrayElementOffset = j * NetProperties[i]->ElementSize;
FInitFromPropertyStackParams NewStackParams{
/*Property=*/NetProperties[i],
/*Offset=*/StackParams.Offset + ArrayElementOffset,
/*RelativeHandle=*/StackParams.RelativeHandle,
/*ParentChecksum=*/StructChecksum,
/*StaticArrayIndex=*/j,
/*RecursingNetSerializeStruct=*/StackParams.RecursingNetSerializeStruct
};
StackParams.RelativeHandle = InitFromProperty_r<BuildType>(SharedParams, NewStackParams);
return StackParams.RelativeHandle;
else
++StackParams.RelativeHandle;
StackParams.Offset += GetOffsetForProperty<BuildType>(*StackParams.Property);
AddPropertyCmd(SharedParams, StackParams);
Cmd.Property = StackParams.Property;
Cmd.Type = ERepLayoutCmdType::Property; // Initially set to generic type
Cmd.Offset = StackParams.Offset;
Cmd.ElementSize = Cmd.Property->ElementSize;
Cmd.RelativeHandle = StackParams.RelativeHandle;
Cmd.ParentIndex = SharedParams.ParentIndex;
// 将Property的信息编码成uint32
Cmd.CompatibleChecksum = GetRepLayoutCmdCompatibleChecksum(SharedParams, StackParams);
// 记录Cmd.Type(例如ERepLayoutCmdType::PropertyObject)
return StackParams.RelativeHandle;
DOREPLIFETIME 宏说明:
// --- 收集FLifetimeProperty到LifetimeProps
void UObject::GetLifetimeReplicatedProps( TArray< class FLifetimeProperty > & OutLifetimeProps ) const
#define DOREPLIFETIME(c,v) DOREPLIFETIME_WITH_PARAMS(c,v,FDoRepLifetimeParams())
#define DOREPLIFETIME_WITH_PARAMS(c,v,params) \
FProperty* ReplicatedProperty = GetReplicatedProperty(StaticClass(), c::StaticClass(),GET_MEMBER_NAME_CHECKED(c,v)); \
RegisterReplicatedLifetimeProperty(ReplicatedProperty, OutLifetimeProps, params); \
RegisterReplicatedLifetimeProperty(ReplicatedProperty, OutLifetimeProps, params);
RegisterReplicatedLifetimeProperty(ReplicatedProperty, OutLifetimeProps, Params);
FRepPropertyDescriptor(const FProperty* Property) : PropertyName(TEXT("")), RepIndex(Property->RepIndex), ArrayDim(Property->ArrayDim)
void RegisterReplicatedLifetimeProperty(const NetworkingPrivate::FRepPropertyDescriptor& PropertyDescriptor, TArray<FLifetimeProperty>& OutLifetimeProps, const FDoRepLifetimeParams& Params)
for (int32 i = 0; i < PropertyDescriptor.ArrayDim; i++)
const uint16 RepIndex = PropertyDescriptor.RepIndex + i;
FLifetimeProperty LifetimeProp(RepIndex, Params.Condition, Params.RepNotifyCondition, Params.bIsPushBased);
OutLifetimeProps.Add(LifetimeProp);
// --- 构建所有Cmd对应的,在ShadowMemory内部的偏移
template<ERepBuildType ShadowType>
static void BuildShadowOffsets(UStruct* Owner, TArray<FRepParentCmd>& Parents, TArray<FRepLayoutCmd>& Cmds, int32& ShadowOffset)
if (ShadowType == ERepBuildType::Class && !!GUsePackedShadowBuffers)
ShadowOffset = 0;
struct FParentCmdIndexAndAlignment
// 使用alignof获取最小的对齐方式(int32->4,char->1,则struct {int32, char}->4)
FParentCmdIndexAndAlignment(int32 ParentIndex, const FRepParentCmd& Parent): Index(ParentIndex), Alignment(Parent.Property->GetMinAlignment())
// 根据所有属性的alignof进行排序,可以最大程度的减少padding
bool operator< (const FParentCmdIndexAndAlignment& RHS) const
return Alignment < RHS.Alignment;
TArray<FParentCmdIndexAndAlignment> IndexAndAlignmentArray;
for (int32 i = 0; i < Parents.Num(); ++i)
IndexAndAlignmentArray.Emplace(i, Parents[i]);
IndexAndAlignmentArray.StableSort();
for (int32 i = 0; i < IndexAndAlignmentArray.Num(); ++i)
for (auto CmdIt = Cmds.CreateIterator() + Parent.CmdStart; CmdIt.GetIndex() < Parent.CmdEnd; ++CmdIt)
BuildShadowOffsets_r</*bAlreadyAligned=*/false>(CmdIt, ShadowOffset);
// 特殊处理动态数组(DynamicArray和Return之间的Cmd)
if (CmdIt->Type == ERepLayoutCmdType::DynamicArray || EnumHasAnyFlags(CmdIt->Flags, ERepLayoutCmdFlags::IsStruct))
else if (!bAlreadyAligned)
if (ShadowOffset > 0)
// bool类型可以通过bitfields占用同一个字节(Offset相同说明地址上是同一个字节)
if (ERepLayoutCmdType::PropertyBool == CmdIt->Type && CmdIt.GetIndex() > 0)
const TArray<FRepLayoutCmd>::TIterator PrevCmdIt = CmdIt - 1;
if (ERepLayoutCmdType::PropertyBool == PrevCmdIt->Type && PrevCmdIt->Offset == CmdIt->Offset)
ShadowOffset = PrevCmdIt->ShadowOffset;
// 内存对齐到新的Align
ShadowOffset = Align(ShadowOffset, CmdIt->Property->GetMinAlignment());
CmdIt->ShadowOffset = ShadowOffset;
ShadowOffset += CmdIt->ElementSize;
Parent.ShadowOffset = Cmds[Parent.CmdStart].ShadowOffset;
创建 FRepChangedPropertyTracker、FRepState
// 创建 FRepChangedPropertyTracker、FRepState
void FObjectReplicator::InitRecentProperties(uint8* Source)
// 创建 FRepChangedPropertyTracker
TSharedPtr<FRepChangedPropertyTracker> RepChangedPropertyTracker = bCreateSendingState ? ConnectionDriver->FindOrCreateRepChangedPropertyTracker(MyObject) : nullptr;
// 创建 FRepState
RepState = LocalRepLayout.CreateRepState(Source, RepChangedPropertyTracker, Flags);
// --- 创建 FRepChangedPropertyTracker
TSharedPtr<FRepChangedPropertyTracker> FNetPropertyConditionManager::FindOrCreatePropertyTracker(const FObjectKey ObjectKey)
TSharedPtr<FRepChangedPropertyTracker> Tracker = FindPropertyTracker(ObjectKey); // PropertyTrackerMap.FindRef(ObjectKey);
if (!Tracker.IsValid())
UClass* ObjectClass = Obj->GetClass();
ObjectClass->SetUpRuntimeReplicationData();
FCustomPropertyConditionState ActiveState(NumProperties);
TBitArray<> CurrentState.Init(true, NumProperties);
// 自定义是否需要同步 DOREPCUSTOMCONDITION_ACTIVE_FAST(ACharacter, RepRootMotion, CharacterMovement->CurrentRootMotion.HasActiveRootMotionSources() || IsPlayingNetworkedRootMotionMontage());
Obj->GetReplicatedCustomConditionState(ActiveState);
// --- 创建 FRepState
TUniquePtr<FRepState> FRepLayout::CreateRepState(const FConstRepObjectDataBuffer Source, TSharedPtr<FRepChangedPropertyTracker>& InRepChangedPropertyTracker, ECreateRepStateFlags CreateFlags) const
TUniquePtr<FRepState> RepState(new FRepState());
RepState->SendingRepState.Reset(new FSendingRepState());
RepState->SendingRepState->RepChangedPropertyTracker = InRepChangedPropertyTracker;
RebuildConditionalProperties(RepState->SendingRepState.Get(), FReplicationFlags());
RepState->SendingRepState->InactiveParents.Init(false, Parents.Num());
RepState->ReceivingRepState.Reset(new FReceivingRepState(MoveTemp(StaticBuffer)));
创建 FReplicationChangelistMgr、FRepChangelistState、ShadowBuffer
// 创建 FReplicationChangelistMgr、FRepChangelistState、ShadowBuffer
void FObjectReplicator::StartReplicating(class UActorChannel * InActorChannel)
ChangelistMgr = WorldNetDriver->GetReplicationChangeListMgr(Object);
FReplicationChangelistMgrWrapper* ReplicationChangeListMgrPtr = ReplicationChangeListMap.Find(Object);
if (!ReplicationChangeListMgrPtr)
FReplicationChangelistMgrWrapper Wrapper(Object, RepLayout->CreateReplicationChangelistMgr(Object, GetCreateReplicationChangelistMgrFlags()));
// --- 创建 FReplicationChangelistMgr
TSharedPtr<FReplicationChangelistMgr> FRepLayout::CreateReplicationChangelistMgr(const UObject* InObject, const ECreateReplicationChangelistMgrFlags CreateFlags) const
const uint8* ShadowStateSource = (const uint8*)InObject->GetArchetype();
return MakeShareable(new FReplicationChangelistMgr(AsShared(), ShadowStateSource, InObject, DeltaChangelistState));
FRepChangelistState::StaticBuffer(InRepLayout->CreateShadowBuffer(InSource))
// --- 创建 ShadowBuffer
FRepStateStaticBuffer FRepLayout::CreateShadowBuffer(const FConstRepObjectDataBuffer Source) const
FRepStateStaticBuffer ShadowData(AsShared());
InitRepStateStaticBuffer(ShadowData, Source);
return ShadowData;
// --- --- 初始化 ShadowBuffer
void FRepLayout::InitRepStateStaticBuffer(FRepStateStaticBuffer& ShadowData, const FConstRepObjectDataBuffer Source) const
ShadowData.Buffer.SetNumZeroed(ShadowDataBufferSize);
ConstructProperties(ShadowData);
for (const FRepParentCmd& Parent : Parents)
Parent.Property->InitializeValue(ShadowData + Parent); // return InBuffer + Cmd.ShadowOffset;
if (PropertyFlags & CPF_ZeroConstructor)
FMemory::Memzero(Dest,ElementSize * ArrayDim);
else
InitializeValueInternal(Dest);
CopyProperties(ShadowData, Source);
for (const FRepParentCmd& Parent : Parents)
// Array内部会处理其它数据
if (Parent.ArrayIndex == 0)
Parent.Property->CopyCompleteValue(ShadowData + Parent, Source + Parent);
if (PropertyFlags & CPF_IsPlainOldData)
FMemory::Memcpy( Dest, Src, ElementSize * ArrayDim );
else
CopyValuesInternal(Dest, Src, ArrayDim);
属性同步
Tick的时候进行检测:
void UNetDriver::TickFlush(float DeltaSeconds)
Updated = ServerReplicateActors(DeltaSeconds);
// 走UReplicationGraph
if (ReplicationDriver)
return ReplicationDriver->ServerReplicateActors(DeltaSeconds);
...
UActorChannel::ReplicateActor()
同步的主要内容:
int64 UActorChannel::ReplicateActor()
UNetConnection* OwningConnection = Actor->GetNetConnection();
// 新Actor
if (RepFlags.bNetInitial && OpenedLocally)
Connection->PackageMap->SerializeNewActor(Bunch, this, static_cast<AActor*&>(Actor));
Actor->OnSerializeNewActor(Bunch);
bWroteSomethingImportant |= ActorReplicator->ReplicateProperties(Bunch, RepFlags);
const bool bHasRepLayout = RepLayout->ReplicateProperties(SendingRepState, ChangelistMgr->GetRepChangelistState(), (uint8*)Object, ObjectClass, OwningChannel, Writer, RepFlags);
bWroteSomethingImportant |= DoSubObjectReplication(Bunch, RepFlags);
bWroteSomethingImportant |= UpdateDeletedSubObjects(Bunch);
if (bWroteSomethingImportant)
// SendBunch之后再聊
FPacketIdRange PacketRange = SendBunch( &Bunch, 1 );
for (auto RepComp = ReplicationMap.CreateIterator(); RepComp; ++RepComp)
RepComp.Value()->PostSendBunch(PacketRange, Bunch.bReliable);
属性变化检测
查找变化属性,写入ShadowMemory
拿之前记录的的ShadowData内的数据进行比较
// FObjectReplicator::ReplicateProperties
bool FObjectReplicator::ReplicateProperties_r( FOutBunch & Bunch, FReplicationFlags RepFlags, FNetBitWriter& Writer)
FNetSerializeCB::UpdateChangelistMgr(*RepLayout, SendingRepState, *ChangelistMgr, Object, Connection->Driver->ReplicationFrame, RepFlags, OwningChannel->bForceCompareProperties || bUseCheckpointRepState);
RepLayout.UpdateChangelistMgr(RepState, InChangelistMgr, InObject, ReplicationFrame, RepFlags, bForceCompare);
Result = CompareProperties(RepState, &InChangelistMgr.RepChangelistState, (const uint8*)InObject, RepFlags);
static void CompareParentProperties(const FComparePropertiesSharedParams& SharedParams, FComparePropertiesStackParams& StackParams)
CompareProperties_r(SharedParams, StackParams, Parent.CmdStart, Parent.CmdEnd, Cmd.RelativeHandle - 1);
if(!PropertiesAreIdentical(Cmd, ShadowData.Data, Data.Data, SharedParams.NetSerializeLayouts))
// PropertiesAreIdenticalNative(Cmd, A, B, NetSerializeLayouts)
// 将变化后的属性复制到Shadow内存
StoreProperty(Cmd, ShadowData.Data, Data.Data);
StackParams.Changed.Add(Handle);
如果是UStruct,默认的比较为:
bool FStructProperty::Identical( const void* A, const void* B, uint32 PortFlags ) const
return Struct->CompareScriptStruct(A, B, PortFlags);
bool UScriptStruct::CompareScriptStruct(const void* A, const void* B, uint32 PortFlags) const
// 遍历所有UProperty递归比较
for( TFieldIterator<FProperty> It(this); It; ++It )
for( int32 i=0; i<It->ArrayDim; i++ )
if( !It->Identical_InContainer(A,B,i,PortFlags) )
return false;
可以使用以下方式定制比较,以减少比较时间
bool Identical(const FXXX* Other, uint32 PortFlags) const;
template<>
struct TStructOpsTypeTraits<FXXX> : public TStructOpsTypeTraitsBase2<FXXX>
{
enum
{
WithIdentical = true,
};
};
发送数据
bool FObjectReplicator::ReplicateProperties_r( FOutBunch & Bunch, FReplicationFlags RepFlags, FNetBitWriter& Writer)
// 下面的Data是(uint8*)Object
const bool bHasRepLayout = RepLayout->ReplicateProperties(SendingRepState, ChangelistMgr->GetRepChangelistState(), (uint8*)Object, ObjectClass, OwningChannel, Writer, RepFlags);
// 同步条件发生变化的处理
if (RepState->RepFlags.Value != RepFlags.Value)
...
// 维护ChangeHistory
...
RepState->HistoryEnd++;
UpdateChangelistHistory(RepState, ObjectClass, Data, OwningChannel->Connection, &Changed);
BuildSharedSerialization(Data, Changed, true, RepChangelistState->SharedSerialization);
BuildSharedSerialization_r(HandleIterator, Data, bWriteHandle, bDoChecksum, 0, SharedInfo);
while (HandleIterator.NextHandle())
// 写入到:ChangelistMgr->GetRepChangelistState()->SharedSerialization
SharedInfo.WriteSharedProperty(Cmd, PropertyKey, HandleIterator.CmdIndex, HandleIterator.Handle, Data.Data, bWriteHandle, bDoChecksum);
// 写Handle,用于找Cmd也就是发生修改的属性的信息
WritePropertyHandle(*SerializedProperties, Handle, bDoChecksum);
Cmd.Property->NetSerializeItem(*SerializedProperties, nullptr, const_cast<uint8*>(Data.Data));
// 筛选掉非激活的
FilterChangeList(UnfilteredChanged, RepState->InactiveParents, NewlyInactiveChangelist, Changed);
// 发送
SendProperties(RepState, ChangeTracker, Data, ObjectClass, Writer, Changed, RepChangelistState->SharedSerialization, RepFlags.bSerializePropertyNames ? ESerializePropertyType::Name : ESerializePropertyType::Handle);
SendProperties_r(RepState, Writer, bDoChecksum, HandleIterator, Data, 0, &SharedInfo, SerializePropertyType);
while (HandleIterator.NextHandle())
// 发送Handle和属性值
WritePropertyHandle(Writer, HandleIterator.Handle, bDoChecksum);
Cmd.Property->NetSerializeItem(Writer, Writer.PackageMap, const_cast<uint8*>(Data.Data));
if ( RemoteFunctions != nullptr && RemoteFunctions->GetNumBits() > 0 )
Writer.SerializeBits( RemoteFunctions->GetData(), RemoteFunctions->GetNumBits() );
const bool WroteImportantData = Writer.GetNumBits() != 0;
if ( WroteImportantData )
OwningChannel->WriteContentBlockPayload( Object, Bunch, bHasRepLayout, Writer );
接收数据
在接收到一个包可以组成一个完整的Bunch后,处理这个Bunch的数据
void UActorChannel::ProcessBunch( FInBunch & Bunch )
Replicator->ReceivedBunch( Reader, RepFlags, bHasRepLayout, bHasUnmapped )
UObject* Object = GetObject();
const FRepLayout& LocalRepLayout = *RepLayout;
FReceivingRepState* ReceivingRepState = RepState->GetReceivingRepState();
if (bHasRepLayout)
bool bLocalHasUnmapped = false;
LocalRepLayout.ReceiveProperties(OwningChannel, ObjectClass, RepState->GetReceivingRepState(), Object, Bunch, bLocalHasUnmapped, bGuidsChanged, ReceivePropFlags)
if (ReceiveProperties_r(Params, StackParams))
static bool ReceivePropertyHelper(...)
const FRepLayoutCmd& Cmd = Cmds[CmdIndex];
// 客户端ShadowMemory,用于保存接收之前的值
StoreProperty(Cmd, ShadowData + Cmd, Data + SwappedCmd);
// 反序列化
Cmd.Property->NetSerializeItem(Bunch, Bunch.PackageMap, Data + SwappedCmd);
// 判断是否需要调用RepNotify
if (Parent.RepNotifyCondition == REPNOTIFY_Always || !PropertiesAreIdentical(Cmd, ShadowData + Cmd, Data + SwappedCmd, NetSerializeLayouts))
RepNotifies->AddUnique(Parent.Property);
bOutHasUnmapped |= bLocalHasUnmapped;
UObject同步
UObject都是通过OwnedActor进行同步
int64 UActorChannel::ReplicateActor()
bWroteSomethingImportant |= DoSubObjectReplication(Bunch, RepFlags);
// 两种同步的情况,参考AActor::bReplicateUsingRegisteredSubObjectList,大部分都是false,也就是如果有需要,要重载ReplicateSubobjects
if (Actor->IsUsingRegisteredSubObjectList())
bWroteSomethingImportant |= ReplicateRegisteredSubObjects(Bunch, OutRepFlags);
else
bWroteSomethingImportant |= Actor->ReplicateSubobjects(this, &Bunch, &OutRepFlags);
bool AActor::ReplicateSubobjects(UActorChannel *Channel, FOutBunch *Bunch, FReplicationFlags *RepFlags)
for (UActorComponent* ActorComp : ReplicatedComponents)
UActorChannel::SetCurrentSubObjectOwner(ActorComp);
UActorChannel::SetCurrentSubObjectOwner(this);
WroteSomething |= Channel->ReplicateSubobject(ActorComp, *Bunch, *RepFlags);
// 同上,参考UActorComponent::bReplicateUsingRegisteredSubObjectList
if (ReplicatedComponent->IsUsingRegisteredSubObjectList() && !DataChannelInternal::bTestingLegacyMethodForComparison)
const TStaticBitArray<COND_Max> ConditionMap = UE::Net::BuildConditionMapFromRepFlags(RepFlags);
bWroteSomethingImportant |= WriteComponentSubObjects(ReplicatedComponent, Bunch, RepFlags, ConditionMap);
else
return ReplicateSubobject(StaticCast<UObject*>(ReplicatedComponent), Bunch, RepFlags);
通过重写ReplicateSubobjects函数,支持增加额外的同步SubObjects,函数内调用的是UActorChannel::ReplicateSubobject
处理单个Object:Object同步一共是三部分,第一是Object本身(Load),第二是变化的属性,第三是指针的处理。
发送对象和属性变化
写入对象和属性变化如下:
bool UActorChannel::ReplicateSubobject(UObject* SubObj, FOutBunch& Bunch, FReplicationFlags RepFlags)
bWroteSomethingImportant = WriteSubObjectInBunch(SubObj, Bunch, RepFlags);
TSharedRef<FObjectReplicator>* FoundReplicator = FindReplicator(Obj);
TSharedRef<FObjectReplicator>& ObjectReplicator = !bFoundReplicator ? CreateReplicator(Obj) : *FoundReplicator;
// 从ObjectReplicator判定是否是新的Object
const bool bIsNewSubObject = (ObjectReplicator->bSentSubObjectCreation == false) || bNewToReplay;
if (bIsNewSubObject)
ObjRepFlags.bNetInitial = true;
// 同步属性,同上
bool bWroteSomething = ObjectReplicator.Get().ReplicateProperties(Bunch, ObjRepFlags);
// 新的对象且没有属性修改,直接调用WriteContentBlockHeader,写入整个Object
if (bIsNewSubObject && !bWroteSomething)
FNetBitWriter EmptyPayload;
WriteContentBlockPayload( Obj, Bunch, false, EmptyPayload );
// WriteContentBlockHeader( Obj, Bunch, bHasRepLayout );
Bunch.WriteBit( bHasRepLayout ? 1 : 0 );
Bunch.WriteBit( IsActor ? 1 : 0 );
// 序列化对象
Bunch << Obj;
if ( Connection->Driver->IsServer() )
if ( Obj->IsNameStableForNetworking() )
Bunch.WriteBit( 1 );
else
Bunch.WriteBit( 0 );
Bunch.WriteBit( 0 );
Bunch << ObjClass;
Bunch.WriteBit(bActorIsOuter ? 1 : 0)
if (!bActorIsOuter)
Bunch << ObjOuter;
bWroteSomething = true;
Object指针在服务器往客户端传递的时候,会生成一个NetGUID,存在UNetConnection::PackageMap(UPackageMapClient)::GuidCache内的Map内。所以同步的指针实际上只是GUID而已。
bool FObjectPropertyBase::NetSerializeItem( FArchive& Ar, UPackageMap* Map, void* Data, TArray<uint8> * MetaData ) const
UObject* Object = GetObjectPropertyValue(Data);
// GUID相关信息
UPackageMapClient::SerializeObject
// Ar.IsSaving()
FNetworkGUID NetGUID = GuidCache->GetOrAssignNetGUID( Object );
InternalWriteObject( Ar, NetGUID, Object, TEXT( "" ), NULL );
Ar << NetGUID;
// 存在路径的对象,通过Path本地StaticLoad出来后再关联GUID
if (ExportFlags.bHasPath)
FNetworkGUID OuterNetGUID = GuidCache->GetOrAssignNetGUID(ObjectOuter);
InternalWriteObject(Ar, OuterNetGUID, ObjectOuter, TEXT( "" ), nullptr);
Ar << ObjectPathName;
// 同步置空操作或是合法对象,才会进行设置
if (!Object || IsValidChecked(Object))
SetObjectPropertyValue(Data, Object);
服务器创建新的NetGUID的过程如下:
FNetworkGUID FNetGUIDCache::GetOrAssignNetGUID(UObject* Object, const TWeakObjectPtr<UObject>* WeakObjectPtr)
if (!Object || !SupportsObject(Object, &WeakObject))
IsNameStableForNetworking // RF_WasLoaded | RF_DefaultSubObject | RF_ClassDefaultObject、IsNative、IsDefaultSubobject
IsSupportedForNetworking // Actor都是Supported,也就是说UObject需要考虑这个,UActorComponent用的是bReplicates
return FNetworkGUID();
// 缓存的Object->GUID
FNetworkGUID NetGUID = NetGUIDLookup.FindRef(WeakObject);
if (NetGUID.IsValid())
// bReadOnly的处理
return NetGUID;
// 只在服务器允许生成GUID
if(!bIsNetGUIDAuthority)
return FNetworkGUID::GetDefault();
return AssignNewNetGUID_Server(Object);
// 是不是静态的
const int32 IsStatic = IsDynamicObject( Object ) ? 0 : 1;
// 下标静态和动态分开
const FNetworkGUID NewNetGuid = FNetworkGUID::CreateFromIndex(++NetworkGuidIndex[IsStatic], IsStatic != 0);
// 最后一位用于标记是否静态
NewGuid.ObjectId = NetIndex << 1 | (bIsStatic ? 1 : 0);
RegisterNetGUID_Server( NewNetGuid, Object );
// 创建FNetGuidCacheObject
FNetGuidCacheObject CacheObject;
// 初始化 Object、OuterGUID、PathName、NetworkChecksum、bNoLoad
// 加到ObjectLookup、NetGUIDLookup
RegisterNetGUID_Internal( NetGUID, CacheObject );
接收对象和属性变化
对于新的Object,在收到包的时候,直接读取GUID和加载对象,并进行关联
void UChannel::ReceivedRawBunch( FInBunch & Bunch, bool & bOutSkipAck )
if ( Bunch.bHasPackageMapExports && !Connection->IsInternalAck() )
Cast<UPackageMapClient>( Connection->PackageMap )->ReceiveNetGUIDBunch( Bunch );
int32 NumGUIDsInBunch = 0;
InBunch << NumGUIDsInBunch;
while( NumGUIDsRead < NumGUIDsInBunch )
UObject* Obj = NULL;
const FNetworkGUID LoadedGUID = InternalLoadObject( InBunch, Obj, 0 );
NumGUIDsRead++;
- 尝试本地加载Object
FNetworkGUID UPackageMapClient::InternalLoadObject( FArchive & Ar, UObject *& Object, const int32 InternalLoadObjectRecursionCount )
FNetworkGUID NetGUID;
Ar << NetGUID;
if ( NetGUID.IsValid() && !NetGUID.IsDefault() )
Object = GetObjectFromNetGUID( NetGUID, GuidCache->IsExportingNetGUIDBunch );
return GuidCache->GetObjectFromNetGUID( NetGUID, bIgnoreMustBeMapped );
FNetGuidCacheObject * CacheObjectPtr = ObjectLookup.Find( NetGUID );
if (Object == nullptr && bIsPackage)
Object = LoadPackage(nullptr, Path, LOAD_None);
// 存在路径的对象,通过Path本地StaticLoad出来后再关联GUID
if ( ExportFlags.bHasPath )
// 对于Path的Object,Outer应该是个Package,先加载
UObject* ObjOuter = NULL;
FNetworkGUID OuterGUID = InternalLoadObject( Ar, ObjOuter, InternalLoadObjectRecursionCount + 1 );
Ar << ObjectName;
// 如果Outer是空,说明本身已经是Package了
const bool bIsPackage = NetGUID.IsStatic() && !OuterGUID.IsValid();
// DefaultObject直接处理
if (NetGUID.IsDefault())
Object = StaticFindObject(UObject::StaticClass(), ObjOuter, *ObjectName, false);
NetGUID = GuidCache->GetOrAssignNetGUID( Object );
if (Object == nullptr && bIsPackage)
FPackagePath Path = FPackagePath::FromPackageNameChecked(ObjectName);
Object = LoadPackage(nullptr, Path, LOAD_None);
return NetGUID;
// 注册GUID,包含Path信息
GuidCache->RegisterNetGUIDFromPath_Client( NetGUID, ObjectName, OuterGUID, NetworkChecksum, ExportFlags.bNoLoad, bIgnoreWhenMissing );
const FNetGuidCacheObject* ExistingCacheObjectPtr = ObjectLookup.Find( NetGUID );
if ( ExistingCacheObjectPtr != NULL )
return;
FNetGuidCacheObject CacheObject;
CacheObject.PathName = FName( *PathName );
RegisterNetGUID_Internal( NetGUID, CacheObject );
// 从GUID-Path读取对象
Object = GuidCache->GetObjectFromNetGUID( NetGUID, GuidCache->IsExportingNetGUIDBunch );
FNetGuidCacheObject * CacheObjectPtr = ObjectLookup.Find( NetGUID );
// 找到OuterPackage
if ( CacheObjectPtr->OuterGUID.IsValid() )
FNetGuidCacheObject * OuterCacheObject = ObjectLookup.Find( CacheObjectPtr->OuterGUID );
ObjOuter = GetObjectFromNetGUID( CacheObjectPtr->OuterGUID, bIgnoreMustBeMapped );
// 通过Package和PathName找到、加载Object
Object = FindObjectFast<UObject>(ObjOuter, CacheObjectPtr->PathName);
if ( Object == NULL && !CacheObjectPtr->bNoLoad )
Object = StaticLoadObject( UObject::StaticClass(), ObjOuter, *CacheObjectPtr->PathName.ToString(), NULL, LOAD_NoWarn );
CacheObjectPtr->Object = Object;
NetGUIDLookup.Add( Object, NetGUID );
// 更新QueuedBunchObjectReferences内GUID索引的对象
UpdateQueuedBunchObjectReference(NetGUID, Object);
if (TWeakPtr<FQueuedBunchObjectReference>* WeakObjectReference = QueuedBunchObjectReferences.Find(NetGUID))
ObjectReference->Object = NewObject;
如果是运行时生成的Object,在以下位置处理本地生成:
void UActorChannel::ProcessBunch( FInBunch & Bunch )
// Actor处理...
while ( !Bunch.AtEnd() && Connection != NULL && Connection->GetConnectionState() != USOCK_Closed )
FNetBitReader Reader( Bunch.PackageMap, 0 );
UObject* RepObj = ReadContentBlockPayload( Bunch, Reader, bHasRepLayout );
UObject* RepObj = ReadContentBlockHeader( Bunch, bObjectDeleted, bOutHasRepLayout );
// 尝试加载
Connection->PackageMap->SerializeObject(Bunch, UObject::StaticClass(), SubObj, &NetGUID);
// Class
UObject* SubObjClassObj = nullptr;
if (bSerializeClass)
Connection->PackageMap->SerializeObject(Bunch, UObject::StaticClass(), SubObjClassObj, &ClassNetGUID);
UClass* SubObjClass = Cast< UClass >(SubObjClassObj);
// Outer
UObject*ObjOuter = Actor;
if (!bActorIsOuter)
Bunch << ObjOuter;
// 本地创建Object,并关联GUID
if (!SubObj)
SubObj = NewObject< UObject >(ObjOuter, SubObjClass);
Actor->OnSubobjectCreatedFromReplication( SubObj );
Connection->Driver->GuidCache->RegisterNetGUID_Client( NetGUID, SubObj );
Connection->Driver->GuidCache->ImportedNetGuids.Add( NetGUID );
TSharedRef< FObjectReplicator > & Replicator = FindOrCreateReplicator( RepObj );
bool bHasUnmapped = false;
// 属性同步
Replicator->ReceivedBunch( Reader, RepFlags, bHasRepLayout, bHasUnmapped )
// 引用了其它的还没有对应GUID的对象,加到将自己加到UnmappedReplicators
if ( bHasUnmapped )
Connection->Driver->UnmappedReplicators.Add( &Replicator.Get() );
// 移除对象的处理
TArray<TWeakObjectPtr<UObject>, TInlineAllocator<16>> ReferencesToRemove;
Connection->Driver->GetNetworkObjectList().RemoveMultipleSubObjectChannelReference(Actor, ReferencesToRemove, this);
客户端同步指针:
bool FObjectPropertyBase::NetSerializeItem( FArchive& Ar, UPackageMap* Map, void* Data, TArray<uint8> * MetaData ) const
bool UPackageMapClient::SerializeObject( FArchive& Ar, UClass* Class, UObject*& Object, FNetworkGUID *OutNetGUID)
// Ar.IsLoading()
// 同上
NetGUID = InternalLoadObject(Ar, Object, 0);
// TODO 没找到的情况
if ( NetGUID.IsValid() && bShouldTrackUnmappedGuids && !GuidCache->IsGUIDBroken( NetGUID, false ) )
if ( Object == nullptr )
TrackedUnmappedNetGuids.Add( NetGUID );
else if ( NetGUID.IsDynamic() )
TrackedMappedDynamicNetGuids.Add( NetGUID );
UObject指针同步
上面有提到,如果传递下来的指针,要么GUID在本地找到对象,要么是Static的对象可以本地加载,另外一种情况就是,当前指针对应的UObject还没有在本地创建。接下来就讲一下这种情况。
指针下来UObject没创建的情况,加入到TrackedUnmappedNetGuids内:
bool FObjectPropertyBase::NetSerializeItem( FArchive& Ar, UPackageMap* Map, void* Data, TArray<uint8> * MetaData ) const
bool UPackageMapClient::SerializeObject( FArchive& Ar, UClass* Class, UObject*& Object, FNetworkGUID *OutNetGUID)
if ( NetGUID.IsValid() && bShouldTrackUnmappedGuids && !GuidCache->IsGUIDBroken( NetGUID, false ) )
if ( Object == nullptr )
TrackedUnmappedNetGuids.Add( NetGUID );
Object内部存在这种情况的指针成员时,将ObjectReplicator加入到UnmappedReplicators内:
void UActorChannel::ProcessBunch( FInBunch & Bunch )
...
while ( !Bunch.AtEnd() && Connection != NULL && Connection->GetConnectionState() != USOCK_Closed )
...
TSharedRef< FObjectReplicator > & Replicator = FindOrCreateReplicator( RepObj );
bool bHasUnmapped = false;
// 属性同步
Replicator->ReceivedBunch( Reader, RepFlags, bHasRepLayout, bHasUnmapped )
bool bLocalHasUnmapped = false;
LocalRepLayout.ReceiveProperties(OwningChannel, ObjectClass, RepState->GetReceivingRepState(), Object, Bunch, bLocalHasUnmapped, bGuidsChanged, ReceivePropFlags)
const TSet<FNetworkGUID>& TrackedUnmappedGuids = Bunch.PackageMap->GetTrackedUnmappedGuids();
const bool bHasUnmapped = TrackedUnmappedGuids.Num()> 0;
bOutHasUnmapped |= bLocalHasUnmapped;
// 引用了其它的还没有对应GUID的对象,加到将自己加到UnmappedReplicators
if ( bHasUnmapped )
Connection->Driver->UnmappedReplicators.Add( &Replicator.Get() );
本地新生成的Object的GUID会加到ImportedNetGuids内:
bool UPackageMapClient::SerializeNewActor(FArchive& Ar, class UActorChannel *Channel, class AActor*& Actor)
if ( GuidCache.IsValid() )
GuidCache->ImportedNetGuids.Add(NetGUID);
UObject* UActorChannel::ReadContentBlockHeader(FInBunch& Bunch, bool& bObjectDeleted, bool& bOutHasRepLayout)
Connection->Driver->GuidCache->ImportedNetGuids.Add( NetGUID );
然后在Tick时,去更新UnmappedReplicators内引用的指针:
void UNetDriver::TickFlush(float DeltaSeconds)
if (!IsUsingIrisReplication())
UpdateUnmappedObjects();
void UNetDriver::UpdateUnmappedObjects()
TSet<FObjectReplicator*> ForceUpdateReplicators;
for (FObjectReplicator* Replicator : UnmappedReplicators)
if (Replicator->bForceUpdateUnmapped)
Replicator->bForceUpdateUnmapped = false;
ForceUpdateReplicators.Add(Replicator);
if (ImportedNetGuidsRef.Num() || ForceUpdateReplicators.Num())
for (auto It = ImportedNetGuidsRef.CreateIterator(); It; ++It)
// 本地已经创建Object了
if (GuidCache->GetObjectFromNetGUID(NetworkGuid, false) != nullptr)
NewlyMappedGuids.Add(NetworkGuid);
It.RemoveCurrent();
// 没找到的情况,Outer加到PendingOuterNetGuidsRef内(Outer : TSet<Sub GUID>)
if (!bMappedOrBroken)
const FNetworkGUID OuterGUID = GuidCache->GetOuterNetGUID(NetworkGuid);
TSet<FNetworkGUID>& PendingGuidsRef = PendingOuterNetGuidsRef.FindOrAdd(OuterGUID);
PendingGuidsRef.Add(NetworkGuid);
if (UnmappedGuids.Num())
ImportedNetGuidsRef.Append(UnmappedGuids);
for (const FNetworkGUID& NetGuid : NewlyMappedGuids)
...
for (FObjectReplicator* Replicator : ReplicatorsToUpdate)
if (UnmappedReplicators.Contains(Replicator))
Replicator->UpdateUnmappedObjects(bHasMoreUnmapped);
// 所有都解决了,可以移除了
if (!bHasMoreUnmapped)
UnmappedReplicators.Remove(Replicator);
void FObjectReplicator::UpdateUnmappedObjects(bool & bOutHasMoreUnmapped)
LocalRepLayout.UpdateUnmappedObjects(ReceivingRepState, Connection->PackageMap, Object, Parms, bCalledPreNetReceive, bSomeObjectsWereMapped, bOutHasMoreUnmapped);
UpdateUnmappedObjects_r(...)
// 遍历所有的引用的对象GUID
for (auto It = GuidReferencesMap->CreateIterator(); It; ++It)
FGuidReferences& GuidReferences = It.Value();
// 指针成员信息
const FRepLayoutCmd& Cmd = Cmds[GuidReferences.CmdIndex];
const int32 ShadowOffset = (AbsOffset - Cmd.Offset) + Cmd.ShadowOffset;
bool bMappedSomeGUIDs = GuidReferences.UpdateUnmappedGUIDs(Connection->PackageMap, OriginalObject, Cmd.Property, AbsOffset);
for (auto UnmappedIt = UnmappedGUIDs.CreateIterator(); UnmappedIt; ++UnmappedIt)
UObject* Object = InPackageMap->GetObjectFromNetGUID(GUID, false);
// 成功找到对象
if (Object != nullptr)
InPackageMap->RemoveUnmappedNetGUIDReference(GUID);
UnmappedIt.RemoveCurrent();
bMappedSomeGUIDs = true;
if (bMappedSomeGUIDs)
bOutSomeObjectsWereMapped = true;
StoreProperty(Cmd, ShadowData + ShadowOffset, Data + AbsOffset);
// 设置指针
Cmd.Property->NetSerializeItem(Reader, Connection->PackageMap, Data + AbsOffset);
// 添加到同步通知数组,同上方属性同步
if (Parent.RepNotifyCondition == REPNOTIFY_Always || !PropertiesAreIdentical(Cmd, ShadowData + ShadowOffset, Data + AbsOffset, NetSerializeLayouts))
RepState->RepNotifies.AddUnique(Parent.Property);
// 调用RepNotify
CallRepNotifies(false);
AActor同步
AActor大部分都是和UObject走的一样的,就是多了AActor相关的处理,例如SpawnActor、ActorChannel、Velocity
发送新的Actor
int64 UActorChannel::ReplicateActor()
// ObjectReplicator设置的Flag
if (RepFlags.bNetInitial && OpenedLocally)
Connection->PackageMap->SerializeNewActor(Bunch, this, static_cast<AActor*&>(Actor));
Actor->OnSerializeNewActor(Bunch);
接受新的Actor
如果是刚刚同步下的Actor,先存下GUID
void UActorChannel::ReceivedBunch( FInBunch & Bunch )
if (Actor == NULL && Bunch.bOpen)
Bunch << ActorNetGUID;
// 超时的话,需要加到Queue内之后处理(UNetDriver::ClientIncomingBunchFrameTimeLimitMS)
if (PendingGuidResolves.Num() > 0 || QueuedBunches.Num() > 0 || Connection->KeepProcessingActorChannelBunchesMap.Contains(ActorNetGUID) ||
Connection->Driver->ShouldQueueBunchesForActorGUID(ActorNetGUID) ||
Connection->Driver->HasExceededIncomingBunchFrameProcessingTime())
ProcessBunch(Bunch);
void UActorChannel::ProcessBunch( FInBunch & Bunch )
// 同步下新的Actor
if( Actor == NULL )
AActor* NewChannelActor = NULL;
bSpawnedNewActor = Connection->PackageMap->SerializeNewActor(Bunch, this, NewChannelActor);
反序列化,本地SpawnActor,并设置一些基础信息,关联GUID(中间会附带自身为UObject的处理)
bool UPackageMapClient::SerializeNewActor(FArchive& Ar, class UActorChannel *Channel, class AActor*& Actor)
FNetworkGUID NetGUID;
UObject *NewObj = Actor;
// 处理好UObject的流程,这个同上
SerializeObject(Ar, AActor::StaticClass(), NewObj, &NetGUID);
Channel->ActorNetGUID = NetGUID;
// 运行时创建的Actor(静态的以及处理好了)
if ( NetGUID.IsDynamic() )
if (Ar.IsSaving())
// 同步 Level、Transform、Velocity等
if ( Ar.IsLoading() )
Actor = World->SpawnActorAbsolute(Archetype->GetClass(), FTransform(Rotation, SpawnLocation), SpawnInfo);
// 速度、旋转等
GuidCache->RegisterNetGUID_Client(NetGUID, Actor);
CacheObject.Object = MakeWeakObjectPtr(const_cast<UObject*>(Object));
// 加到ObjectLookup、NetGUIDLookup
RegisterNetGUID_Internal( NetGUID, CacheObject );
设置ActorChannel信息
void UActorChannel::SetChannelActor(AActor* InActor, ESetChannelActorFlags Flags)
Actor = InActor;
bActorIsPendingKill = false;
if (Actor)
Connection->AddActorChannel(Actor, this);
// 创建本地ActorReplicator
ActorReplicator = FindOrCreateReplicator(Actor);
// 加到激活列表
Connection->Driver->GetNetworkObjectList().MarkActive(Actor, Connection, Connection->Driver);
TSharedPtr<FNetworkObjectInfo>* NetworkObjectInfoPtr = FindOrAdd(Actor, NetDriver);
return MarkActiveInternal(*NetworkObjectInfoPtr, Connection, NetDriver);
ActiveNetworkObjects.Add(ObjectInfo);
Connection->Driver->GetNetworkObjectList().ClearRecentlyDormantConnection(Actor, Connection, Connection->Driver);
TODO
同步底层保序
客户端连接过程