// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. #include "BlueprintCompilerCppBackendModulePrivatePCH.h" #include "BlueprintCompilerCppBackendUtils.h" #include "BlueprintCompilerCppBackend.h" #include "EdGraphSchema_K2.h" #include "Editor/UnrealEd/Public/Kismet2/StructureEditorUtils.h" #include "Engine/InheritableComponentHandler.h" FString FSafeContextScopedEmmitter::GetAdditionalIndent() const { return bSafeContextUsed ? TEXT("\t") : FString(); } bool FSafeContextScopedEmmitter::IsSafeContextUsed() const { return bSafeContextUsed; } FString FSafeContextScopedEmmitter::ValidationChain(const FBPTerminal* Term, FBlueprintCompilerCppBackend& CppBackend) { TArray SafetyConditions; for (; Term; Term = Term->Context) { if (!Term->IsStructContextType() && (Term->Type.PinSubCategory != TEXT("self"))) { ensure(!Term->bIsLiteral); SafetyConditions.Add(CppBackend.TermToText(Term, false)); } } FString Result; for (int32 Iter = SafetyConditions.Num() - 1; Iter >= 0; --Iter) { Result += FString(TEXT("IsValid(")); Result += SafetyConditions[Iter]; Result += FString(TEXT(")")); if (Iter) { Result += FString(TEXT(" && ")); } } return Result; } FSafeContextScopedEmmitter::FSafeContextScopedEmmitter(FString& InBody, const FBPTerminal* Term, FBlueprintCompilerCppBackend& CppBackend, const TCHAR* InCurrentIndent) : Body(InBody), bSafeContextUsed(false), CurrentIndent(InCurrentIndent) { const FString Conditions = ValidationChain(Term, CppBackend); if (!Conditions.IsEmpty()) { bSafeContextUsed = true; Body += CurrentIndent; Body += TEXT("if ("); Body += Conditions; Body += TEXT(")\n"); Body += CurrentIndent; Body += TEXT("{\n"); } } FSafeContextScopedEmmitter::~FSafeContextScopedEmmitter() { if (bSafeContextUsed) { Body += CurrentIndent; Body += TEXT("}\n"); } } void FEmitHelper::ArrayToString(const TArray& Array, FString& String, const TCHAR* Separator) { if (Array.Num()) { String += Array[0]; } for (int32 i = 1; i < Array.Num(); ++i) { String += TEXT(", "); String += Array[i]; } } bool FEmitHelper::HasAllFlags(uint64 Flags, uint64 FlagsToCheck) { return FlagsToCheck == (Flags & FlagsToCheck); } FString FEmitHelper::HandleRepNotifyFunc(const UProperty* Property) { check(Property); if (HasAllFlags(Property->PropertyFlags, CPF_Net | CPF_RepNotify)) { return FString::Printf(TEXT("ReplicatedUsing=%s"), *Property->RepNotifyFunc.ToString()); } else if (HasAllFlags(Property->PropertyFlags, CPF_Net)) { return TEXT("Replicated"); } return FString(); } bool FEmitHelper::MetaDataCanBeNative(const FName MetaDataName) { if (MetaDataName == TEXT("ModuleRelativePath")) { return false; } return true; } FString FEmitHelper::HandleMetaData(const UField* Field, bool AddCategory, TArray* AdditinalMetaData) { FString MetaDataStr; check(Field); UPackage* Package = Field->GetOutermost(); check(Package); const UMetaData* MetaData = Package->GetMetaData(); check(MetaData); const TMap* ValuesMap = MetaData->ObjectMetaDataMap.Find(Field); TArray MetaDataStrings; if (ValuesMap && ValuesMap->Num()) { for (auto& Pair : *ValuesMap) { if (!MetaDataCanBeNative(Pair.Key)) { continue; } if (!Pair.Value.IsEmpty()) { FString Value = Pair.Value.Replace(TEXT("\n"), TEXT("")); MetaDataStrings.Emplace(FString::Printf(TEXT("%s=\"%s\""), *Pair.Key.ToString(), *Value)); } else { MetaDataStrings.Emplace(Pair.Key.ToString()); } } } if (AddCategory && (!ValuesMap || !ValuesMap->Find(TEXT("Category")))) { MetaDataStrings.Emplace(TEXT("Category")); } if (AdditinalMetaData) { MetaDataStrings.Append(*AdditinalMetaData); } MetaDataStrings.Remove(FString()); if (MetaDataStrings.Num()) { MetaDataStr += TEXT("meta=("); ArrayToString(MetaDataStrings, MetaDataStr, TEXT(", ")); MetaDataStr += TEXT(")"); } return MetaDataStr; } #ifdef HANDLE_CPF_TAG static_assert(false, "Macro HANDLE_CPF_TAG redefinition."); #endif #define HANDLE_CPF_TAG(TagName, CheckedFlags) if (HasAllFlags(Flags, (CheckedFlags))) { Tags.Emplace(TagName); } TArray FEmitHelper::ProperyFlagsToTags(uint64 Flags) { TArray Tags; // EDIT FLAGS if (HasAllFlags(Flags, (CPF_Edit | CPF_EditConst | CPF_DisableEditOnInstance))) { Tags.Emplace(TEXT("VisibleDefaultsOnly")); } else if (HasAllFlags(Flags, (CPF_Edit | CPF_EditConst | CPF_DisableEditOnTemplate))) { Tags.Emplace(TEXT("VisibleInstanceOnly")); } else if (HasAllFlags(Flags, (CPF_Edit | CPF_EditConst))) { Tags.Emplace(TEXT("VisibleAnywhere")); } else if (HasAllFlags(Flags, (CPF_Edit | CPF_DisableEditOnInstance))) { Tags.Emplace(TEXT("EditDefaultsOnly")); } else if (HasAllFlags(Flags, (CPF_Edit | CPF_DisableEditOnTemplate))) { Tags.Emplace(TEXT("EditInstanceOnly")); } else if (HasAllFlags(Flags, (CPF_Edit))) { Tags.Emplace(TEXT("EditAnywhere")); } // BLUEPRINT EDIT if (HasAllFlags(Flags, (CPF_BlueprintVisible | CPF_BlueprintReadOnly))) { Tags.Emplace(TEXT("BlueprintReadOnly")); } else if (HasAllFlags(Flags, (CPF_BlueprintVisible))) { Tags.Emplace(TEXT("BlueprintReadWrite")); } // CONFIG if (HasAllFlags(Flags, (CPF_GlobalConfig | CPF_Config))) { Tags.Emplace(TEXT("GlobalConfig")); } else if (HasAllFlags(Flags, (CPF_Config))) { Tags.Emplace(TEXT("Config")); } // OTHER HANDLE_CPF_TAG(TEXT("Transient"), CPF_Transient) HANDLE_CPF_TAG(TEXT("DuplicateTransient"), CPF_DuplicateTransient) HANDLE_CPF_TAG(TEXT("TextExportTransient"), CPF_TextExportTransient) HANDLE_CPF_TAG(TEXT("NonPIEDuplicateTransient"), CPF_NonPIEDuplicateTransient) HANDLE_CPF_TAG(TEXT("Export"), CPF_ExportObject) HANDLE_CPF_TAG(TEXT("NoClear"), CPF_NoClear) HANDLE_CPF_TAG(TEXT("EditFixedSize"), CPF_EditFixedSize) HANDLE_CPF_TAG(TEXT("NotReplicated"), CPF_RepSkip) HANDLE_CPF_TAG(TEXT("Interp"), CPF_Edit | CPF_BlueprintVisible | CPF_Interp) HANDLE_CPF_TAG(TEXT("NonTransactional"), CPF_NonTransactional) HANDLE_CPF_TAG(TEXT("BlueprintAssignable"), CPF_BlueprintAssignable) HANDLE_CPF_TAG(TEXT("BlueprintCallable"), CPF_BlueprintCallable) HANDLE_CPF_TAG(TEXT("BlueprintAuthorityOnly"), CPF_BlueprintAuthorityOnly) HANDLE_CPF_TAG(TEXT("AssetRegistrySearchable"), CPF_AssetRegistrySearchable) HANDLE_CPF_TAG(TEXT("SimpleDisplay"), CPF_SimpleDisplay) HANDLE_CPF_TAG(TEXT("AdvancedDisplay"), CPF_AdvancedDisplay) HANDLE_CPF_TAG(TEXT("SaveGame"), CPF_SaveGame) //TODO: //HANDLE_CPF_TAG(TEXT("Instanced"), CPF_PersistentInstance | CPF_ExportObject | CPF_InstancedReference) return Tags; } TArray FEmitHelper::FunctionFlagsToTags(uint64 Flags) { TArray Tags; //Pointless: BlueprintNativeEvent, BlueprintImplementableEvent //Pointless: CustomThunk //TODO: SealedEvent //TODO: Unreliable //TODO: ServiceRequest, ServiceResponse HANDLE_CPF_TAG(TEXT("Exec"), FUNC_Exec) HANDLE_CPF_TAG(TEXT("Server"), FUNC_Net | FUNC_NetServer) HANDLE_CPF_TAG(TEXT("Client"), FUNC_Net | FUNC_NetClient) HANDLE_CPF_TAG(TEXT("NetMulticast"), FUNC_Net | FUNC_NetMulticast) HANDLE_CPF_TAG(TEXT("Reliable"), FUNC_NetReliable) HANDLE_CPF_TAG(TEXT("BlueprintCallable"), FUNC_BlueprintCallable) HANDLE_CPF_TAG(TEXT("BlueprintPure"), FUNC_BlueprintCallable | FUNC_BlueprintPure) HANDLE_CPF_TAG(TEXT("BlueprintAuthorityOnly"), FUNC_BlueprintAuthorityOnly) HANDLE_CPF_TAG(TEXT("BlueprintCosmetic"), FUNC_BlueprintCosmetic) HANDLE_CPF_TAG(TEXT("WithValidation"), FUNC_NetValidate) return Tags; } #undef HANDLE_CPF_TAG bool FEmitHelper::IsBlueprintNativeEvent(uint64 FunctionFlags) { return HasAllFlags(FunctionFlags, FUNC_Event | FUNC_BlueprintEvent | FUNC_Native); } bool FEmitHelper::IsBlueprintImplementableEvent(uint64 FunctionFlags) { return HasAllFlags(FunctionFlags, FUNC_Event | FUNC_BlueprintEvent) && !HasAllFlags(FunctionFlags, FUNC_Native); } FString FEmitHelper::EmitUFuntion(UFunction* Function, TArray* AdditinalMetaData) { TArray Tags = FEmitHelper::FunctionFlagsToTags(Function->FunctionFlags); auto FunctionOwnerClass = Function->GetOwnerClass(); if (FunctionOwnerClass->IsChildOf()) { Tags.Emplace(TEXT("BlueprintNativeEvent")); } const bool bMustHaveCategory = (Function->FunctionFlags & (FUNC_BlueprintCallable | FUNC_BlueprintPure)) != 0; Tags.Emplace(FEmitHelper::HandleMetaData(Function, bMustHaveCategory, AdditinalMetaData)); Tags.Remove(FString()); FString AllTags; FEmitHelper::ArrayToString(Tags, AllTags, TEXT(", ")); return FString::Printf(TEXT("UFUNCTION(%s)"), *AllTags); } int32 FEmitHelper::ParseDelegateDetails(UFunction* Signature, FString& OutParametersMacro, FString& OutParamNumberStr) { int32 ParameterNum = 0; FStringOutputDevice Parameters; for (TFieldIterator PropIt(Signature); PropIt && (PropIt->PropertyFlags & CPF_Parm); ++PropIt) { Parameters += ", "; PropIt->ExportCppDeclaration(Parameters, EExportedDeclaration::MacroParameter, NULL, EPropertyExportCPPFlags::CPPF_CustomTypeName); ++ParameterNum; } FString ParamNumberStr; switch (ParameterNum) { case 0: ParamNumberStr = TEXT(""); break; case 1: ParamNumberStr = TEXT("_OneParam"); break; case 2: ParamNumberStr = TEXT("_TwoParams"); break; case 3: ParamNumberStr = TEXT("_ThreeParams"); break; case 4: ParamNumberStr = TEXT("_FourParams"); break; case 5: ParamNumberStr = TEXT("_FiveParams"); break; case 6: ParamNumberStr = TEXT("_SixParams"); break; case 7: ParamNumberStr = TEXT("_SevenParams"); break; case 8: ParamNumberStr = TEXT("_EightParams"); break; default: ParamNumberStr = TEXT("_TooMany"); break; } OutParametersMacro = Parameters; OutParamNumberStr = ParamNumberStr; return ParameterNum; } TArray FEmitHelper::EmitSinglecastDelegateDeclarations(const TArray& Delegates) { TArray Results; for (auto It : Delegates) { check(It); auto Signature = It->SignatureFunction; check(Signature); FString ParamNumberStr, Parameters; ParseDelegateDetails(Signature, Parameters, ParamNumberStr); Results.Add(*FString::Printf(TEXT("DECLARE_DYNAMIC_DELEGATE%s(%s%s)"), *ParamNumberStr, *It->GetCPPType(NULL, EPropertyExportCPPFlags::CPPF_CustomTypeName), *Parameters)); } return Results; } TArray FEmitHelper::EmitMulticastDelegateDeclarations(UClass* SourceClass) { TArray Results; for (TFieldIterator It(SourceClass, EFieldIteratorFlags::ExcludeSuper); It; ++It) { auto Signature = It->SignatureFunction; check(Signature); FString ParamNumberStr, Parameters; ParseDelegateDetails(Signature, Parameters, ParamNumberStr); Results.Add(*FString::Printf(TEXT("DECLARE_DYNAMIC_MULTICAST_DELEGATE%s(%s%s)"), *ParamNumberStr, *It->GetCPPType(NULL, EPropertyExportCPPFlags::CPPF_CustomTypeName), *Parameters)); } return Results; } FString FEmitHelper::EmitLifetimeReplicatedPropsImpl(UClass* SourceClass, const FString& CppClassName, const TCHAR* InCurrentIndent) { FString Result; bool bFunctionInitilzed = false; for (TFieldIterator It(SourceClass, EFieldIteratorFlags::ExcludeSuper); It; ++It) { if ((It->PropertyFlags & CPF_Net) != 0) { if (!bFunctionInitilzed) { Result += FString::Printf(TEXT("%svoid %s::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const\n"), InCurrentIndent, *CppClassName); Result += FString::Printf(TEXT("%s{\n"), InCurrentIndent); Result += FString::Printf(TEXT("%s\tSuper::GetLifetimeReplicatedProps(OutLifetimeProps);\n"), InCurrentIndent); bFunctionInitilzed = true; } Result += FString::Printf(TEXT("%s\tDOREPLIFETIME( %s, %s);\n"), InCurrentIndent, *CppClassName, *It->GetNameCPP()); } } if (bFunctionInitilzed) { Result += FString::Printf(TEXT("%s}\n"), InCurrentIndent); } return Result; } FString FEmitHelper::GatherNativeHeadersToInclude(UField* SourceItem, const TArray& PersistentHeaders) { TSet HeaderFiles; HeaderFiles.Append(PersistentHeaders); { TArray ReferencedObjects; { FReferenceFinder ReferenceFinder(ReferencedObjects, NULL, false, false, false, true); TArray ObjectsToCheck; GetObjectsWithOuter(SourceItem, ObjectsToCheck, true); //CDO? for (auto Obj : ObjectsToCheck) { ReferenceFinder.FindReferences(Obj); } } auto EngineSourceDir = FPaths::EngineSourceDir(); auto GameSourceDir = FPaths::GameSourceDir(); auto CurrentPackage = SourceItem->GetTypedOuter(); for (auto Obj : ReferencedObjects) { auto Field = Cast(Obj); FString PackPath; if (Field && (Field->GetTypedOuter() != CurrentPackage) && FSourceCodeNavigation::FindClassHeaderPath(Field, PackPath)) { if (!PackPath.RemoveFromStart(EngineSourceDir)) { if (!PackPath.RemoveFromStart(GameSourceDir)) { PackPath = FPaths::GetCleanFilename(PackPath); } } HeaderFiles.Add(PackPath); } } } FString Result; for (auto HeaderFile : HeaderFiles) { Result += FString::Printf(TEXT("#include \"%s\"\n"), *HeaderFile); } return Result; } FString FEmitHelper::LiteralTerm(const FEdGraphPinType& Type, const FString& CustomValue, UObject* LiteralObject) { auto Schema = GetDefault(); if (UEdGraphSchema_K2::PC_String == Type.PinCategory) { return FString::Printf(TEXT("TEXT(\"%s\")"), *CustomValue.ReplaceCharWithEscapedChar()); } else if (UEdGraphSchema_K2::PC_Text == Type.PinCategory) { return FString::Printf(TEXT("FText::FromString(TEXT(\"%s\"))"), *CustomValue.ReplaceCharWithEscapedChar()); } else if (UEdGraphSchema_K2::PC_Float == Type.PinCategory) { float Value = CustomValue.IsEmpty() ? 0.0f : FCString::Atof(*CustomValue); return FString::Printf(TEXT("%f"), Value); } else if (UEdGraphSchema_K2::PC_Int == Type.PinCategory) { int32 Value = CustomValue.IsEmpty() ? 0 : FCString::Atoi(*CustomValue); return FString::Printf(TEXT("%d"), Value); } else if ((UEdGraphSchema_K2::PC_Byte == Type.PinCategory) || (UEdGraphSchema_K2::PC_Enum == Type.PinCategory)) { auto TypeEnum = Cast(Type.PinSubCategoryObject.Get()); if (TypeEnum) { return FString::Printf(TEXT("%s::%s"), *TypeEnum->GetName(), CustomValue.IsEmpty() ? *TypeEnum->GetEnumName(TypeEnum->NumEnums()-1) : *CustomValue); } else { uint8 Value = CustomValue.IsEmpty() ? 0 : FCString::Atoi(*CustomValue); return FString::Printf(TEXT("%u"), Value); } } else if (UEdGraphSchema_K2::PC_Boolean == Type.PinCategory) { const bool bValue = CustomValue.ToBool(); return bValue ? TEXT("true") : TEXT("false"); } else if (UEdGraphSchema_K2::PC_Name == Type.PinCategory) { return CustomValue.IsEmpty() ? TEXT("FName()") : FString::Printf(TEXT("FName(TEXT(\"%s\"))"), *(FName(*CustomValue).ToString().ReplaceCharWithEscapedChar())); } else if (UEdGraphSchema_K2::PC_Struct == Type.PinCategory) { auto StructType = Cast(Type.PinSubCategoryObject.Get()); ensure(StructType); if (StructType == TBaseStructure::Get()) { FVector Vect = FVector::ZeroVector; FDefaultValueHelper::ParseVector(CustomValue, /*out*/ Vect); return FString::Printf(TEXT("FVector(%f,%f,%f)"), Vect.X, Vect.Y, Vect.Z); } else if (StructType == TBaseStructure::Get()) { FRotator Rot = FRotator::ZeroRotator; FDefaultValueHelper::ParseRotator(CustomValue, /*out*/ Rot); return FString::Printf(TEXT("FRotator(%f,%f,%f)"), Rot.Pitch, Rot.Yaw, Rot.Roll); } else if (StructType == TBaseStructure::Get()) { FTransform Trans = FTransform::Identity; Trans.InitFromString(CustomValue); const FQuat Rot = Trans.GetRotation(); const FVector Translation = Trans.GetTranslation(); const FVector Scale = Trans.GetScale3D(); return FString::Printf(TEXT("FTransform( FQuat(%f,%f,%f,%f), FVector(%f,%f,%f), FVector(%f,%f,%f) )"), Rot.X, Rot.Y, Rot.Z, Rot.W, Translation.X, Translation.Y, Translation.Z, Scale.X, Scale.Y, Scale.Z); } else if (StructType == TBaseStructure::Get()) { FLinearColor LinearColor; LinearColor.InitFromString(CustomValue); return FString::Printf(TEXT("FLinearColor(%f,%f,%f,%f)"), LinearColor.R, LinearColor.G, LinearColor.B, LinearColor.A); } else if (StructType == TBaseStructure::Get()) { FColor Color; Color.InitFromString(CustomValue); return FString::Printf(TEXT("FColor(%d,%d,%d,%d)"), Color.R, Color.G, Color.B, Color.A); } if (StructType == TBaseStructure::Get()) { FVector2D Vect = FVector2D::ZeroVector; Vect.InitFromString(CustomValue); return FString::Printf(TEXT("FVector2D(%f,%f)"), Vect.X, Vect.Y); } else { //@todo: This needs to be more robust, since import text isn't really proper for struct construction. ensure(CustomValue.IsEmpty()); const FString StructName = FString(TEXT("F")) + StructType->GetName(); const FString ConstructBody = CustomValue.IsEmpty() ? (StructType->IsA() ? TEXT("::GetDefaultValue()") : TEXT("{}")) : CustomValue; return StructName + ConstructBody; } } else if (Type.PinSubCategory == UEdGraphSchema_K2::PSC_Self) { return TEXT("this"); } else if (UEdGraphSchema_K2::PC_Class == Type.PinCategory) { if (auto FoundClass = Cast(LiteralObject)) { return FString::Printf(TEXT("%s%s::StaticClass()"), FoundClass->GetPrefixCPP(), *FoundClass->GetName()); } return FString(TEXT("nullptr")); } else if((UEdGraphSchema_K2::PC_AssetClass == Type.PinCategory) || (UEdGraphSchema_K2::PC_Asset == Type.PinCategory)) { return LiteralObject ? FString::Printf(TEXT("FStringAssetReference(TEXT(\"%s\"))"), *(LiteralObject->GetPathName().ReplaceCharWithEscapedChar())) : FString(TEXT("nullptr")); } else if (UEdGraphSchema_K2::PC_Object == Type.PinCategory) { if (LiteralObject) { UClass* FoundClass = Cast(Type.PinSubCategoryObject.Get()); FString ClassString = FoundClass ? (FString(FoundClass->GetPrefixCPP()) + FoundClass->GetName()) : TEXT("UObject"); return FString::Printf(TEXT("LoadObject<%s>(nullptr, TEXT(\"%s\"))"), *ClassString, *(LiteralObject->GetPathName().ReplaceCharWithEscapedChar())); } else { return FString(TEXT("nullptr")); } } /* else if (CoerceProperty->IsA(UInterfaceProperty::StaticClass())) { if (Term->Type.PinSubCategory == PSC_Self) { return TEXT("this"); } else { ensureMsgf(false, TEXT("It is not possible to express this interface property as a literal value!")); return Term->Name; } } */ ensureMsgf(false, TEXT("It is not possible to express this type as a literal value!")); return CustomValue; } FString FEmitHelper::DefaultValue(const FEdGraphPinType& Type) { return LiteralTerm(Type, FString(), nullptr); } UFunction* FEmitHelper::GetOriginalFunction(UFunction* Function) { check(Function); const FName FunctionName = Function->GetFName(); UClass* Owner = Function->GetOwnerClass(); check(Owner); for (auto& Inter : Owner->Interfaces) { if (UFunction* Result = Inter.Class->FindFunctionByName(FunctionName)) { return GetOriginalFunction(Result); } } for (UClass* SearchClass = Owner->GetSuperClass(); SearchClass; SearchClass = SearchClass->GetSuperClass()) { if (UFunction* Result = SearchClass->FindFunctionByName(FunctionName)) { return GetOriginalFunction(Result); } } return Function; } bool FEmitHelper::ShoulsHandleAsNativeEvent(UFunction* Function) { check(Function); auto OriginalFunction = FEmitHelper::GetOriginalFunction(Function); check(OriginalFunction); if (OriginalFunction != Function) { const uint32 FlagsToCheckMask = EFunctionFlags::FUNC_Event | EFunctionFlags::FUNC_BlueprintEvent | EFunctionFlags::FUNC_Native; const uint32 FlagsToCheck = OriginalFunction->FunctionFlags & FlagsToCheckMask; auto OriginalOwner = OriginalFunction->GetOwnerClass(); const bool bBPInterface = Cast(OriginalOwner) && OriginalOwner->IsChildOf(); return (FlagsToCheck == FlagsToCheckMask) || bBPInterface; } return false; } bool FEmitHelper::ShoulsHandleAsImplementableEvent(UFunction* Function) { check(Function); auto OriginalFunction = FEmitHelper::GetOriginalFunction(Function); check(OriginalFunction); if (OriginalFunction != Function) { const uint32 FlagsToCheckMask = EFunctionFlags::FUNC_Event | EFunctionFlags::FUNC_BlueprintEvent | EFunctionFlags::FUNC_Native; const uint32 FlagsToCheck = OriginalFunction->FunctionFlags & FlagsToCheckMask; return (FlagsToCheck == (EFunctionFlags::FUNC_Event | EFunctionFlags::FUNC_BlueprintEvent)); } return false; } bool FEmitHelper::GenerateAssignmentCast(const FEdGraphPinType& LType, const FEdGraphPinType& RType, FString& OutCastBegin, FString& OutCastEnd) { // BYTE to ENUM cast auto LTypeEnum = Cast(LType.PinSubCategoryObject.Get()); if ((LType.PinCategory == UEdGraphSchema_K2::PC_Byte) && (RType.PinCategory == UEdGraphSchema_K2::PC_Byte) && !RType.PinSubCategoryObject.IsValid() && LTypeEnum) { FString EnumCppType = !LTypeEnum->CppType.IsEmpty() ? LTypeEnum->CppType : LTypeEnum->GetName(); OutCastBegin = FString::Printf(TEXT("static_cast<%s>("), *EnumCppType); OutCastEnd = TEXT(")"); return true; } return false; } void FEmitDefaultValueHelper::OuterGenerate(const UProperty* Property, const uint8* DataContainer, const FString OuterPath, const uint8* OptionalDefaultDataContainer, FString& OutResult, bool bAllowProtected) { if (Property->HasAnyPropertyFlags(CPF_EditorOnly)) { UE_LOG(LogK2Compiler, Log, TEXT("FEmitDefaultValueHelper Skip EditorOnly property: %s"), *Property->GetPathName()); return; } if (Property->HasAnyPropertyFlags(CPF_NativeAccessSpecifierPrivate) || (!bAllowProtected && Property->HasAnyPropertyFlags(CPF_NativeAccessSpecifierProtected))) { UE_LOG(LogK2Compiler, Warning, TEXT("FEmitDefaultValueHelper Cannot access property: %s"), *Property->GetPathName()); return; } const bool bStaticArray = (Property->ArrayDim > 1); for (int32 ArrayIndex = 0; ArrayIndex < Property->ArrayDim; ++ArrayIndex) { if (!OptionalDefaultDataContainer || !Property->Identical_InContainer(DataContainer, OptionalDefaultDataContainer, ArrayIndex)) { const uint8* ValuePtr = Property->ContainerPtrToValuePtr(DataContainer, ArrayIndex); const FString ArrayPost = bStaticArray ? FString::Printf(TEXT("[%d]"), ArrayIndex) : TEXT(""); const FString PathToMember = FString::Printf(TEXT("%s%s%s"), *OuterPath, *Property->GetNameCPP(), *ArrayPost); InnerGenerate(Property, ValuePtr, PathToMember, OutResult); } } } FString FEmitDefaultValueHelper::GenerateGetDefaultValue(const UUserDefinedStruct* Struct) { check(Struct); const FString StructName = FString(TEXT("F")) + Struct->GetName(); FString Result = FString::Printf(TEXT("\tstatic %s GetDefaultValue()\n\t{\n\t\t%s DefaultData__;\n"), *StructName, *StructName); FStructOnScope StructData(Struct); FStructureEditorUtils::Fill_MakeStructureDefaultValue(Struct, StructData.GetStructMemory()); for (auto Property : TFieldRange(Struct)) { OuterGenerate(Property, StructData.GetStructMemory(), TEXT("\t\tDefaultData__."), nullptr, Result); } Result += TEXT("\n\t\treturn DefaultData__;\n\t}\n"); return Result; } void FEmitDefaultValueHelper::InnerGenerate(const UProperty* Property, const uint8* ValuePtr, const FString& PathToMember, FString& OutResult) { auto OneLineConstruction = [](const UProperty* LocalProperty, const uint8* LocalValuePtr, FString& OutResult) -> bool { FString ValueStr; bool bComplete = true; if (!HandleSpecialTypes(LocalProperty, LocalValuePtr, ValueStr)) { auto StructProperty = Cast(LocalProperty); LocalProperty->ExportTextItem(ValueStr, LocalValuePtr, LocalValuePtr, nullptr, EPropertyPortFlags::PPF_ExportCpp); if (ValueStr.IsEmpty() && StructProperty) { check(StructProperty->Struct); ValueStr = FString::Printf(TEXT("F%s()"), *StructProperty->Struct->GetName()); bComplete = false; } if (ValueStr.IsEmpty()) { UE_LOG(LogK2Compiler, Warning, TEXT("FEmitDefaultValueHelper Cannot generate initilization: %s"), *LocalProperty->GetPathName()); } } OutResult += ValueStr; return bComplete; }; auto StructProperty = Cast(Property); check(!StructProperty || StructProperty->Struct); auto ArrayProperty = Cast(Property); check(!ArrayProperty || ArrayProperty->Inner); if (!Property->GetOuter()->IsA()) { FString ValueStr; const bool bComplete = OneLineConstruction(Property, ValuePtr, ValueStr); OutResult += FString::Printf(TEXT("%s = %s;\n"), *PathToMember, *ValueStr); if (bComplete && !ArrayProperty) { return; } } if (StructProperty) { const FString LocalPathToMember = FString::Printf(TEXT("%s."), *PathToMember); for (auto LocalProperty : TFieldRange(StructProperty->Struct)) { OuterGenerate(LocalProperty, ValuePtr, LocalPathToMember, nullptr, OutResult); } } if (ArrayProperty) { FScriptArrayHelper ScriptArrayHelper(ArrayProperty, ValuePtr); for (int32 Index = 0; Index < ScriptArrayHelper.Num(); ++Index) { const uint8* LocalValuePtr = ScriptArrayHelper.GetRawPtr(Index); FString ValueStr; const bool bComplete = OneLineConstruction(ArrayProperty->Inner, LocalValuePtr, ValueStr); OutResult += FString::Printf(TEXT("%s.Add(%s);\n"), *PathToMember, *ValueStr); if (!bComplete) { const FString LocalPathToMember = FString::Printf(TEXT("%s[%d]"), *PathToMember, Index); InnerGenerate(ArrayProperty->Inner, LocalValuePtr, LocalPathToMember, OutResult); } } } } bool FEmitDefaultValueHelper::HandleSpecialTypes(const UProperty* Property, const uint8* ValuePtr, FString& OutResult) { //TODO: Use Path maps for Objects if (auto StructProperty = Cast(Property)) { if (TBaseStructure::Get() == StructProperty->Struct) { check(ValuePtr); const FTransform* Transform = reinterpret_cast(ValuePtr); const auto Rotation = Transform->GetRotation(); const auto Translation = Transform->GetTranslation(); const auto Scale = Transform->GetScale3D(); OutResult = FString::Printf(TEXT("FTransform(FQuat(%f, %f, %f, %f), FVector(%f, %f, %f), FVector(%f, %f, %f))") , Rotation.X, Rotation.Y, Rotation.Z, Rotation.W , Translation.X, Translation.Y, Translation.Z , Scale.X, Scale.Y, Scale.Z); return true; } if (TBaseStructure::Get() == StructProperty->Struct) { const FVector* Vector = reinterpret_cast(ValuePtr); OutResult = FString::Printf(TEXT("FVector(%f, %f, %f)"), Vector->X, Vector->Y, Vector->Z); return true; } } return false; } bool FEmitDefaultValueHelper::HandleNonNativeComponent(UBlueprintGeneratedClass* BPGC, FName Name, bool bNew, const FString MemberPath, FString& OutResult) { USCS_Node* Node = nullptr; for (UBlueprintGeneratedClass* NodeOwner = BPGC; NodeOwner && !Node; NodeOwner = Cast(NodeOwner->GetSuperClass())) { Node = NodeOwner->SimpleConstructionScript ? NodeOwner->SimpleConstructionScript->FindSCSNode(Name) : nullptr; } if (!Node) { return false; } ensure(bNew == (Node->GetOuter() == BPGC->SimpleConstructionScript)); UObject* ComponentTemplate = bNew ? Node->ComponentTemplate : (BPGC->InheritableComponentHandler ? BPGC->InheritableComponentHandler->GetOverridenComponentTemplate(Node) : nullptr); if (!ComponentTemplate) { return false; } auto ComponentClass = ComponentTemplate->GetClass(); if (bNew) { OutResult += FString::Printf(TEXT("%s = CreateDefaultSubobject<%s%s>(TEXT(\"%s\"));\n") , *MemberPath, ComponentClass->GetPrefixCPP(), *ComponentClass->GetName(), *ComponentTemplate->GetName()); } UObject* ParentTemplateComponent = bNew ? nullptr : Node->GetActualComponentTemplate(Cast(BPGC->GetSuperClass())); ensure(bNew || ParentTemplateComponent); ensure(ParentTemplateComponent != ComponentTemplate); ensure(!ParentTemplateComponent || (ParentTemplateComponent->GetClass() == ComponentClass)); const UObject* ObjectToCompare = bNew ? ComponentClass->GetDefaultObject(false) : ParentTemplateComponent; const FString LocalPathToMember = FString::Printf(TEXT("%s->"), *MemberPath); for (auto Property : TFieldRange(ComponentClass)) { OuterGenerate(Property , reinterpret_cast(ComponentTemplate), LocalPathToMember , reinterpret_cast(ObjectToCompare), OutResult); } return true; } FString FEmitDefaultValueHelper::GenerateConstructor(UClass* InBPGC) { auto BPGC = CastChecked(InBPGC); const FString CppClassName = FString(BPGC->GetPrefixCPP()) + BPGC->GetName(); FString Result; Result += FString::Printf(TEXT("%s::%s(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)\n{\n"), *CppClassName, *CppClassName); UObject* CDO = BPGC->GetDefaultObject(false); UObject* ParentCDO = BPGC->GetSuperClass()->GetDefaultObject(false); check(CDO && ParentCDO); const bool bIsActor = BPGC->IsChildOf(); for (auto Property : TFieldRange(BPGC)) { const bool bNewProperty = Property->GetOwnerStruct() == BPGC; const bool bIsAccessible = bNewProperty || !Property->HasAnyPropertyFlags(CPF_NativeAccessSpecifierPrivate); if (bIsAccessible) { auto ObjectProperty = Cast(Property); const bool bComponentProp = ObjectProperty && ObjectProperty->PropertyClass && ObjectProperty->PropertyClass->IsChildOf(); const bool bNullValue = ObjectProperty && (nullptr == ObjectProperty->GetPropertyValue_InContainer(CDO)); if (bIsActor && bComponentProp && bNullValue) { if (HandleNonNativeComponent(BPGC, Property->GetFName(), bNewProperty, FString::Printf(TEXT("\t%s"), *Property->GetNameCPP()), Result)) { continue; } } //1. Instanced object //2. Component templates outside SCS // FindComponentTemplateByName in UCLass // Random Referenced Objects in UClass (Timelines, other Templates) - Let's fill them while creating CDO ? //3. Native, changed component ??? OuterGenerate(Property, reinterpret_cast(CDO), TEXT("\t"), bNewProperty ? nullptr : reinterpret_cast(ParentCDO), Result, true); } } Result += TEXT("\n}\n"); return Result; }