Update directive utilities plugin

This commit is contained in:
2026-08-15 21:06:43 +02:00
parent 2fe7f83a38
commit a06fed1cba
103 changed files with 18211 additions and 1071 deletions

View File

@@ -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;
}

View File

@@ -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