WingVariables for the win

This commit is contained in:
2026-03-31 16:43:28 -04:00
parent 43f2439f29
commit d590d71421
23 changed files with 269 additions and 964 deletions

View File

@@ -5,12 +5,9 @@
#include "WingHandler.h"
#include "WingFetcher.h"
#include "WingUtils.h"
#include "WingFunctionArgs.h"
#include "WingVariables.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"
@@ -35,11 +32,11 @@ public:
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=(Optional, Description="Input variables, one per line"))
FString InputVariables;
UPROPERTY(meta=(Description="Return values expressed as: int x,float y"))
FString ReturnValues;
UPROPERTY(meta=(Optional, Description="Output variables, one per line"))
FString OutputVariables;
virtual FString GetDescription() const override
{
@@ -81,9 +78,10 @@ public:
if (!WingUtils::FindNoneWithInternalID(InternalID, WingUtils::AllGraphs(BP), TEXT("Graph")))
return;
// Validate argument and return value types before making changes
if (!Arguments.IsEmpty() && !WingFunctionArgs::CheckArgs(Arguments)) return;
if (!ReturnValues.IsEmpty() && !WingFunctionArgs::CheckArgs(ReturnValues)) return;
// Parse and validate variables before making changes
WingVariables Vars;
if (!Vars.InputVariables.ParseString(InputVariables)) return;
if (!Vars.OutputVariables.ParseString(OutputVariables)) return;
// Create the Graph
UEdGraph* NewGraph = FBlueprintEditorUtils::CreateNewGraph(BP, InternalID,
@@ -104,18 +102,11 @@ public:
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;
// Create the variables on the new graph
if (!Vars.SetBackingStore(NewGraph)) return;
if (!Vars.Check()) return;
if (!Vars.Create()) return;
UWingServer::Printf(TEXT("Created %s graph: %s\n"), *GraphType, *WingUtils::FormatName(NewGraph));
}
};

View File

@@ -1,50 +0,0 @@
#pragma once
#include "CoreMinimal.h"
#include "WingServer.h"
#include "WingHandler.h"
#include "WingFetcher.h"
#include "WingUtils.h"
#include "WingTypes.h"
#include "WingVariables.h"
#include "Engine/Blueprint.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "BlueprintVariable_Create.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UWing_BlueprintVariable_Create : public UObject, public IWingHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Blueprint path"))
FString Blueprint;
UPROPERTY(meta=(Description="Variable declarations"))
FString Variables;
virtual FString GetDescription() const override
{
return TEXT("Add new member variables to a Blueprint. "
"Format: 'type name (flags) = default', one per line.");
}
virtual void Handle() override
{
WingFetcher F;
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
if (!BP) return;
// Parse the variable declarations.
WingVariables Vars(BP);
if (!Vars.BlueprintVariables.ParseString(Variables)) return;
if (!Vars.Check()) return;
if (!Vars.Create()) return;
UWingServer::Printf(TEXT("Success.\n"));
}
};

View File

@@ -1,49 +0,0 @@
#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 "Kismet2/BlueprintEditorUtils.h"
#include "BlueprintVariable_Delete.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UWing_BlueprintVariable_Delete : public UObject, public IWingHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Blueprint name or package path"))
FString Blueprint;
UPROPERTY(meta=(Description="Name of the variable to delete"))
FString Variable;
virtual FString GetDescription() const override
{
return TEXT("Remove a member variable from a Blueprint.");
}
virtual void Handle() override
{
WingFetcher F;
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
if (!BP) return;
FWingBlueprintVar Editor(BP, Variable);
if (Editor.NotFound()) return;
FBlueprintEditorUtils::RemoveMemberVariable(BP, Editor.Desc->VarName);
UWingServer::Printf(TEXT("Removed variable %s from %s\n"),
*Variable, *WingUtils::FormatName(BP));
}
};

View File

@@ -1,39 +0,0 @@
#pragma once
#include "CoreMinimal.h"
#include "WingServer.h"
#include "WingHandler.h"
#include "WingFetcher.h"
#include "WingVariables.h"
#include "BlueprintVariable_Dump.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UWing_BlueprintVariable_Dump : public UObject, public IWingHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Blueprint name or package path"))
FString Blueprint;
virtual FString GetDescription() const override
{
return TEXT("List all member variables of a Blueprint.");
}
virtual void Handle() override
{
WingFetcher F;
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
if (!BP) return;
WingVariables Vars(BP);
Vars.Load();
Vars.Print(UWingServer::GetPrintBuffer());
}
};

View File

@@ -1,48 +0,0 @@
#pragma once
#include "CoreMinimal.h"
#include "WingServer.h"
#include "WingHandler.h"
#include "WingFetcher.h"
#include "WingUtils.h"
#include "WingVariables.h"
#include "Engine/Blueprint.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "BlueprintVariable_Modify.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UWing_BlueprintVariable_Modify : public UObject, public IWingHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Blueprint name or package path"))
FString Blueprint;
UPROPERTY(meta=(Description="Variable declarations, one per line. Format: type name (flags) = default"))
FString Variables;
virtual FString GetDescription() const override
{
return TEXT("Modify existing Blueprint variables. Format: 'type name (flags) = default', one per line.");
}
virtual void Handle() override
{
WingFetcher F;
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
if (!BP) return;
// Parse the variable declarations.
WingVariables Vars(BP);
if (!Vars.BlueprintVariables.ParseString(Variables)) return;
if (!Vars.Check()) return;
if (!Vars.Modify()) return;
UWingServer::Printf(TEXT("Success.\n"));
}
};

View File

@@ -1,43 +0,0 @@
#pragma once
#include "CoreMinimal.h"
#include "WingServer.h"
#include "WingHandler.h"
#include "WingFetcher.h"
#include "WingVariables.h"
#include "BlueprintVariable_Remove.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UWing_BlueprintVariable_Remove : public UObject, public IWingHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Blueprint path"))
FString Blueprint;
UPROPERTY(meta=(Description="Variable names to remove, comma-separated"))
FString Variables;
virtual FString GetDescription() const override
{
return TEXT("Remove member variables from a Blueprint.");
}
virtual void Handle() override
{
WingFetcher F;
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
if (!BP) return;
WingVariables Vars(BP);
if (!Vars.BlueprintVariables.ParseNamesString(Variables)) return;
if (!Vars.Remove()) return;
UWingServer::Printf(TEXT("Success.\n"));
}
};

View File

