UE网络-WithNetSerializer详解

WithNetSerializer是什么

在网络同步过程中, 序列化使用自定义结构体自身序列化函数:NetSerialize.

自定义结构体指定WithNetSerializer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
USTRUCT()
struct FExampleStruct1
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY()
int32 ExampleIntProperty;

/**
* @param Ar FArchive to read or write from.
* @param Map PackageMap used to resolve references to UObject*
* @param bOutSuccess return value to signify if the serialization was succesfull (if false, an error will be logged by the calling function)
*
* @return return true if the serialization was fully mapped. If false, the property will be considered 'dirty' and will replicate again on the next update.
* This is needed for UActor* properties. If an actor's Actorchannel is not fully mapped, properties referencing it must stay dirty.
* Note that UPackageMap::SerializeObject returns false if an object is unmapped. Generally, you will want to return false from your ::NetSerialize
* if you make any calls to ::SerializeObject that return false.
*
*/
bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess);
};

template<>
struct TStructOpsTypeTraits< FExampleStruct1 > : public TStructOpsTypeTraitsBase2< FExampleStruct1 >
{
enum
{
WithNetSerializer = true,
};
};

将WithNetSerializer映射到EStructFlags::STRUCT_NetSerializeNative

构建UScriptStruct

在PreInit时候,构建UScriptStruct

将WithNetSerializer属性映射到EStructFlags::STRUCT_NetSerializeNative

构建UScriptStruct, 通过模板, 指定WithNetSerializer属性.

好了, 到此为止, 已经将我们指定的WithNetSerializer映射到EStructFlags::STRUCT_NetSerializeNative上了. 即UScriptStruct.StructFlags已经包含EStructFlags::STRUCT_NetSerializeNative属性.

将EStructFlags::STRUCT_NetSerializeNative映射到ERepParentFlags::IsNetSerialize

在创建RepLayout时(例如:FRepLayout::InitFromClass), 设置RepStruct信息.

这样, 就将STRUCT_NetSerializeNative属性映射成IsNetSerialize, 并存储在FRepParentCmd.Flags中.

使用ERepParentFlags::IsNetSerialize

通过1-4,已经将WithNetSerializer->EStructFlags::STRUCT_NetSerializeNative->ERepParentFlags::IsNetSerialize的转化讲清楚了, 并最终存储在FRepParentCmd.Flags中. 这里所说的使用ERepParentFlags::IsNetSerialize其实包括EStructFlags::STRUCT_NetSerializeNative和ERepParentFlags::IsNetSerialize的使用.

在序列化时候, 调用自身定义函数

1
2
3
4
5
6
7
8
9
10
--UNetDriver.TickFlush()
|--UNetDriver.ServerReplicateActors()
| |--UNetDriver.ServerReplicateActors_ProcessPrioritizedActors()
| | |--UActorChannel.ReplicateActor()
| | | |--FObjectReplicator.ReplicateProperties()
| | | | |--FRepLayout.ReplicateProperties()
| | | | | |--FRepLayout.SendProperties()
| | | | | | |--FRepLayout.SendProperties_r()
| | | | | | | |--FStructProperty.NetSerializeItem()
| | | | | | | | |--FExampleStruct.NetSerialize()

重点

RepLayout中针对WithNetSerializer结构体布局

这里还要着重说一下, 声明了WithNetSerializer类型的RepLayout布局. 对于WithNetSerializer类型的结构体, 其ParentCmd和Cmd和其他类型是不同的. 无论该结构体有多少属性需要同步, 都仅仅设置为Struct本身.

以FRepMovement为例, ParentCmd为:

Cmd为:

自定义结构体FExampleStruct, Parent布局类型:

Cmd布局类型:

关键代码:

针对WithNetSerializer如何进行Compare

对于FRepMovement,在初始化CmdType属性的时候设置为ERepLayoutCmdType::RepMovement

所以比较函数为:

对于FExampleStruct在初始化CmdType属性的时候,设置为ERepLayoutCmdType::Property.

所以比较函数为:

最终, 调用的是

引用:

流程图:https://github.com/fdcumt/DrawIO