BlueprintGraph_Create

This commit is contained in:
2026-03-21 00:34:51 -04:00
parent 8fbfd588f5
commit e493155035
3 changed files with 123 additions and 116 deletions

View File

@@ -1,116 +0,0 @@
#pragma once
#include "CoreMinimal.h"
#include "WingServer.h"
#include "WingHandler.h"
#include "WingFetcher.h"
#include "WingUtils.h"
#include "Engine/Blueprint.h"
#include "EdGraph/EdGraph.h"
#include "EdGraph/EdGraphNode.h"
#include "EdGraphSchema_K2.h"
#include "K2Node_CustomEvent.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "BlueprintGraph_Create.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UWing_BlueprintGraph_Create : public UObject, public IWingHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Blueprint name or package path"))
FString Blueprint;
UPROPERTY(meta=(Description="Name for the new graph"))
FString Graph;
UPROPERTY(meta=(Description="Type of graph: function, macro, or customEvent"))
FString GraphType;
virtual FString GetDescription() const override
{
return TEXT("Create a new function, macro, or custom event graph in a Blueprint.");
}
virtual void Handle() override
{
if (GraphType != TEXT("function") && GraphType != TEXT("macro") && GraphType != TEXT("customEvent"))
{
UWingServer::Printf(TEXT("ERROR: Invalid GraphType '%s'. Valid values: function, macro, customEvent\n"), *GraphType);
return;
}
WingFetcher F;
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
if (!BP) return;
// Check graph name uniqueness
if (!WingUtils::FindExactlyNoneNamed(Graph, WingUtils::AllGraphs(BP), TEXT("Graph")))
return;
// For custom events, also check for existing custom events with the same name
if (GraphType == TEXT("customEvent"))
{
for (UK2Node_CustomEvent* CE : WingUtils::AllNodes<UK2Node_CustomEvent>(BP))
{
if (CE->CustomFunctionName == FName(*Graph))
{
UWingServer::Printf(TEXT("ERROR: A custom event named '%s' already exists in %s\n"), *Graph, *WingUtils::FormatName(BP));
return;
}
}
}
if (GraphType == TEXT("function"))
{
UEdGraph* NewGraph = FBlueprintEditorUtils::CreateNewGraph(BP, FName(*Graph),
UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass());
if (!NewGraph)
{
UWingServer::Print(TEXT("ERROR: Failed to create function graph\n"));
return;
}
FBlueprintEditorUtils::AddFunctionGraph(BP, NewGraph, /*bIsUserCreated=*/true, /*SignatureFromObject=*/static_cast<UClass*>(nullptr));
UWingServer::Printf(TEXT("Created function graph: %s\n"), *WingUtils::FormatName(NewGraph));
}
else if (GraphType == TEXT("macro"))
{
UEdGraph* NewGraph = FBlueprintEditorUtils::CreateNewGraph(BP, FName(*Graph),
UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass());
if (!NewGraph)
{
UWingServer::Print(TEXT("ERROR: Failed to create macro graph\n"));
return;
}
FBlueprintEditorUtils::AddMacroGraph(BP, NewGraph, /*bIsUserCreated=*/true, /*SignatureFromClass=*/nullptr);
UWingServer::Printf(TEXT("Created macro graph: %s\n"), *WingUtils::FormatName(NewGraph));
}
else // customEvent
{
UEdGraph* EventGraph = nullptr;
if (BP->UbergraphPages.Num() > 0)
EventGraph = BP->UbergraphPages[0];
if (!EventGraph)
{
UWingServer::Print(TEXT("ERROR: Blueprint has no EventGraph to add a custom event to\n"));
return;
}
UK2Node_CustomEvent* NewEvent = NewObject<UK2Node_CustomEvent>(EventGraph);
NewEvent->CustomFunctionName = FName(*Graph);
NewEvent->bIsEditable = true;
EventGraph->AddNode(NewEvent, /*bFromUI=*/false, /*bSelectNewNode=*/false);
NewEvent->CreateNewGuid();
NewEvent->PostPlacedNewNode();
NewEvent->AllocateDefaultPins();
UWingServer::Printf(TEXT("Created custom event: %s\n"), *WingUtils::FormatName(NewEvent));
}
}
};

View File