@@ -10,7 +10,6 @@
#include "Animation/AnimBlueprint.h"
#include "Animation/Skeleton.h"
#include "WingActorComponent.h"
#include "WingFunctionArgs.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "AnimationGraph.h"
#include "AnimationGraphSchema.h"
@@ -66,25 +65,8 @@ public:
}
// Variables
if (!BP->NewVariables.IsEmpty())
{
UWingServer::Print(TEXT("\nVariables:\n"));
for (const FBPVariableDescription& V : BP->NewVariables)
{
if (V.VarType.PinCategory == UEdGraphSchema_K2::PC_MCDelegate) continue;
UWingServer::Printf(TEXT(" %s %s"),
*UWingTypes::TypeToText(V.VarType),
*WingUtils::FormatName(V));
if (!V.DefaultValue.IsEmpty())
UWingServer::Printf(TEXT(" = %s"), *V.DefaultValue);
if (!V.Category.IsEmpty() && V.Category.ToString() != TEXT("Default"))
UWingServer::Printf(TEXT(" [%s]"), *V.Category.ToString());
UWingServer::Print(TEXT("\n"));
}
}
// Variables (new format)
WingVariables BlueprintVars(BP);
WingVariables BlueprintVars;
BlueprintVars.SetBackingStore(BP);
BlueprintVars.Load();
BlueprintVars.Print(UWingServer::GetPrintBuffer());
@@ -192,38 +174,32 @@ public:
private:
void PrintEventDispatcher(UEdGraph* Graph)
{
TWeakObjectPtr<UK2Node_EditablePinBase> EntryNode;
TWeakObjectPtr<UK2Node_EditablePinBase> ResultNode;
FBlueprintEditorUtils::GetEntryAndResultNodes(Graph, EntryNode, ResultNode);
WingVariables Vars;
Vars.SetBackingStore(Graph);
Vars.Load();
FString Args;
if (EntryNode.IsValid() && WingFunctionArgs::HasArgs(EntryNode.Get()))
Args = WingFunctionArgs::GetArgs(EntryNode.Get());
UWingServer::Printf(TEXT(" %s(%s)\n"), *WingUtils::FormatName(Graph), *Args);
FStringBuilderBase &Out = UWingServer::GetPrintBuffer();
Out.Appendf(TEXT(" %s("), *WingUtils::FormatName(Graph));
Vars.InputVariables.PrintCompact(Out);
Out.Append(TEXT(")\n"));
}
void PrintGraph(UEdGraph* Graph, const TCHAR* Type, UClass* Interface = nullptr)
{
TWeakObjectPtr<UK2Node_EditablePinBase> EntryNode;
TWeakObjectPtr<UK2Node_EditablePinBase> ResultNode;
FBlueprintEditorUtils::GetEntryAndResultNodes(Graph, EntryNode, ResultNode);
WingVariables Vars;
Vars.SetBackingStore(Graph);
Vars.Load();
FString InputArgs;
FString OutputArgs;
if (EntryNode.IsValid() && WingFunctionArgs::HasArgs(EntryNode.Get()))
InputArgs = WingFunctionArgs::GetArgs(EntryNode.Get());
if (ResultNode.IsValid() && WingFunctionArgs::HasArgs(ResultNode.Get()))
OutputArgs = WingFunctionArgs::GetArgs(ResultNode.Get());
UWingServer::Printf(TEXT(" %s %s"), Type, *WingUtils::FormatName(Graph));
if (!InputArgs.IsEmpty())
UWingServer::Printf(TEXT("(%s)"), *InputArgs);
if (!OutputArgs.IsEmpty())
UWingServer::Printf(TEXT(" -> (%s)"), *OutputArgs);
FStringBuilderBase &Out = UWingServer::GetPrintBuffer();
Out.Appendf(TEXT(" %s %s"), Type, *WingUtils::FormatName(Graph));
Out.AppendChar('(');
Vars.InputVariables.PrintCompact(Out);
Out.Append(TEXT(") -> ("));
Vars.OutputVariables.PrintCompact(Out);
Out.AppendChar(')');
if (Interface)
UWingServer::Printf(TEXT(" [%s]"), *WingUtils::FormatName(Interface));
UWingServer::Print(TEXT("\n"));
Out.Appendf(TEXT(" [%s]"), *WingUtils::FormatName(Interface));
Out.AppendChar('\n');
}
};

View File

@@ -4,7 +4,6 @@
#include "WingServer.h"
#include "WingHandler.h"
#include "WingFetcher.h"
#include "WingFunctionArgs.h"
#include "WingUtils.h"
#include "Engine/Blueprint.h"
#include "EdGraphSchema_K2.h"
@@ -28,8 +27,8 @@ public:
UPROPERTY(meta=(Description="Name of the new event dispatcher"))
FString Dispatcher;
UPROPERTY(meta=(Description="Arguments expressed as: int x,float y"))
FString Arguments;
UPROPERTY(meta=(Description="Input Variables, one per line, expressed as: type var = value"))
FString InputVariables;
virtual FString GetDescription() const override
{
@@ -45,11 +44,13 @@ public:
// Check for valid proposed name
FName InternalID = WingUtils::CheckProposedName(Dispatcher);
if (InternalID.IsNone()) return;
if (!WingUtils::FindNoneWithInternalID(InternalID, BP->NewVariables, TEXT("Variable"))) return;
if (!WingUtils::FindNoneWithInternalID(InternalID, WingUtils::AllGraphs(BP), TEXT("Graph"))) return;
TSet<FName> Names;
FBlueprintEditorUtils::GetClassVariableList(BP, Names);
if (!WingUtils::FindNoDuplicateName(Names, InternalID, TEXT("variable or component"))) return;
// Make sure the argument types are valid.
if (!WingFunctionArgs::CheckArgs(Arguments)) return;
// Parse the arguments.
WingVariables Vars;
if (!Vars.InputVariables.ParseString(InputVariables)) return;
// Add the delegate variable
FEdGraphPinType DelegateType;
@@ -78,16 +79,9 @@ public:
BP->DelegateSignatureGraphs.Add(SigGraph);
// Store the function arguments
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;
}
if (!WingFunctionArgs::SetArgs(EntryNode.Get(), Arguments)) return;
if (!Vars.SetBackingStore(SigGraph)) return;
if (!Vars.Check()) return;
if (!Vars.Create()) return;
UWingServer::Printf(TEXT("Created event dispatcher %s in %s\n"), *Dispatcher, *WingUtils::FormatName(BP));
}
};

