Files
UnrealEngineUWP/Engine/Source/Developer/CQTest/Public/ObjectBuilder.h
sebastian lewicki 695acb1980 Upgrading the CQTest plugin to an Engine module
Introducing the CQTestEnhancedInput plugin as this requires use of the EnhancedInput Engine Plugin (Engine modules cannot make use of the Engine Plugin)
#jira UE-217806
#rb Devin.Doucette, Jerome.Delattre, rob.huyett, sean.sweeney

[CL 36039088 by sebastian lewicki in ue5-main branch]
2024-09-05 10:24:20 -04:00

321 lines
10 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Misc/Build.h"
#if WITH_AUTOMATION_WORKER
#include "Containers/Array.h"
#include "UObject/UObjectBaseUtility.h"
#include "UObject/UObjectGlobals.h"
#include "UObject/Package.h"
#include "Components/SpawnHelper.h"
DEFINE_LOG_CATEGORY_STATIC(LogObjectBuilder, Log, Log);
template <typename TUObject>
class TObjectBuilder final
{
public:
TObjectBuilder(UObject* InOwner = static_cast<UObject*>(GetTransientPackage()))
: Object(NewObject<TUObject>(InOwner))
{
static_assert(std::is_base_of<UObject, TUObject>::value, "Template param must be a UObject type");
static_assert(!std::is_base_of<AActor, TUObject>::value, "Template param must not be an Actor type, supply a FSpawnHelper to the constructor for Actor configuration");
check(Object);
}
TObjectBuilder(FSpawnHelper& Spawner, UClass* Clazz = nullptr)
: Object(nullptr)
{
static_assert(std::is_base_of<AActor, TUObject>::value, "Template param must be an Actor type, use the default constructor for non-actor UObjects");
FActorSpawnParameters DeferredConstructionParams;
DeferredConstructionParams.bDeferConstruction = true;
Object = &Spawner.SpawnActor<TUObject>(DeferredConstructionParams, Clazz);
}
template <typename T>
TObjectBuilder<TUObject>& SetParam(const FName& InPropertyName, T InValue)
{
using Raw = std::decay_t<T>;
if (!Object)
{
UE_LOG(LogObjectBuilder, Error, TEXT("Tried to SetParam on property %s after Spawning. ObjectBuilder can only be used to paramaterise pre-spawn"), *InPropertyName.ToString());
return *this;
}
auto TrySet = [&](auto Property) {
if (!Property)
{
UE_LOG(LogObjectBuilder, Error, TEXT("Failed to find %s property %s on object %s, check type and Property name are correct"), *GetTypeName<Raw>(), *InPropertyName.ToString(), *Object->GetName());
return;
}
if (!IsCompatibleWith<Raw>(*Property))
{
UE_LOG(LogObjectBuilder, Error, TEXT("Type mismatch: Tried to set %s property %s on object %s to a %s"), *Property->GetCPPType(nullptr, 0), *InPropertyName.ToString(), *Object->GetName(), *GetTypeName<Raw>());
return;
}
if (auto TypeMatched = Property->template ContainerPtrToValuePtr<Raw>(Object))
{
*TypeMatched = InValue;
}
else
{
UE_LOG(LogObjectBuilder, Error, TEXT("Container type mismatch: Could not cast from %s to %s"), *Property->GetCPPType(nullptr, 0), *GetTypeName<Raw>());
}
};
if constexpr (TIsArray<Raw>::Value)
{
TrySet(FindFProperty<FArrayProperty>(Object->GetClass(), InPropertyName));
}
else if constexpr (TIsTMap<Raw>::Value)
{
TrySet(FindFProperty<FMapProperty>(Object->GetClass(), InPropertyName));
}
else if constexpr (TIsTSet<Raw>::Value)
{
TrySet(FindFProperty<FSetProperty>(Object->GetClass(), InPropertyName));
}
else
{
if (auto Property = FindFProperty<FProperty>(Object->GetClass(), InPropertyName))
{
if (this->template IsCompatibleWith<Raw>(*Property))
{
Property->SetValue_InContainer(Object, &InValue);
}
else
{
UE_LOG(LogObjectBuilder, Error, TEXT("Type mismatch: Tried to set %s property %s on object %s to a %s"), *Property->GetCPPType(nullptr, 0), *InPropertyName.ToString(), *Object->GetName(), *this->template GetTypeName<Raw>());
}
}
else
{
UE_LOG(LogObjectBuilder, Error, TEXT("Failed to find %s property %s on object %s, check type and Property name are correct"), *GetTypeName<Raw>(), *InPropertyName.ToString(), *Object->GetName());
}
}
return *this;
}
template<typename T = TUObject>
T& Spawn(FTransform InTransform = FTransform::Identity)
{
if (!Object)
{
UE_LOG(LogObjectBuilder, Error, TEXT("Tried to spawn from builder multiple times. Builder can only spawn a single object"));
auto* DefaultObject = NewObject<T>();
return *DefaultObject;
}
if constexpr (std::is_base_of_v<AActor, T>)
{
Object->FinishSpawning(InTransform);
}
auto& ObjectRef = *Object;
Object = nullptr;
return ObjectRef;
}
template<typename TComponent, typename T = TUObject>
TObjectBuilder<TUObject>& AddComponentTo(TComponent* InComponentToAdd = nullptr)
{
static_assert(std::is_base_of_v<AActor, T>, "Can only add components to AActors");
static_assert(std::is_base_of<UActorComponent, TComponent>::value, "Template param must be a UActorComponent type");
if (!Object)
{
UE_LOG(LogObjectBuilder, Error, TEXT("Tried to AddComponentTo actor after Spawning. ObjectBuilder can only be used to paramaterise pre-spawn"));
return *this;
}
TComponent* Component = InComponentToAdd;
if (Component)
{
Component->Rename(nullptr, Object);
}
else
{
Component = NewObject<TComponent>(Object);
check(Component);
}
Component->RegisterComponent();
return *this;
}
template<typename TChildActorType, typename T = TUObject>
TObjectBuilder<TUObject>& AddChildActorComponentTo()
{
static_assert(std::is_base_of_v<AActor, T>, "Can only add components to AActors");
static_assert(std::is_base_of<AActor, TChildActorType>::value, "Template param must be an Actor type");
if (!Object)
{
UE_LOG(LogObjectBuilder, Error, TEXT("Tried to AddChildActorComponentTo actor after Spawning. ObjectBuilder can only be used to paramaterise pre-spawn"));
return *this;
}
auto* Component = NewObject<UChildActorComponent>(Object);
Component->SetChildActorClass(TChildActorType::StaticClass());
Component->RegisterComponent();
return *this;
}
private:
TUObject* Object;
template <typename T>
bool IsCompatibleWith(const FProperty& Prop) const
{
using Raw = std::decay_t<T>;
if constexpr (std::is_pointer_v<T> && std::is_base_of_v<UObject, std::remove_pointer_t<Raw>>)
{
if (auto AsProperty = CastField<FObjectProperty>(&Prop))
{
return std::remove_pointer_t<Raw>::StaticClass()->IsChildOf(AsProperty->PropertyClass);
}
}
else if constexpr (TIsTObjectPtr<Raw>::Value)
{
if (auto AsProperty = CastField<FObjectProperty>(&Prop))
{
return Raw::ElementType::StaticClass()->IsChildOf(AsProperty->PropertyClass);
}
}
else if constexpr (TIsTArray<Raw>::Value)
{
if (auto AsProperty = CastField<FArrayProperty>(&Prop))
{
return IsCompatibleWith<typename Raw::ElementType>(*AsProperty->Inner);
}
}
else if constexpr (TIsTSet<Raw>::Value)
{
if (auto AsProperty = CastField<FSetProperty>(&Prop))
{
return IsCompatibleWith<typename Raw::ElementType>(*AsProperty->ElementProp);
}
}
else if constexpr (TIsTMap<Raw>::Value)
{
if (auto AsProperty = CastField<FMapProperty>(&Prop))
{
using Key = typename Raw::KeyInitType;
using Value = typename Raw::ValueInitType;
return IsCompatibleWith<Key>(*AsProperty->KeyProp) && IsCompatibleWith<Value>(*AsProperty->ValueProp);
}
}
else if constexpr (TIsEnum<Raw>::Value)
{
if (auto AsProperty = CastField<FEnumProperty>(&Prop))
{
return AsProperty->GetUnderlyingProperty()->GetSize() == sizeof(T);
}
}
else if constexpr (std::is_base_of_v<UObject, Raw>)
{
if (auto AsProperty = CastField<FObjectProperty>(&Prop))
{
return Raw::StaticClass()->IsChildOf(AsProperty->PropertyClass);
}
}
else if constexpr (std::is_same_v<FVector, Raw>)
{
if (auto AsProperty = CastField<FStructProperty>(&Prop))
{
return AsProperty->Struct->GetFName() == NAME_Vector;
}
}
else if constexpr (std::is_same_v<FStructProperty, typename TPropType<Raw>::Value>)
{
if (auto AsProperty = CastField<FStructProperty>(&Prop))
{
return AsProperty->Struct->GetName() == Raw::StaticStruct()->GetName();
}
}
else
{
using PropType = typename TPropType<Raw>::Value;
if (auto AsProperty = CastField<PropType>(&Prop))
{
return PropType::StaticClass()->IsChildOf(AsProperty->GetClass());
}
}
return false;
}
template<typename T>
FString GetTypeName() const
{
using Raw = std::decay_t<T>;
if constexpr (std::is_pointer_v<Raw> && std::is_base_of_v<UObject, std::remove_pointer_t<Raw>>)
{
return FString::Printf(TEXT("%s*"), *std::remove_pointer_t<Raw>::StaticClass()->GetName());
}
else if constexpr (TIsTObjectPtr<Raw>::Value)
{
using PtrType = typename Raw::ElementType;
return FString::Printf(TEXT("TObjectPtr<%s>"), *GetTypeName<PtrType>());
}
else if constexpr (TIsTArray<Raw>::Value)
{
using ElementType = typename Raw::ElementType;
return FString::Printf(TEXT("TArray<%s>"), *GetTypeName<ElementType>());
}
else if constexpr (TIsTSet<Raw>::Value)
{
using ElementType = typename Raw::ElementType;
return FString::Printf(TEXT("TSet<%s>"), *GetTypeName<ElementType>());
}
else if constexpr (TIsTMap<Raw>::Value)
{
using Key = typename Raw::KeyInitType;
using Value = typename Raw::ValueInitType;
return FString::Printf(TEXT("TMap<%s, %s>"), *GetTypeName<Key>(), *GetTypeName<Value>());
}
else if constexpr (TIsEnum<Raw>::Value)
{
return TEXT("EnumProperty");
}
else if constexpr (std::is_same_v<FVector, Raw>)
{
return TEXT("FVector");
}
else if constexpr (std::is_base_of_v<UObject, Raw>)
{
return FString::Printf(TEXT("%s"), *Raw::StaticClass()->GetName());
}
else if constexpr (std::is_same_v<FStructProperty, typename TPropType<Raw>::Value>)
{
return Raw::StaticStruct()->GetName();
}
else
{
return TNameOf<Raw>::GetName();
}
}
template<typename T> struct TPropType { using Value = FStructProperty; };
template <> struct TPropType<int8> { using Value = FInt8Property; };
template <> struct TPropType<uint8> { using Value = FByteProperty; };
template <> struct TPropType<int16> { using Value = FInt16Property; };
template <> struct TPropType<uint16> { using Value = FUInt16Property; };
template <> struct TPropType<int32> { using Value = FIntProperty; };
template <> struct TPropType<uint32> { using Value = FUInt32Property; };
template <> struct TPropType<int64> { using Value = FInt64Property; };
template <> struct TPropType<uint64> { using Value = FUInt64Property; };
template <> struct TPropType<bool> { using Value = FBoolProperty; };
template <> struct TPropType<float> { using Value = FFloatProperty; };
template <> struct TPropType<double> { using Value = FDoubleProperty; };
template <> struct TPropType<FName> { using Value = FNameProperty; };
};
#endif // WITH_AUTOMATION_WORKER