属性同步

时间:2024-10-02 07:44:56

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;