View File

@@ -1,60 +0,0 @@
#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 "WingFunctionArgs.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "EventDispatcher_Dump.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UWing_EventDispatcher_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;
FBPVariableDescription* Var = WingUtils::FindOneWithExternalID(Dispatcher, BP->NewVariables, TEXT("Dispatcher"));
if (!Var) return;
TObjectPtr<UEdGraph>* SigGraph = WingUtils::FindOneWithExternalID(Dispatcher, BP->DelegateSignatureGraphs, TEXT("Dispatcher Signature Graph"));
if (!SigGraph) return;
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;
}
FString Args = WingFunctionArgs::GetArgs(EntryNode.Get());
UWingServer::Printf(TEXT("Event dispatcher %s in %s:\n"), *Dispatcher, *WingUtils::FormatName(BP));
UWingServer::Printf(TEXT(" Arguments: %s\n"), *Args);
}
};

View File

@@ -1,64 +0,0 @@
#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 "Kismet2/BlueprintEditorUtils.h"
#include "EventDispatcher_Modify.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UWing_EventDispatcher_Modify : 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 modify"))
FString Dispatcher;
UPROPERTY(meta=(Description="New arguments expressed as: int x,float y"))
FString Arguments;
virtual FString GetDescription() const override
{
return TEXT("Modify the arguments of an existing Blueprint event dispatcher.");
}
virtual void Handle() override
{
WingFetcher F;
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
if (!BP) return;
FBPVariableDescription* Var = WingUtils::FindOneWithExternalID(Dispatcher, BP->NewVariables, TEXT("Dispatcher"));
if (!Var) return;
TObjectPtr<UEdGraph>* SigGraph = WingUtils::FindOneWithExternalID(Dispatcher, BP->DelegateSignatureGraphs, TEXT("Dispatcher Signature Graph"));
if (!SigGraph) return;
// Make sure the argument types are valid.
if (!WingFunctionArgs::CheckArgs(Arguments)) return;
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;
}
if (!WingFunctionArgs::SetArgs(EntryNode.Get(), Arguments)) return;
UWingServer::Printf(TEXT("Modified event dispatcher %s in %s\n"), *Dispatcher, *WingUtils::FormatName(BP));
}
};

View File

@@ -1,54 +0,0 @@
#pragma once
#include "CoreMinimal.h"
#include "WingHandler.h"
#include "WingServer.h"
#include "WingFetcher.h"
#include "WingUtils.h"
#include "WingFunctionArgs.h"
#include "GraphNode_SetArgs.generated.h"
UCLASS()
class UWing_GraphNode_SetArgs : public UObject, public IWingHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Path to a graph node (FunctionEntry, FunctionResult, CustomEvent, or Tunnel)"))
FString Node;
UPROPERTY(meta=(Description="Parameter list, such as 'int x,float y'"))
FString Args;
UPROPERTY(meta=(Optional, Description="Also rename the node (which renames a Function or Custom Event)"))
FString Rename;
virtual FString GetDescription() const override
{
return TEXT("Set the parameter list of a FunctionEntry, FunctionResult, CustomEvent, or Tunnel node.");
}
virtual void Handle() override
{
WingFetcher F;
UEdGraphNode* NodeObj = F.Walk(Node).Cast<UEdGraphNode>();
if (!NodeObj) return;
if (!WingFunctionArgs::HasArgs(NodeObj))
{
UWingServer::Printf(TEXT("ERROR: Node does not support editable args\n"));
UWingServer::SuggestManual(WingManual::Section::HandlerHelp);
return;
}
if (!Rename.IsEmpty())
{
if (!WingUtils::CheckCanRename(NodeObj, Rename)) return;
NodeObj->OnRenameNode(Rename);
}
if (!WingFunctionArgs::SetArgs(NodeObj, Args)) return;
UWingServer::Printf(TEXT("Args set to: %s\n"), *WingFunctionArgs::GetArgs(NodeObj));
}
};

View File

