概述
UE只有Actor类有属性同步功能,Actor开启属性同步的前提是Actor的bReplicated属性为true,属性同步只有Server可以往Client同步,NetDriver类中负责发送和接收属性同步数据,在Server端每帧调用UNetDriver::TickFlush,将要同步的Actor的属性发送到所有连接的Client,在Client每帧调用UNetDriver::TickDispatch接收属性同步消息并修改对应Actor的属性
TickFlush(发送)
首先判断是Server并且客户端连接数量大于0,才会去执行ServerReplicateActors去同步属性
ServerReplicateActors_PrepConnections,将每个客户端连接的ViewTarget设置成PlayerController
构造ConsiderList数组,用于存放经过筛选后的需要复制的Actor,数组大小是用的GetNetworkObjectList().GetActiveObjects().Num(),我们看下为什么用这个设置数组大小
如果一个Actor打开了复制(Replicates=true),那么就会在NetDriver的NetworkObjectList中储存这个Actor,所以就解释了为什么要用这个数组的大小设置ConsiderList的大小
ServerReplicateActors_BuildConsiderList
依旧是遍历GetNetworkObjectList().GetActiveObjects()
然后根据一些条件对Actor进行过滤
这里是计算Actor的下一帧同步时间【World->TimeSeconds + UpdateDelayRandomStream.FRand() * ServerTickTime + ActorInfo->OptimalNetUpdateDelta : 1.0f / Actor->NetUpdateFrequency】
将需要同步的Actor加入ConsiderList中,将部分过滤掉的Actor(休眠的、远程权限为空的)从NetworkObjectList移除,下次属性复制时就不会遍历到这些过滤掉的Actor
遍历每一个客户端连接
ServerReplicateActors_PrioritizeActors
如果Actor还没有建立Channel(每个需要同步的Actor都有一个ActorChannel,用于收发同步消息),且与角色不相关(距离剔除),就跳过,这里的NetCullDistanceSequared就是Actor最大同步距离
还有只和Owner相关的,但当前连接不是Owner的 以及 休眠的Actor都跳过,最后对Actor按照优先级进行排序
ServerReplicateActors_ProcessPrioritizedActors
先遍历每个Actor,如果当前Actor没有对应的Channel,则创建一个Channel
去Channel的ReplicateActor执行Actor属性复制
如果Actor是第一次发送,则还需要包含Actor的创建信息(GUID、Actor的CDO信息),具体可见我的另一篇博文Actor第一次被创建时经历了什么
bWroteSomethingImportant是用来判断属性是否有变化,有变化后才会发送属性同步包
SendBunch中先创建一个FOutBunch数组,然后添加RPC指针类型参数包
最后遍历OutgoingBunches发送包,并记录Bunch在当前Packet中的顺序
发包就是调用NetConnection::FlushNet中调用LowLevelSebd方法发送的
TickDisPatch(接收)
待完成