UE网络-WithNetSharedSerializer详解

WithNetSharedSerialization是什么

在进行属性同步时, 每当一个属性发生变化都要发给关心该属性的所有Connection, 如果这些Connection共享一份, 那就可以节省很多内存, 序列化等时间.

一般与WithNetSerializer组合使用.

自定义结构体指定WithNetSharedSerialization

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
31
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,
WithNetSharedSerialization = true,
};
};

WithNetSharedSerialization映射到EStructFlags::STRUCT_NetSharedSerialization

构建UScriptStruct

在PreInit时候,构建UScriptStruct

img

WithNetSharedSerialization属性映射到EStructFlags::STRUCT_NetSharedSerialization

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

img

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

将EStructFlags::STRUCT_NetSharedSerialization映射到ERepLayoutCmdFlags::IsSharedSerialization

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

img

Struct类型为:FStructProperty

img

检测Struct->StructFlags是否包含STRUCT_NetSharedSerialization属性:

img

这样, 就将STRUCT_NetSharedSerialization属性映射成IsSharedSerialization, 并存储在Cmd.Flags中.

img

使用WithNetSharedSerialization

通过1-4,已经将WithNetSharedSerialization->EStructFlags::STRUCT_NetSharedSerialization->ERepLayoutCmdFlags::IsSharedSerialization的转化讲清楚了, 并最终存储在Cmd.Flags中. 这里所说的使用WithNetSharedSerialization其实包括EStructFlags::STRUCT_NetSharedSerialization和ERepLayoutCmdFlags::IsSharedSerialization的使用.

在序列化时候, 优先处理SharedSerialization属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//序列化流程
--UNetDriver.TickFlush()
|--UNetDriver.ServerReplicateActors()
| |--UNetDriver.ServerReplicateActors_ProcessPrioritizedActors()
| | |--UActorChannel.ReplicateActor()
| | | |--FObjectReplicator.ReplicateProperties()
| | | | |--FRepLayout.ReplicateProperties()
// 优先构建SharedSerialization属性
| | | | | |--FRepLayout.BuildSharedSerialization()
| | | | | |--FRepLayout.SendProperties()
| | | | | | |--FRepLayout.SendProperties_r()
| | | | | | | |--FStructProperty.NetSerializeItem()
| | | | | | | | |--FExampleStruct.NetSerialize()

| | | | | |--FRepLayout.SendProperties()
| | | | | | |--FRepLayout.SendProperties_r()
| | | | | | | |--FStructProperty.NetSerializeItem()
| | | | | | | | |--FExampleStruct.NetSerialize()

| | | | |--FObjectReplicator.ReplicateCustomDeltaProperties()
| | | | | |--FObjectReplicator.SendCustomDeltaProperty()
| | | | | | |--FNetSerializeCB.SendCustomDeltaProperty()
| | | | | | | |--FRepLayout.SendCustomDeltaProperty()
| | | | | | | | |--FExampleArray.NetDeltaSerialize()

都是在需要序列化时候, 优先构建SharedSerialization属性, 比如发送RPC和属性同步.

img

一个结构体标记为WithNetSharedSerialization, 会进行SharedSerialization吗?

不会. 为什么呢?

只有在FRepLayout.Cmds中的属性, 才会去判断是否为IsSharedSerialization

img

一般情况下, 单独将结构体标记为WithNetSharedSerialization不会有任何效果, 因为该结构体在构建Replayout时候会被分割成具体的一个个基础类型(float,int, byte等), 在检测是否IsSharedSerialization时候, 该属性就丢失了.

img
img

那么怎么才能不解析一个结构体, 将该结构体作为一个基础类型放入Cmd中呢?

标记为:WithNetSerializer就不会进行分割, 然后整体放入Cmd中.

img

官方示例:

img

自己实现的实例:

img

结果:

img