@@ -5,11 +5,7 @@
#include "WingServer.h"
#include "WingFetcher.h"
#include "WingVariables.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "K2Node_EditablePinBase.h"
#include "K2Node_FunctionEntry.h"
#include "GraphVariables_Create.generated.h"
#include "Variables_Create.generated.h"
// ---------------------------------------------------------------------------
@@ -17,36 +13,40 @@
// ---------------------------------------------------------------------------
UCLASS()
class UWing_GraphVariables_Create : public UObject, public IWingHandler
class UWing_Variables_Create : public UObject, public IWingHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Path to a function or macro graph (e.g. '/Game/MyBP,graph:MyFunction')"))
FString Graph;
UPROPERTY(meta=(Description="Path to a blueprint, graph, or custom event node"))
FString Object;
UPROPERTY(meta=(Optional, Description="Inputs to the graph"))
UPROPERTY(meta=(Optional, Description="Blueprint variables, one per line"))
FString BlueprintVariables;
UPROPERTY(meta=(Optional, Description="Input variables, one per line"))
FString InputVariables;
UPROPERTY(meta=(Optional, Description="Outputs to the graph"))
UPROPERTY(meta=(Optional, Description="Output variables, one per line"))
FString OutputVariables;
UPROPERTY(meta=(Optional, Description="Locals to the graph"))
UPROPERTY(meta=(Optional, Description="Local variables, one per line"))
FString LocalVariables;
virtual FString GetDescription() const override
{
return TEXT("Add new inputs, outputs, and local variables to a graph. "
"Format: 'type name (flags) = default', one per line");
return TEXT("Add new variables. Format: 'type name (flags) = default', one per line.");
}
virtual void Handle() override
{
WingFetcher F;
UEdGraph* G = F.Walk(Graph).Cast<UEdGraph>();
if (!G) return;
UObject* Obj = F.Walk(Object).Cast<UObject>();
if (!Obj) return;
WingVariables Vars(G);
WingVariables Vars;
if (!Vars.SetBackingStore(Obj)) return;
if (!Vars.BlueprintVariables.ParseString(BlueprintVariables)) return;
if (!Vars.InputVariables.ParseString(InputVariables)) return;
if (!Vars.OutputVariables.ParseString(OutputVariables)) return;
if (!Vars.LocalVariables.ParseString(LocalVariables)) return;

View File

@@ -5,8 +5,7 @@
#include "WingServer.h"
#include "WingFetcher.h"
#include "WingVariables.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "GraphVariables_Dump.generated.h"
#include "Variables_Dump.generated.h"
// ---------------------------------------------------------------------------
@@ -14,26 +13,28 @@
// ---------------------------------------------------------------------------
UCLASS()
class UWing_GraphVariables_Dump : public UObject, public IWingHandler
class UWing_Variables_Dump : public UObject, public IWingHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Path to a function graph (e.g. '/Game/MyBP,graph:MyFunction')"))
FString Graph;
UPROPERTY(meta=(Description="Path to a blueprint, graph, or custom event node"))
FString Object;
virtual FString GetDescription() const override
{
return TEXT("List all arguments, return values, and local variables of a function graph.");
return TEXT("List all variables of a blueprint, function graph,"
"macro graph, event dispatcher graph, or custom event node");
}
virtual void Handle() override
{
WingFetcher F;
UEdGraph* G = F.Walk(Graph).Cast<UEdGraph>();
if (!G) return;
UObject* Obj = F.Walk(Object).Cast<UObject>();
if (!Obj) return;
WingVariables Vars(G);
WingVariables Vars;
if (!Vars.SetBackingStore(Obj)) return;
Vars.Load();
Vars.Print(UWingServer::GetPrintBuffer());
}

View File

@@ -5,7 +5,7 @@
#include "WingServer.h"
#include "WingFetcher.h"
#include "WingVariables.h"
#include "GraphVariables_Modify.generated.h"
#include "Variables_Modify.generated.h"
// ---------------------------------------------------------------------------
@@ -13,35 +13,41 @@
// ---------------------------------------------------------------------------
UCLASS()
class UWing_GraphVariables_Modify : public UObject, public IWingHandler
class UWing_Variables_Modify : public UObject, public IWingHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Path to a graph"))
FString Graph;
UPROPERTY(meta=(Description="Path to a blueprint, graph, or custom event node"))
FString Object;
UPROPERTY(meta=(Optional, Description="Inputs to the graph"))
UPROPERTY(meta=(Optional, Description="Blueprint variables, one per line"))
FString BlueprintVariables;
UPROPERTY(meta=(Optional, Description="Input variables, one per line"))
FString InputVariables;
UPROPERTY(meta=(Optional, Description="Outputs to the graph"))
UPROPERTY(meta=(Optional, Description="Output variables, one per line"))
FString OutputVariables;
UPROPERTY(meta=(Optional, Description="Locals to the graph"))
UPROPERTY(meta=(Optional, Description="Local variables, one per line"))
FString LocalVariables;
virtual FString GetDescription() const override
{
return TEXT("Modify existing arguments, return values, and local variables of a graph.");
return TEXT("Modify variables of a blueprint, function graph, "
"macro graph, event dispatcher graph, or custom event node. ");
}
virtual void Handle() override
{
WingFetcher F;
UEdGraph* G = F.Walk(Graph).Cast<UEdGraph>();
if (!G) return;
UObject* Obj = F.Walk(Object).Cast<UObject>();
if (!Obj) return;
WingVariables Vars(G);
WingVariables Vars;
if (!Vars.SetBackingStore(Obj)) return;
if (!Vars.BlueprintVariables.ParseString(BlueprintVariables)) return;
if (!Vars.InputVariables.ParseString(InputVariables)) return;
if (!Vars.OutputVariables.ParseString(OutputVariables)) return;
if (!Vars.LocalVariables.ParseString(LocalVariables)) return;

View File

@@ -5,7 +5,7 @@
#include "WingHandler.h"
#include "WingFetcher.h"
#include "WingVariables.h"
#include "GraphVariables_Remove.generated.h"
#include "Variables_Remove.generated.h"
// ---------------------------------------------------------------------------
@@ -13,13 +13,16 @@
// ---------------------------------------------------------------------------
UCLASS()
class UWing_GraphVariables_Remove : public UObject, public IWingHandler
class UWing_Variables_Remove : public UObject, public IWingHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Path to a function or macro graph"))
FString Graph;
UPROPERTY(meta=(Description="Path to a blueprint, graph, or custom event node"))
FString Object;
UPROPERTY(meta=(Optional, Description="Blueprint variable names to remove, comma-separated"))
FString BlueprintVariables;
UPROPERTY(meta=(Optional, Description="Input variable names to remove, comma-separated"))
FString InputVariables;
@@ -32,16 +35,18 @@ public:
virtual FString GetDescription() const override
{
return TEXT("Remove inputs, outputs, and local variables from a graph.");
return TEXT("Remove variables from a blueprint, graph, or custom event node.");
}
virtual void Handle() override
{
WingFetcher F;
UEdGraph* G = F.Walk(Graph).Cast<UEdGraph>();
if (!G) return;
UObject* Obj = F.Walk(Object).Cast<UObject>();
if (!Obj) return;
WingVariables Vars(G);
WingVariables Vars;
if (!Vars.SetBackingStore(Obj)) return;
if (!Vars.BlueprintVariables.ParseNamesString(BlueprintVariables)) return;
if (!Vars.InputVariables.ParseNamesString(InputVariables)) return;
if (!Vars.OutputVariables.ParseNamesString(OutputVariables)) return;
if (!Vars.LocalVariables.ParseNamesString(LocalVariables)) return;

View File

@@ -1,152 +0,0 @@
#include "WingBlueprintVar.h"
#include "WingServer.h"
#include "WingTypes.h"
#include "WingUtils.h"
#include "EdGraphSchema_K2.h"
#include "Kismet2/BlueprintEditorUtils.h"
FWingBlueprintVar::FWingBlueprintVar(UBlueprint* BP, const FString& VarName)
{
Desc = WingUtils::FindOneWithExternalID(VarName, BP->NewVariables, TEXT("Variable"));
if (!Desc) return;
if (Desc->VarType.PinCategory == UEdGraphSchema_K2::PC_MCDelegate)
{
UWingServer::Printf(TEXT("Cannot edit event dispatchers using BlueprintVariable functions.\n"));
Desc = nullptr;
return;
}
// Try to find the default value property on the CDO.
if (BP->GeneratedClass)
{
UObject* CDO = BP->GeneratedClass->GetDefaultObject();
FProperty* Prop = BP->GeneratedClass->FindPropertyByName(Desc->VarName);
if (CDO && Prop)
DefaultValueProp = FWingProperty(Prop, CDO);
}
}
void FWingBlueprintVar::Dump()
{
LoadFlags();
LoadDefault();
TArray<FWingProperty> Props = MergedProperties();
for (FWingProperty& P : Props)
{
UWingServer::Printf(TEXT(" %s %s = %s\n"),
*UWingTypes::TypeToText(P.Prop),
*WingUtils::FormatName(P.Prop),
*P.GetText());
}
}
bool FWingBlueprintVar::ApplyJson(const FJsonObject* Json)
{
bool bHasDefault = Json->HasField(TEXT("DefaultValue"));
bool bHasType = Json->HasField(TEXT("VarType"));
if (bHasDefault && bHasType)
{
UWingServer::Print(TEXT(
"ERROR: Cannot set VarType and DefaultValue in the same call.\n"
"Change the type first, then recompile the blueprint,\n"
"then set the default.\n"));
return false;
}
LoadFlags();
TArray<FWingProperty> Props = MergedProperties();
if (!FWingProperty::PopulateFromJson(Props, Json, true))
return false;
SaveFlags();
if (bHasDefault)
return SaveDefault();
return true;
}
void FWingBlueprintVar::LoadFlags()
{
InstanceEditable = !(Desc->PropertyFlags & CPF_DisableEditOnInstance);
BlueprintReadOnly = (Desc->PropertyFlags & CPF_BlueprintReadOnly) != 0;
ExposeToCinematics = (Desc->PropertyFlags & CPF_Interp) != 0;
ExposeOnSpawn = Desc->HasMetaData(FBlueprintMetadata::MD_ExposeOnSpawn);
Private = Desc->HasMetaData(FBlueprintMetadata::MD_Private);
if (Desc->HasMetaData(TEXT("tooltip")))
Description = Desc->GetMetaData(TEXT("tooltip"));
else
Description.Empty();
}
void FWingBlueprintVar::LoadDefault()
{
if (DefaultValueProp)
DefaultValue = DefaultValueProp.GetText();
else
DefaultValue.Empty();
}
void FWingBlueprintVar::SaveFlags()
{
// CPF flags
if (InstanceEditable)
Desc->PropertyFlags &= ~CPF_DisableEditOnInstance;
else
Desc->PropertyFlags |= CPF_DisableEditOnInstance;
if (BlueprintReadOnly)
Desc->PropertyFlags |= CPF_BlueprintReadOnly;
else
Desc->PropertyFlags &= ~CPF_BlueprintReadOnly;
if (ExposeToCinematics)
Desc->PropertyFlags |= CPF_Interp;
else
Desc->PropertyFlags &= ~CPF_Interp;
// Metadata flags
if (ExposeOnSpawn)
Desc->SetMetaData(FBlueprintMetadata::MD_ExposeOnSpawn, TEXT("true"));
else
Desc->RemoveMetaData(FBlueprintMetadata::MD_ExposeOnSpawn);
if (Private)
Desc->SetMetaData(FBlueprintMetadata::MD_Private, TEXT("true"));
else
Desc->RemoveMetaData(FBlueprintMetadata::MD_Private);
// Description/tooltip
if (!Description.IsEmpty())
Desc->SetMetaData(TEXT("tooltip"), Description);
else
Desc->RemoveMetaData(TEXT("tooltip"));
}
bool FWingBlueprintVar::SaveDefault()
{
if (DefaultValueProp)
return DefaultValueProp.SetText(DefaultValue);
return true;
}
TArray<FWingProperty> FWingBlueprintVar::MergedProperties()
{
TArray<FWingProperty> Props = FWingProperty::GetAll(
FBPVariableDescription::StaticStruct(), Desc, CPF_Edit);
FWingProperty::Remove(Props, TEXT("PropertyFlags"));
FWingProperty::Remove(Props, TEXT("MetaDataArray"));
FWingProperty::Remove(Props, TEXT("VarName"));
FWingProperty::Remove(Props, TEXT("VarGuid"));
FWingProperty::Remove(Props, TEXT("DefaultValue"));
Props.Append(FWingProperty::GetAll(
FWingBlueprintVar::StaticStruct(), this, (EPropertyFlags)0));
// Remove DefaultValue if we don't have a CDO property to back it.
if (!DefaultValueProp)
FWingProperty::Remove(Props, TEXT("DefaultValue"));
return Props;
}

View File

@@ -1,132 +0,0 @@
#include "WingFunctionArgs.h"
#include "K2Node_EditablePinBase.h"
#include "K2Node_FunctionResult.h"
#include "K2Node_Tunnel.h"
#include "WingTypes.h"
#include "WingUtils.h"
#include "WingServer.h"
#include "Kismet2/BlueprintEditorUtils.h"
bool WingFunctionArgs::HasArgs(UEdGraphNode* Node)
{
UK2Node_EditablePinBase* Editable = Cast<UK2Node_EditablePinBase>(Node);
if (!Editable) return false;
return Editable->IsEditable();
}
FString WingFunctionArgs::GetArgs(UEdGraphNode* Node)
{
UK2Node_EditablePinBase* Editable = Cast<UK2Node_EditablePinBase>(Node);
if (!Editable) return FString();
TStringBuilder<256> SB;
for (const TSharedPtr<FUserPinInfo>& Pin : Editable->UserDefinedPins)
{
if (SB.Len() > 0) SB << TEXT(",");
SB << UWingTypes::TypeToText(Pin->PinType) << TEXT(" ") << WingUtils::FormatName(*Pin);
}
return FString(SB);
}
EEdGraphPinDirection WingFunctionArgs::GetPinDirection(UK2Node_EditablePinBase* Node)
{
// FunctionResult takes inputs; Tunnel depends on its flags; everything else outputs.
if (Node->IsA<UK2Node_FunctionResult>())
return EGPD_Input;
if (UK2Node_Tunnel* Tunnel = Cast<UK2Node_Tunnel>(Node))
return Tunnel->bCanHaveInputs ? EGPD_Input : EGPD_Output;
return EGPD_Output;
}
bool WingFunctionArgs::ParseArgs(const FString& Args, TArray<FParsedArg>& OutArgs)
{
FString Trimmed = Args.TrimStartAndEnd();
if (Trimmed.IsEmpty()) return true;
TArray<FString> Parts;
Trimmed.ParseIntoArray(Parts, TEXT(","));
for (const FString& Part : Parts)
{
FString Token = Part.TrimStartAndEnd();
if (Token.IsEmpty()) continue;
// Split "type name"
FString TypeStr, NameStr;
if (!Token.Split(TEXT(" "), &TypeStr, &NameStr))
{
UWingServer::Printf(TEXT("ERROR: Malformed parameter list near '%s'\n"), *Token);
return false;
}
TypeStr.TrimStartAndEndInline();
NameStr.TrimStartAndEndInline();
if (TypeStr.IsEmpty() || NameStr.IsEmpty())
{
UWingServer::Printf(TEXT("ERROR: Malformed parameter list near '%s'\n"), *Token);
return false;
}
FParsedArg Arg;
UWingTypes::Requirements Req;
Req.BlueprintType = true;
Req.Blueprintable = false;
Req.AllowContainer = true;
if (!UWingTypes::TextToType(TypeStr, Arg.PinType, Req)) return false;
Arg.PinName = FName(*NameStr);
OutArgs.Add(MoveTemp(Arg));
}
return true;
}
bool WingFunctionArgs::SetArgs(UEdGraphNode* Node, const FString& Args)
{
UK2Node_EditablePinBase* Editable = Cast<UK2Node_EditablePinBase>(Node);
if (!Editable || !Editable->IsEditable())
{
UWingServer::Printf(TEXT("ERROR: Node does not contain an editable parameter list\n"));
return false;
}
// Parse the args string.
TArray<FParsedArg> NewArgs;
if (!ParseArgs(Args, NewArgs))
{
UWingServer::SuggestManual(WingManual::Section::ParameterLists);
UWingServer::SuggestManual(WingManual::Section::Types);
return false;
}
EEdGraphPinDirection Direction = GetPinDirection(Editable);
// Replace the UserDefinedPins array directly.
Editable->UserDefinedPins.Empty();
for (const FParsedArg& Arg : NewArgs)
{
TSharedPtr<FUserPinInfo> PinInfo = MakeShareable(new FUserPinInfo());
PinInfo->PinName = Arg.PinName;
PinInfo->PinType = Arg.PinType;
PinInfo->DesiredPinDirection = Direction;
Editable->UserDefinedPins.Add(PinInfo);
}
// ReconstructNode rebuilds real pins from UserDefinedPins
// and rewires old connections by matching pin names.
Editable->ReconstructNode();
return true;
}
bool WingFunctionArgs::CheckArgs(const FString &Args)
{
TArray<FParsedArg> NewArgs;
if (!ParseArgs(Args, NewArgs))
{
UWingServer::Printf(TEXT("ERROR: Invalid parameter list: %s\n"), *Args);
UWingServer::SuggestManual(WingManual::Section::ParameterLists);
UWingServer::SuggestManual(WingManual::Section::Types);
return false;
}
return true;
}

View File

@@ -10,7 +10,6 @@
#include "EdGraphNode_Comment.h"
#include "K2Node_CallFunction.h"
#include "K2Node_FunctionEntry.h"
#include "WingFunctionArgs.h"
#include "WingVariables.h"
#include "MaterialGraph/MaterialGraphNode.h"
#include "Kismet2/BlueprintEditorUtils.h"
@@ -205,10 +204,6 @@ void WingGraphExport::EmitNode(UEdGraphNode* Node)
Output.Appendf(TEXT("\nnode %s: %s\n"), *WingUtils::FormatName(Node), *WingUtils::FormatNodeTitle(Node));
// Emit function args (if applicable).
if (WingFunctionArgs::HasArgs(Node))
Output.Appendf(TEXT(" args %s\n"), *WingFunctionArgs::GetArgs(Node));
// Emit material expression properties (if applicable).
EmitMaterialProperties(Node, Output, true);
@@ -289,7 +284,8 @@ void WingGraphExport::EmitMaterialProperties(UEdGraphNode* Node, FStringBuilderB
void WingGraphExport::EmitLocalVariables()
{
WingVariables Vars(Graph);
WingVariables Vars;
Vars.SetBackingStore(Graph);
Vars.Load();
Vars.Print(Output);
}

View File

@@ -7,6 +7,7 @@
#include "EdGraphSchema_K2.h"
#include "K2Node_FunctionEntry.h"
#include "K2Node_FunctionResult.h"
#include "K2Node_CustomEvent.h"
#include "K2Node_Tunnel.h"
#include "K2Node_EditablePinBase.h"
#include "Kismet2/BlueprintEditorUtils.h"
@@ -58,7 +59,18 @@ void WingVariableList::Print(FStringBuilderBase &Out)
Out.Append(TEXT("\n"));
}
}
void WingVariableList::PrintCompact(FStringBuilderBase &Out)
{
bool First = true;
for (const Var& V : Variables)
{
if (!First) Out << TEXT(",");
First = false;
Out << UWingTypes::TypeToText(V.Type) << TEXT(" ") << WingUtils::ExternalizeID(V.Name);
}
}
void WingVariableList::ClearLinks()
{
for (Var &V : Variables)
@@ -241,6 +253,8 @@ void WingVariables::Load()
Empty();
if (Blueprint != nullptr) return LoadBlueprint();
if (Graph != nullptr) return LoadGraph();
if (CustomEvent != nullptr) return LoadCustomEvent();
ErrorNoBackingStore();
}
void WingVariables::LoadBlueprint()
@@ -337,12 +351,17 @@ void WingVariables::LoadEditablePinBase(UK2Node_EditablePinBase* Node, WingVaria
}
}
void WingVariables::LoadCustomEvent()
{
LoadEditablePinBase(CustomEvent, InputVariables);
}
bool WingVariables::Check()
{
if (Blueprint) return CheckBlueprint();
if (Graph) return CheckGraph();
check(false);
return false;
if (CustomEvent) return CheckCustomEvent();
return ErrorNoBackingStore();
}
bool WingVariables::CheckBlueprint()
@@ -363,7 +382,7 @@ bool WingVariables::CheckGraph()
bool AllowOutput = (T == EGraphType::GT_Function) || (T == EGraphType::GT_Macro);
if ((!AllowLocal) && (!AllowInput) && (!AllowOutput))
{
UWingServer::Printf(TEXT("This graph has no editable variables."));
UWingServer::Printf(TEXT("This graph type has no variables."));
return false;
}
@@ -375,12 +394,22 @@ bool WingVariables::CheckGraph()
return OK;
}
bool WingVariables::CheckCustomEvent()
{
bool OK = true;
if (!BlueprintVariables.CheckSanity(Flags_None, false)) OK = false;
if (!LocalVariables.CheckSanity(Flags_None, false)) OK = false;
if (!InputVariables.CheckSanity(Flags_None, true)) OK = false;
if (!OutputVariables.CheckSanity(Flags_None, false)) OK = false;
return OK;
}
bool WingVariables::Modify()
{
if (Blueprint) return ModifyBlueprint();
if (Graph) return ModifyGraph();
check(false);
return false;
if (CustomEvent) return ModifyCustomEvent();
return ErrorNoBackingStore();
}
bool WingVariables::ModifyBlueprint()
@@ -488,28 +517,36 @@ bool WingVariables::ModifyGraph()
Desc->VarType = V.Type;
if (V.DefaultSpecified) Desc->DefaultValue = V.DefaultValue;
}
for (Var &V : InputVariables.Variables)
{
TSharedPtr<FUserPinInfo> *Found =
WingUtils::FindOneWithInternalID(V.Name, InputNode->UserDefinedPins, TEXT("input variable"));
if (!Found) return false;
(*Found)->PinType = V.Type;
if (V.DefaultSpecified) (*Found)->PinDefaultValue = V.DefaultValue;
}
for (Var &V : OutputVariables.Variables)
{
TSharedPtr<FUserPinInfo> *Found =
WingUtils::FindOneWithInternalID(V.Name, OutputNode->UserDefinedPins, TEXT("output variable"));
if (!Found) return false;
(*Found)->PinType = V.Type;
if (V.DefaultSpecified) (*Found)->PinDefaultValue = V.DefaultValue;
}
if (!ModifyEditablePinBase(InputVariables, InputNode)) return false;
if (!ModifyEditablePinBase(OutputVariables, OutputNode)) return false;
if (InputNode) InputNode->ReconstructNode();
if (OutputNode) OutputNode->ReconstructNode();
return true;
}
bool WingVariables::ModifyCustomEvent()
{
if (!CheckCustomEvent()) return false;
ClearLinks();
if (!ModifyEditablePinBase(InputVariables, CustomEvent)) return false;
CustomEvent->ReconstructNode();
return true;
}
bool WingVariables::ModifyEditablePinBase(WingVariableList &List, UK2Node_EditablePinBase *Node)
{
for (Var &V : List.Variables)
{
TSharedPtr<FUserPinInfo> *Found =
WingUtils::FindOneWithInternalID(V.Name, Node->UserDefinedPins, List.ListName);
if (!Found) return false;
(*Found)->PinType = V.Type;
if (V.DefaultSpecified) (*Found)->PinDefaultValue = V.DefaultValue;
}
return true;
}
bool WingVariables::GetGraphNodes(
UK2Node_EditablePinBase *&InputNode,
UK2Node_EditablePinBase *&OutputNode,
@@ -554,8 +591,8 @@ bool WingVariables::Create()
{
if (Blueprint) return CreateBlueprint();
if (Graph) return CreateGraph();
check(false);
return false;
if (CustomEvent) return CreateCustomEvent();
return ErrorNoBackingStore();
}
bool WingVariables::CreateBlueprint()
@@ -635,6 +672,25 @@ bool WingVariables::CreateGraph()
return true;
}
bool WingVariables::CreateCustomEvent()
{
if (!CheckCustomEvent()) return false;
ClearLinks();
// Check for name collisions against existing pins.
TSet<FName> Names;
if (!WingUtils::FindNoDuplicateNames(
Names, CustomEvent->UserDefinedPins, TEXT("event parameter"))) return false;
if (!WingUtils::FindNoDuplicateNames(
Names, InputVariables.Variables, TEXT("event parameter"))) return false;
for (const Var& V : InputVariables.Variables)
AddUserPinInfo(V, EGPD_Output, CustomEvent);
CustomEvent->ReconstructNode();
return true;
}
void WingVariables::AddUserPinInfo(const Var &V, EEdGraphPinDirection Dir, UK2Node_EditablePinBase *Node)
{
TSharedPtr<FUserPinInfo> Result = MakeShareable( new FUserPinInfo() );
@@ -649,8 +705,8 @@ bool WingVariables::Remove()
{
if (Blueprint) return RemoveBlueprint();
if (Graph) return RemoveGraph();
check(false);
return false;
if (CustomEvent) return RemoveCustomEvent();
return ErrorNoBackingStore();
}
bool WingVariables::RemoveBlueprint()
@@ -715,3 +771,55 @@ bool WingVariables::RemoveGraph()
if (OutputNode) OutputNode->ReconstructNode();
return true;
}
bool WingVariables::RemoveCustomEvent()
{
// Verify that all named parameters exist before removing anything.
for (const Var& V : InputVariables.Variables)
{
TSharedPtr<FUserPinInfo>* Found =
WingUtils::FindOneWithInternalID(V.Name, CustomEvent->UserDefinedPins, TEXT("event parameter"));
if (!Found) return false;
}
for (const Var& V : InputVariables.Variables)
CustomEvent->RemoveUserDefinedPinByName(V.Name);
CustomEvent->ReconstructNode();
return true;
}
bool WingVariables::SetBackingStore(UObject *Obj)
{
Blueprint = nullptr;
Graph = nullptr;
CustomEvent = nullptr;
if (UBlueprint *BP = Cast<UBlueprint>(Obj))
{
Blueprint = BP;
return true;
}
if (UEdGraph *G = Cast<UEdGraph>(Obj))
{
Graph = G;
return true;
}
if (UK2Node_CustomEvent *E = Cast<UK2Node_CustomEvent>(Obj))
{
CustomEvent = E;
return true;
}
UWingServer::Printf(TEXT(
"ERROR: The variable editor can only edit blueprints, "
"graphs, and custom event nodes. Passed in: %s"),
*WingUtils::FormatName(Obj->GetClass()));
return false;
}
bool WingVariables::ErrorNoBackingStore()
{
UWingServer::Printf(TEXT(
"ERROR: The variable editor was not successfully "
"set up with an object to edit."));
return false;
}

