Halfway done with Dispatcher stuff

This commit is contained in:
2026-03-20 16:18:03 -04:00
parent 0ac9a01859
commit ca6b3f9768
6 changed files with 235 additions and 11 deletions

Binary file not shown.

View File

@@ -0,0 +1,100 @@
#pragma once
#include "CoreMinimal.h"
#include "WingServer.h"
#include "WingHandler.h"
#include "WingFetcher.h"
#include "WingJson.h"
#include "WingBlueprintVar.h"
#include "WingUtils.h"
#include "Engine/Blueprint.h"
#include "EdGraphSchema_K2.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "BlueprintDispatcher_Create.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UWing_BlueprintDispatcher_Create : public UObject, public IWingHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Blueprint name or package path"))
FString Blueprint;
UPROPERTY(meta=(Description="Name of the new event dispatcher"))
FString Name;
UPROPERTY(meta=(Optional, Description="Configuration: DelegateArgs, Category, Description, InstanceEditable, BlueprintReadOnly, Private, etc."))
FWingJsonObject Config;
virtual FString GetDescription() const override
{
return TEXT("Add a new event dispatcher to a Blueprint. Pass Config to set parameters, category, flags, etc.");
}
virtual void Handle() override
{
WingFetcher F;
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
if (!BP) return;
// Check for duplicate variable name
FName VarFName(*WingUtils::UnsanitizeName(Name));
if (FBlueprintEditorUtils::FindNewVariableIndex(BP, VarFName) != INDEX_NONE)
{
UWingServer::Printf(TEXT("ERROR: Variable or dispatcher '%s' already exists in %s\n"), *Name, *WingUtils::FormatName(BP));
return;
}
for (UEdGraph* Graph : WingUtils::AllGraphs(BP))
{
if (Graph->GetFName() == VarFName)
{
UWingServer::Printf(TEXT("ERROR: A graph named '%s' already exists in %s\n"), *Name, *WingUtils::FormatName(BP));
return;
}
}
// Add the delegate variable
FEdGraphPinType DelegateType;
DelegateType.PinCategory = UEdGraphSchema_K2::PC_MCDelegate;
if (!FBlueprintEditorUtils::AddMemberVariable(BP, VarFName, DelegateType))
{
UWingServer::Printf(TEXT("ERROR: Failed to add event dispatcher '%s' to %s\n"), *Name, *WingUtils::FormatName(BP));
return;
}
// Create the signature graph
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
UEdGraph* SigGraph = FBlueprintEditorUtils::CreateNewGraph(BP, VarFName,
UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass());
if (!SigGraph)
{
UWingServer::Printf(TEXT("ERROR: Failed to create signature graph for '%s'\n"), *Name);
return;
}
SigGraph->bEditable = false;
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);
// Find the newly created variable and apply config
FWingBlueprintVar Editor(BP, Name);
if (Editor.NotFound()) return;
if (Config.Json && Config.Json->Values.Num() > 0)
{
if (!Editor.ApplyJson(Config.Json.Get()))
return;
}
UWingServer::Printf(TEXT("Created event dispatcher %s in %s\n"), *Name, *WingUtils::FormatName(BP));
}
};

View File

@@ -0,0 +1,51 @@
#pragma once
#include "CoreMinimal.h"
#include "WingServer.h"
#include "WingHandler.h"
#include "WingFetcher.h"
#include "WingUtils.h"
#include "WingBlueprintVar.h"
#include "Engine/Blueprint.h"
#include "BlueprintDispatcher_Dump.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UWing_BlueprintDispatcher_Dump : public UObject, public IWingHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Blueprint name or package path"))
FString Blueprint;
UPROPERTY(meta=(Description="Name of the event dispatcher to inspect"))
FString Dispatcher;
virtual FString GetDescription() const override
{
return TEXT("Show all editable properties of a Blueprint event dispatcher.");
}
virtual void Handle() override
{
WingFetcher F;
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
if (!BP) return;
FWingBlueprintVar Editor(BP, Dispatcher);
if (Editor.NotFound()) return;
if (!Editor.IsEventDispatcher())
{
UWingServer::Printf(TEXT("ERROR: '%s' is not an event dispatcher\n"), *Dispatcher);
return;
}
UWingServer::Printf(TEXT("Event dispatcher %s in %s:\n"), *Dispatcher, *WingUtils::FormatName(BP));
Editor.Dump();
}
};

View File