@@ -0,0 +1,121 @@
#pragma once
#include "CoreMinimal.h"
#include "WingServer.h"
#include "WingHandler.h"
#include "WingFetcher.h"
#include "WingUtils.h"
#include "WingFunctionArgs.h"
#include "Engine/Blueprint.h"
#include "EdGraph/EdGraph.h"
#include "EdGraph/EdGraphNode.h"
#include "K2Node_EditablePinBase.h"
#include "K2Node_FunctionResult.h"
#include "EdGraphSchema_K2.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "BlueprintGraph_Create.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UWing_BlueprintGraph_Create : public UObject, public IWingHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Blueprint name or package path"))
FString Blueprint;
UPROPERTY(meta=(Description="Name for the new graph"))
FString Graph;
UPROPERTY(meta=(Description="Type of graph: function or macro"))
FString GraphType;
UPROPERTY(meta=(Description="Arguments expressed as: int x,float y"))
FString Arguments;
UPROPERTY(meta=(Description="Return values expressed as: int x,float y"))
FString ReturnValues;
virtual FString GetDescription() const override
{
return TEXT("Create a new function or macro graph in a Blueprint.");
}
virtual void Handle() override
{
if (GraphType != TEXT("function") && GraphType != TEXT("macro"))
{
UWingServer::Printf(TEXT("ERROR: Invalid GraphType '%s'. Valid values: function, macro\n"), *GraphType);
return;
}
WingFetcher F;
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
if (!BP) return;
// Check that this graph type is valid for this blueprint type
if (BP->BlueprintType == BPTYPE_Interface)
{
UWingServer::Print(TEXT("ERROR: Cannot add graphs to interface blueprints.\n"));
return;
}
if (BP->BlueprintType == BPTYPE_MacroLibrary && GraphType == TEXT("function"))
{
UWingServer::Print(TEXT("ERROR: Macro libraries cannot contain functions.\n"));
return;
}
if (BP->BlueprintType == BPTYPE_FunctionLibrary && GraphType == TEXT("macro"))
{
UWingServer::Print(TEXT("ERROR: Function libraries cannot contain macros.\n"));
return;
}
// Check graph name uniqueness and legality
if (!WingUtils::FindExactlyNoneNamed(Graph, WingUtils::AllGraphs(BP), TEXT("Graph")))
return;
FString InternalName = WingUtils::CheckProposedName(Graph);
if (InternalName.IsEmpty()) return;
// Validate argument and return value types before making changes
if (!Arguments.IsEmpty() && !WingFunctionArgs::CheckArgs(Arguments)) return;
if (!ReturnValues.IsEmpty() && !WingFunctionArgs::CheckArgs(ReturnValues)) return;
// Create the Graph
UEdGraph* NewGraph = FBlueprintEditorUtils::CreateNewGraph(BP, FName(InternalName),
UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass());
if (!NewGraph)
{
UWingServer::Print(TEXT("ERROR: Failed to create graph\n"));
return;
}
if (GraphType == TEXT("function"))
{
FBlueprintEditorUtils::AddFunctionGraph(BP, NewGraph, /*bIsUserCreated=*/true, /*SignatureFromObject=*/static_cast<UClass*>(nullptr));
FBlueprintEditorUtils::FindOrCreateFunctionResultNode(FBlueprintEditorUtils::GetEntryNode(NewGraph));
}
else if (GraphType == TEXT("macro"))
{
FBlueprintEditorUtils::AddMacroGraph(BP, NewGraph, /*bIsUserCreated=*/true, /*SignatureFromClass=*/nullptr);
}
// Try GetEntryAndResultNodes first (works for both functions and macros)
TWeakObjectPtr<UK2Node_EditablePinBase> Entry;
TWeakObjectPtr<UK2Node_EditablePinBase> Exit;
FBlueprintEditorUtils::GetEntryAndResultNodes(NewGraph, Entry, Exit);
if ((Entry == nullptr) || (Exit == nullptr))
{
UWingServer::Printf(TEXT("Could not get graph entry and exit nodes.\n"));
return;
}
if (!WingFunctionArgs::SetArgs(Entry.Get(), Arguments)) return;
if (!WingFunctionArgs::SetArgs(Exit.Get(), ReturnValues)) return;
UWingServer::Printf(TEXT("Created %s graph: %s\n"), *GraphType, *WingUtils::FormatName(NewGraph));
}
};

View File

@@ -5,6 +5,7 @@
#include "WingTypes.h" #include "WingTypes.h"
#include "WingUtils.h" #include "WingUtils.h"
#include "WingServer.h" #include "WingServer.h"
#include "Kismet2/BlueprintEditorUtils.h"
bool WingFunctionArgs::HasArgs(UEdGraphNode* Node) bool WingFunctionArgs::HasArgs(UEdGraphNode* Node)
{ {
@@ -117,3 +118,4 @@ bool WingFunctionArgs::CheckArgs(const FString &Args)
} }
return true; return true;
} }