View File

@@ -1,58 +0,0 @@
#pragma once
#include "CoreMinimal.h"
#include "WingProperty.h"
#include "Engine/Blueprint.h"
#include "WingBlueprintVar.generated.h"
// Editor-friendly view of a blueprint variable's properties.
// Wraps an FBPVariableDescription, exposing commonly-used flags
// and metadata as simple UPROPERTYs that the property system can
// populate from JSON.
USTRUCT()
struct FWingBlueprintVar
{
GENERATED_BODY()
FBPVariableDescription* Desc = nullptr;
FWingProperty DefaultValueProp;
FWingBlueprintVar() = default;
FWingBlueprintVar(UBlueprint* BP, const FString& VarName);
bool NotFound() const { return Desc == nullptr; }
UPROPERTY(EditAnywhere, meta=(Optional, Description="Default value in Unreal text format"))
FString DefaultValue;
UPROPERTY(EditAnywhere, meta=(Optional, Description="Variable description/tooltip"))
FString Description;
UPROPERTY(EditAnywhere, meta=(Optional, Description="Allow editing on instances"))
bool InstanceEditable = false;
UPROPERTY(EditAnywhere, meta=(Optional, Description="Read-only in blueprints"))
bool BlueprintReadOnly = false;
UPROPERTY(EditAnywhere, meta=(Optional, Description="Expose as a pin when spawning"))
bool ExposeOnSpawn = false;
UPROPERTY(EditAnywhere, meta=(Optional, Description="Private to this blueprint"))
bool Private = false;
UPROPERTY(EditAnywhere, meta=(Optional, Description="Expose to cinematics/sequencer"))
bool ExposeToCinematics = false;
// Load from Desc, populate from JSON, save back to Desc.
bool ApplyJson(const FJsonObject* Json);
// Print all properties and their current values.
void Dump();
private:
void LoadFlags();
void LoadDefault();
void SaveFlags();
bool SaveDefault();
TArray<FWingProperty> MergedProperties();
};