@@ -67,6 +67,7 @@ public:
UWingServer::Print(TEXT("\nVariables:\n")); UWingServer::Print(TEXT("\nVariables:\n"));
for (const FBPVariableDescription& V : BP->NewVariables) for (const FBPVariableDescription& V : BP->NewVariables)
{ {
if (V.VarType.PinCategory == UEdGraphSchema_K2::PC_MCDelegate) continue;
UWingServer::Printf(TEXT(" %s %s"), UWingServer::Printf(TEXT(" %s %s"),
*UWingTypes::TypeToText(V.VarType), *UWingTypes::TypeToText(V.VarType),
*WingUtils::FormatName(V)); *WingUtils::FormatName(V));

View File

@@ -1,9 +1,11 @@
#include "WingBlueprintVar.h" #include "WingBlueprintVar.h"
#include "WingFunctionArgs.h"
#include "WingJson.h" #include "WingJson.h"
#include "WingServer.h" #include "WingServer.h"
#include "WingTypes.h" #include "WingTypes.h"
#include "WingUtils.h" #include "WingUtils.h"
#include "EdGraphSchema_K2.h" #include "EdGraphSchema_K2.h"
#include "K2Node_EditablePinBase.h"
#include "Kismet2/BlueprintEditorUtils.h" #include "Kismet2/BlueprintEditorUtils.h"
FWingBlueprintVar::FWingBlueprintVar(UBlueprint* BP, const FString& VarName) FWingBlueprintVar::FWingBlueprintVar(UBlueprint* BP, const FString& VarName)
@@ -11,6 +13,26 @@ FWingBlueprintVar::FWingBlueprintVar(UBlueprint* BP, const FString& VarName)
Desc = WingUtils::FindExactlyOneNamed(VarName, BP->NewVariables); Desc = WingUtils::FindExactlyOneNamed(VarName, BP->NewVariables);
if (!Desc) return; if (!Desc) return;
if (Desc->VarType.PinCategory == UEdGraphSchema_K2::PC_MCDelegate)
{
// Find the matching signature graph by name.
for (UEdGraph* Graph : BP->DelegateSignatureGraphs)
{
if (Graph->GetFName() == Desc->VarName)
{
SigGraph = Graph;
break;
}
}
if (!SigGraph)
{
UWingServer::Printf(TEXT("ERROR: Signature graph not found for event dispatcher '%s'\n"), *VarName);
Desc = nullptr;
return;
}
}
else
{
// Try to find the default value property on the CDO. // Try to find the default value property on the CDO.
if (BP->GeneratedClass) if (BP->GeneratedClass)
{ {
@@ -19,12 +41,14 @@ FWingBlueprintVar::FWingBlueprintVar(UBlueprint* BP, const FString& VarName)
if (CDO && Prop) if (CDO && Prop)
DefaultValueProp = FWingProperty(Prop, CDO); DefaultValueProp = FWingProperty(Prop, CDO);
} }
}
} }
void FWingBlueprintVar::Dump() void FWingBlueprintVar::Dump()
{ {
LoadFlags(); LoadFlags();
LoadDefault(); LoadDefault();
LoadDelegateArgs();
TArray<FWingProperty> Props = MergedProperties(); TArray<FWingProperty> Props = MergedProperties();
for (FWingProperty& P : Props) for (FWingProperty& P : Props)
{ {
@@ -38,6 +62,7 @@ void FWingBlueprintVar::Dump()
bool FWingBlueprintVar::ApplyJson(const FJsonObject* Json) bool FWingBlueprintVar::ApplyJson(const FJsonObject* Json)
{ {
bool bHasDefault = Json->HasField(TEXT("DefaultValue")); bool bHasDefault = Json->HasField(TEXT("DefaultValue"));
bool bHasDelegateArgs = Json->HasField(TEXT("DelegateArgs"));
bool bHasType = Json->HasField(TEXT("VarType")); bool bHasType = Json->HasField(TEXT("VarType"));
if (bHasDefault && bHasType) if (bHasDefault && bHasType)
{ {
@@ -49,6 +74,7 @@ bool FWingBlueprintVar::ApplyJson(const FJsonObject* Json)
} }
LoadFlags(); LoadFlags();
LoadDelegateArgs();
TArray<FWingProperty> Props = MergedProperties(); TArray<FWingProperty> Props = MergedProperties();
if (!WingJson::PopulateFromJson(Props, Json, true)) if (!WingJson::PopulateFromJson(Props, Json, true))
@@ -56,7 +82,9 @@ bool FWingBlueprintVar::ApplyJson(const FJsonObject* Json)
SaveFlags(); SaveFlags();
if (bHasDefault) if (bHasDefault)
return SaveDefault(); if (!SaveDefault()) return false;
if (bHasDelegateArgs)
if (!SaveDelegateArgs()) return false;
return true; return true;
} }
@@ -82,6 +110,18 @@ void FWingBlueprintVar::LoadDefault()
DefaultValue.Empty(); DefaultValue.Empty();
} }
void FWingBlueprintVar::LoadDelegateArgs()
{
if (!SigGraph) return;
TWeakObjectPtr<UK2Node_EditablePinBase> EntryNode;
TWeakObjectPtr<UK2Node_EditablePinBase> ResultNode;
FBlueprintEditorUtils::GetEntryAndResultNodes(SigGraph, EntryNode, ResultNode);
if (EntryNode.IsValid())
DelegateArgs = WingFunctionArgs::GetArgs(EntryNode.Get());
else
DelegateArgs.Empty();
}
void FWingBlueprintVar::SaveFlags() void FWingBlueprintVar::SaveFlags()
{ {
// CPF flags // CPF flags
@@ -125,6 +165,20 @@ bool FWingBlueprintVar::SaveDefault()
return true; return true;
} }
bool FWingBlueprintVar::SaveDelegateArgs()
{
if (!SigGraph) return true;
TWeakObjectPtr<UK2Node_EditablePinBase> EntryNode;
TWeakObjectPtr<UK2Node_EditablePinBase> ResultNode;
FBlueprintEditorUtils::GetEntryAndResultNodes(SigGraph, EntryNode, ResultNode);
if (!EntryNode.IsValid())
{
UWingServer::Print(TEXT("ERROR: Entry node not found in delegate signature graph\n"));
return false;
}
return WingFunctionArgs::SetArgs(EntryNode.Get(), DelegateArgs);
}
TArray<FWingProperty> FWingBlueprintVar::MergedProperties() TArray<FWingProperty> FWingBlueprintVar::MergedProperties()
{ {
TArray<FWingProperty> Props = FWingProperty::GetAll( TArray<FWingProperty> Props = FWingProperty::GetAll(
@@ -139,9 +193,20 @@ TArray<FWingProperty> FWingBlueprintVar::MergedProperties()
Props.Append(FWingProperty::GetAll( Props.Append(FWingProperty::GetAll(
FWingBlueprintVar::StaticStruct(), this, (EPropertyFlags)0)); FWingBlueprintVar::StaticStruct(), this, (EPropertyFlags)0));
if (SigGraph)
{
FWingProperty::Remove(Props, TEXT("VarType"));
FWingProperty::Remove(Props, TEXT("DefaultValue"));
FWingProperty::Remove(Props, TEXT("ExposeOnSpawn"));
FWingProperty::Remove(Props, TEXT("ExposeToCinematics"));
}
else
{
FWingProperty::Remove(Props, TEXT("DelegateArgs"));
// Remove DefaultValue if we don't have a CDO property to back it. // Remove DefaultValue if we don't have a CDO property to back it.
if (!DefaultValueProp) if (!DefaultValueProp)
FWingProperty::Remove(Props, TEXT("DefaultValue")); FWingProperty::Remove(Props, TEXT("DefaultValue"));
}
return Props; return Props;
} }

View File

@@ -16,11 +16,13 @@ struct FWingBlueprintVar
FBPVariableDescription* Desc = nullptr; FBPVariableDescription* Desc = nullptr;
FWingProperty DefaultValueProp; FWingProperty DefaultValueProp;
UEdGraph* SigGraph = nullptr;
FWingBlueprintVar() = default; FWingBlueprintVar() = default;
FWingBlueprintVar(UBlueprint* BP, const FString& VarName); FWingBlueprintVar(UBlueprint* BP, const FString& VarName);
bool NotFound() const { return Desc == nullptr; } bool NotFound() const { return Desc == nullptr; }
bool IsEventDispatcher() const { return SigGraph != nullptr; }
UPROPERTY(EditAnywhere, meta=(Optional, Description="Default value in Unreal text format")) UPROPERTY(EditAnywhere, meta=(Optional, Description="Default value in Unreal text format"))
FString DefaultValue; FString DefaultValue;
@@ -43,6 +45,9 @@ struct FWingBlueprintVar
UPROPERTY(EditAnywhere, meta=(Optional, Description="Expose to cinematics/sequencer")) UPROPERTY(EditAnywhere, meta=(Optional, Description="Expose to cinematics/sequencer"))
bool ExposeToCinematics = false; bool ExposeToCinematics = false;
UPROPERTY(EditAnywhere, meta=(Optional, Description="Delegate parameters, e.g. 'int x,float y'"))
FString DelegateArgs;
// Load from Desc, populate from JSON, save back to Desc. // Load from Desc, populate from JSON, save back to Desc.
bool ApplyJson(const FJsonObject* Json); bool ApplyJson(const FJsonObject* Json);
@@ -52,7 +57,9 @@ struct FWingBlueprintVar
private: private:
void LoadFlags(); void LoadFlags();
void LoadDefault(); void LoadDefault();
void LoadDelegateArgs();
void SaveFlags(); void SaveFlags();
bool SaveDefault(); bool SaveDefault();
bool SaveDelegateArgs();
TArray<FWingProperty> MergedProperties(); TArray<FWingProperty> MergedProperties();
}; };