# Unreal Iris(二)ReplicationState
ReplicationState 是 Unreal Iris Replication System 中用于描述对象同步状态。要完成对于状态数据的同步需要多个模块的分工合作:
- NetSerializer:针对网络同步对象的序列化和反序列化规则。
 - ReplicationStateDescriptor:针对 UObject 中各个成员,在 Replication System 中的如何说明以及应用何种规则的描述。
 - Protocol:上述两者结合,对 UObject 的成员进行描述组织,使其成为可以进行网络同步的数据载体,再通过特有的序列化规则将数据发送出去的协议。
 
# FPropertyNetSerializerInfo && FNetSerializer
FNetSerializer 是整个同步流程中数据处理的核心,负责数据的序列化、反序列化、增量序列化、增量反序列化等操作,里面的函数会在打包解包等过程中使用:
struct FNetSerializer  | |
{ | |
	uint32 Version; | |
	ENetSerializerTraits Traits; | |
NetSerializeFunction Serialize; // 序列化  | |
NetDeserializeFunction Deserialize; // 反序列化  | |
NetSerializeDeltaFunction SerializeDelta; // 增量序列化  | |
NetDeserializeDeltaFunction DeserializeDelta; // 增量反序列化  | |
NetQuantizeFunction Quantize; // 对象转 pod  | |
NetDequantizeFunction Dequantize; //pod 转对象  | |
	NetIsEqualFunction IsEqual; | |
	NetValidateFunction Validate; | |
	NetCloneDynamicStateFunction CloneDynamicState; | |
	NetFreeDynamicStateFunction FreeDynamicState; | |
NetCollectNetReferencesFunction CollectNetReferences; // 引用收集  | |
const FNetSerializerConfig* DefaultConfig;  | |
	uint16 QuantizedTypeSize; | |
	uint16 QuantizedTypeAlignment; | |
	uint16 ConfigTypeSize; | |
	uint16 ConfigTypeAlignment; | |
const TCHAR* Name;  | |
};  | 
FPropertyNetSerializerInfo 则用于描述单个 UClass 的数据加工规则,一个 UClass 可以支持多个 FPropertyNetSerializerInfo ,通过 IsSupported 接口对具体的 UClass 实例采用不同的加工方案。


序列化器的声明:
UE_NET_DECLARE_SERIALIZER(FInt8NetSerializer, IRISCORE_API);  | |
UE_NET_IMPLEMENT_SERIALIZER(FInt8NetSerializer);  | |
#define UE_NET_DECLARE_SERIALIZER(SerializerName, Api) struct Api SerializerName ## NetSerializerInfo  \ | |
{ \  | |
static const UE::Net::FNetSerializer Serializer; \  | |
static uint32 GetQuantizedTypeSize(); \  | |
static uint32 GetQuantizedTypeAlignment(); \  | |
static const FNetSerializerConfig* GetDefaultConfig(); \  | |
}; | |
/** Implement a serializer using the struct named SerializerName. */ | |
#define UE_NET_IMPLEMENT_SERIALIZER(SerializerName) const UE::Net::FNetSerializer SerializerName ## NetSerializerInfo::Serializer = UE::Net::TNetSerializer<SerializerName>::ConstructNetSerializer(TEXT(#SerializerName)); \ | |
uint32 SerializerName ## NetSerializerInfo::GetQuantizedTypeSize() { return UE::Net::TNetSerializerBuilder<SerializerName>::GetQuantizedTypeSize(); }; \  | |
uint32 SerializerName ## NetSerializerInfo::GetQuantizedTypeAlignment() { return UE::Net::TNetSerializerBuilder<SerializerName>::GetQuantizedTypeAlignment(); }; \  | |
const FNetSerializerConfig* SerializerName ## NetSerializerInfo::GetDefaultConfig() { return UE::Net::TNetSerializerBuilder<SerializerName>::GetDefaultConfig(); };  | 
RegisterDefaultPropertyNetSerializerInfos 注册时机在 IrisCoreModule 加载时触发:
void RegisterPropertyNetSerializerSelectorTypes()  | |
{ | |
using namespace UE::Net;  | |
using namespace UE::Net::Private;  | |
FPropertyNetSerializerInfoRegistry::Reset();  | |
FInternalNetSerializerDelegates::BroadcastPreFreezeNetSerializerRegistry();  | |
RegisterDefaultPropertyNetSerializerInfos();  | |
FPropertyNetSerializerInfoRegistry::Freeze();  | |
FInternalNetSerializerDelegates::BroadcastPostFreezeNetSerializerRegistry();  | |
} | |
void RegisterDefaultPropertyNetSerializerInfos()  | |
{ | |
	// Register supported types | |
	// Integer types | |
UE_NET_REGISTER_NETSERIALIZER_INFO(FInt8Property);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(FInt16Property);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(FIntProperty);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(FInt64Property);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(FUint8PropertyNetSerializerInfo);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(FUInt16Property);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(FUInt32Property);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(FUInt64Property);  | |
	// Enum types | |
UE_NET_REGISTER_NETSERIALIZER_INFO(FEnumAsBytePropertyNetSerializerInfo);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(FEnumPropertyNetSerializerInfo);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(FNetRoleNetSerializerInfo);  | |
	// Float types | |
UE_NET_REGISTER_NETSERIALIZER_INFO(FFloatProperty);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(FDoubleProperty);  | |
	// Object and field types | |
UE_NET_REGISTER_NETSERIALIZER_INFO(FObjectProperty);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(FWeakObjectProperty);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(FScriptInterfacePropertyNetSerializerInfo);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(FSoftObjectProperty);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(FFieldPathProperty);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_SoftObjectPath);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_SoftClassPath);  | |
	// String types | |