View File

@@ -1,40 +0,0 @@
#pragma once
#include "CoreMinimal.h"
#include "EdGraph/EdGraphPin.h"
class UEdGraphNode;
class UK2Node_EditablePinBase;
struct WingFunctionArgs
{
// Returns true if the node is an EditablePinBase subclass that
// actually supports user-defined pins (FunctionEntry, FunctionResult,
// CustomEvent, or Tunnel).
static bool HasArgs(UEdGraphNode* Node);
// Returns the user-defined pins as a string like "int x,float y".
static FString GetArgs(UEdGraphNode* Node);
// Sets the user-defined pins from a string like "int x,float y".
// Returns true on success.
static bool SetArgs(UEdGraphNode* Node, const FString& Args);
// Returns true if the arguments are valid, if not prints error.
static bool CheckArgs(const FString &Args);
private:
// A parsed argument: type + name.
struct FParsedArg
{
FEdGraphPinType PinType;
FName PinName;
};
// Parse "int x,float y" into an array of FParsedArg.
// Returns false and prints an error on failure.
static bool ParseArgs(const FString& Args, TArray<FParsedArg>& OutArgs);
// Determine the pin direction for user-defined pins on this node.
static EEdGraphPinDirection GetPinDirection(UK2Node_EditablePinBase* Node);
};

