Files
UnrealEngine/Engine/Source/Editor/BlueprintGraph/Private/K2Node_GetSubsystem.cpp
2025-05-18 13:04:45 +08:00

820 lines
29 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "K2Node_GetSubsystem.h"
#include "BlueprintActionDatabaseRegistrar.h"
#include "BlueprintEditorSettings.h"
#include "BlueprintNodeSpawner.h"
#include "Containers/EnumAsByte.h"
#include "Containers/UnrealString.h"
#include "Delegates/Delegate.h"
#include "EdGraph/EdGraphPin.h"
#include "EdGraphSchema_K2.h"
#include "Editor/UnrealEdEngine.h"
#include "EditorSubsystem.h"
#include "Engine/Blueprint.h"
#include "Engine/MemberReference.h"
#include "GameFramework/PlayerController.h"
#include "HAL/PlatformCrt.h"
#include "HAL/PlatformMath.h"
#include "Internationalization/Internationalization.h"
#include "K2Node_CallFunction.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Kismet2/CompilerResultsLog.h"
#include "KismetCompiler.h"
#include "Misc/AssertionMacros.h"
#include "Preferences/UnrealEdOptions.h"
#include "SourceCodeNavigation.h"
#include "Styling/AppStyle.h"
#include "Subsystems/AudioEngineSubsystem.h"
#include "Subsystems/EditorSubsystemBlueprintLibrary.h"
#include "Subsystems/EngineSubsystem.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "Subsystems/LocalPlayerSubsystem.h"
#include "Subsystems/Subsystem.h"
#include "Subsystems/SubsystemBlueprintLibrary.h"
#include "Subsystems/WorldSubsystem.h"
#include "Templates/Casts.h"
#include "Templates/UnrealTemplate.h"
#include "UObject/Class.h"
#include "UObject/NameTypes.h"
#include "UObject/Object.h"
#include "UObject/ObjectPtr.h"
#include "UObject/UObjectHash.h"
#include "UObject/WeakObjectPtr.h"
#include "UObject/WeakObjectPtrTemplates.h"
#include "UnrealEdGlobals.h"
class FArchive;
class UEdGraph;
// ************************************************************************************
// UK2Node_GetSubsystem
// ************************************************************************************
void UK2Node_GetSubsystem::Serialize( FArchive& Ar )
{
Super::Serialize( Ar );
if (CustomClass == nullptr)
{
if (UEdGraphPin* ClassPin = FindPin(TEXT("Class"), EGPD_Input))
{
ClassPin->SetSavePinIfOrphaned(false);
}
}
}
void UK2Node_GetSubsystem::Initialize( UClass* NodeClass )
{
CustomClass = NodeClass;
}
void UK2Node_GetSubsystem::AllocateDefaultPins()
{
// If required add the world context pin
if (GetBlueprint()->ParentClass->HasMetaDataHierarchical(FBlueprintMetadata::MD_ShowWorldContextPin))
{
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Object, UObject::StaticClass(), TEXT("WorldContext"));
}
// Add blueprint pin
if (!CustomClass)
{
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Class, USubsystem::StaticClass(), TEXT("Class"));
}
// Result pin
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Object, (CustomClass ? (UClass*)CustomClass : USubsystem::StaticClass()), UEdGraphSchema_K2::PN_ReturnValue);
Super::AllocateDefaultPins();
}
bool UK2Node_GetSubsystem::IsCompatibleWithGraph(const UEdGraph* TargetGraph) const
{
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(TargetGraph);
return Super::IsCompatibleWithGraph(TargetGraph) &&
(!Blueprint || FBlueprintEditorUtils::FindUserConstructionScript(Blueprint) != TargetGraph);
}
FSlateIcon UK2Node_GetSubsystem::GetIconAndTint(FLinearColor& OutColor) const
{
OutColor = GetNodeTitleColor();
static FSlateIcon Icon(FAppStyle::GetAppStyleSetName(), "Kismet.AllClasses.FunctionIcon");
return Icon;
}
FLinearColor UK2Node_GetSubsystem::GetNodeTitleColor() const
{
return FLinearColor(1.f, 0.078f, 0.576f, 1.f);
}
FText UK2Node_GetSubsystem::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
if (CustomClass)
{
if (TitleType == ENodeTitleType::FullTitle)
{
return CustomClass->GetDisplayNameText();
}
const FString& ClassName = CustomClass->GetName();
return FText::FormatNamed(NSLOCTEXT("K2Node", "GetSubsystem_NodeTitleFormat", "Get {ClassName}"), TEXT("ClassName"), FText::FromString(ClassName));
}
return GetTooltipText();
}
void UK2Node_GetSubsystem::GetNodeAttributes(TArray<TKeyValuePair<FString, FString>>& OutNodeAttributes) const
{
const FString ClassToSpawnStr = CustomClass ? CustomClass->GetName() : TEXT("InvalidClass");
OutNodeAttributes.Add(TKeyValuePair<FString, FString>(TEXT("Type"), TEXT("GetSubsystems")));
OutNodeAttributes.Add(TKeyValuePair<FString, FString>(TEXT("Class"), GetClass()->GetName()));
OutNodeAttributes.Add(TKeyValuePair<FString, FString>(TEXT("Name"), GetName()));
OutNodeAttributes.Add(TKeyValuePair<FString, FString>(TEXT("ObjectClass"), ClassToSpawnStr));
}
void UK2Node_GetSubsystem::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
Super::ExpandNode(CompilerContext, SourceGraph);
static const FName WorldContextObject_ParamName(TEXT("ContextObject"));
static const FName Class_ParamName(TEXT("Class"));
UK2Node_GetSubsystem* GetSubsystemNode = this;
UEdGraphPin* SpawnWorldContextPin = GetSubsystemNode->GetWorldContextPin();
UEdGraphPin* SpawnClassPin = GetSubsystemNode->GetClassPin();
UEdGraphPin* SpawnNodeResult = GetSubsystemNode->GetResultPin();
UClass* SpawnClass = (SpawnClassPin != nullptr) ? Cast<UClass>(SpawnClassPin->DefaultObject) : nullptr;
if (SpawnClassPin && (SpawnClassPin->LinkedTo.Num() == 0) && !SpawnClass)
{
CompilerContext.MessageLog.Error(*NSLOCTEXT("K2Node", "GetSubsystem_Error", "Node @@ must have a class specified.").ToString(), GetSubsystemNode);
GetSubsystemNode->BreakAllNodeLinks();
return;
}
// Choose appropriate underlying Getter
FName Get_FunctionName;
if (CustomClass->IsChildOf<UGameInstanceSubsystem>())
{
Get_FunctionName = GET_FUNCTION_NAME_CHECKED(USubsystemBlueprintLibrary, GetGameInstanceSubsystem);
}
else if (CustomClass->IsChildOf<UWorldSubsystem>())
{
Get_FunctionName = GET_FUNCTION_NAME_CHECKED(USubsystemBlueprintLibrary, GetWorldSubsystem);
}
else if (CustomClass->IsChildOf<ULocalPlayerSubsystem>())
{
Get_FunctionName = GET_FUNCTION_NAME_CHECKED(USubsystemBlueprintLibrary, GetLocalPlayerSubsystem);
}
else if (CustomClass->IsChildOf<UAudioEngineSubsystem>())
{
Get_FunctionName = GET_FUNCTION_NAME_CHECKED(USubsystemBlueprintLibrary, GetAudioEngineSubsystem);
}
else
{
CompilerContext.MessageLog.Error(*NSLOCTEXT("K2Node", "GetSubsystem_Error", "Node @@ must have a class specified.").ToString(), GetSubsystemNode);
GetSubsystemNode->BreakAllNodeLinks();
return;
}
//////////////////////////////////////////////////////////////////////////
// create 'USubsystemBlueprintLibrary::Get[something]Subsystem' call node
UK2Node_CallFunction* CallGetNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(GetSubsystemNode, SourceGraph);
CallGetNode->FunctionReference.SetExternalMember(Get_FunctionName, USubsystemBlueprintLibrary::StaticClass());
CallGetNode->AllocateDefaultPins();
UEdGraphPin* CallCreateWorldContextPin = CallGetNode->FindPinChecked(WorldContextObject_ParamName);
UEdGraphPin* CallCreateClassTypePin = CallGetNode->FindPinChecked(Class_ParamName);
UEdGraphPin* CallCreateResult = CallGetNode->GetReturnValuePin();
if (SpawnClassPin && SpawnClassPin->LinkedTo.Num() > 0)
{
// Copy the 'class' connection from the spawn node to 'USubsystemBlueprintLibrary::Get[something]Subsystem'
CompilerContext.MovePinLinksToIntermediate(*SpawnClassPin, *CallCreateClassTypePin);
}
else
{
// Copy class literal onto 'USubsystemBlueprintLibrary::Get[something]Subsystem' call
CallCreateClassTypePin->DefaultObject = *CustomClass;
}
// Copy the world context connection from the spawn node to 'USubsystemBlueprintLibrary::Get[something]Subsystem' if necessary
if (SpawnWorldContextPin)
{
CompilerContext.MovePinLinksToIntermediate(*SpawnWorldContextPin, *CallCreateWorldContextPin);
}
// Move result connection from spawn node to 'USubsystemBlueprintLibrary::Get[something]Subsystem'
CallCreateResult->PinType = SpawnNodeResult->PinType;
CompilerContext.MovePinLinksToIntermediate(*SpawnNodeResult, *CallCreateResult);
//////////////////////////////////////////////////////////////////////////
// Break any links to the expanded node
GetSubsystemNode->BreakAllNodeLinks();
}
void UK2Node_GetSubsystem::ReallocatePinsDuringReconstruction(TArray<UEdGraphPin*>& OldPins)
{
if (!CustomClass)
{
if (auto ClassNode = GetClassPin(&OldPins))
{
CustomClass = Cast<UClass>(ClassNode->DefaultObject);
}
}
AllocateDefaultPins();
if (CustomClass)
{
UEdGraphPin* ResultPin = GetResultPin();
ResultPin->PinType.PinSubCategoryObject = *CustomClass;
}
}
class FNodeHandlingFunctor* UK2Node_GetSubsystem::CreateNodeHandler(class FKismetCompilerContext& CompilerContext) const
{
return new FNodeHandlingFunctor(CompilerContext);
}
void UK2Node_GetSubsystem::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
{
static TArray<UClass*> Subclasses;
Subclasses.Reset();
GetDerivedClasses(UGameInstanceSubsystem::StaticClass(), Subclasses);
GetDerivedClasses(UWorldSubsystem::StaticClass(), Subclasses);
GetDerivedClasses(ULocalPlayerSubsystem::StaticClass(), Subclasses);
GetDerivedClasses(UAudioEngineSubsystem::StaticClass(), Subclasses);
auto CustomizeCallback = [](UEdGraphNode* Node, bool bIsTemplateNode, UClass* Subclass)
{
auto TypedNode = CastChecked<UK2Node_GetSubsystem>(Node);
TypedNode->Initialize(Subclass);
};
UClass* ActionKey = GetClass();
if (ActionRegistrar.IsOpenForRegistration(ActionKey))
{
for (UClass* Iter : Subclasses)
{
if (!UEdGraphSchema_K2::IsAllowableBlueprintVariableType(Iter, true))
{
continue;
}
UBlueprintNodeSpawner* Spawner = UBlueprintNodeSpawner::Create(ActionKey);
check(Spawner);
Spawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(CustomizeCallback, Iter);
ActionRegistrar.AddBlueprintAction(ActionKey, Spawner);
}
}
}
FText UK2Node_GetSubsystem::GetMenuCategory() const
{
if (CustomClass->IsChildOf<UGameInstanceSubsystem>())
{
return NSLOCTEXT("K2Node", "GetSubsystem_GameInstanceSubsystemsMenuCategory", "GameInstance Subsystems");
}
else if (CustomClass->IsChildOf<ULocalPlayerSubsystem>())
{
return NSLOCTEXT("K2Node", "GetSubsystem_LocalPlayerSubsystemsMenuCategory", "LocalPlayer Subsystems");
}
else if (CustomClass->IsChildOf<UWorldSubsystem>())
{
return NSLOCTEXT("K2Node", "GetSubsystem_WorldSubsystemsMenuCategory", "World Subsystems");
}
else if (CustomClass->IsChildOf<UAudioEngineSubsystem>())
{
return NSLOCTEXT("K2Node", "GetSubsystem_AudioEngineSubsystemsMenuCategory", "AudioEngine Subsystems");
}
return NSLOCTEXT("K2Node", "GetSubsystem_InvalidSubsystemTypeMenuCategory", "Invalid Subsystem Type");
}
FText UK2Node_GetSubsystem::GetTooltipText() const
{
if (CustomClass)
{
FText SubsystemTypeText;
if (CustomClass->IsChildOf<UGameInstanceSubsystem>())
{
SubsystemTypeText = NSLOCTEXT("K2Node", "GetSubsystem_GameInstanceSubsystemTooltip", "GameInstance Subsystem");
}
else if (CustomClass->IsChildOf<UWorldSubsystem>())
{
SubsystemTypeText = NSLOCTEXT("K2Node", "GetSubsystem_WorldSubsystemTooltip", "World Subsystem");
}
else if (CustomClass->IsChildOf<UAudioEngineSubsystem>())
{
SubsystemTypeText = NSLOCTEXT("K2Node", "GetSubsystem_AudioEngineSubsystemTooltip", "AudioEngine Subsystem");
}
else
{
SubsystemTypeText = NSLOCTEXT("K2Node", "GetSubsystem_LocalPlayerSubsystemTooltip", "LocalPlayer Subsystem");
}
return FText::FormatNamed(NSLOCTEXT("K2Node", "GetSubsystem_TooltipFormat", "Get {ClassName} ({SubsystemType})\n\n{ClassTooltip}"),
TEXT("ClassName"), CustomClass->GetDisplayNameText(),
TEXT("SubsystemType"), SubsystemTypeText,
TEXT("ClassTooltip"), CustomClass->GetToolTipText(/*bShortTooltip=*/ true));
}
return NSLOCTEXT("K2Node", "GetSubsystem_InvalidSubsystemTypeTooltip", "Invalid Subsystem Type");
}
UEdGraphPin* UK2Node_GetSubsystem::GetWorldContextPin() const
{
UEdGraphPin* Pin = FindPin(TEXT("WorldContext"));
check(Pin == NULL || Pin->Direction == EGPD_Input);
return Pin;
}
UEdGraphPin* UK2Node_GetSubsystem::GetResultPin() const
{
UEdGraphPin* Pin = FindPinChecked(UEdGraphSchema_K2::PN_ReturnValue);
check(Pin->Direction == EGPD_Output);
return Pin;
}
UEdGraphPin* UK2Node_GetSubsystem::GetClassPin(const TArray<UEdGraphPin*>* InPinsToSearch /*= nullptr */) const
{
const TArray<UEdGraphPin*>* PinsToSearch = InPinsToSearch ? InPinsToSearch : &Pins;
UEdGraphPin* Pin = NULL;
for (auto PinIt = PinsToSearch->CreateConstIterator(); PinIt; ++PinIt)
{
UEdGraphPin* TestPin = *PinIt;
if (TestPin && TestPin->PinName == TEXT("Class"))
{
Pin = TestPin;
break;
}
}
return Pin;
}
// ************************************************************************************
// UK2Node_GetSubsystemFromPC
// ************************************************************************************
void UK2Node_GetSubsystemFromPC::AllocateDefaultPins()
{
// If required add the world context pin
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Object, APlayerController::StaticClass(), TEXT("PlayerController"));
// Add blueprint pin
if (!CustomClass)
{
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Class, USubsystem::StaticClass(), TEXT("Class"));
}
// Result pin
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Object, (CustomClass ? (UClass*)CustomClass : ULocalPlayerSubsystem::StaticClass()), UEdGraphSchema_K2::PN_ReturnValue);
// Skip the UK2Node_GetSubsystem implementation
UK2Node::AllocateDefaultPins();
}
void UK2Node_GetSubsystemFromPC::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
// Skip the UK2Node_GetSubsystem implementation
UK2Node::ExpandNode(CompilerContext, SourceGraph);
static const FName PlayerController_ParamName(TEXT("PlayerController"));
static const FName Class_ParamName(TEXT("Class"));
UK2Node_GetSubsystemFromPC* GetSubsystemFromPCNode = this;
UEdGraphPin* SpawnPlayerControllerPin = GetSubsystemFromPCNode->GetPlayerControllerPin();
UEdGraphPin* SpawnClassPin = GetSubsystemFromPCNode->GetClassPin();
UEdGraphPin* SpawnNodeResult = GetSubsystemFromPCNode->GetResultPin();
UClass* SpawnClass = (SpawnClassPin != nullptr) ? Cast<UClass>(SpawnClassPin->DefaultObject) : nullptr;
if (SpawnClassPin && (SpawnClassPin->LinkedTo.Num() == 0) && !SpawnClass)
{
CompilerContext.MessageLog.Error(*NSLOCTEXT("K2Node", "GetSubsystem_Error", "Node @@ must have a class specified.").ToString(), GetSubsystemFromPCNode);
GetSubsystemFromPCNode->BreakAllNodeLinks();
return;
}
// Choose appropriate underlying Getter
FName Get_FunctionName;
if (CustomClass->IsChildOf<ULocalPlayerSubsystem>())
{
Get_FunctionName = GET_FUNCTION_NAME_CHECKED(USubsystemBlueprintLibrary, GetLocalPlayerSubSystemFromPlayerController);
}
else
{
CompilerContext.MessageLog.Error(*NSLOCTEXT("K2Node", "GetSubsystem_Error", "Node @@ must have a class specified.").ToString(), GetSubsystemFromPCNode);
GetSubsystemFromPCNode->BreakAllNodeLinks();
return;
}
//////////////////////////////////////////////////////////////////////////
// create 'USubsystemBlueprintLibrary::GetLocalPlayerSubSystemFromPlayerController' call node
UK2Node_CallFunction* CallGetNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(GetSubsystemFromPCNode, SourceGraph);
CallGetNode->FunctionReference.SetExternalMember(Get_FunctionName, USubsystemBlueprintLibrary::StaticClass());
CallGetNode->AllocateDefaultPins();
UEdGraphPin* CallCreatePlayerControllerPin = CallGetNode->FindPinChecked(PlayerController_ParamName);
UEdGraphPin* CallCreateClassTypePin = CallGetNode->FindPinChecked(Class_ParamName);
UEdGraphPin* CallCreateResult = CallGetNode->GetReturnValuePin();
if (SpawnClassPin && SpawnClassPin->LinkedTo.Num() > 0)
{
// Copy the 'class' connection from the spawn node to 'USubsystemBlueprintLibrary::GetLocalPlayerSubSystemFromPlayerController'
CompilerContext.MovePinLinksToIntermediate(*SpawnClassPin, *CallCreateClassTypePin);
}
else
{
// Copy class literal onto 'USubsystemBlueprintLibrary::GetLocalPlayerSubSystemFromPlayerController' call
CallCreateClassTypePin->DefaultObject = *CustomClass;
}
// Copy the world context connection from the spawn node to 'USubsystemBlueprintLibrary::GetLocalPlayerSubSystemFromPlayerController' if necessary
if (SpawnPlayerControllerPin)
{
CompilerContext.MovePinLinksToIntermediate(*SpawnPlayerControllerPin, *CallCreatePlayerControllerPin);
}
// Move result connection from spawn node to 'USubsystemBlueprintLibrary::Get[something]Subsystem'
CallCreateResult->PinType = SpawnNodeResult->PinType;
CompilerContext.MovePinLinksToIntermediate(*SpawnNodeResult, *CallCreateResult);
//////////////////////////////////////////////////////////////////////////
// Break any links to the expanded node
GetSubsystemFromPCNode->BreakAllNodeLinks();
}
void UK2Node_GetSubsystemFromPC::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
{
static TArray<UClass*> Subclasses;
Subclasses.Reset();
GetDerivedClasses(ULocalPlayerSubsystem::StaticClass(), Subclasses);
auto CustomizeCallback = [](UEdGraphNode* Node, bool bIsTemplateNode, UClass* Subclass)
{
auto TypedNode = CastChecked<UK2Node_GetSubsystemFromPC>(Node);
TypedNode->Initialize(Subclass);
};
UClass* ActionKey = GetClass();
if (ActionRegistrar.IsOpenForRegistration(ActionKey))
{
for (UClass* Iter : Subclasses)
{
if (!UEdGraphSchema_K2::IsAllowableBlueprintVariableType(Iter, true))
{
continue;
}
UBlueprintNodeSpawner* Spawner = UBlueprintNodeSpawner::Create(ActionKey);
check(Spawner);
Spawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(CustomizeCallback, Iter);
ActionRegistrar.AddBlueprintAction(ActionKey, Spawner);
}
}
}
FText UK2Node_GetSubsystemFromPC::GetMenuCategory() const
{
return NSLOCTEXT("K2Node", "GetSubsystemFromPC_MenuCategory", "PlayerController|LocalPlayer Subsystems");
}
FText UK2Node_GetSubsystemFromPC::GetTooltipText() const
{
if (CustomClass)
{
return FText::FormatNamed(NSLOCTEXT("K2Node", "GetSubsystemFromPC_TooltipFormat", "Get {ClassName} from Player Controller"), TEXT("ClassName"), CustomClass->GetDisplayNameText());
}
return NSLOCTEXT("K2Node", "GetSubsystemFromPC_InvalidSubsystemTypeTooltip", "Invalid Subsystem Type");
}
UEdGraphPin* UK2Node_GetSubsystemFromPC::GetPlayerControllerPin() const
{
UEdGraphPin* Pin = FindPin(TEXT("PlayerController"));
check(Pin == NULL || Pin->Direction == EGPD_Input);
return Pin;
}
// ************************************************************************************
// UK2Node_GetEngineSubsystem
// ************************************************************************************
void UK2Node_GetEngineSubsystem::AllocateDefaultPins()
{
// Add blueprint pin
if (!CustomClass)
{
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Class, USubsystem::StaticClass(), TEXT("Class"));
}
// Result pin
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Object, (CustomClass ? (UClass*)CustomClass : UEngineSubsystem::StaticClass()), UEdGraphSchema_K2::PN_ReturnValue);
// Skip the UK2Node_GetSubsystem implementation
UK2Node::AllocateDefaultPins();
}
void UK2Node_GetEngineSubsystem::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
// Skip the UK2Node_GetSubsystem implementation
UK2Node::ExpandNode(CompilerContext, SourceGraph);
static const FName Class_ParamName(TEXT("Class"));
UK2Node_GetEngineSubsystem* GetEngineSubsystemNode = this;
UEdGraphPin* SpawnClassPin = GetEngineSubsystemNode->GetClassPin();
UEdGraphPin* SpawnNodeResult = GetEngineSubsystemNode->GetResultPin();
UClass* SpawnClass = (SpawnClassPin != nullptr) ? Cast<UClass>(SpawnClassPin->DefaultObject) : nullptr;
if (SpawnClassPin && (SpawnClassPin->LinkedTo.Num() == 0) && !SpawnClass)
{
CompilerContext.MessageLog.Error(*NSLOCTEXT("K2Node", "GetSubsystem_Error", "Node @@ must have a class specified.").ToString(), GetEngineSubsystemNode);
GetEngineSubsystemNode->BreakAllNodeLinks();
return;
}
// Choose appropriate underlying Getter
FName Get_FunctionName;
if (CustomClass->IsChildOf<UEngineSubsystem>())
{
Get_FunctionName = GET_FUNCTION_NAME_CHECKED(USubsystemBlueprintLibrary, GetEngineSubsystem);
}
else
{
CompilerContext.MessageLog.Error(*NSLOCTEXT("K2Node", "GetSubsystem_Error", "Node @@ must have a class specified.").ToString(), GetEngineSubsystemNode);
GetEngineSubsystemNode->BreakAllNodeLinks();
return;
}
//////////////////////////////////////////////////////////////////////////
// create 'USubsystemBlueprintLibrary::GetEngineSubsystem' call node
UK2Node_CallFunction* CallGetNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(GetEngineSubsystemNode, SourceGraph);
CallGetNode->FunctionReference.SetExternalMember(Get_FunctionName, USubsystemBlueprintLibrary::StaticClass());
CallGetNode->AllocateDefaultPins();
UEdGraphPin* CallCreateClassTypePin = CallGetNode->FindPinChecked(Class_ParamName);
UEdGraphPin* CallCreateResult = CallGetNode->GetReturnValuePin();
if (SpawnClassPin && SpawnClassPin->LinkedTo.Num() > 0)
{
// Copy the 'class' connection from the spawn node to 'USubsystemBlueprintLibrary::GetLocalPlayerSubSystemFromPlayerController'
CompilerContext.MovePinLinksToIntermediate(*SpawnClassPin, *CallCreateClassTypePin);
}
else
{
// Copy class literal onto 'USubsystemBlueprintLibrary::GetLocalPlayerSubSystemFromPlayerController' call
CallCreateClassTypePin->DefaultObject = *CustomClass;
}
// Move result connection from spawn node to 'USubsystemBlueprintLibrary::Get[something]Subsystem'
CallCreateResult->PinType = SpawnNodeResult->PinType;
CompilerContext.MovePinLinksToIntermediate(*SpawnNodeResult, *CallCreateResult);
//////////////////////////////////////////////////////////////////////////
// Break any links to the expanded node
GetEngineSubsystemNode->BreakAllNodeLinks();
}
void UK2Node_GetEngineSubsystem::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
{
static TArray<UClass*> Subclasses;
Subclasses.Reset();
GetDerivedClasses(UEngineSubsystem::StaticClass(), Subclasses);
auto CustomizeCallback = [](UEdGraphNode* Node, bool bIsTemplateNode, UClass* Subclass)
{
auto TypedNode = CastChecked<UK2Node_GetEngineSubsystem>(Node);
TypedNode->Initialize(Subclass);
};
UClass* ActionKey = GetClass();
if (ActionRegistrar.IsOpenForRegistration(ActionKey))
{
for (UClass* Iter : Subclasses)
{
if (!UEdGraphSchema_K2::IsAllowableBlueprintVariableType(Iter, true))
{
continue;
}
UBlueprintNodeSpawner* Spawner = UBlueprintNodeSpawner::Create(ActionKey);
check(Spawner);
Spawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(CustomizeCallback, Iter);
ActionRegistrar.AddBlueprintAction(ActionKey, Spawner);
}
}
}
FText UK2Node_GetEngineSubsystem::GetMenuCategory() const
{
return NSLOCTEXT("K2Node", "GetEngineSubsystem_MenuCategory", "Engine Subsystems");
}
FText UK2Node_GetEngineSubsystem::GetTooltipText() const
{
if (CustomClass)
{
return FText::FormatNamed(NSLOCTEXT("K2Node", "GetEngineSubsystem_TooltipFormat", "Get {ClassName} an Engine Subsystem"), TEXT("ClassName"), CustomClass->GetDisplayNameText());
}
return NSLOCTEXT("K2Node", "GetEngineSubsystem_InvalidSubsystemTypeTooltip", "Invalid Subsystem Type");
}
// ************************************************************************************
// UK2Node_GetEditorSubsystem
// ************************************************************************************
void UK2Node_GetEditorSubsystem::AllocateDefaultPins()
{
// Add blueprint pin
if (!CustomClass)
{
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Class, USubsystem::StaticClass(), TEXT("Class"));
}
// Result pin
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Object, (CustomClass ? (UClass*)CustomClass : UEditorSubsystem::StaticClass()), UEdGraphSchema_K2::PN_ReturnValue);
// Skip the UK2Node_GetSubsystem implementation
UK2Node::AllocateDefaultPins();
}
void UK2Node_GetEditorSubsystem::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
// Skip the UK2Node_GetSubsystem implementation
UK2Node::ExpandNode(CompilerContext, SourceGraph);
static const FName Class_ParamName(TEXT("Class"));
UK2Node_GetEditorSubsystem* GetEditorSubsystemNode = this;
UEdGraphPin* SpawnClassPin = GetEditorSubsystemNode->GetClassPin();
UEdGraphPin* SpawnNodeResult = GetEditorSubsystemNode->GetResultPin();
UClass* SpawnClass = (SpawnClassPin != nullptr) ? Cast<UClass>(SpawnClassPin->DefaultObject) : nullptr;
if (SpawnClassPin && (SpawnClassPin->LinkedTo.Num() == 0) && !SpawnClass)
{
CompilerContext.MessageLog.Error(*NSLOCTEXT("K2Node", "GetSubsystem_Error", "Node @@ must have a class specified.").ToString(), GetEditorSubsystemNode);
GetEditorSubsystemNode->BreakAllNodeLinks();
return;
}
// Choose appropriate underlying Getter
FName Get_FunctionName;
if (CustomClass->IsChildOf<UEditorSubsystem>())
{
Get_FunctionName = GET_FUNCTION_NAME_CHECKED(UEditorSubsystemBlueprintLibrary, GetEditorSubsystem);
}
else
{
CompilerContext.MessageLog.Error(*NSLOCTEXT("K2Node", "GetSubsystem_Error", "Node @@ must have a class specified.").ToString(), GetEditorSubsystemNode);
GetEditorSubsystemNode->BreakAllNodeLinks();
return;
}
//////////////////////////////////////////////////////////////////////////
// create 'UEditorSubsystemBlueprintLibrary::GetEditorSubsystem' call node
UK2Node_CallFunction* CallGetNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(GetEditorSubsystemNode, SourceGraph);
CallGetNode->FunctionReference.SetExternalMember(Get_FunctionName, UEditorSubsystemBlueprintLibrary::StaticClass());
CallGetNode->AllocateDefaultPins();
UEdGraphPin* CallCreateClassTypePin = CallGetNode->FindPinChecked(Class_ParamName);
UEdGraphPin* CallCreateResult = CallGetNode->GetReturnValuePin();
if (SpawnClassPin && SpawnClassPin->LinkedTo.Num() > 0)
{
// Copy the 'class' connection from the spawn node to 'USubsystemUEditorSubsystemBlueprintLibraryBlueprintLibrary::GetLocalPlayerSubSystemFromPlayerController'
CompilerContext.MovePinLinksToIntermediate(*SpawnClassPin, *CallCreateClassTypePin);
}
else
{
// Copy class literal onto 'UEditorSubsystemBlueprintLibrary::GetLocalPlayerSubSystemFromPlayerController' call
CallCreateClassTypePin->DefaultObject = *CustomClass;
}
// Move result connection from spawn node to 'UEditorSubsystemBlueprintLibrary::Get[something]Subsystem'
CallCreateResult->PinType = SpawnNodeResult->PinType;
CompilerContext.MovePinLinksToIntermediate(*SpawnNodeResult, *CallCreateResult);
//////////////////////////////////////////////////////////////////////////
// Break any links to the expanded node
GetEditorSubsystemNode->BreakAllNodeLinks();
}
void UK2Node_GetEditorSubsystem::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
{
static TArray<UClass*> Subclasses;
Subclasses.Reset();
GetDerivedClasses(UEditorSubsystem::StaticClass(), Subclasses);
auto CustomizeCallback = [](UEdGraphNode* Node, bool bIsTemplateNode, UClass* Subclass)
{
auto TypedNode = CastChecked<UK2Node_GetEditorSubsystem>(Node);
TypedNode->Initialize(Subclass);
};
UClass* ActionKey = GetClass();
if (ActionRegistrar.IsOpenForRegistration(ActionKey))
{
for (UClass* Iter : Subclasses)
{
if (!UEdGraphSchema_K2::IsAllowableBlueprintVariableType(Iter, true))
{
continue;
}
UBlueprintNodeSpawner* Spawner = UBlueprintNodeSpawner::Create(ActionKey);
check(Spawner);
Spawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(CustomizeCallback, Iter);
ActionRegistrar.AddBlueprintAction(ActionKey, Spawner);
}
}
}
FText UK2Node_GetEditorSubsystem::GetMenuCategory() const
{
return NSLOCTEXT("K2Node", "GetEditorSubsystem_MenuCategory", "Editor Subsystems");
}
FText UK2Node_GetEditorSubsystem::GetTooltipText() const
{
if (CustomClass)
{
return FText::FormatNamed(NSLOCTEXT("K2Node", "GetEditorSubsystem_TooltipFormat", "Get {ClassName} an Editor Subsystem"), TEXT("ClassName"), CustomClass->GetDisplayNameText());
}
return NSLOCTEXT("K2Node", "GetEditorSubsystem_InvalidSubsystemTypeTooltip", "Invalid Subsystem Type");
}
bool UK2Node_GetSubsystem::CanJumpToDefinition() const
{
if (CustomClass && ensure(GUnrealEd) && GUnrealEd->GetUnrealEdOptions()->IsCPPAllowed())
{
return true;
}
return Super::CanJumpToDefinition();
}
void UK2Node_GetSubsystem::JumpToDefinition() const
{
bool bSucceeded = false;
if (CustomClass && ensure(GUnrealEd) && GUnrealEd->GetUnrealEdOptions()->IsCPPAllowed())
{
// Attempt to navigate to the header file where the class is defined if the
// blueprint preferences allow for it
if (GetDefault<UBlueprintEditorSettings>()->bNavigateToNativeFunctionsFromCallNodes)
{
if (FSourceCodeNavigation::CanNavigateToClass(CustomClass))
{
bSucceeded = FSourceCodeNavigation::NavigateToClass(CustomClass);
}
}
}
// Otherwise fall back to the base class which will just bring focus to this node
if (!bSucceeded)
{
Super::JumpToDefinition();
}
}
bool UK2Node_GetEditorSubsystem::IsActionFilteredOut(class FBlueprintActionFilter const& Filter)
{
for(UBlueprint* BP : Filter.Context.Blueprints)
{
if (!FBlueprintEditorUtils::IsEditorUtilityBlueprint(BP))
{
return true;
}
}
return false;
}
void UK2Node_GetEditorSubsystem::ValidateNodeDuringCompilation(class FCompilerResultsLog& MessageLog) const
{
Super::ValidateNodeDuringCompilation(MessageLog);
const UBlueprint* BP = FBlueprintEditorUtils::FindBlueprintForNodeChecked(this);
if (!FBlueprintEditorUtils::IsEditorUtilityBlueprint(BP))
{
const FText ErrorText = NSLOCTEXT("K2Node", "GetEditorSubsystem_Error", "Editor Subsystems can only be used in Editor Utilities / Blutilities");
MessageLog.Error(*ErrorText.ToString(), this);
}
}