static_assert(!HasDynamicStateIsBool || (HasFreeDynamicState && HasCloneDynamicState), "FNetSerializer must implement CloneDynamicState and FreeDynamicState when it has dynamic state.");
// We need to manually serialize the properties as there are some replicated properties that don't need to be replicated depending on the data // as is the case with some of the bools that are covered by the ReplicationFlags.
// We need to manually serialize the properties as there are some replicated properties that don't need to be replicated depending on the data // as is the case with some of the bools that are covered by the ReplicationFlags. const FReplicationStateDescriptor* Descriptor = StructNetSerializerConfigForHitResult.StateDescriptor.GetReference(); const FReplicationStateMemberDescriptor* MemberDescriptors = Descriptor->MemberDescriptors; const FReplicationStateMemberSerializerDescriptor* MemberSerializerDescriptors = Descriptor->MemberSerializerDescriptors; const FReplicationStateMemberDebugDescriptor* MemberDebugDescriptors = Descriptor->MemberDebugDescriptors;
// For now just use normal serialization for delta as this struct typically is a one off. voidFHitResultNetSerializer::SerializeDelta(FNetSerializationContext& Context, const FNetSerializeDeltaArgs& Args) { NetSerializeDeltaDefault<Serialize>(Context, Args); }
// We do a full quantize even though we wont necessarily serialize them. // 注:做了一次 Full Quantize FNetQuantizeArgs HitResultQuantizeArgs = {}; HitResultQuantizeArgs.NetSerializerConfig = &StructNetSerializerConfigForHitResult; HitResultQuantizeArgs.Source = Args.Source; HitResultQuantizeArgs.Target = NetSerializerValuePointer(&TargetValue.HitResult); StructNetSerializer->Quantize(Context, HitResultQuantizeArgs); }
if (Value0.ReplicationFlags != Value1.ReplicationFlags) { returnfalse; }
// Do a per member compare of relevant members const FReplicationStateDescriptor* Descriptor = StructNetSerializerConfigForHitResult.StateDescriptor.GetReference(); const FReplicationStateMemberDescriptor* MemberDescriptors = Descriptor->MemberDescriptors; const FReplicationStateMemberSerializerDescriptor* MemberSerializerDescriptors = Descriptor->MemberSerializerDescriptors; const FReplicationStateMemberDebugDescriptor* MemberDebugDescriptors = Descriptor->MemberDebugDescriptors;
// 在 OnPostFreezeNetSerializerRegistry 中,我们可以配置序列化器 // 并初始化一些需要使用的内容 // 通常是写自己的 StateDescriptor // Desriptor 属性包括 // 是否包括 Super Class 的 Descriptor // GetLifeTimeProperties 的属性 (class 适用) // 是否启用 FastArraySerializer // 等 void FHitResultNetSerializer::FNetSerializerRegistryDelegates::OnPostFreezeNetSerializerRegistry() { // Setup serializer { // In this case we want to build a descriptor based on the struct members rather than the serializer we try to register FReplicationStateDescriptorBuilder::FParameters Params; Params.SkipCheckForCustomNetSerializerForStruct = true;
// Had do comment this out as the size differs between editor and non-editor builds. //if (HitResultStruct->GetStructureSize() != 240 || HitResultStruct->GetMinAlignment() != 8) //{ // LowLevelFatalError(TEXT("%s Size: %d Alignment: %d"), TEXT("FHitResult layout has changed. Need to update FHitResultNetSerializer."), HitResultStruct->GetStructureSize(), HitResultStruct->GetMinAlignment()); //}
// 用于检查 Serializer 是否适配当前版本的 Struct,这里粗暴的使用了 MemberCount if (Descriptor->MemberCount > 32U) { LowLevelFatalError(TEXT("%s Has more than 32 replicated members."), TEXT("FHitResult has changed. Need to update FHitResultNetSerializer."), Descriptor->MemberCount); }
// Build property -> Member index lookup table FName PropertyNames[EPropertyName::PropertyName_ConditionallyReplicatedPropertyCount];
for (const FString& Name : ControllerNames) { UClass* TestClass = nullptr;
for (TObjectIterator<UClass> It; It; ++It) { UClass* Class = *It;
FString FullName = Name;
// Search for SomethingTestController and ControllerSomethingTest. The last is legacy FString PartialName1 = Name + TEXT("Controller"); FString PartialName2 = FString(TEXT("Controller")) + Name;
if (Class->IsChildOf<UGauntletTestController>()) { FString ClassName = Class->GetName(); if (ClassName == FullName || ClassName.EndsWith(PartialName1) || ClassName.EndsWith(PartialName2)) { // Gauntlet has a couple of test classes, so need to differentiate between "GauntletFooTest" and "GameFooTest". // that will both be launched via -gauntlet=FooTest bool GauntletDefault = ClassName.StartsWith(TEXT("Gauntlet"));
TestClass = Class;
// If not gauntlet stop searching if (!GauntletDefault) { break; } } } }
checkf(TestClass, TEXT("Could not find class for controller %s"), *Name);
// Important - add the controller first! Some controllers may trigger GC's which would // then result in them being collected... Controllers.Add(NewController); } }
for (auto Controller : Controllers) { Controller->OnInit(); } }
/** * Called prior to a map change */ virtualvoidOnPreMapChange(){}
/** * Called after a map change. GetCurrentMap() will now return the new map */ virtualvoidOnPostMapChange(UWorld* World){}
/** * Called periodically to let the controller check and control state */ virtualvoidOnTick(float TimeDelta){}
/** * Called when a state change is applied to the module. States are game-driven. * GetCurrentState() == OldState until this function returns */ virtualvoidOnStateChange(FName OldState, FName NewState){}
/// <summary> /// Returns information about how to configure our Unreal processes. For the most part the majority /// of Unreal tests should only need to override this function /// </summary>
/** * Function that gets called from within Map_Check to allow this actor to check itself * for any potential errors and register them with map check dialog. */ virtual void CheckForErrors();