View File

@@ -5,6 +5,7 @@
#include "Engine/Blueprint.h"
struct WingTokenizer;
class UK2Node_CustomEvent;
class UK2Node_EditablePinBase;
class UK2Node_FunctionEntry;
struct FUserPinInfo;
@@ -54,6 +55,9 @@ public:
// Print the variables to a string builder.
void Print(FStringBuilderBase &Out);
// Print compact: "type name,type name,..."
void PrintCompact(FStringBuilderBase &Out);
// Clear the BPVar and Pin fields.
void ClearLinks();
@@ -77,11 +81,13 @@ class WingVariables
{
public:
using Var = WingVariableList::Var;
WingVariables() {}
// The backing store. Only one of these should be set.
UBlueprint *Blueprint = nullptr;
UEdGraph *Graph = nullptr;
UK2Node_CustomEvent *CustomEvent = nullptr;
// The Workspace. At any given time, these may or may not contain
// the same data as the backing store.
@@ -91,10 +97,10 @@ public:
WingVariableList InputVariables{TEXT("Input Variables")};
WingVariableList OutputVariables{TEXT("Output Variables")};
// Constructors. Just initialize the pointers to the backing store.
// Configure the backing store. On failure,
// prints a message and returns false.
WingVariables(UBlueprint *BP) : Blueprint(BP) {}
WingVariables(UEdGraph *G) : Graph(G) {}
bool SetBackingStore(UObject *Obj);
// Clear the workspace. Doesn't affect the backing store.
@@ -155,11 +161,20 @@ private:
bool RemoveBlueprint();
bool RemoveGraph();
void LoadCustomEvent();
bool CheckCustomEvent();
bool ModifyCustomEvent();
bool CreateCustomEvent();
bool RemoveCustomEvent();
bool GetGraphNodes(
UK2Node_EditablePinBase *&InputNode,
UK2Node_EditablePinBase *&OutputNode,
UK2Node_FunctionEntry *&LocalNode);
bool ModifyEditablePinBase(WingVariableList &List, UK2Node_EditablePinBase *Node);
void AddUserPinInfo(const Var &V, EEdGraphPinDirection Dir, UK2Node_EditablePinBase *Node);
bool ErrorNoBackingStore();
};

