文章目录
- 类型
- 同步初始化
- 创建 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、ShadowBufferNewRef->StartReplicating(this);
void FObjectReplicator::InitWithObject(UObject* InObject, UNetConnection* InConnection, bool bUseDefaultState)// 创建 FRepLayoutRepLayout = Connection->Driver->GetObjectClassRepLayout(ObjectClass);// 创建 FRepChangedPropertyTracker、FRepStateInitRecentProperties(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();// 处理对应的CmdRelativeHandle = InitFromProperty_r<ERepBuildType::Class>(SharedParams, StackParams);// CmdEnd = 终点+1Parents[ParentHandle].CmdEnd = Cmds.Num();// 增加一个Return的特殊CmdAddReturnCmd(Cmds);TArray<FLifetimeProperty> LifetimeProps;UObject* Object = InObjectClass->GetDefaultObject();// 调用CDO的GetLifetimeReplicatedProps,收集FLifetimeProperty到LifetimePropsObject->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;// 记录RepFuncif (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的特殊Cmdconst uint32 ArrayChecksum = AddArrayCmd(SharedParams, StackParams);FRepLayoutCmd& Cmd = SharedParams.Cmds.AddZeroed_GetRef();Cmd.Type = ERepLayoutCmdType::DynamicArray;InitFromProperty_r<BuildType>(SharedParams, NewStackParams);// 增加一个Return的特殊CmdAddReturnCmd(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,就不需要知道内部的结构了,所以只增加一个Cmdif (EnumHasAnyFlags(Struct->StructFlags, STRUCT_NetSerializeNative))SharedParams.bHasNetSerializeProperties = true;++StackParams.RelativeHandle;AddPropertyCmd(SharedParams, StackParams);return StackParams.RelativeHandle;// 和UClass::SetUpRuntimeReplicationData类似,递归增加Cmdreturn 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 typeCmd.Offset = StackParams.Offset;Cmd.ElementSize = Cmd.Property->ElementSize;Cmd.RelativeHandle = StackParams.RelativeHandle;Cmd.ParentIndex = SharedParams.ParentIndex;// 将Property的信息编码成uint32Cmd.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进行排序,可以最大程度的减少paddingbool operator< (const FParentCmdIndexAndAlignment& RHS) constreturn 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;// 内存对齐到新的AlignShadowOffset = 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)// 创建 FRepChangedPropertyTrackerTSharedPtr<FRepChangedPropertyTracker> RepChangedPropertyTracker = bCreateSendingState ? ConnectionDriver->FindOrCreateRepChangedPropertyTracker(MyObject) : nullptr;// 创建 FRepStateRepState = 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) constTUniquePtr<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) constconst 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) constFRepStateStaticBuffer ShadowData(AsShared());InitRepStateStaticBuffer(ShadowData, Source);return ShadowData;
// --- --- 初始化 ShadowBuffer
void FRepLayout::InitRepStateStaticBuffer(FRepStateStaticBuffer& ShadowData, const FConstRepObjectDataBuffer Source) constShadowData.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);elseInitializeValueInternal(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 );elseCopyValuesInternal(Dest, Src, ArrayDim);
属性同步
Tick的时候进行检测:
void UNetDriver::TickFlush(float DeltaSeconds)Updated = ServerReplicateActors(DeltaSeconds);// 走UReplicationGraphif (ReplicationDriver)return ReplicationDriver->ServerReplicateActors(DeltaSeconds);...UActorChannel::ReplicateActor()
同步的主要内容:
int64 UActorChannel::ReplicateActor()UNetConnection* OwningConnection = Actor->GetNetConnection();// 新Actorif (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 ) constreturn 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*)Objectconst 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()->SharedSerializationSharedInfo.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);// 判断是否需要调用RepNotifyif (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,也就是如果有需要,要重载ReplicateSubobjectsif (Actor->IsUsingRegisteredSubObjectList())bWroteSomethingImportant |= ReplicateRegisteredSubObjects(Bunch, OutRepFlags);elsebWroteSomethingImportant |= 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::bReplicateUsingRegisteredSubObjectListif (ReplicatedComponent->IsUsingRegisteredSubObjectList() && !DataChannelInternal::bTestingLegacyMethodForComparison)const TStaticBitArray<COND_Max> ConditionMap = UE::Net::BuildConditionMapFromRepFlags(RepFlags);bWroteSomethingImportant |= WriteComponentSubObjects(ReplicatedComponent, Bunch, RepFlags, ConditionMap);elsereturn 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判定是否是新的Objectconst bool bIsNewSubObject = (ObjectReplicator->bSentSubObjectCreation == false) || bNewToReplay;if (bIsNewSubObject)ObjRepFlags.bNetInitial = true;// 同步属性,同上bool bWroteSomething = ObjectReplicator.Get().ReplicateProperties(Bunch, ObjRepFlags);// 新的对象且没有属性修改,直接调用WriteContentBlockHeader,写入整个Objectif (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 );elseBunch.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 ) constUObject* Object = GetObjectPropertyValue(Data);// GUID相关信息UPackageMapClient::SerializeObject// Ar.IsSaving()FNetworkGUID NetGUID = GuidCache->GetOrAssignNetGUID( Object );InternalWriteObject( Ar, NetGUID, Object, TEXT( "" ), NULL );Ar << NetGUID;// 存在路径的对象,通过Path本地StaticLoad出来后再关联GUIDif (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、IsDefaultSubobjectIsSupportedForNetworking // Actor都是Supported,也就是说UObject需要考虑这个,UActorComponent用的是bReplicatesreturn FNetworkGUID();// 缓存的Object->GUIDFNetworkGUID NetGUID = NetGUIDLookup.FindRef(WeakObject);if (NetGUID.IsValid())// bReadOnly的处理return NetGUID;// 只在服务器允许生成GUIDif(!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 );// 创建FNetGuidCacheObjectFNetGuidCacheObject CacheObject;// 初始化 Object、OuterGUID、PathName、NetworkChecksum、bNoLoad// 加到ObjectLookup、NetGUIDLookupRegisterNetGUID_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出来后再关联GUIDif ( 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 );// 找到OuterPackageif ( CacheObjectPtr->OuterGUID.IsValid() )FNetGuidCacheObject * OuterCacheObject = ObjectLookup.Find( CacheObjectPtr->OuterGUID );ObjOuter = GetObjectFromNetGUID( CacheObjectPtr->OuterGUID, bIgnoreMustBeMapped );// 通过Package和PathName找到、加载ObjectObject = 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);// ClassUObject* SubObjClassObj = nullptr;if (bSerializeClass)Connection->PackageMap->SerializeObject(Bunch, UObject::StaticClass(), SubObjClassObj, &ClassNetGUID);UClass* SubObjClass = Cast< UClass >(SubObjClassObj);// OuterUObject*ObjOuter = Actor;if (!bActorIsOuter)Bunch << ObjOuter;// 本地创建Object,并关联GUIDif (!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的对象,加到将自己加到UnmappedReplicatorsif ( 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 ) constbool 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 ) constbool 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的对象,加到将自己加到UnmappedReplicatorsif ( 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(...)// 遍历所有的引用的对象GUIDfor (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);// 调用RepNotifyCallRepNotifies(false);
AActor同步
AActor大部分都是和UObject走的一样的,就是多了AActor相关的处理,例如SpawnActor、ActorChannel、Velocity
发送新的Actor
int64 UActorChannel::ReplicateActor()// ObjectReplicator设置的Flagif (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 )// 同步下新的Actorif( 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、NetGUIDLookupRegisterNetGUID_Internal( NetGUID, CacheObject );
设置ActorChannel信息
void UActorChannel::SetChannelActor(AActor* InActor, ESetChannelActorFlags Flags)Actor = InActor;bActorIsPendingKill = false;if (Actor)Connection->AddActorChannel(Actor, this);// 创建本地ActorReplicatorActorReplicator = 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
同步底层保序
客户端连接过程