Files
integration/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Blueprint_AddEventDispatcher.h
2026-03-12 01:31:57 -04:00

149 lines
4.3 KiB
C++

#pragma once
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPFetcher.h"
#include "MCPUtils.h"
#include "Engine/Blueprint.h"
#include "EdGraph/EdGraph.h"
#include "EdGraph/EdGraphPin.h"
#include "K2Node_FunctionEntry.h"
#include "K2Node_EditablePinBase.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Blueprint_AddEventDispatcher.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
USTRUCT()
struct FDispatcherParamEntry
{
GENERATED_BODY()
UPROPERTY()
FString Name;
UPROPERTY()
FString Type;
};
UCLASS()
class UMCP_Blueprint_AddEventDispatcher : public UObject, public IMCPHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Path to a blueprint, e.g. /Game/Foo/MyBlueprint"))
FString Path;
UPROPERTY(meta=(Description="Name for the new event dispatcher"))
FString DispatcherName;
UPROPERTY(meta=(Optional, Description="Array of parameter objects, each with 'name' and 'type' fields"))
FMCPJsonArray Parameters;
virtual FString GetDescription() const override
{
return TEXT("Create a new multicast event dispatcher on a Blueprint, optionally with parameters.");
}
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
{
MCPFetcher F(Result);
UBlueprint* BP = F.Walk(Path).Cast<UBlueprint>();
if (!BP) return;
FName DispatcherFName(*DispatcherName);
// Check for name uniqueness against existing variables
for (const FBPVariableDescription& Var : BP->NewVariables)
{
if (Var.VarName == DispatcherFName)
{
Result.Appendf(TEXT("Error: A variable or dispatcher named '%s' already exists.\n"), *DispatcherName);
return;
}
}
// Check against existing graphs (functions, macros, etc.)
if (!MCPUtils::AllGraphsNamed(BP, DispatcherName).IsEmpty())
{
Result.Appendf(TEXT("Error: A graph named '%s' already exists.\n"), *DispatcherName);
return;
}
// Add a member variable with PC_MCDelegate pin type
F.PreEdit();
FEdGraphPinType DelegateType;
DelegateType.PinCategory = UEdGraphSchema_K2::PC_MCDelegate;
if (!FBlueprintEditorUtils::AddMemberVariable(BP, DispatcherFName, DelegateType))
{
Result.Appendf(TEXT("Error: Failed to add delegate variable for '%s'.\n"), *DispatcherName);
return;
}
// Create the signature graph
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
UEdGraph* SigGraph = FBlueprintEditorUtils::CreateNewGraph(BP, DispatcherFName,
UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass());
if (!SigGraph)
{
Result.Append(TEXT("Error: Failed to create delegate signature graph.\n"));
return;
}
K2Schema->CreateDefaultNodesForGraph(*SigGraph);
K2Schema->CreateFunctionGraphTerminators(*SigGraph, static_cast<UClass*>(nullptr));
K2Schema->AddExtraFunctionFlags(SigGraph, FUNC_BlueprintCallable | FUNC_BlueprintEvent | FUNC_Public);
K2Schema->MarkFunctionEntryAsEditable(SigGraph, true);
BP->DelegateSignatureGraphs.Add(SigGraph);
// Add parameters if provided
int32 ParamCount = 0;
if (Parameters.Array.Num() > 0)
{
UK2Node_EditablePinBase* EntryNode = nullptr;
for (UK2Node_FunctionEntry* FE : MCPUtils::AllNodes<UK2Node_FunctionEntry>(SigGraph))
{
EntryNode = FE;
break;
}
if (!EntryNode)
{
F.PostEdit();
MCPUtils::SaveBlueprintPackage(BP);
Result.Append(TEXT("Error: Event dispatcher created but entry node not found — parameters could not be added.\n"));
return;
}
for (const TSharedPtr<FJsonValue>& ParamVal : Parameters.Array)
{
FDispatcherParamEntry Entry;
if (!MCPUtils::PopulateFromJson(FDispatcherParamEntry::StaticStruct(), &Entry, ParamVal, Result)) return;
if (Entry.Name.IsEmpty() || Entry.Type.IsEmpty()) continue;
FEdGraphPinType PinType;
if (!MCPUtils::ResolveTypeFromString(Entry.Type, PinType, Result))
return;
EntryNode->CreateUserDefinedPin(FName(*Entry.Name), PinType, EGPD_Output);
ParamCount++;
}
}
F.PostEdit();
MCPUtils::SaveBlueprintPackage(BP);
Result.Appendf(TEXT("Created event dispatcher '%s'"), *DispatcherName);
if (ParamCount > 0)
Result.Appendf(TEXT(" with %d parameter(s)"), ParamCount);
Result.Append(TEXT(".\n"));
}
};