View File

@@ -2,7 +2,8 @@
"""
Human-friendly MCP test client.
Usage: ue-wingman.py UserManual
Usage: ue-wingman.py <command> [key=value ...]
ue-wingman.py (reads JSON with "command" from stdin)
"""
import sys
@@ -17,27 +18,28 @@ TIMEOUT = 120
def main():
args = sys.argv[1:]
if not args:
print("Usage: ue-wingman.py ShowCommands [key=value ...]")
sys.exit(1)
no_args_commands = {"ShowCommands", "UserManual"}
if len(args) == 1 and args[0] not in no_args_commands:
# No extra arguments: read JSON object from stdin.
# Accumulate lines and try to parse after each one.
# No arguments: read a complete JSON object from stdin.
# The JSON must already contain a "command" key.
decoder = json.JSONDecoder()
raw = ""
msg = None
for line in sys.stdin:
raw += line
stripped = raw.strip()
try:
msg, _ = decoder.raw_decode(raw.lstrip())
msg, _ = decoder.raw_decode(stripped)
break
except json.JSONDecodeError:
except json.JSONDecodeError as e:
if e.pos < len(stripped):
print(f"Malformed JSON: {e.msg}")
sys.exit(1)
continue
if msg is None:
print("Could not parse a complete JSON object from stdin")
sys.exit(1)
msg["command"] = args[0]
if "command" not in msg:
print("JSON object must contain a \"command\" key")
sys.exit(1)
else:
msg = {"command": args[0]}
for arg in args[1:]: