Files
UnrealEngineUWP/Engine/Source/Editor/BlueprintGraph/Private/K2Node_MacroInstance.cpp
jordan hoffmann 0e53056bc6 Clang now optimizes away this==nullptr. Calls to IsChildOf from a null UObject pointer will cause undefined behavior. This is a retroactive attempt to pad potentially dangerous calls to IsChildOf with a null check in the following directories:
- Plugins/BlueprintContext
- Editor/GlueprintGraph
- Editor/GraphEditor
- Editor/Kismet
- Editor/KismetCompiler
- Editor/UnrealEd/Private/Kismet2

note: if you're seeing this CL in the perforce history because you're trying to figure out why there's a null check that doesn't make sense, This is why. The goal of this CL is to preserve the behavior before IsChildOf changed rather than analyze whether that behavior makes sense. Use your best judgement

#rb marc.audy
#preflight 6299023a6438e3c731307a69

[CL 20474984 by jordan hoffmann in ue5-main branch]
2022-06-02 16:09:18 -04:00

533 lines
15 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "K2Node_MacroInstance.h"
#include "Engine/Blueprint.h"
#include "Framework/Commands/UIAction.h"
#include "ToolMenus.h"
#include "EdGraphSchema_K2.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Styling/AppStyle.h"
#include "Editor.h"
#include "EditorCategoryUtils.h"
#include "BlueprintActionFilter.h"
#include "Settings/EditorStyleSettings.h"
#define LOCTEXT_NAMESPACE "K2Node_MacroInstance"
UK2Node_MacroInstance::UK2Node_MacroInstance(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
bReconstructNode = false;
}
void UK2Node_MacroInstance::Serialize(FArchive& Ar)
{
Super::Serialize(Ar);
if (Ar.UEVer() < VER_UE4_K2NODE_REFERENCEGUIDS)
{
MacroGraphReference.SetGraph(MacroGraph_DEPRECATED);
}
}
bool UK2Node_MacroInstance::IsActionFilteredOut(FBlueprintActionFilter const& Filter)
{
bool bIsFilteredOut = false;
FBlueprintActionContext const& FilterContext = Filter.Context;
for (UEdGraph* Graph : FilterContext.Graphs)
{
if (!CanPasteHere(Graph))
{
bIsFilteredOut = true;
break;
}
}
return bIsFilteredOut;
}
void UK2Node_MacroInstance::PostPasteNode()
{
const UBlueprint* InstanceOwner = GetBlueprint();
// Find the owner of the macro graph
if (const UEdGraph* MacroGraph = MacroGraphReference.GetGraph())
{
UObject* MacroOwner = MacroGraph->GetOuter();
UBlueprint* MacroOwnerBP = nullptr;
while (MacroOwner)
{
MacroOwnerBP = Cast<UBlueprint>(MacroOwner);
if (MacroOwnerBP)
{
break;
}
MacroOwner = MacroOwner->GetOuter();
}
if ((MacroOwnerBP != nullptr)
&& (MacroOwnerBP->BlueprintType != BPTYPE_MacroLibrary)
&& (MacroOwnerBP != InstanceOwner))
{
// If this is a graph from another blueprint that is NOT a library, disallow the connection!
MacroGraphReference.SetGraph(nullptr);
}
}
else
{
// Can't find the referenced macro, fully clear this reference
MacroGraphReference.SetGraph(nullptr);
}
Super::PostPasteNode();
}
void UK2Node_MacroInstance::AllocateDefaultPins()
{
UK2Node::AllocateDefaultPins();
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
PreloadObject(MacroGraphReference.GetBlueprint());
UEdGraph* MacroGraph = MacroGraphReference.GetGraph();
if (MacroGraph != nullptr)
{
PreloadObject(MacroGraph);
// Preload the macro graph, if needed, so that we can get the proper pins
if (MacroGraph->HasAnyFlags(RF_NeedLoad))
{
PreloadObject(MacroGraph);
FBlueprintEditorUtils::PreloadMembers(MacroGraph);
}
for (TArray<UEdGraphNode*>::TIterator NodeIt(MacroGraph->Nodes); NodeIt; ++NodeIt)
{
if (UK2Node_Tunnel* TunnelNode = Cast<UK2Node_Tunnel>(*NodeIt))
{
// Only want exact tunnel nodes, more specific nodes like composites or macro instances shouldn't be grabbed.
if (TunnelNode->GetClass() == UK2Node_Tunnel::StaticClass())
{
for (TArray<UEdGraphPin*>::TIterator PinIt(TunnelNode->Pins); PinIt; ++PinIt)
{
UEdGraphPin* PortPin = *PinIt;
// We're not interested in any pins that have been expanded internally on the macro
if (PortPin->ParentPin == nullptr)
{
UEdGraphPin* NewLocalPin = CreatePin(UEdGraphPin::GetComplementaryDirection(PortPin->Direction), PortPin->PinType, PortPin->PinName);
Schema->SetPinAutogeneratedDefaultValue(NewLocalPin, PortPin->GetDefaultAsString());
}
}
}
}
}
}
CacheWildcardPins();
}
void UK2Node_MacroInstance::PreloadRequiredAssets()
{
PreloadObject(MacroGraphReference.GetBlueprint());
UEdGraph* MacroGraph = MacroGraphReference.GetGraph();
PreloadObject(MacroGraph);
Super::PreloadRequiredAssets();
}
FText UK2Node_MacroInstance::GetTooltipText() const
{
UEdGraph* MacroGraph = MacroGraphReference.GetGraph();
if (FKismetUserDeclaredFunctionMetadata* Metadata = GetAssociatedGraphMetadata(MacroGraph))
{
if (!Metadata->ToolTip.IsEmpty())
{
return Metadata->ToolTip;
}
}
if (MacroGraph == nullptr)
{
return NSLOCTEXT("K2Node", "Macro_Tooltip", "Macro");
}
else if (CachedTooltip.IsOutOfDate(this))
{
// FText::Format() is slow, so we cache this to save on performance
CachedTooltip.SetCachedText(FText::Format(NSLOCTEXT("K2Node", "MacroGraphInstance_Tooltip", "{0} instance"), FText::FromName(MacroGraph->GetFName())), this);
}
return CachedTooltip;
}
FText UK2Node_MacroInstance::GetKeywords() const
{
FText Keywords = GetAssociatedGraphMetadata(GetMacroGraph())->Keywords;
// If the Macro has Compact Node Title data, append the compact node title as a Keyword so it can be searched.
if (ShouldDrawCompact())
{
Keywords = FText::Format(FText::FromString(TEXT("{0} {1}")), Keywords, GetCompactNodeTitle());
}
return Keywords;
}
FText UK2Node_MacroInstance::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
UEdGraph* MacroGraph = MacroGraphReference.GetGraph();
FText Result = NSLOCTEXT("K2Node", "MacroInstance", "Macro instance");
if (MacroGraph)
{
Result = FText::FromString(MacroGraph->GetName());
if ((GEditor != NULL) && (GetDefault<UEditorStyleSettings>()->bShowFriendlyNames))
{
Result = FText::FromString(FName::NameToDisplayString(Result.ToString(), false));
}
}
return Result;
}
FLinearColor UK2Node_MacroInstance::GetNodeTitleColor() const
{
UEdGraph* MacroGraph = MacroGraphReference.GetGraph();
if (FKismetUserDeclaredFunctionMetadata* Metadata = GetAssociatedGraphMetadata(MacroGraph))
{
return Metadata->InstanceTitleColor.ToFColor(false);
}
return FLinearColor::White;
}
void UK2Node_MacroInstance::GetNodeContextMenuActions(UToolMenu* Menu, UGraphNodeContextMenuContext* Context) const
{
if ( Context->Pin == nullptr )
{
{
FToolMenuSection& Section = Menu->AddSection("K2NodeMacroInstance", NSLOCTEXT("K2Node", "MacroInstanceHeader", "Macro Instance"));
Section.AddMenuEntry(
"MacroInstanceFindInContentBrowser",
NSLOCTEXT("K2Node", "MacroInstanceFindInContentBrowser", "Find in Content Browser"),
NSLOCTEXT("K2Node", "MacroInstanceFindInContentBrowserTooltip", "Finds the Blueprint Macro Library that contains this Macro in the Content Browser"),
FSlateIcon(FAppStyle::GetAppStyleSetName(), "Icons.Search"),
FUIAction( FExecuteAction::CreateStatic( &UK2Node_MacroInstance::FindInContentBrowser, MakeWeakObjectPtr(const_cast<UK2Node_MacroInstance*>(this)) ) )
);
}
}
}
FKismetUserDeclaredFunctionMetadata* UK2Node_MacroInstance::GetAssociatedGraphMetadata(const UEdGraph* AssociatedMacroGraph)
{
if (AssociatedMacroGraph)
{
// Look at the graph for the entry node, to get the default title color
TArray<UK2Node_Tunnel*> TunnelNodes;
AssociatedMacroGraph->GetNodesOfClass(TunnelNodes);
for (int32 i = 0; i < TunnelNodes.Num(); i++)
{
UK2Node_Tunnel* Node = TunnelNodes[i];
if (Node->IsEditable())
{
if (Node->bCanHaveOutputs)
{
return &(Node->MetaData);
}
}
}
}
return nullptr;
}
void UK2Node_MacroInstance::FindInContentBrowser(TWeakObjectPtr<UK2Node_MacroInstance> MacroInstance)
{
if ( MacroInstance.IsValid() )
{
UEdGraph* InstanceMacroGraph = MacroInstance.Get()->MacroGraphReference.GetGraph();
if ( InstanceMacroGraph )
{
UBlueprint* BlueprintToSync = FBlueprintEditorUtils::FindBlueprintForGraph(InstanceMacroGraph);
if ( BlueprintToSync )
{
TArray<UObject*> ObjectsToSync;
ObjectsToSync.Add( BlueprintToSync );
GEditor->SyncBrowserToObjects(ObjectsToSync);
}
}
}
}
void UK2Node_MacroInstance::NotifyPinConnectionListChanged(UEdGraphPin* ChangedPin)
{
Super::NotifyPinConnectionListChanged(ChangedPin);
// added a link?
if (ChangedPin->LinkedTo.Num() > 0)
{
// ... to a wildcard pin?
bool const bIsWildcardPin = ChangedPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Wildcard;
if (bIsWildcardPin)
{
// get type of pin we just got linked to
FEdGraphPinType const LinkedPinType = ChangedPin->LinkedTo[0]->PinType;
// change all other wildcard pins to the new type
// note we're assuming only one wildcard type per Macro node, for now
for(int32 PinIdx=0; PinIdx<Pins.Num(); PinIdx++)
{
UEdGraphPin* const TmpPin = Pins[PinIdx];
if (TmpPin)
{
if (TmpPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Wildcard)
{
// only copy the category stuff to preserve array and ref status
TmpPin->PinType.PinCategory = LinkedPinType.PinCategory;
TmpPin->PinType.PinSubCategory = LinkedPinType.PinSubCategory;
TmpPin->PinType.PinSubCategoryObject = LinkedPinType.PinSubCategoryObject;
}
}
}
ResolvedWildcardType = LinkedPinType;
bReconstructNode = true;
}
}
else
{
// reconstruct on disconnects so we can revert back to wildcards if necessary
bReconstructNode = true;
}
}
void UK2Node_MacroInstance::NodeConnectionListChanged()
{
Super::NodeConnectionListChanged();
if (bReconstructNode)
{
ReconstructNode();
UBlueprint* const Blueprint = GetBlueprint();
if (Blueprint && !Blueprint->bBeingCompiled)
{
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
Blueprint->BroadcastChanged();
}
}
}
FString UK2Node_MacroInstance::GetDocumentationLink() const
{
return TEXT("Shared/GraphNodes/Blueprint/UK2Node_MacroInstance");
}
FString UK2Node_MacroInstance::GetDocumentationExcerptName() const
{
UEdGraph* MacroGraph = MacroGraphReference.GetGraph();
if (MacroGraph != nullptr)
{
return MacroGraph->GetName();
}
return Super::GetDocumentationExcerptName();
}
void UK2Node_MacroInstance::PostReconstructNode()
{
bReconstructNode = false;
Super::PostReconstructNode();
}
FName UK2Node_MacroInstance::GetCornerIcon() const
{
if (UEdGraph* MacroGraph = MacroGraphReference.GetGraph())
{
FBlueprintMacroCosmeticInfo CosmeticInfo = FBlueprintEditorUtils::GetCosmeticInfoForMacro(MacroGraph);
if (CosmeticInfo.bContainsLatentNodes)
{
return TEXT("Graph.Latent.LatentIcon");
}
}
return Super::GetCornerIcon();
}
FSlateIcon UK2Node_MacroInstance::GetIconAndTint(FLinearColor& OutColor) const
{
const char* IconName = "GraphEditor.Macro_16x";
// Special case handling for standard macros
// @TODO Change this to a SlateBurushAsset pointer on the graph or something similar, to allow any macro to have an icon
UEdGraph* MacroGraph = MacroGraphReference.GetGraph();
if(MacroGraph != nullptr && MacroGraph->GetOuter()->GetName() == TEXT("StandardMacros"))
{
FName MacroName = FName(*MacroGraph->GetName());
if( MacroName == TEXT("ForLoop" ) ||
MacroName == TEXT("ForLoopWithBreak") ||
MacroName == TEXT("WhileLoop") )
{
IconName = "GraphEditor.Macro.Loop_16x";
}
else if( MacroName == TEXT("Gate") )
{
IconName = "GraphEditor.Macro.Gate_16x";
}
else if( MacroName == TEXT("Do N") )
{
IconName = "GraphEditor.Macro.DoN_16x";
}
else if (MacroName == TEXT("DoOnce"))
{
IconName = "GraphEditor.Macro.DoOnce_16x";
}
else if (MacroName == TEXT("IsValid"))
{
IconName = "GraphEditor.Macro.IsValid_16x";
}
else if (MacroName == TEXT("FlipFlop"))
{
IconName = "GraphEditor.Macro.FlipFlop_16x";
}
else if ( MacroName == TEXT("ForEachLoop") ||
MacroName == TEXT("ForEachLoopWithBreak") )
{
IconName = "GraphEditor.Macro.ForEach_16x";
}
}
return FSlateIcon(FAppStyle::GetAppStyleSetName(), IconName);
}
FText UK2Node_MacroInstance::GetCompactNodeTitle() const
{
FText ResultText;
if (FKismetUserDeclaredFunctionMetadata* MacroGraphMetadata = GetAssociatedGraphMetadata(GetMacroGraph()))
{
ResultText = MacroGraphMetadata->CompactNodeTitle;
}
return ResultText;
}
bool UK2Node_MacroInstance::ShouldDrawCompact() const
{
return !GetCompactNodeTitle().IsEmpty();
}
bool UK2Node_MacroInstance::CanPasteHere(const UEdGraph* TargetGraph) const
{
bool bCanPaste = false;
UBlueprint* MacroBlueprint = GetSourceBlueprint();
UBlueprint* TargetBlueprint = FBlueprintEditorUtils::FindBlueprintForGraph(TargetGraph);
if ((MacroBlueprint != nullptr) && (TargetBlueprint != nullptr))
{
// Only allow "local" macro instances or instances from a macro library blueprint with the same parent class
check(MacroBlueprint->ParentClass != nullptr && TargetBlueprint->ParentClass != nullptr);
bCanPaste = (MacroBlueprint == TargetBlueprint) || (MacroBlueprint->BlueprintType == BPTYPE_MacroLibrary
&& TargetBlueprint->ParentClass && TargetBlueprint->ParentClass->IsChildOf(MacroBlueprint->ParentClass));
}
// Macro Instances are not allowed in it's own graph
UEdGraph* MacroGraph = GetMacroGraph();
bCanPaste &= (MacroGraph != TargetGraph);
// nor in Function graphs if the macro has latent functions in it
bool const bIsTargetFuncGraph = (TargetGraph->GetSchema()->GetGraphType(TargetGraph) == GT_Function);
bCanPaste &= (!bIsTargetFuncGraph || !FBlueprintEditorUtils::CheckIfGraphHasLatentFunctions(MacroGraph));
return bCanPaste && Super::CanPasteHere(TargetGraph);
}
void UK2Node_MacroInstance::PostFixupAllWildcardPins(bool bInAllWildcardPinsUnlinked)
{
if (bInAllWildcardPinsUnlinked)
{
// Reset the type to a wildcard because there are no longer any wildcard pins linked to determine a type with
ResolvedWildcardType.ResetToDefaults();
// Collapse any wildcard pins that are split and set their type back to wildcard
for (UEdGraphPin* Pin : WildcardPins)
{
GetSchema()->RecombinePin(Pin);
Pin->PinType.PinCategory = UEdGraphSchema_K2::PC_Wildcard;
Pin->PinType.PinSubCategory = NAME_None;
Pin->PinType.PinSubCategoryObject = nullptr;
}
}
}
bool UK2Node_MacroInstance::HasExternalDependencies(TArray<class UStruct*>* OptionalOutput) const
{
UBlueprint* OtherBlueprint = MacroGraphReference.GetBlueprint();
const bool bResult = OtherBlueprint && (OtherBlueprint != GetBlueprint());
if (bResult && OptionalOutput)
{
if (UClass* OtherClass = *OtherBlueprint->GeneratedClass)
{
OptionalOutput->AddUnique(OtherClass);
}
for (UEdGraphPin* Pin : Pins)
{
if (Pin->PinType.PinSubCategoryObject.IsValid())
{
if (UStruct* Struct = Cast<UStruct>(Pin->PinType.PinSubCategoryObject.Get()))
{
OptionalOutput->AddUnique(Struct);
}
else
{
OptionalOutput->AddUnique(Pin->PinType.PinSubCategoryObject.Get()->GetClass());
}
}
}
}
const bool bSuperResult = Super::HasExternalDependencies(OptionalOutput);
return bSuperResult || bResult;
}
void UK2Node_MacroInstance::GetNodeAttributes( TArray<TKeyValuePair<FString, FString>>& OutNodeAttributes ) const
{
FString MacroName( TEXT( "InvalidMacro" ));
if( UEdGraph* MacroGraph = GetMacroGraph() )
{
MacroName = MacroGraph->GetName();
}
OutNodeAttributes.Add( TKeyValuePair<FString, FString>( TEXT( "Type" ), TEXT( "Macro" ) ));
OutNodeAttributes.Add( TKeyValuePair<FString, FString>( TEXT( "Class" ), GetClass()->GetName() ));
OutNodeAttributes.Add( TKeyValuePair<FString, FString>( TEXT( "Name" ), MacroName ));
}
FText UK2Node_MacroInstance::GetMenuCategory() const
{
FText MenuCategory = FEditorCategoryUtils::GetCommonCategory(FCommonEditorCategory::Utilities);
if (UEdGraph* MacroGraph = GetMacroGraph())
{
FKismetUserDeclaredFunctionMetadata* MacroGraphMetadata = UK2Node_MacroInstance::GetAssociatedGraphMetadata(MacroGraph);
if ((MacroGraphMetadata != nullptr) && !MacroGraphMetadata->Category.IsEmpty())
{
MenuCategory = MacroGraphMetadata->Category;
}
}
return MenuCategory;
}
FBlueprintNodeSignature UK2Node_MacroInstance::GetSignature() const
{
FBlueprintNodeSignature NodeSignature = Super::GetSignature();
NodeSignature.AddSubObject(GetMacroGraph());
return NodeSignature;
}
#undef LOCTEXT_NAMESPACE