UE_NET_REGISTER_NETSERIALIZER_INFO(FNameProperty);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(FStrProperty);  | |
	// Special types | |
UE_NET_REGISTER_NETSERIALIZER_INFO(FNativeBoolPropertyNetSerializerInfo);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(FBitFieldPropertyNetSerializerInfo);  | |
	// Named structs that we support | |
UE_NET_REGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_Guid);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(NAME_Vector);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(NAME_Vector3f);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(NAME_Vector3d);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(NAME_Rotator);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(NAME_Quat);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(NAME_Quat4f);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(NAME_Quat4d);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_Vector_NetQuantize100);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_Vector_NetQuantize10);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_Vector_NetQuantize);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_Vector_NetQuantizeNormal);  | |
UE_NET_REGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_DateTime);  | |
} | 
如果定义了复杂的结构体,但是其中各个成员都有支持的序列化器,那么该结构体也是可以被自动识别并序列化的。
如果结构体重写了 bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess) 函数,需要在配置项中进行声明:
[/Script/IrisCore.ReplicationStateDescriptorConfig] | |
+SupportsStructNetSerializerList=(StructName=GameplayCueParameters)  | |
+SupportsStructNetSerializerList=(StructName=GameplayAbilityTargetData_LocationInfo)  | |
+SupportsStructNetSerializerList=(StructName=GameplayAbilityTargetData_ActorArray)  | |
+SupportsStructNetSerializerList=(StructName=GameplayAbilityTargetData_SingleTargetHit)  | |
+SupportsStructNetSerializerList=(StructName=LyraGameplayAbilityTargetData_SingleTargetHit)  | |
+SupportsStructNetSerializerList=(StructName=NetLevelVisibilityTransactionId)  | |
+SupportsStructNetSerializerList=(StructName=Vector2D)  | |
+SupportsStructNetSerializerList=(StructName=GameplayDebuggerNetPack)  | 
/** Metadata about a gameplay cue execution */ | |
USTRUCT(BlueprintType, meta = (HasNativeBreak = "/Script/GameplayAbilities.AbilitySystemBlueprintLibrary.BreakGameplayCueParameters", HasNativeMake = "/Script/GameplayAbilities.AbilitySystemBlueprintLibrary.MakeGameplayCueParameters"))  | |
struct GAMEPLAYABILITIES_API FGameplayCueParameters  | |
{ | |
	/** Optimized serializer */ | |
bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess);  | |
    //... | |
};  | 
# FPropertyReplicationStateDescriptorBuilder && FReplicationStateDescriptorBuilder
FPropertyReplicationStateDescriptorBuilder 用来生成对象的 FPropertyReplicationStateDescriptorBuilder 。生成过程中会把 UClass 内的成员分为四类:
FunctionsPropertyReplicationStateBuilde:RPC Function。
InitPropertyReplicationStateBuilder:只用于 Init 的成员属性。
LifetimeConditionalsReplicationStateBuilder:有特定的生命周期同步条件的成员属性。
RegularPropertyReplicationStateBuilder:常规属性,既可以在对象初始化的时候同步,也可以在生命周期的各个阶段同步。
每类成员会通过单独的 FPropertyReplicationStateDescriptorBuilder 生成对应的 FReplicationStateDescriptor 。 FReplicationStateDescriptor 大致结构如下:

可能为了提高寻址效率吧, FPropertyReplicationStateDescriptorBuilder 在创建 FReplicationStateDescriptor 的时候,会把指针内的对象和 FReplicationStateDescriptor 本身分配在一块连续的内存中,在接下里要介绍的结构中也有广泛运用。
# LifetimeConditionalsReplicationState
对象中需要参与同步的属性,需要提前在对象中声明其所需的同步周期:
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const  | 
并且指明哪些属性的同步规则,Unreal 提供了很多的宏用来干这些事情:
#define DOREPLIFETIME_WITH_PARAMS(c,v,params) \ | |
{ \  | |
FProperty* ReplicatedProperty = GetReplicatedProperty(StaticClass(), c::StaticClass(),GET_MEMBER_NAME_CHECKED(c,v)); \  | |
PRAGMA_DISABLE_DEPRECATION_WARNINGS \  | |
RegisterReplicatedLifetimeProperty(ReplicatedProperty, OutLifetimeProps, FixupParams<decltype(c::v)>(params)); \  | |
PRAGMA_ENABLE_DEPRECATION_WARNINGS \  | |
} | |
#define DOREPLIFETIME(c,v) DOREPLIFETIME_WITH_PARAMS(c,v,FDoRepLifetimeParams()) | |
#define DOREPLIFETIME_CONDITION(c,v,cond) \ | |
{ \  | |
static_assert(cond != COND_NetGroup, "COND_NetGroup cannot be used on replicated properties. Only when registering subobjects"); \  | |
FDoRepLifetimeParams LocalDoRepParams; \  | |
LocalDoRepParams.Condition = cond; \  | |
DOREPLIFETIME_WITH_PARAMS(c,v,LocalDoRepParams); \  | |
} | |
#define DOREPLIFETIME_WITH_PARAMS_FAST(c,v,params) \ | |
{ \  | |
static const bool bIsValid_##c_##v = ValidateReplicatedClassInheritance(StaticClass(), c::StaticClass(), TEXT(#v)); \  | |
const TCHAR* DoRepPropertyName_##c_##v(TEXT(#v)); \  | |
const NetworkingPrivate::FRepPropertyDescriptor PropertyDescriptor_##c_##v(DoRepPropertyName_##c_##v, (int32)c::ENetFields_Private::v, 1); \  | |
\ | |
PRAGMA_DISABLE_DEPRECATION_WARNINGS \  | |
RegisterReplicatedLifetimeProperty(PropertyDescriptor_##c_##v, OutLifetimeProps, FixupParams<decltype(c::v)>(params)); \  | |
PRAGMA_ENABLE_DEPRECATION_WARNINGS \  | |
} | 
定义好之后,提取出来大概长这个样子:

FMemberProperty 用于描述某个 UClass 中成员的基本信息,也就是每个 Property 的相关信息:
const FProperty* Property; //property 本身  | |
const UFunction* PropertyRepNotifyFunction; // 触发属性复制时的回调函数  | |
const FPropertyNetSerializerInfo* SerializerInfo; //property 数据加工器  | |
CreateAndRegisterReplicationFragmentFunc CreateAndRegisterReplicationFragmentFunction; // 创建 Fragement 函数(FPropertyNetSerializerInfo)  | |
EMemberPropertyTraits Traits; // Traits property  | |
ELifetimeCondition ReplicationCondition; // 同步生命周期条件  | |
uint16 ChangeMaskBits; // 记录成员内部的脏标记  | |
FMemoryLayoutUtil::FSizeAndAlignment ExternalSizeAndAlignment; // 访问结构化对象时所需的内存偏移  | 
FReplicationStateDescriptorBuilder 职责其实是把 UClass 内的 MetaData 提取出来,归类之后存储在 FPropertyReplicationStateDescriptorBuilder 的 FMemberProperty 和 FMemberFunction 中。然后调用 FPropertyReplicationStateDescriptorBuilder 的 Build 函数获取创建好的 FReplicationStateDescriptor 。
常用函数:
FReplicationStateDescriptorBuilder::CreateDescriptorsForClassFReplicationStateDescriptorBuilder::CreateDescriptorForStructFReplicationStateDescriptorBuilder::CreateDescriptorForFunction
转换流程:

# FReplicationStateDescriptor && FPropertyReplicationState
FReplicationStateDescriptor 用来描述一组成员的 MetaData,可以通过 FReplicationStateDescriptor 对对象属性进行偏移寻址、序列化反序列化等操作。是所有对象打解包的重要依仗,并且 FReplicationStateDescriptor 本身是根据类型绑定的,一个 UClass 可以关联多个存储了不同 MemberDescriptor 的 FReplicationStateDescriptor ,但是这个 UClass 所对应的 UObject 都可以通过这几个 FReplicationStateDescriptor 进行描述和访问。
FPropertyReplicationState 是 FReplicationStateDescriptor 的「实例化」。按照 FReplicationStateDescriptor 描述的规则构建出的网络层面的对象,其主要负责对 UObject 上的属性值做临时性的存储。
# FReplicationFragment
FReplicationFragment 是比 FPropertyReplicationState 更上一层级的抽象。负责关联 FReplicationStateDescriptor 和 FPropertyReplicationState ,并在此基础上关联 UObject 的实例,这样就可以通过 Fragment 快速对 UObject 进行各项操作。

这里以用的最多的 FPropertyReplicationFragment 展开一下:
- 通过 
FPropertyReplicationFragment把网络数据回写 UObject 实例:通过 FReplicationReader 读取网络中收到的数据包,交由FPropertyReplicationState::PushPropertyReplicationState把数据回写入 UObject 实例,然后把旧的 UObject 数据存储在 PrevReplicationState 中。 

- 通过 
FPropertyReplicationFragment把 UObject 实例中的脏数据提取:通过FPropertyReplicationFragment::PollReplicatedState再每帧开始同步前,根据脏标记从 UObject 实例上获取最新的脏数据,并且存储在 SrcReplicationState,后面再根据优先级序列化到 SendStateBuffer 中。 

# FReplicationInstanceProtocol && FReplicationProtocol
FReplicationInstanceProtocol 结构图(简单以 FPropertyReplicationFragment 举例):

FReplicationProtocol 结构图(简单以 FPropertyReplicationFragment 举例):

两者是基于 FPropertyReplicationFragment 更上层级的抽象,并且也是对外暴露的最开放的结构。
FReplicationProtocol更倾向于存储配置或者规则性质的内容。FReplicationInstanceProtocol则更多的存储和 Instance 有关的 State 信息。
# 总结
首先,除开 NetSerializerInfo 是在 Module 启动的时候就完成了所有类型的注册。其余的内容包括: Protocol、Fragment、ReplicationStateDescriptor、ReplicationState 都是在当有对象触发同步的时候以懒加载的方式创建并注册的。我们也可以简单整理出上述结构的关系图:

- FPropertyNetSerializerInfoRegistry 管理所有的 FPropertyNetSerializerInfo 进而管理所有的 FNetSerializer。它们在 IrisModule 启动的时候就被完全初始化好,以供后面的系统使用。
 - FReplicationStateDescriptorRegistry 管理所有的 FReplicationStateDescriptor。
 - FReplicationStateDescriptor 由存储若干个 FReplicationStateMemberSerializerDescriptor。并且保存了对应于 UClass 中 FProperty 的引用。
 - FReplicationStateMemberSerializerDescriptor 关联该 FProperty 的 FNetSerializer 用来处理打解包等序列化操作。
 - FReplicationProtocol 存储了 UClass 的全部描述信息(若干个 FReplicationStateDescriptor)
 - FPropertyReplicationState 保存若干个 FProperty 的描述(FReplicationStateDescriptor)和对应的数据(StateBuffer)。
 - FPropertyReplicationFragment 再次基础上关联了某个 UObject 实例,并实现了 StateBuffer <-> UObject 实例数据交换的逻辑。
 - FReplicationInstanceProtocol 管理多个 
FPropertyReplicationFragment,也就是一个完整 UObject 实例的全部数据的定义和存储。 - FReplicationProtocolManager 负责缓存所有已经创建的 FReplicationProtocol 避免重复调用构造函数。
 
除去 FNetSerializer 以外,其他结构的创建都从对象的 BeginReplication 开始:


整个 Replication System 其实是完全解耦于 Gameplay 的,不依赖 Gameplay 通过事件推送获取对象脏信息,而是通过 Poll 的轮询方式定期进行检查。数据的交互通过 ReplicationState 完成,除此以外其他任何模块都有内部独立的索引,引用关系等。