Update directive utilities plugin
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) 2026 Unreal Directive. Licensed under the MIT License.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class DirectiveUtilitiesBlueprintNodes : ModuleRules
|
||||
{
|
||||
public DirectiveUtilitiesBlueprintNodes(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"BlueprintGraph",
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"DirectiveUtilitiesRuntime",
|
||||
}
|
||||
);
|
||||
|
||||
PrivateDependencyModuleNames.Add("UnrealEd");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2026 Unreal Directive. Licensed under the MIT License.
|
||||
|
||||
#include "DirectiveUtilitiesBlueprintNodes.h"
|
||||
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "Nodes/DirectiveUtilMapNodeMigration.h"
|
||||
#include "UObject/UObjectGlobals.h"
|
||||
#include "UObject/UObjectIterator.h"
|
||||
|
||||
void FDirectiveUtilitiesBlueprintNodesModule::StartupModule()
|
||||
{
|
||||
AssetLoadedHandle = FCoreUObjectDelegates::OnAssetLoaded.AddRaw(this, &FDirectiveUtilitiesBlueprintNodesModule::HandleAssetLoaded);
|
||||
|
||||
for (TObjectIterator<UBlueprint> Blueprint; Blueprint; ++Blueprint)
|
||||
{
|
||||
HandleAssetLoaded(*Blueprint);
|
||||
}
|
||||
}
|
||||
|
||||
void FDirectiveUtilitiesBlueprintNodesModule::ShutdownModule()
|
||||
{
|
||||
FCoreUObjectDelegates::OnAssetLoaded.Remove(AssetLoadedHandle);
|
||||
}
|
||||
|
||||
void FDirectiveUtilitiesBlueprintNodesModule::HandleAssetLoaded(UObject* Asset)
|
||||
{
|
||||
UBlueprint* Blueprint = Cast<UBlueprint>(Asset);
|
||||
if (Blueprint && !Blueprint->HasAnyFlags(RF_Transient))
|
||||
{
|
||||
DirectiveUtilMapNodeMigration::UpgradeLegacyAppendNodes(*Blueprint);
|
||||
}
|
||||
}
|
||||
|
||||
IMPLEMENT_MODULE(FDirectiveUtilitiesBlueprintNodesModule, DirectiveUtilitiesBlueprintNodes)
|
||||
@@ -0,0 +1,127 @@
|
||||
// Copyright (c) 2026 Unreal Directive. Licensed under the MIT License.
|
||||
|
||||
#include "Nodes/DirectiveUtilMapNodeMigration.h"
|
||||
|
||||
#include "EdGraph/EdGraph.h"
|
||||
#include "EdGraph/EdGraphPin.h"
|
||||
#include "EdGraphSchema_K2.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "K2Node_CallFunction.h"
|
||||
#include "Kismet2/BlueprintEditorUtils.h"
|
||||
#include "Libraries/DirectiveUtilMapFunctionLibrary.h"
|
||||
#include "Nodes/K2Node_DirectiveUtilMapAppend.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
bool IsLegacyAppendNode(const UK2Node_CallFunction& Node)
|
||||
{
|
||||
return !Node.IsA<UK2Node_DirectiveUtilMapAppend>()
|
||||
&& Node.FunctionReference.GetMemberParentClass() == UDirectiveUtilMapFunctionLibrary::StaticClass()
|
||||
&& Node.FunctionReference.GetMemberName() == GET_FUNCTION_NAME_CHECKED(UDirectiveUtilMapFunctionLibrary, Map_Append);
|
||||
}
|
||||
|
||||
void CopyMapPinTypes(const UK2Node_CallFunction& LegacyNode, UK2Node_DirectiveUtilMapAppend& NewNode)
|
||||
{
|
||||
const FEdGraphPinType* MapType = nullptr;
|
||||
for (const FName PinName : {FName(TEXT("TargetMap")), FName(TEXT("SourceMap"))})
|
||||
{
|
||||
const UEdGraphPin* LegacyPin = LegacyNode.FindPin(PinName);
|
||||
if (!LegacyPin)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!LegacyPin->LinkedTo.IsEmpty() && LegacyPin->LinkedTo[0]->PinType.IsMap())
|
||||
{
|
||||
MapType = &LegacyPin->LinkedTo[0]->PinType;
|
||||
break;
|
||||
}
|
||||
if (LegacyPin->PinType.IsMap()
|
||||
&& LegacyPin->PinType.PinCategory != UEdGraphSchema_K2::PC_Wildcard
|
||||
&& LegacyPin->PinType.PinValueType.TerminalCategory != UEdGraphSchema_K2::PC_Wildcard)
|
||||
{
|
||||
MapType = &LegacyPin->PinType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!MapType)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (const FName PinName : {FName(TEXT("TargetMap")), FName(TEXT("SourceMap"))})
|
||||
{
|
||||
if (UEdGraphPin* NewPin = NewNode.FindPin(PinName))
|
||||
{
|
||||
NewPin->PinType.PinCategory = MapType->PinCategory;
|
||||
NewPin->PinType.PinSubCategory = MapType->PinSubCategory;
|
||||
NewPin->PinType.PinSubCategoryObject = MapType->PinSubCategoryObject;
|
||||
NewPin->PinType.PinSubCategoryMemberReference = MapType->PinSubCategoryMemberReference;
|
||||
NewPin->PinType.PinValueType = MapType->PinValueType;
|
||||
NewPin->PinType.bIsWeakPointer = MapType->bIsWeakPointer;
|
||||
NewPin->PinType.bIsUObjectWrapper = MapType->bIsUObjectWrapper;
|
||||
NewPin->PinType.bSerializeAsSinglePrecisionFloat = MapType->bSerializeAsSinglePrecisionFloat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ReplaceLegacyAppendNode(UK2Node_CallFunction& LegacyNode)
|
||||
{
|
||||
UEdGraph* Graph = LegacyNode.GetGraph();
|
||||
const UEdGraphSchema_K2* Schema = Graph ? Cast<UEdGraphSchema_K2>(Graph->GetSchema()) : nullptr;
|
||||
if (!Graph || !Schema)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UK2Node_DirectiveUtilMapAppend* NewNode = NewObject<UK2Node_DirectiveUtilMapAppend>(Graph, NAME_None, RF_Transactional);
|
||||
Graph->AddNode(NewNode, false, false);
|
||||
NewNode->CreateNewGuid();
|
||||
NewNode->PostPlacedNewNode();
|
||||
NewNode->AllocateDefaultPins();
|
||||
NewNode->AdvancedPinDisplay = LegacyNode.AdvancedPinDisplay;
|
||||
NewNode->SetEnabledState(LegacyNode.GetDesiredEnabledState(), LegacyNode.HasUserSetTheEnabledState());
|
||||
CopyMapPinTypes(LegacyNode, *NewNode);
|
||||
|
||||
if (Schema->ReplaceOldNodeWithNew(&LegacyNode, NewNode, {}))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
NewNode->DestroyNode();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool DirectiveUtilMapNodeMigration::UpgradeLegacyAppendNodes(UBlueprint& Blueprint)
|
||||
{
|
||||
TArray<UEdGraph*> Graphs;
|
||||
Blueprint.GetAllGraphs(Graphs);
|
||||
|
||||
bool bModified = false;
|
||||
for (UEdGraph* Graph : Graphs)
|
||||
{
|
||||
if (!Graph)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
TArray<UK2Node_CallFunction*> FunctionNodes;
|
||||
Graph->GetNodesOfClass(FunctionNodes);
|
||||
for (UK2Node_CallFunction* FunctionNode : FunctionNodes)
|
||||
{
|
||||
if (FunctionNode && IsLegacyAppendNode(*FunctionNode))
|
||||
{
|
||||
bModified |= ReplaceLegacyAppendNode(*FunctionNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bModified)
|
||||
{
|
||||
FBlueprintEditorUtils::MarkBlueprintAsModified(&Blueprint);
|
||||
}
|
||||
|
||||
return bModified;
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
// Copyright (c) 2026 Unreal Directive. Licensed under the MIT License.
|
||||
|
||||
#include "Nodes/K2Node_DirectiveUtilMapAppend.h"
|
||||
|
||||
#include "BlueprintActionDatabaseRegistrar.h"
|
||||
#include "BlueprintNodeSpawner.h"
|
||||
#include "EdGraph/EdGraph.h"
|
||||
#include "EdGraph/EdGraphPin.h"
|
||||
#include "EdGraphSchema_K2.h"
|
||||
#include "Kismet2/CompilerResultsLog.h"
|
||||
#include "Libraries/DirectiveUtilMapFunctionLibrary.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "K2Node_DirectiveUtilMapAppend"
|
||||
|
||||
namespace DirectiveUtilMapAppendPins
|
||||
{
|
||||
const FName TargetMap(TEXT("TargetMap"));
|
||||
const FName SourceMap(TEXT("SourceMap"));
|
||||
}
|
||||
|
||||
UK2Node_DirectiveUtilMapAppend::UK2Node_DirectiveUtilMapAppend()
|
||||
{
|
||||
FunctionReference.SetExternalMember(
|
||||
GET_FUNCTION_NAME_CHECKED(UDirectiveUtilMapFunctionLibrary, Map_Append),
|
||||
UDirectiveUtilMapFunctionLibrary::StaticClass());
|
||||
}
|
||||
|
||||
void UK2Node_DirectiveUtilMapAppend::AllocateDefaultPins()
|
||||
{
|
||||
Super::AllocateDefaultPins();
|
||||
ConformMapPins();
|
||||
}
|
||||
|
||||
void UK2Node_DirectiveUtilMapAppend::PostReconstructNode()
|
||||
{
|
||||
Super::PostReconstructNode();
|
||||
ConformMapPins();
|
||||
}
|
||||
|
||||
void UK2Node_DirectiveUtilMapAppend::NotifyPinConnectionListChanged(UEdGraphPin* Pin)
|
||||
{
|
||||
Super::NotifyPinConnectionListChanged(Pin);
|
||||
|
||||
if (IsAppendMapPin(Pin) && ConformMapPins())
|
||||
{
|
||||
GetGraph()->NotifyGraphChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void UK2Node_DirectiveUtilMapAppend::ValidateNodeDuringCompilation(FCompilerResultsLog& MessageLog) const
|
||||
{
|
||||
Super::ValidateNodeDuringCompilation(MessageLog);
|
||||
|
||||
const UEdGraphPin* TargetMap = FindPin(DirectiveUtilMapAppendPins::TargetMap);
|
||||
const UEdGraphPin* SourceMap = FindPin(DirectiveUtilMapAppendPins::SourceMap);
|
||||
if (!TargetMap || !SourceMap || TargetMap->LinkedTo.IsEmpty() || SourceMap->LinkedTo.IsEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!HaveMatchingMapTypes(TargetMap->LinkedTo[0]->PinType, SourceMap->LinkedTo[0]->PinType))
|
||||
{
|
||||
MessageLog.Error(*LOCTEXT("MismatchedMapTypes", "Append requires matching map types on @@.").ToString(), this);
|
||||
}
|
||||
}
|
||||
|
||||
void UK2Node_DirectiveUtilMapAppend::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
|
||||
{
|
||||
UClass* ActionKey = GetClass();
|
||||
if (ActionRegistrar.IsOpenForRegistration(ActionKey))
|
||||
{
|
||||
UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
|
||||
check(NodeSpawner);
|
||||
ActionRegistrar.AddBlueprintAction(ActionKey, NodeSpawner);
|
||||
}
|
||||
}
|
||||
|
||||
bool UK2Node_DirectiveUtilMapAppend::IsConnectionDisallowed(
|
||||
const UEdGraphPin* MyPin,
|
||||
const UEdGraphPin* OtherPin,
|
||||
FString& OutReason) const
|
||||
{
|
||||
if (Super::IsConnectionDisallowed(MyPin, OtherPin, OutReason))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!IsAppendMapPin(MyPin) || !OtherPin || !IsConcreteMap(OtherPin->PinType))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const FName SiblingName = MyPin->PinName == DirectiveUtilMapAppendPins::TargetMap
|
||||
? DirectiveUtilMapAppendPins::SourceMap
|
||||
: DirectiveUtilMapAppendPins::TargetMap;
|
||||
const UEdGraphPin* SiblingPin = FindPin(SiblingName);
|
||||
if (!SiblingPin)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const FEdGraphPinType* SiblingType = &SiblingPin->PinType;
|
||||
if (!SiblingPin->LinkedTo.IsEmpty())
|
||||
{
|
||||
SiblingType = &SiblingPin->LinkedTo[0]->PinType;
|
||||
}
|
||||
|
||||
if (IsConcreteMap(*SiblingType) && !HaveMatchingMapTypes(*SiblingType, OtherPin->PinType))
|
||||
{
|
||||
OutReason = LOCTEXT("MapTypeMismatch", "Both maps must have the same key and value types.").ToString();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UK2Node_DirectiveUtilMapAppend::ConformMapPins()
|
||||
{
|
||||
UEdGraphPin* TargetMap = FindPin(DirectiveUtilMapAppendPins::TargetMap);
|
||||
UEdGraphPin* SourceMap = FindPin(DirectiveUtilMapAppendPins::SourceMap);
|
||||
if (!TargetMap || !SourceMap)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const FEdGraphPinType* ResolvedType = nullptr;
|
||||
for (const UEdGraphPin* MapPin : {TargetMap, SourceMap})
|
||||
{
|
||||
if (!MapPin->LinkedTo.IsEmpty() && IsConcreteMap(MapPin->LinkedTo[0]->PinType))
|
||||
{
|
||||
ResolvedType = &MapPin->LinkedTo[0]->PinType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const FEdGraphPinType PreviousTargetType = TargetMap->PinType;
|
||||
const FEdGraphPinType PreviousSourceType = SourceMap->PinType;
|
||||
if (ResolvedType)
|
||||
{
|
||||
ApplyMapType(*TargetMap, *ResolvedType);
|
||||
ApplyMapType(*SourceMap, *ResolvedType);
|
||||
}
|
||||
else
|
||||
{
|
||||
ResetMapType(*TargetMap);
|
||||
ResetMapType(*SourceMap);
|
||||
}
|
||||
|
||||
return PreviousTargetType != TargetMap->PinType || PreviousSourceType != SourceMap->PinType;
|
||||
}
|
||||
|
||||
bool UK2Node_DirectiveUtilMapAppend::IsAppendMapPin(const UEdGraphPin* Pin)
|
||||
{
|
||||
return Pin && (Pin->PinName == DirectiveUtilMapAppendPins::TargetMap || Pin->PinName == DirectiveUtilMapAppendPins::SourceMap);
|
||||
}
|
||||
|
||||
bool UK2Node_DirectiveUtilMapAppend::IsConcreteMap(const FEdGraphPinType& PinType)
|
||||
{
|
||||
return PinType.IsMap()
|
||||
&& PinType.PinCategory != UEdGraphSchema_K2::PC_Wildcard
|
||||
&& PinType.PinValueType.TerminalCategory != UEdGraphSchema_K2::PC_Wildcard;
|
||||
}
|
||||
|
||||
bool UK2Node_DirectiveUtilMapAppend::HaveMatchingMapTypes(
|
||||
const FEdGraphPinType& First,
|
||||
const FEdGraphPinType& Second)
|
||||
{
|
||||
return First.IsMap()
|
||||
&& Second.IsMap()
|
||||
&& First.PinCategory == Second.PinCategory
|
||||
&& First.PinSubCategory == Second.PinSubCategory
|
||||
&& First.PinSubCategoryObject == Second.PinSubCategoryObject
|
||||
&& First.bIsWeakPointer == Second.bIsWeakPointer
|
||||
&& First.bIsUObjectWrapper == Second.bIsUObjectWrapper
|
||||
&& First.PinValueType.TerminalCategory == Second.PinValueType.TerminalCategory
|
||||
&& First.PinValueType.TerminalSubCategory == Second.PinValueType.TerminalSubCategory
|
||||
&& First.PinValueType.TerminalSubCategoryObject == Second.PinValueType.TerminalSubCategoryObject
|
||||
&& First.PinValueType.bTerminalIsWeakPointer == Second.PinValueType.bTerminalIsWeakPointer
|
||||
&& First.PinValueType.bTerminalIsUObjectWrapper == Second.PinValueType.bTerminalIsUObjectWrapper;
|
||||
}
|
||||
|
||||
void UK2Node_DirectiveUtilMapAppend::ApplyMapType(UEdGraphPin& Pin, const FEdGraphPinType& MapType)
|
||||
{
|
||||
Pin.PinType.PinCategory = MapType.PinCategory;
|
||||
Pin.PinType.PinSubCategory = MapType.PinSubCategory;
|
||||
Pin.PinType.PinSubCategoryObject = MapType.PinSubCategoryObject;
|
||||
Pin.PinType.PinSubCategoryMemberReference = MapType.PinSubCategoryMemberReference;
|
||||
Pin.PinType.PinValueType = MapType.PinValueType;
|
||||
Pin.PinType.bIsWeakPointer = MapType.bIsWeakPointer;
|
||||
Pin.PinType.bIsUObjectWrapper = MapType.bIsUObjectWrapper;
|
||||
Pin.PinType.bSerializeAsSinglePrecisionFloat = MapType.bSerializeAsSinglePrecisionFloat;
|
||||
}
|
||||
|
||||
void UK2Node_DirectiveUtilMapAppend::ResetMapType(UEdGraphPin& Pin)
|
||||
{
|
||||
Pin.PinType.PinCategory = UEdGraphSchema_K2::PC_Wildcard;
|
||||
Pin.PinType.PinSubCategory = NAME_None;
|
||||
Pin.PinType.PinSubCategoryObject = nullptr;
|
||||
Pin.PinType.PinSubCategoryMemberReference = FSimpleMemberReference();
|
||||
Pin.PinType.PinValueType = FEdGraphTerminalType();
|
||||
Pin.PinType.PinValueType.TerminalCategory = UEdGraphSchema_K2::PC_Wildcard;
|
||||
Pin.PinType.bIsWeakPointer = false;
|
||||
Pin.PinType.bIsUObjectWrapper = false;
|
||||
Pin.PinType.bSerializeAsSinglePrecisionFloat = false;
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2026 Unreal Directive. Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
class UObject;
|
||||
|
||||
class FDirectiveUtilitiesBlueprintNodesModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
|
||||
private:
|
||||
void HandleAssetLoaded(UObject* Asset);
|
||||
|
||||
FDelegateHandle AssetLoadedHandle;
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) 2026 Unreal Directive. Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
|
||||
class UBlueprint;
|
||||
|
||||
namespace DirectiveUtilMapNodeMigration
|
||||
{
|
||||
DIRECTIVEUTILITIESBLUEPRINTNODES_API bool UpgradeLegacyAppendNodes(UBlueprint& Blueprint);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2026 Unreal Directive. Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "K2Node_CallFunction.h"
|
||||
#include "K2Node_DirectiveUtilMapAppend.generated.h"
|
||||
|
||||
class FBlueprintActionDatabaseRegistrar;
|
||||
class FCompilerResultsLog;
|
||||
class UEdGraphPin;
|
||||
|
||||
UCLASS()
|
||||
class DIRECTIVEUTILITIESBLUEPRINTNODES_API UK2Node_DirectiveUtilMapAppend : public UK2Node_CallFunction
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UK2Node_DirectiveUtilMapAppend();
|
||||
|
||||
virtual void AllocateDefaultPins() override;
|
||||
virtual void PostReconstructNode() override;
|
||||
virtual void NotifyPinConnectionListChanged(UEdGraphPin* Pin) override;
|
||||
virtual void ValidateNodeDuringCompilation(FCompilerResultsLog& MessageLog) const override;
|
||||
virtual void GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const override;
|
||||
virtual bool IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const override;
|
||||
|
||||
private:
|
||||
bool ConformMapPins();
|
||||
static bool IsAppendMapPin(const UEdGraphPin* Pin);
|
||||
static bool IsConcreteMap(const FEdGraphPinType& PinType);
|
||||
static bool HaveMatchingMapTypes(const FEdGraphPinType& First, const FEdGraphPinType& Second);
|
||||
static void ApplyMapType(UEdGraphPin& Pin, const FEdGraphPinType& MapType);
|
||||
static void ResetMapType(UEdGraphPin& Pin);
|
||||
};
|
||||
Reference in New Issue
Block a user