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;