/* * Tencent is pleased to support the open source community by making Puerts available. * Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. * Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may * be subject to their corresponding license terms. This file is subject to the terms and conditions defined in file 'LICENSE', * which is part of this source code package. */ #include "PEBlueprintAsset.h" #include "Modules/ModuleManager.h" #include "KismetCompilerModule.h" #include "Kismet2/KismetEditorUtilities.h" #include "Kismet2/BlueprintEditorUtils.h" #include "AssetToolsModule.h" #if (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1) || ENGINE_MAJOR_VERSION > 5 #include "AssetRegistry/AssetRegistryModule.h" #else #include "AssetRegistryModule.h" #endif #include "FileHelpers.h" #include "Misc/PackageName.h" #include "UObject/MetaData.h" #include "CoreGlobals.h" #include "K2Node_FunctionEntry.h" #include "EdGraphSchema_K2_Actions.h" #include "K2Node_Event.h" #include "K2Node_CustomEvent.h" #include "K2Node_FunctionResult.h" #include "GameFramework/InputSettings.h" #include "K2Node_InputAxisEvent.h" #include "K2Node_InputAction.h" #include "K2Node_CallFunction.h" #include "K2Node_ComponentBoundEvent.h" #include "ScopedTransaction.h" #include "Kismet/KismetSystemLibrary.h" #include "TypeScriptGeneratedClass.h" #include "TypeScriptBlueprint.h" #include "utility" #include "PuertsModule.h" #include "Engine/SimpleConstructionScript.h" #include "Engine/SCS_Node.h" #include "Kismet2/ComponentEditorUtils.h" #include "Editor.h" #include "HAL/PlatformFileManager.h" #include "Framework/Application/SlateApplication.h" #include "Misc/MessageDialog.h" #define LOCTEXT_NAMESPACE "UPEBlueprintAsset" DEFINE_LOG_CATEGORY_STATIC(PuertsEditorModule, Log, All); static bool IsPlaying() { return GEditor && IPuertsModule::IsInPIEMode(); } #define CanChangeCheckWithBoolRet() \ if (IsPlaying()) \ { \ UE_LOG(PuertsEditorModule, Error, TEXT("change the layout of class[%s] in PIE mode is forbiden!"), \ *GeneratedClass->GetName()); \ NeedSave = false; \ return false; \ } #define CanChangeCheck() \ if (IsPlaying()) \ { \ UE_LOG(PuertsEditorModule, Error, TEXT("change the layout of class[%s] in PIE mode is forbiden!"), \ *GeneratedClass->GetName()); \ NeedSave = false; \ return; \ } bool UPEBlueprintAsset::Existed(const FString& InName, const FString& InPath) { FString BPPath = FString(TEXT(TS_BLUEPRINT_PATH)) / InName + TEXT(".uasset"); if (BPPath[0] == TEXT('/') || BPPath[0] == TEXT('\\')) { BPPath = BPPath.Mid(1); } BPPath = FPaths::ProjectContentDir() / BPPath; IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile(); return PlatformFile.FileExists(*BPPath); } bool UPEBlueprintAsset::LoadOrCreate( const FString& InName, const FString& InPath, UClass* ParentClass, int32 InSetFlags, int32 InClearFlags) { FString PackageName = FString(TEXT("/Game" TS_BLUEPRINT_PATH)) / InPath / InName; // UE_LOG(LogTemp, Warning, TEXT("LoadOrCreate.PackageName: %s"), *PackageName); Blueprint = LoadObject(nullptr, *PackageName, nullptr, LOAD_NoWarn | LOAD_NoRedirects); if (Blueprint) { GeneratedClass = Blueprint->GeneratedClass; if (auto TypeScriptGeneratedClass = Cast(GeneratedClass)) { HasConstructor = TypeScriptGeneratedClass->HasConstructor; } Package = Cast(Blueprint->GetOuter()); if (Blueprint->ParentClass != ParentClass) { CanChangeCheckWithBoolRet(); Blueprint->ParentClass = ParentClass; NeedSave = true; } else { NeedSave = false; } return true; } if (!ParentClass) return false; if (IsPlaying()) { UE_LOG(PuertsEditorModule, Error, TEXT("create class[%s] in PIE mode is forbiden!"), *InName); return false; } NeedSave = true; UClass* BlueprintClass = UBlueprint::StaticClass(); UClass* BlueprintGeneratedClass = UTypeScriptGeneratedClass::StaticClass(); if (!ParentClass->IsChildOf(AActor::StaticClass())) { BlueprintClass = UTypeScriptBlueprint::StaticClass(); } // UE_LOG(LogTemp, Warning, TEXT("BlueprintClass: %s"), *BlueprintClass->GetName()); // UE_LOG(LogTemp, Warning, TEXT("BlueprintGeneratedClass: %s"), *BlueprintGeneratedClass->GetName()); // FString Name; // FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked("AssetTools"); // AssetToolsModule.Get().CreateUniqueAssetName(PackageName, TEXT(""), PackageName, Name); // UE_LOG(LogTemp, Warning, TEXT("Name: %s, PackageName: %s, InName:%s, InPath:%s"), *Name, *PackageName, *InName, *InPath); #if ENGINE_MINOR_VERSION < 26 && ENGINE_MAJOR_VERSION <= 4 Package = CreatePackage(NULL, *PackageName); #else Package = CreatePackage(*PackageName); #endif check(Package); EBlueprintType BlueprintType = ParentClass && ParentClass->IsChildOf(UBlueprintFunctionLibrary::StaticClass()) ? BPTYPE_FunctionLibrary : BPTYPE_Normal; // Create and init a new Blueprint Blueprint = FKismetEditorUtilities::CreateBlueprint( ParentClass, Package, *InName, BlueprintType, BlueprintClass, BlueprintGeneratedClass, FName("PuertsAutoGen")); if (Blueprint) { // Notify the asset registry FAssetRegistryModule::AssetCreated(Blueprint); // Mark the package dirty... Package->MarkPackageDirty(); GeneratedClass = Blueprint->GeneratedClass; return true; } else { return false; } } bool UPEBlueprintAsset::LoadOrCreateWithMetaData(const FString& InName, const FString& InPath, UClass* InParentClass, int32 InSetFlags, int32 InClearFlags, UPEClassMetaData* InMetaData) { if (!IsValid(InParentClass)) { // the parent class should be valid return false; } if (!LoadOrCreate(InName, InPath, InParentClass, InSetFlags, InClearFlags)) { // create the class return false; } if (IsValid(InMetaData)) { // apply the meta data NeedSave = InMetaData->Apply(GeneratedClass, Blueprint) || NeedSave; if (NeedSave) CanChangeCheckWithBoolRet(); } return true; } bool IsImplementationDesiredAsFunction(UBlueprint* InBlueprint, const UFunction* OverrideFunc) { // If the original function was created in a parent blueprint, then prefer a BP function if (OverrideFunc) { FName OverrideName = *OverrideFunc->GetName(); TSet GraphNames; FBlueprintEditorUtils::GetAllGraphNames(InBlueprint, GraphNames); for (const FName& Name : GraphNames) { if (Name == OverrideName) { return true; } } } // Otherwise, we would prefer an event return false; } static FEdGraphPinType ToFEdGraphPinType(FPEGraphPinType InGraphPinType, FPEGraphTerminalType InPinValueType) { if (InGraphPinType.PinSubCategoryObject && InGraphPinType.PinCategory == UEdGraphSchema_K2::PC_Object) { if (InGraphPinType.PinSubCategoryObject->IsA()) { InGraphPinType.PinCategory = UEdGraphSchema_K2::PC_Struct; } else if (InGraphPinType.PinSubCategoryObject->IsA()) { InGraphPinType.PinCategory = UEdGraphSchema_K2::PC_Byte; } else { InGraphPinType.PinCategory = UEdGraphSchema_K2::PC_Object; } } FName InGraphSubCategory; FName InPinValueSubCategory; #if (ENGINE_MAJOR_VERSION >= 5) if (InGraphPinType.PinCategory == UEdGraphSchema_K2::PC_Float) { InGraphPinType.PinCategory = UEdGraphSchema_K2::PC_Real; InGraphSubCategory = UEdGraphSchema_K2::PC_Double; } if (InPinValueType.PinCategory == UEdGraphSchema_K2::PC_Float) { InPinValueType.PinCategory = UEdGraphSchema_K2::PC_Real; InPinValueSubCategory = UEdGraphSchema_K2::PC_Double; } #endif FEdGraphPinType PinType(InGraphPinType.PinCategory, InGraphSubCategory, InGraphPinType.PinSubCategoryObject, (EPinContainerType) InGraphPinType.PinContainerType, InGraphPinType.bIsReference, FEdGraphTerminalType()); if (PinType.ContainerType == EPinContainerType::Map) { PinType.PinValueType.TerminalCategory = InPinValueType.PinCategory; PinType.PinValueType.TerminalSubCategory = InPinValueSubCategory; PinType.PinValueType.TerminalSubCategoryObject = InPinValueType.PinSubCategoryObject; if (InPinValueType.PinSubCategoryObject && InPinValueType.PinCategory == UEdGraphSchema_K2::PC_Object) { if (InPinValueType.PinSubCategoryObject->IsA()) { PinType.PinValueType.TerminalCategory = UEdGraphSchema_K2::PC_Struct; } else if (InPinValueType.PinSubCategoryObject->IsA()) { PinType.PinValueType.TerminalCategory = UEdGraphSchema_K2::PC_Byte; } else { PinType.PinValueType.TerminalCategory = UEdGraphSchema_K2::PC_Object; } } } return PinType; } void UPEBlueprintAsset::AddParameter(FName InParameterName, FPEGraphPinType InGraphPinType, FPEGraphTerminalType InPinValueType) { ParameterNames.Add(InParameterName); ParameterIsIn.Add(InGraphPinType.bIn); ParameterTypes.Add(ToFEdGraphPinType(InGraphPinType, InPinValueType)); } void UPEBlueprintAsset::AddParameterWithMetaData( FName InParameterName, FPEGraphPinType InGraphPinType, FPEGraphTerminalType InPinValueType, UPEParamMetaData* InMetaData) { ParameterNames.Add(InParameterName); ParameterIsIn.Add(InGraphPinType.bIn); FEdGraphPinType PinType = ToFEdGraphPinType(InGraphPinType, InPinValueType); if (IsValid(InMetaData)) { if (InMetaData->Apply(PinType)) { // "Currently Parameter Type Don't Affect NeedSava In Add Parameter Process" } } ParameterTypes.Add(PinType); } static TArray GatherAllResultNodes(UK2Node_EditablePinBase* TargetNode) { TArray Result; if (UK2Node_FunctionResult* ResultNode = Cast(TargetNode)) { for (auto& Node : ResultNode->GetAllResultNodes()) { if (Node) { Result.Add(Node); } } return Result; } if (TargetNode) { Result.Add(TargetNode); } return Result; } #if ENGINE_MINOR_VERSION <= 23 && ENGINE_MAJOR_VERSION < 5 UFunction* GetInterfaceFunction(UBlueprint* Blueprint, const FName FuncName) { UFunction* Function = nullptr; // If that class is an interface class implemented by this function, then return true for (const FBPInterfaceDescription& I : Blueprint->ImplementedInterfaces) { if (I.Interface) { Function = FindField(I.Interface, FuncName); if (Function) { // found it, done return Function; } } } // Check if it is in a native class or parent class for (UClass* TempClass = Blueprint->ParentClass; (nullptr != TempClass) && (nullptr == Function); TempClass = TempClass->GetSuperClass()) { for (const FImplementedInterface& I : TempClass->Interfaces) { Function = FindField(I.Class, FuncName); if (Function) { // found it, done return Function; } } } return nullptr; } UClass* const GetOverrideFunctionClass(UBlueprint* Blueprint, const FName FuncName, UFunction** OutFunction) { if (!Blueprint->SkeletonGeneratedClass) { return nullptr; } UFunction* OverrideFunc = GetInterfaceFunction(Blueprint, FuncName); if (OverrideFunc == nullptr) { OverrideFunc = FindField(Blueprint->SkeletonGeneratedClass, FuncName); // search up the class hierarchy, we want to find the original declaration of the function to match // FBlueprintEventNodeSpawner. Doing so ensures that we can find the existing node if there is one: const UClass* Iter = Blueprint->SkeletonGeneratedClass->GetSuperClass(); while (Iter != nullptr && OverrideFunc == nullptr) { if (UFunction* F = Iter->FindFunctionByName(FuncName)) { OverrideFunc = F; } else { break; } Iter = Iter->GetSuperClass(); } } if (OutFunction != nullptr) { *OutFunction = OverrideFunc; } return (OverrideFunc ? CastChecked(OverrideFunc->GetOuter())->GetAuthoritativeClass() : nullptr); } #endif void UPEBlueprintAsset::AddFunction(FName InName, bool IsVoid, FPEGraphPinType InGraphPinType, FPEGraphTerminalType InPinValueType, int32 InSetFlags, int32 InClearFlags) { InSetFlags &= ~InClearFlags; InSetFlags &= ~FUNC_Native; const int32 NetMask = FUNC_Net | FUNC_NetMulticast | FUNC_NetServer | FUNC_NetClient | FUNC_NetReliable; if (InSetFlags & NetMask) { InSetFlags |= FUNC_Net; } UClass* SuperClass = GeneratedClass->GetSuperClass(); UFunction* ParentFunction = SuperClass->FindFunctionByName(InName); UFunction* Function = GeneratedClass->FindFunctionByName(InName, EIncludeSuperFlag::ExcludeSuper); TArray AxisNames; TArray ActionNames; GetDefault()->GetAxisNames(AxisNames); GetDefault()->GetActionNames(ActionNames); UK2Node_EditablePinBase* FunctionEntryNode = nullptr; bool IsCustomEvent = false; // Create the function graph. const bool bUserCreated = true; if (ParentFunction) { // UE_LOG(LogTemp, Warning, TEXT("Override Function %s"), *ParentFunction->GetName()); // FBlueprintEditorUtils::AddFunctionGraph(Blueprint, FunctionGraph, bUserCreated, ParentFunction); UFunction* OverrideFunc = nullptr; #if ENGINE_MINOR_VERSION <= 23 && ENGINE_MAJOR_VERSION < 5 UClass* const OverrideFuncClass = GetOverrideFunctionClass(Blueprint, InName, &OverrideFunc); #else UClass* const OverrideFuncClass = FBlueprintEditorUtils::GetOverrideFunctionClass(Blueprint, InName, &OverrideFunc); #endif check(OverrideFunc); UEdGraph* EventGraph = FBlueprintEditorUtils::FindEventGraph(Blueprint); if (UEdGraphSchema_K2::FunctionCanBePlacedAsEvent(OverrideFunc) && !IsImplementationDesiredAsFunction(Blueprint, OverrideFunc) && EventGraph) { // Add to event graph FName EventName = OverrideFunc->GetFName(); UK2Node_Event* ExistingNode = FBlueprintEditorUtils::FindOverrideForFunction(Blueprint, OverrideFuncClass, EventName); if (!ExistingNode && !Function) { ExistingNode = FBlueprintEditorUtils::FindOverrideForFunction(Blueprint, UObject::StaticClass(), EventName); if (ExistingNode) { if (!ExistingNode->IsNodeEnabled()) { CanChangeCheck(); ExistingNode->SetEnabledState(ENodeEnabledState::Enabled); ExistingNode->NodeComment.Empty(); NeedSave = true; } } else { CanChangeCheck(); UK2Node_Event* NewEventNode = FEdGraphSchemaAction_K2NewNode::SpawnNode(EventGraph, EventGraph->GetGoodPlaceForNewNode(), EK2NewNodeFlags::SelectNewNode, [EventName, OverrideFuncClass](UK2Node_Event* NewInstance) { NewInstance->EventReference.SetExternalMember(EventName, OverrideFuncClass); NewInstance->bOverrideFunction = true; }); NeedSave = true; } } OverrideAdded.Add(InName); } else { if (FunctionAdded.Contains(InName)) return; UEdGraph* const ExistingGraph = FindObject(Blueprint, *InName.ToString()); if (!ExistingGraph) { CanChangeCheck(); const FScopedTransaction Transaction(LOCTEXT("CreateOverrideFunctionGraph", "Create Override Function Graph")); Blueprint->Modify(); // Implement the function graph UEdGraph* const NewGraph = FBlueprintEditorUtils::CreateNewGraph( Blueprint, InName, UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass()); FBlueprintEditorUtils::AddFunctionGraph(Blueprint, NewGraph, /*bIsUserCreated=*/false, OverrideFuncClass); NewGraph->Modify(); NeedSave = true; } FunctionAdded.Add(InName); } ParameterNames.Empty(); ParameterIsIn.Empty(); ParameterTypes.Empty(); return; } else if (AxisNames.Contains(InName)) { TArray AllEvents; // TODO: K2Node_InputTouchEvent,K2Node_InputVectorAxisEvent,K2Node_InputAxisKeyEvent,UK2Node_InputKeyEvent FBlueprintEditorUtils::GetAllNodesOfClass(Blueprint, AllEvents); UEdGraph* EventGraph = FBlueprintEditorUtils::FindEventGraph(Blueprint); if (EventGraph && !AllEvents.FindByPredicate([&](UK2Node_InputAxisEvent* Node) { return Node->InputAxisName == InName; })) { CanChangeCheck(); // UE_LOG(LogTemp, Warning, TEXT("Add Axis: %s"), *InName.ToString()); FEdGraphSchemaAction_K2NewNode::SpawnNode(EventGraph, EventGraph->GetGoodPlaceForNewNode(), EK2NewNodeFlags::SelectNewNode, [InName](UK2Node_InputAxisEvent* NewInstance) { NewInstance->Initialize(InName); }); NeedSave = true; } } // Create Action node and PrintString node // then Connection them. // UK2Node_InputAction Node must have one connected node to create function "InpActEvt_%s_%s" else if (ActionNames.Contains(InName)) { TArray AllEvents; FBlueprintEditorUtils::GetAllNodesOfClass(Blueprint, AllEvents); UEdGraph* EventGraph = FBlueprintEditorUtils::FindEventGraph(Blueprint); if (EventGraph && !AllEvents.FindByPredicate([&](UK2Node_InputAction* Node) { return Node->InputActionName == InName; })) { CanChangeCheck(); UK2Node_InputAction* NewNode = FEdGraphSchemaAction_K2NewNode::SpawnNode(EventGraph, EventGraph->GetGoodPlaceForNewNode(), EK2NewNodeFlags::SelectNewNode, [InName](UK2Node_InputAction* NewInstance) { NewInstance->InputActionName = InName; }); // UK2Node_CallFunction UK2Node_CallFunction* NewNode2 = FEdGraphSchemaAction_K2NewNode::SpawnNode(EventGraph, EventGraph->GetGoodPlaceForNewNode(), EK2NewNodeFlags::SelectNewNode, [InName](UK2Node_CallFunction* NewInstance) { NewInstance->FunctionReference.SetExternalMember(FName("PrintString"), UKismetSystemLibrary::StaticClass()); }); EventGraph->GetSchema()->TryCreateConnection(NewNode->Pins[0], NewNode2->Pins[0]); NeedSave = true; } } else if (InSetFlags & FUNC_Net) { TArray AllEvents; FBlueprintEditorUtils::GetAllNodesOfClass(Blueprint, AllEvents); UEdGraph* EventGraph = FBlueprintEditorUtils::FindEventGraph(Blueprint); auto Iter = AllEvents.FindByPredicate([&](UK2Node_CustomEvent* Node) { return Node->CustomFunctionName == InName; }); if (EventGraph) { if (EventGraph && !Iter) { CanChangeCheck(); // 处理标签改变的情况 Blueprint->FunctionGraphs.RemoveAll([&](UEdGraph* Graph) { return Graph->GetFName() == InName; }); UEdGraph* ExistingGraph = FindObject(Blueprint, *(InName.ToString())); if (ExistingGraph) { ExistingGraph->Rename(*FString::Printf(TEXT("%s%s"), *ExistingGraph->GetName(), TEXT("__Removed")), nullptr, REN_DontCreateRedirectors | REN_DoNotDirty | REN_ForceNoResetLoaders); } UK2Node_CustomEvent* EventNode = FEdGraphSchemaAction_K2NewNode::SpawnNode(EventGraph, EventGraph->GetGoodPlaceForNewNode(), EK2NewNodeFlags::SelectNewNode, [InName](UK2Node_Event* NewInstance) { NewInstance->CustomFunctionName = InName; NewInstance->bIsEditable = true; }); FunctionEntryNode = EventNode; NeedSave = true; } else { FunctionEntryNode = *Iter; } IsCustomEvent = true; FunctionAdded.Add(InName); } } else { if (FunctionAdded.Contains(InName)) return; TArray GraphList; Blueprint->GetAllGraphs(GraphList); UEdGraph** ExistedGraph = GraphList.FindByPredicate([&](UEdGraph* Graph) { return Graph->GetFName() == InName; }); UEdGraph* FunctionGraph; if (ExistedGraph) { // UE_LOG(LogTemp, Warning, TEXT("FunctionGraph %s existed, delete it!"), *InName.ToString()); // FBlueprintEditorUtils::RemoveGraph(Blueprint, *ExistedGraph); FunctionGraph = *ExistedGraph; } else { CanChangeCheck(); UEdGraph* EventGraph = FBlueprintEditorUtils::FindEventGraph(Blueprint); if (EventGraph) { EventGraph->Nodes.RemoveAll( [&](UEdGraphNode* GraphNode) { UK2Node_CustomEvent* CustomEvent = Cast(GraphNode); return CustomEvent && CustomEvent->CustomFunctionName == InName; }); UEdGraph* ExistingGraph = FindObject(Blueprint, *(InName.ToString())); if (ExistingGraph) { ExistingGraph->Rename(*FString::Printf(TEXT("%s%s"), *ExistingGraph->GetName(), TEXT("__Removed")), nullptr, REN_DontCreateRedirectors | REN_DoNotDirty | REN_ForceNoResetLoaders); } } FunctionGraph = FBlueprintEditorUtils::CreateNewGraph(Blueprint, InName, // FBlueprintEditorUtils::FindUniqueKismetName(Blueprint, FuncName.ToString()), UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass()); FBlueprintEditorUtils::AddFunctionGraph(Blueprint, FunctionGraph, bUserCreated, nullptr); NeedSave = true; } // if (InFlags) //{ // const UEdGraphSchema_K2* K2Schema = Cast(FunctionGraph->GetSchema()); // K2Schema->AddExtraFunctionFlags(FunctionGraph, InFlags); //} TArray EntryNodes; FunctionGraph->GetNodesOfClass(EntryNodes); if (EntryNodes.Num() == 1) { FunctionEntryNode = EntryNodes[0]; } FunctionAdded.Add(InName); } if (FunctionEntryNode) { if (UK2Node_FunctionEntry* TypedEntryNode = Cast(FunctionEntryNode)) { int32 ExtraFlags = TypedEntryNode->GetExtraFlags(); int32 NewExtraFlags = ((ExtraFlags & ~NetMask) | InSetFlags) & ~InClearFlags; if (ExtraFlags != NewExtraFlags) { CanChangeCheck(); TypedEntryNode->SetExtraFlags(NewExtraFlags); NeedSave = true; } } else if (UK2Node_CustomEvent* CustomEventNode = Cast(FunctionEntryNode)) { int32 NewFunctionFlags = ((CustomEventNode->FunctionFlags & ~NetMask) | InSetFlags) & ~InClearFlags; if (CustomEventNode->FunctionFlags != NewFunctionFlags) { CanChangeCheck(); CustomEventNode->FunctionFlags = NewFunctionFlags; NeedSave = true; } } TArray> OldUserDefinedPins = FunctionEntryNode->UserDefinedPins; TArray> InputParameterTypes; TArray> OutputParameterTypes; for (int i = 0; i < ParameterTypes.Num(); ++i) { FEdGraphPinType ParameterType = ParameterTypes[i]; if (ParameterType.bIsReference && !ParameterIsIn[i]) { ParameterType.bIsReference = false; OutputParameterTypes.Add(TPair(ParameterNames[i], ParameterType)); } else { InputParameterTypes.Add(TPair(ParameterNames[i], ParameterType)); } } bool ParameterChanged = OldUserDefinedPins.Num() != InputParameterTypes.Num(); if (!ParameterChanged) { for (int i = 0; i < OldUserDefinedPins.Num(); i++) { if (OldUserDefinedPins[i]->PinType != InputParameterTypes[i].Value) { CanChangeCheck(); ParameterChanged = true; } } } if (ParameterChanged) { for (TSharedPtr pinInfo : OldUserDefinedPins) { FunctionEntryNode->RemoveUserDefinedPin(pinInfo); } for (int i = 0; i < InputParameterTypes.Num(); i++) { FunctionEntryNode->CreateUserDefinedPin(InputParameterTypes[i].Key, InputParameterTypes[i].Value, EGPD_Output); } } auto TryAddOutput = [](TArray TargetNodes, FName PinName, const FEdGraphPinType& PinType) -> bool { bool Changed = false; for (UK2Node_EditablePinBase* Node : TargetNodes) { TArray> OldUserDefinedReturnPins = Node->UserDefinedPins; auto Old = Node->UserDefinedPins.FindByPredicate( [&PinName](const TSharedPtr& UDPin) { return UDPin.IsValid() && (UDPin->PinName == PinName); }); if (!Old || (*Old)->PinType != PinType) { Changed = true; if (Old) { Node->RemoveUserDefinedPinByName(PinName); } Node->CreateUserDefinedPin(PinName, PinType, EGPD_Input, false); } } return Changed; }; bool RetChanged = false; const FName RetValName = FName(TEXT("ReturnValue")); if (!IsVoid && !IsCustomEvent) { FEdGraphPinType PinType = ToFEdGraphPinType(InGraphPinType, InPinValueType); auto FunctionResultNode = FBlueprintEditorUtils::FindOrCreateFunctionResultNode(FunctionEntryNode); RetChanged = RetChanged || TryAddOutput(GatherAllResultNodes(FunctionResultNode), RetValName, PinType); } else if (IsCustomEvent || (IsVoid && OutputParameterTypes.Num() == 0)) { UEdGraph* Graph = FunctionEntryNode->GetGraph(); TArray ResultNodes; if (Graph) { Graph->GetNodesOfClass(ResultNodes); } if (ResultNodes.Num() == 1) { auto FunctionResultNode = ResultNodes[0]; TArray TargetNodes = GatherAllResultNodes(FunctionResultNode); for (UK2Node_EditablePinBase* Node : TargetNodes) { TArray> OldUserDefinedReturnPins = Node->UserDefinedPins; RetChanged = RetChanged || (OldUserDefinedReturnPins.Num() != 0); if (RetChanged) { CanChangeCheck(); Node->Modify(); for (TSharedPtr pinInfo : OldUserDefinedReturnPins) { Node->RemoveUserDefinedPin(pinInfo); } } } } } if (!IsCustomEvent && OutputParameterTypes.Num() > 0) { auto FunctionResultNode = FBlueprintEditorUtils::FindOrCreateFunctionResultNode(FunctionEntryNode); TArray TargetNodes = GatherAllResultNodes(FunctionResultNode); TSet OutputSet; for (auto& Pair : OutputParameterTypes) { RetChanged = TryAddOutput(TargetNodes, Pair.Key, Pair.Value) || RetChanged; if (RetChanged) CanChangeCheck(); OutputSet.Add(Pair.Key); } OutputSet.Add(RetValName); for (UK2Node_EditablePinBase* Node : TargetNodes) { for (TSharedPtr UserDefinedPin : Node->UserDefinedPins) { if (!OutputSet.Contains(UserDefinedPin->PinName)) { CanChangeCheck(); RetChanged = true; Node->RemoveUserDefinedPinByName(UserDefinedPin->PinName); } } } } NeedSave = NeedSave || ParameterChanged || RetChanged; } ParameterNames.Empty(); ParameterIsIn.Empty(); ParameterTypes.Empty(); } void UPEBlueprintAsset::AddFunctionWithMetaData(FName InName, bool IsVoid, FPEGraphPinType InGraphPinType, FPEGraphTerminalType InPinValueType, int32 InSetFlags, int32 InClearFlags, UPEFunctionMetaData* InMetaData) { // a helper function used to find custom event by name static const auto FindCustomEvent = [](UBlueprint* InBlueprint, FName InName) -> UK2Node_CustomEvent* { if (!IsValid(InBlueprint)) { return nullptr; } TArray Result; FBlueprintEditorUtils::GetAllNodesOfClass(InBlueprint, Result); const auto pFindResult = Result.FindByPredicate( [Name = InName](const UK2Node_CustomEvent* InEvent) -> bool { return InEvent->CustomFunctionName == Name; }); return pFindResult == nullptr ? nullptr : *pFindResult; }; // a helper function used to find function entry of a function static const auto FindFunctionEntry = [](UBlueprint* InBlueprint, FName InName) -> UK2Node_FunctionEntry* { if (!IsValid(InBlueprint)) { return nullptr; } TArray Graphs; InBlueprint->GetAllGraphs(Graphs); const auto pFunctionGraph = Graphs.FindByPredicate([Name = InName](const UEdGraph* InGraph) { return InGraph->GetFName() == Name; }); if (pFunctionGraph == nullptr) { return nullptr; } TArray Entries; (*pFunctionGraph)->GetNodesOfClass(Entries); if (Entries.Num() == 1) { return Entries[0]; } return nullptr; }; /** * @brief * function body */ if (IsValid(InMetaData)) { InSetFlags |= static_cast(InMetaData->FunctionFlags); InClearFlags &= ~static_cast(InMetaData->FunctionFlags); } AddFunction(InName, IsVoid, InGraphPinType, InPinValueType, InSetFlags, InClearFlags); if (!IsValid(InMetaData)) { return; } // check if input function is custom event if (UK2Node_CustomEvent* CustomEvent = FindCustomEvent(Blueprint, InName)) { NeedSave = InMetaData->Apply(CustomEvent) || NeedSave; } else if (UK2Node_FunctionEntry* FunctionEntry = FindFunctionEntry(Blueprint, InName)) { NeedSave = InMetaData->Apply(FunctionEntry) || NeedSave; } else { UE_LOG(LogTemp, Warning, TEXT("Currently, Only Custom Event and Function Graph Support MetaData")); } if (NeedSave) CanChangeCheck(); } void UPEBlueprintAsset::ClearParameter() { ParameterNames.Empty(); ParameterIsIn.Empty(); ParameterTypes.Empty(); } void UPEBlueprintAsset::RemoveComponent(FName ComponentName) { auto SCS_Node = Blueprint->SimpleConstructionScript->FindSCSNode(ComponentName); if (SCS_Node) { // from Editor\Kismet\Private\SSCSEditor.cpp: SSCSEditor::RemoveComponentNode FBlueprintEditorUtils::RemoveVariableNodes(Blueprint, SCS_Node->GetVariableName()); TArray EventNodes; FKismetEditorUtilities::FindAllBoundEventsForComponent(Blueprint, SCS_Node->GetVariableName(), EventNodes); if (EventNodes.Num() > 0) { for (UK2Node_ComponentBoundEvent* Node : EventNodes) { UE_LOG(PuertsEditorModule, Error, TEXT("The component that %s was bound to has been deleted! This node is no longer valid"), *Node->GetName()); } } USimpleConstructionScript* SCS = Blueprint->SimpleConstructionScript; // Remove node from SCS tree SCS->RemoveNodeAndPromoteChildren(SCS_Node); // Clear the delegate SCS_Node->SetOnNameChanged(FSCSNodeNameChanged()); // USceneComponent::GetDefaultSceneRootVariableName() if (SCS_Node != SCS->GetDefaultSceneRootNode() && SCS_Node->ComponentTemplate != nullptr) { UE_LOG(LogTemp, Warning, TEXT("NOT DefaultSceneRoot: %s"), *ComponentName.ToString()); const FName TemplateName = SCS_Node->ComponentTemplate->GetFName(); const FString RemovedName = SCS_Node->GetVariableName().ToString() + TEXT("_REMOVED_") + FGuid::NewGuid().ToString(); SCS_Node->ComponentTemplate->Modify(); SCS_Node->ComponentTemplate->Rename(*RemovedName, /*NewOuter =*/nullptr, REN_DontCreateRedirectors); TArray ArchetypeInstances; auto DestroyArchetypeInstances = [&ArchetypeInstances, &RemovedName](UActorComponent* ComponentTemplate) { ComponentTemplate->GetArchetypeInstances(ArchetypeInstances); for (UObject* ArchetypeInstance : ArchetypeInstances) { if (!ArchetypeInstance->HasAllFlags(RF_ArchetypeObject | RF_InheritableComponentTemplate)) { CastChecked(ArchetypeInstance)->DestroyComponent(); ArchetypeInstance->Rename(*RemovedName, nullptr, REN_DontCreateRedirectors); } } }; DestroyArchetypeInstances(SCS_Node->ComponentTemplate); if (Blueprint) { // Children need to have their inherited component template instance of the component renamed out of the way as well TArray ChildrenOfClass; GetDerivedClasses(Blueprint->GeneratedClass, ChildrenOfClass); for (UClass* ChildClass : ChildrenOfClass) { UBlueprintGeneratedClass* BPChildClass = CastChecked(ChildClass); if (UActorComponent* Component = (UActorComponent*) FindObjectWithOuter(BPChildClass, UActorComponent::StaticClass(), TemplateName)) { Component->Modify(); Component->Rename(*RemovedName, /*NewOuter =*/nullptr, REN_DontCreateRedirectors); DestroyArchetypeInstances(Component); } } } } } } void UPEBlueprintAsset::SetupAttachment(FName InComponentName, FName InParentComponentName) { if (Blueprint->SimpleConstructionScript) { auto SCS_Node = Blueprint->SimpleConstructionScript->FindSCSNode(InComponentName); if (!SCS_Node) { UE_LOG(LogTemp, Error, TEXT("SetupAttachment: can not find %s"), *InComponentName.ToString()); return; } if (!SCS_Node->ComponentClass || !SCS_Node->ComponentClass->IsChildOf()) { UE_LOG(LogTemp, Error, TEXT("SetupAttachment: %s not a UActorComponent"), *InComponentName.ToString()); return; } auto Parent_SCS_Node = Blueprint->SimpleConstructionScript->FindSCSNode(InParentComponentName); if (!Parent_SCS_Node) { UE_LOG(LogTemp, Error, TEXT("SetupAttachment: can not find parent %s"), *InParentComponentName.ToString()); return; } if (!Parent_SCS_Node->ComponentClass || !Parent_SCS_Node->ComponentClass->IsChildOf()) { UE_LOG(LogTemp, Error, TEXT("SetupAttachment: %s not a UActorComponent"), *InParentComponentName.ToString()); return; } if (!Parent_SCS_Node->ChildNodes.Contains(SCS_Node)) { NeedSave = true; Blueprint->SimpleConstructionScript->RemoveNode(SCS_Node); USceneComponent* SceneComponentTemplate = Cast(SCS_Node->ComponentClass); if (SceneComponentTemplate) { // Save current state SceneComponentTemplate->Modify(); // Reset the attach socket name SceneComponentTemplate->SetupAttachment(SceneComponentTemplate->GetAttachParent(), NAME_None); SCS_Node->Modify(); SCS_Node->AttachToName = NAME_None; } Parent_SCS_Node->AddChildNode(SCS_Node); } } } void UPEBlueprintAsset::SetupAttachments(TMap InAttachments) { if (Blueprint->SimpleConstructionScript) { for (auto& KV : InAttachments) { SetupAttachment(KV.Key, KV.Value); } for (auto& Component : ComponentsAdded) { auto SCS_Node = Blueprint->SimpleConstructionScript->FindSCSNode(Component); if (SCS_Node) { for (int32 ChildIdx = 0; ChildIdx < SCS_Node->ChildNodes.Num(); ChildIdx++) { USCS_Node* ChildNode = SCS_Node->ChildNodes[ChildIdx]; check(ChildNode != NULL); if (!InAttachments.Contains(ChildNode->GetVariableName()) || InAttachments[ChildNode->GetVariableName()] != Component) { SCS_Node->RemoveChildNode(ChildNode); Blueprint->SimpleConstructionScript->AddNode(ChildNode); SCS_Node->Modify(); NeedSave = true; } } } } } } void UPEBlueprintAsset::AddMemberVariable(FName NewVarName, FPEGraphPinType InGraphPinType, FPEGraphTerminalType InPinValueType, int32 InLFlags, int32 InHFlags, int32 InLifetimeCondition) { uint64 InFlags = (uint64) InHFlags << 32 | InLFlags; FEdGraphPinType PinType = ToFEdGraphPinType(InGraphPinType, InPinValueType); if (PinType.ContainerType == EPinContainerType::None) { if (auto ComponentClass = Cast(PinType.PinSubCategoryObject)) { if (Blueprint->GeneratedClass->IsChildOf() && Blueprint->SimpleConstructionScript && PinType.PinCategory == UEdGraphSchema_K2::PC_Object && (ComponentClass == UActorComponent::StaticClass() || ComponentClass->IsChildOf())) { auto SCSNode = Blueprint->SimpleConstructionScript->FindSCSNode(NewVarName); if (!SCSNode || SCSNode->ComponentClass != ComponentClass) { if (SCSNode) { RemoveComponent(NewVarName); } USCS_Node* NewSCSNode = Blueprint->SimpleConstructionScript->CreateNode(ComponentClass, NewVarName); Blueprint->SimpleConstructionScript->AddNode(NewSCSNode); NeedSave = true; } ComponentsAdded.Add(NewVarName); return; } } } int32 VarIndex = FBlueprintEditorUtils::FindNewVariableIndex(Blueprint, NewVarName); if (VarIndex == INDEX_NONE) { CanChangeCheck(); if (NewVarName == NAME_None) { FString Message = FString::Printf(TEXT("VariableName is None, unable to add variable")); FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(Message)); } else if (FBlueprintEditorUtils::AddMemberVariable(Blueprint, NewVarName, PinType)) { NeedSave = true; } else { FString Message = FString::Printf( TEXT("Failed to add variable: %s. Please check if the parent class already has a variable with the same name."), *NewVarName.ToString()); FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(Message)); } } else { FBPVariableDescription& Variable = Blueprint->NewVariables[VarIndex]; if (Variable.VarType != PinType) { CanChangeCheck(); FBlueprintEditorUtils::ChangeMemberVariableType(Blueprint, NewVarName, PinType); NeedSave = true; } // else //{ // UE_LOG(LogTemp, Error, TEXT("do not changed %s"), *NewVarName.ToString()); //} } VarIndex = FBlueprintEditorUtils::FindNewVariableIndex(Blueprint, NewVarName); if (VarIndex != INDEX_NONE) { FBPVariableDescription& Variable = Blueprint->NewVariables[VarIndex]; const uint64 NetMask = CPF_Net | CPF_RepNotify; uint64 NetFlags = InFlags & NetMask; if ((Variable.PropertyFlags & NetMask) != NetFlags) { CanChangeCheck(); Variable.PropertyFlags &= ~NetMask; Variable.PropertyFlags |= NetFlags; if (Variable.PropertyFlags & CPF_RepNotify) { FString NewFuncNameStr = FString::Printf(TEXT("OnRep_%s"), *NewVarName.ToString()); FName NewFuncName = FName(*NewFuncNameStr); UEdGraph* FuncGraph = FindObject(Blueprint, *NewFuncNameStr); if (!FuncGraph) { FuncGraph = FBlueprintEditorUtils::CreateNewGraph( Blueprint, NewFuncName, UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass()); FBlueprintEditorUtils::AddFunctionGraph(Blueprint, FuncGraph, false, NULL); } FunctionAdded.Add(NewFuncName); Blueprint->NewVariables[VarIndex].RepNotifyFunc = NewFuncName; } NeedSave = true; } if (InLifetimeCondition < COND_Max && Variable.ReplicationCondition != InLifetimeCondition) { CanChangeCheck(); Variable.ReplicationCondition = (ELifetimeCondition) InLifetimeCondition; NeedSave = true; } // The following code recalculates the final flags. // Variables added to the blueprint via FBlueprintEditorUtils::AddMemberVariable come with some default flags. InFlags |= CPF_Edit | CPF_BlueprintVisible | CPF_DisableEditOnInstance; if (Blueprint->NewVariables[VarIndex].VarType.PinCategory == UEdGraphSchema_K2::PC_MCDelegate) { InFlags |= CPF_BlueprintAssignable | CPF_BlueprintCallable; } else if ((Blueprint->NewVariables[VarIndex].VarType.PinCategory == UEdGraphSchema_K2::PC_Object) || (Blueprint->NewVariables[VarIndex].VarType.PinCategory == UEdGraphSchema_K2::PC_Interface)) { check(Blueprint->NewVariables[VarIndex].VarType.PinSubCategoryObject.IsValid()); const UClass* ClassObject = Cast(Blueprint->NewVariables[VarIndex].VarType.PinSubCategoryObject.Get()); check(ClassObject != nullptr); if (ClassObject->IsChildOf(AActor::StaticClass())) { InFlags |= CPF_DisableEditOnTemplate; } } if (Variable.PropertyFlags != InFlags) { CanChangeCheck(); Blueprint->NewVariables[VarIndex].PropertyFlags = InFlags; NeedSave = true; } } MemberVariableAdded.Add(NewVarName); } void UPEBlueprintAsset::AddMemberVariableWithMetaData(FName InNewVarName, FPEGraphPinType InGraphPinType, FPEGraphTerminalType InPinValueType, int32 InLFlags, int32 InHFLags, int32 InLifetimeCondition, UPEPropertyMetaData* InMetaData) { if (IsValid(InMetaData)) { // handle the conflict here EPropertyFlags InputFlags = static_cast((static_cast(InHFLags) << 32) + InLFlags); InputFlags |= InMetaData->PropertyFlags; InLFlags = (static_cast(InputFlags) & 0xffffffff); InHFLags = (static_cast(InputFlags) >> 32); } AddMemberVariable(InNewVarName, InGraphPinType, InPinValueType, InLFlags, InHFLags, InLifetimeCondition); int32 VarIndex = FBlueprintEditorUtils::FindNewVariableIndex(Blueprint, InNewVarName); if (VarIndex != INDEX_NONE && VarIndex != VariableIndexInTS) { if (Blueprint->NewVariables.IsValidIndex(VariableIndexInTS)) { Blueprint->NewVariables.Swap(VarIndex, VariableIndexInTS); VarIndex = VariableIndexInTS; NeedSave = true; } else { UE_LOG(PuertsEditorModule, Error, TEXT("The added variables have been deleted elsewhere, making it impossible to correctly adjust the variable " "order.")) } } if (VarIndex != INDEX_NONE) { ++VariableIndexInTS; } if (VarIndex == INDEX_NONE) { return; } if (!IsValid(InMetaData)) { if (!InMetaData && Blueprint->NewVariables[VarIndex].MetaDataArray.Num() > 0) { NeedSave = true; Blueprint->NewVariables[VarIndex].MetaDataArray.Empty(); } return; } // currently the replicated behaviour is different from cpp NeedSave = InMetaData->Apply(Blueprint->NewVariables[VarIndex]) || NeedSave; if (NeedSave) CanChangeCheck(); } void UPEBlueprintAsset::RemoveNotExistedComponent() { if (IsPlaying()) { return; } if (Blueprint && Blueprint->GeneratedClass && Blueprint->GeneratedClass->IsChildOf()) { ComponentsAdded.Add(TEXT("DefaultSceneRoot")); } if (Blueprint && Blueprint->SimpleConstructionScript) { TArray ToDelete; for (int32 i = 0; i < Blueprint->SimpleConstructionScript->GetAllNodes().Num(); i++) { if (!ComponentsAdded.Contains(Blueprint->SimpleConstructionScript->GetAllNodes()[i]->GetVariableName())) { ToDelete.Add(Blueprint->SimpleConstructionScript->GetAllNodes()[i]->GetVariableName()); } } for (auto Name : ToDelete) { NeedSave = true; RemoveComponent(Name); } } // ComponentsAdded.Empty(); } void UPEBlueprintAsset::RemoveNotExistedMemberVariable() { if (IsPlaying()) { return; } if (Blueprint) { TArray ToDelete; for (int32 i = 0; i < Blueprint->NewVariables.Num(); i++) { if (!MemberVariableAdded.Contains(Blueprint->NewVariables[i].VarName)) { ToDelete.Add(Blueprint->NewVariables[i].VarName); } } for (auto Name : ToDelete) { NeedSave = true; FBlueprintEditorUtils::RemoveMemberVariable(Blueprint, Name); } } MemberVariableAdded.Empty(); } void UPEBlueprintAsset::RemoveNotExistedFunction() { if (IsPlaying()) { return; } if (Blueprint) { if (FBlueprintEditorUtils::SupportsConstructionScript(Blueprint) && Blueprint->SimpleConstructionScript) { if (UEdGraph* ExistingUCS = FBlueprintEditorUtils::FindUserConstructionScript(Blueprint)) { ExistingUCS->bAllowDeletion = false; } else { UEdGraph* UCSGraph = FBlueprintEditorUtils::CreateNewGraph(Blueprint, UEdGraphSchema_K2::FN_UserConstructionScript, UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass()); FBlueprintEditorUtils::AddFunctionGraph(Blueprint, UCSGraph, /*bIsUserCreated=*/false, AActor::StaticClass()); UCSGraph->bAllowDeletion = false; NeedSave = true; } FunctionAdded.Add(UEdGraphSchema_K2::FN_UserConstructionScript); } auto RemovedFunction = Blueprint->FunctionGraphs.RemoveAll([&](UEdGraph* Graph) { return !FunctionAdded.Contains(Graph->GetFName()); }); NeedSave = NeedSave || (RemovedFunction > 0); UEdGraph* EventGraph = FBlueprintEditorUtils::FindEventGraph(Blueprint); if (EventGraph) { auto RemovedCustomEvent = EventGraph->Nodes.RemoveAll( [&](UEdGraphNode* GraphNode) { UK2Node_CustomEvent* CustomEvent = Cast(GraphNode); return CustomEvent && !FunctionAdded.Contains(CustomEvent->CustomFunctionName); }); NeedSave = NeedSave || (RemovedCustomEvent > 0); auto RemoveOverrideEvent = EventGraph->Nodes.RemoveAll( [&](UEdGraphNode* GraphNode) { UK2Node_Event* Event = Cast(GraphNode); return Event && Event->bOverrideFunction && !OverrideAdded.Contains(Event->EventReference.GetMemberName()); }); NeedSave = NeedSave || (RemoveOverrideEvent > 0); } } } void UPEBlueprintAsset::Save() { auto TypeScriptGeneratedClass = Cast(GeneratedClass); if (Blueprint && TypeScriptGeneratedClass) { NeedSave = NeedSave || (TypeScriptGeneratedClass->HasConstructor != HasConstructor); if (NeedSave) CanChangeCheck(); TypeScriptGeneratedClass->HasConstructor = HasConstructor; if (NeedSave) { FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint); FKismetEditorUtilities::CompileBlueprint(Blueprint); for (TFieldIterator FuncIt(TypeScriptGeneratedClass, EFieldIteratorFlags::ExcludeSuper); FuncIt; ++FuncIt) { auto Function = *FuncIt; Function->FunctionFlags &= ~FUNC_Native; auto FunctionFName = Function->GetFName(); FString FunctionName = Function->GetName(); static FString AxisPrefix(TEXT("InpAxisEvt_")); if (FunctionName.StartsWith(AxisPrefix)) { auto FunctionNameWithoutPrefix = FunctionName.Mid(AxisPrefix.Len()); int32 SubPos; if (FunctionNameWithoutPrefix.FindChar('_', SubPos)) { FunctionName = FunctionNameWithoutPrefix.Mid(0, SubPos); } } static FString ActionPrefix(TEXT("InpActEvt_")); if (FunctionName.StartsWith(ActionPrefix)) { auto FunctionNameWithoutPrefix = FunctionName.Mid(ActionPrefix.Len()); int32 SubPos; if (FunctionNameWithoutPrefix.FindChar('_', SubPos)) { FunctionName = FunctionNameWithoutPrefix.Mid(0, SubPos); } } if (FunctionAdded.Contains(*FunctionName)) { TypeScriptGeneratedClass->FunctionToRedirect.Add(FunctionFName); } } TArray PackagesToSave; PackagesToSave.Add(Package); FEditorFileUtils::PromptForCheckoutAndSave(PackagesToSave, false, false); } } FunctionAdded.Empty(); }