2026-03-06 15:06:12 -05:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include "CoreMinimal.h"
|
|
|
|
|
#include "Dom/JsonObject.h"
|
2026-03-12 19:12:37 -04:00
|
|
|
#include "EdGraph/EdGraph.h"
|
2026-03-06 15:06:12 -05:00
|
|
|
#include "EdGraph/EdGraphPin.h"
|
2026-03-12 08:26:18 -04:00
|
|
|
#include "Materials/MaterialInstanceConstant.h"
|
|
|
|
|
#include "MaterialTypes.h"
|
2026-03-06 15:06:12 -05:00
|
|
|
|
|
|
|
|
class UBlueprint;
|
|
|
|
|
class UEdGraphNode;
|
|
|
|
|
class UEdGraphPin;
|
|
|
|
|
class UMaterial;
|
2026-03-10 03:02:41 -04:00
|
|
|
class UMaterialInstance;
|
|
|
|
|
class UMaterialFunction;
|
2026-03-06 15:06:12 -05:00
|
|
|
class UMaterialExpression;
|
2026-03-10 20:15:59 -04:00
|
|
|
struct FEdGraphSchemaAction;
|
2026-03-06 15:06:12 -05:00
|
|
|
class UAnimationStateMachineGraph;
|
|
|
|
|
class UAnimStateNode;
|
|
|
|
|
class UAnimStateTransitionNode;
|
2026-03-10 00:22:56 -04:00
|
|
|
class UActorComponent;
|
|
|
|
|
class UWorld;
|
2026-03-10 03:02:41 -04:00
|
|
|
class UStaticMesh;
|
|
|
|
|
class USkeletalMesh;
|
|
|
|
|
class UAnimSequence;
|
|
|
|
|
class UBlendSpace;
|
|
|
|
|
class UTexture;
|
|
|
|
|
class UScriptStruct;
|
|
|
|
|
class UEnum;
|
2026-03-18 18:39:13 -04:00
|
|
|
class USCS_Node;
|
2026-03-10 00:22:56 -04:00
|
|
|
struct FMemberReference;
|
|
|
|
|
struct FBPVariableDescription;
|
2026-03-19 10:16:44 -04:00
|
|
|
struct FUserPinInfo;
|
2026-03-19 12:37:36 -04:00
|
|
|
struct FBPInterfaceDescription;
|
2026-03-19 13:32:05 -04:00
|
|
|
struct FWingActorComponent;
|
2026-03-19 12:37:36 -04:00
|
|
|
|
2026-03-06 15:06:12 -05:00
|
|
|
// Stateless utility functions used by MCP handlers and the MCP server.
|
|
|
|
|
// This is effectively a namespace — all methods are static.
|
2026-03-18 10:17:58 -04:00
|
|
|
class WingUtils
|
2026-03-06 15:06:12 -05:00
|
|
|
{
|
|
|
|
|
public:
|
2026-03-10 00:22:56 -04:00
|
|
|
////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// Name Formatting
|
|
|
|
|
//
|
|
|
|
|
// The goal here is to centralize the code that outputs
|
|
|
|
|
// names, and have everybody use it, so that names are
|
|
|
|
|
// used consistently. The secondary goal is to choose
|
|
|
|
|
// names that are as uniquely-identifying as is practical.
|
|
|
|
|
// It's not always 100% possible to get perfectly unique
|
|
|
|
|
// names, though, so our code needs to check for ambiguity.
|
|
|
|
|
//
|
|
|
|
|
////////////////////////////////////////////////////////
|
|
|
|
|
|
2026-03-10 01:42:43 -04:00
|
|
|
static FString FormatName(const UWorld *World);
|
|
|
|
|
static FString FormatName(const UBlueprint *BP);
|
|
|
|
|
static FString FormatName(const UActorComponent *C);
|
2026-03-18 18:39:13 -04:00
|
|
|
static FString FormatName(const USCS_Node *Node);
|
2026-03-10 01:42:43 -04:00
|
|
|
static FString FormatName(const UEdGraph *Graph);
|
|
|
|
|
static FString FormatName(const UEdGraphNode* Node);
|
|
|
|
|
static FString FormatName(const UEdGraphPin *Pin);
|
2026-03-10 00:22:56 -04:00
|
|
|
static FString FormatName(const FMemberReference &Ref);
|
|
|
|
|
static FString FormatName(const FBPVariableDescription &Var);
|
2026-03-10 20:15:59 -04:00
|
|
|
static FString FormatName(const UStruct *Struct);
|
2026-03-19 01:52:14 -04:00
|
|
|
static FString FormatName(const UClass *Class);
|
2026-03-10 03:02:41 -04:00
|
|
|
static FString FormatName(const UMaterial *Material);
|
|
|
|
|
static FString FormatName(const UMaterialInstance *MaterialInstance);
|
|
|
|
|
static FString FormatName(const UMaterialFunction *MaterialFunction);
|
|
|
|
|
static FString FormatName(const UMaterialExpression *Expression);
|
|
|
|
|
static FString FormatName(const UStaticMesh *Mesh);
|
|
|
|
|
static FString FormatName(const USkeletalMesh *Mesh);
|
|
|
|
|
static FString FormatName(const UAnimSequence *Anim);
|
|
|
|
|
static FString FormatName(const UBlendSpace *BlendSpace);
|
|
|
|
|
static FString FormatName(const UTexture *Texture);
|
|
|
|
|
static FString FormatName(const UScriptStruct *Struct);
|
|
|
|
|
static FString FormatName(const UEnum *Enum);
|
2026-03-10 20:15:59 -04:00
|
|
|
static FString FormatName(const FProperty *Prop);
|
2026-03-19 10:16:44 -04:00
|
|
|
static FString FormatName(const FUserPinInfo &Pin);
|
2026-03-19 12:37:36 -04:00
|
|
|
static FString FormatName(const FBPInterfaceDescription &IFace);
|
2026-03-19 13:32:05 -04:00
|
|
|
static FString FormatName(const FWingActorComponent &Comp);
|
2026-03-10 00:22:56 -04:00
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// Identifies
|
|
|
|
|
//
|
|
|
|
|
// Return true if the name identifies the object. The
|
|
|
|
|
// FormatName functions, above, always return names that
|
|
|
|
|
// identify the object. However, there may be other
|
|
|
|
|
// names that also identify the object. Identifying names
|
|
|
|
|
// aren't 100% guaranteed to be unique, but very likely.
|
|
|
|
|
//
|
|
|
|
|
////////////////////////////////////////////////////////
|
|
|
|
|
|
2026-03-11 22:03:32 -04:00
|
|
|
template<typename T>
|
|
|
|
|
static bool Identifies(const FString &Name, T&& Obj)
|
|
|
|
|
{
|
|
|
|
|
return FormatName(std::forward<T>(Obj)).Equals(Name, ESearchCase::IgnoreCase);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-18 21:48:56 -04:00
|
|
|
template<typename T>
|
|
|
|
|
static TArray<T*> FindAllNamed(const FString &Name, const TArray<T*> &Array)
|
|
|
|
|
{
|
|
|
|
|
TArray<T*> Result;
|
|
|
|
|
for (T* Elt : Array) if (Identifies(Name, Elt)) Result.Add(Elt);
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-19 12:01:38 -04:00
|
|
|
template<typename T>
|
|
|
|
|
static TArray<T*> FindAllNamed(const FString &Name, TArray<T> &Array)
|
|
|
|
|
{
|
|
|
|
|
TArray<T*> Result;
|
|
|
|
|
for (T& Elt : Array) if (Identifies(Name, Elt)) Result.Add(&Elt);
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-18 21:48:56 -04:00
|
|
|
template<typename T>
|
2026-03-18 23:10:09 -04:00
|
|
|
static T* FindExactlyOneNamed(const FString &Name, const TArray<T*> &Array)
|
2026-03-18 21:48:56 -04:00
|
|
|
{
|
|
|
|
|
int Count = 0;
|
|
|
|
|
T* Result = nullptr;
|
|
|
|
|
for (T* Elt : Array) if (Identifies(Name, Elt)) { Count++; Result = Elt; }
|
|
|
|
|
if (!CheckExactlyOneNamed(Count, T::StaticClass(), Name)) return nullptr;
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-19 12:01:38 -04:00
|
|
|
template<typename T>
|
|
|
|
|
static T* FindExactlyOneNamed(const FString &Name, TArray<T> &Array)
|
|
|
|
|
{
|
|
|
|
|
int Count = 0;
|
|
|
|
|
T* Result = nullptr;
|
|
|
|
|
for (T& Elt : Array) if (Identifies(Name, Elt)) { Count++; Result = &Elt; }
|
|
|
|
|
if (!CheckExactlyOneNamed(Count, T::StaticStruct()->GetName(), Name)) return nullptr;
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-18 21:48:56 -04:00
|
|
|
template<typename T>
|
2026-03-18 23:10:09 -04:00
|
|
|
static bool FindExactlyNoneNamed(const FString &Name, const TArray<T*> &Array)
|
2026-03-18 21:48:56 -04:00
|
|
|
{
|
|
|
|
|
for (T* Elt: Array) if (Identifies(Name, Elt))
|
|
|
|
|
{
|
|
|
|
|
return CheckExactlyNoneNamed(1, T::StaticClass(), Name);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2026-03-10 00:22:56 -04:00
|
|
|
|
2026-03-19 12:01:38 -04:00
|
|
|
template<typename T>
|
|
|
|
|
static bool FindExactlyNoneNamed(const FString &Name, const TArray<T> &Array)
|
|
|
|
|
{
|
|
|
|
|
for (const T& Elt: Array) if (Identifies(Name, Elt))
|
|
|
|
|
{
|
|
|
|
|
return CheckExactlyNoneNamed(1, T::StaticStruct()->GetName(), Name);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-10 00:22:56 -04:00
|
|
|
////////////////////////////////////////////////////////
|
|
|
|
|
|
2026-03-19 15:53:25 -04:00
|
|
|
////////////////////////////////////////////////////////
|
|
|
|
|
// Our parsers reserve certain punctuation marks for parsing
|
|
|
|
|
// types, paths, and the like. For example: Array<Int>.
|
|
|
|
|
// We therefore cannot allow those specific punctuation marks
|
|
|
|
|
// in names. We replace them with similar-looking unicode
|
|
|
|
|
// characters.
|
|
|
|
|
////////////////////////////////////////////////////////
|
2026-03-18 20:31:54 -04:00
|
|
|
static FString SanitizeName(const FString& Name);
|
|
|
|
|
static FString SanitizeName(FName Name);
|
2026-03-19 15:53:25 -04:00
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////
|
2026-03-19 16:26:00 -04:00
|
|
|
// Our name sanitization routine, above, will turn names
|
|
|
|
|
// with spaces into names like "Post·Initiate·Action"
|
|
|
|
|
// containing middle dots instead. When the LLM creates
|
|
|
|
|
// new nodes, graphs, variables, or the like, it might
|
|
|
|
|
// suggest a name containing middle dots. In places
|
|
|
|
|
// like that, where the LLM is naming something new, we
|
|
|
|
|
// run this Unsanitize routine first. This is *not*
|
|
|
|
|
// used for lookups: Lookups are done by comparing
|
|
|
|
|
// sanitized names.
|
2026-03-19 15:53:25 -04:00
|
|
|
////////////////////////////////////////////////////////
|
|
|
|
|
static FString UnsanitizeName(const FString& Name);
|
|
|
|
|
|
2026-03-11 22:03:32 -04:00
|
|
|
static FString FormatNodeTitle(const UEdGraphNode *Node);
|
2026-03-10 00:22:56 -04:00
|
|
|
|
2026-03-08 05:00:07 -04:00
|
|
|
// ----- Enum helpers -----
|
2026-03-19 11:19:31 -04:00
|
|
|
static FString EnumToString(UEnum* Enum, int64 Value);
|
|
|
|
|
static bool StringToEnum(UEnum* Enum, const FString& Str, int64& OutValue);
|
2026-03-13 02:09:19 -04:00
|
|
|
|
2026-03-08 21:28:47 -04:00
|
|
|
template<typename T>
|
2026-03-19 11:19:31 -04:00
|
|
|
static FString EnumToString(TEnumAsByte<T> Value)
|
|
|
|
|
{ return EnumToString(StaticEnum<T>(), (int64)Value); }
|
2026-03-08 21:28:47 -04:00
|
|
|
|
2026-03-08 05:00:07 -04:00
|
|
|
template<typename T>
|
2026-03-19 11:19:31 -04:00
|
|
|
static FString EnumToString(T Value)
|
|
|
|
|
{ return EnumToString(StaticEnum<T>(), (int64)Value); }
|
2026-03-08 05:00:07 -04:00
|
|
|
|
|
|
|
|
template<typename T>
|
2026-03-19 11:19:31 -04:00
|
|
|
static bool StringToEnum(const FString& Str, T& OutValue)
|
|
|
|
|
{ int64 V; if (!StringToEnum(StaticEnum<T>(), Str, V)) return false; OutValue = (T)V; return true; }
|
2026-03-12 17:20:20 -04:00
|
|
|
|
2026-03-06 15:06:12 -05:00
|
|
|
// ----- Blueprint helpers -----
|
2026-03-08 03:44:27 -04:00
|
|
|
static TArray<UEdGraph*> AllGraphs(UBlueprint* BP);
|
|
|
|
|
static TArray<UEdGraphNode*> AllNodes(UBlueprint* BP);
|
|
|
|
|
template<class T> static TArray<T*> AllNodes(UBlueprint* BP)
|
|
|
|
|
{
|
|
|
|
|
TArray<T*> Result;
|
|
|
|
|
for (UEdGraph* Graph : AllGraphs(BP))
|
|
|
|
|
for (UEdGraphNode* Node : Graph->Nodes)
|
|
|
|
|
if (T* Typed = Cast<T>(Node))
|
|
|
|
|
Result.Add(Typed);
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
template<class T> static TArray<T*> AllNodes(UEdGraph* Graph)
|
|
|
|
|
{
|
|
|
|
|
TArray<T*> Result;
|
|
|
|
|
for (UEdGraphNode* Node : Graph->Nodes)
|
|
|
|
|
if (T* Typed = Cast<T>(Node))
|
|
|
|
|
Result.Add(Typed);
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
2026-03-18 21:48:56 -04:00
|
|
|
|
2026-03-06 15:06:12 -05:00
|
|
|
// ----- Material helpers -----
|
|
|
|
|
static void EnsureMaterialGraph(UMaterial* Material);
|
|
|
|
|
|
2026-03-10 20:15:59 -04:00
|
|
|
// If the material editor has a transient preview copy of this material,
|
|
|
|
|
// return that copy (which is what the editor is actually working on).
|
|
|
|
|
// Otherwise return the original.
|
|
|
|
|
static UMaterial* ReplaceMaterialWithTransientCopy(UMaterial* Material);
|
|
|
|
|
|
2026-03-06 15:06:12 -05:00
|
|
|
// ----- Anim blueprint helpers -----
|
|
|
|
|
static UAnimationStateMachineGraph* FindStateMachineGraph(UBlueprint* BP, const FString& GraphName);
|
2026-03-13 14:26:04 -04:00
|
|
|
static UAnimStateNode* FindStateByName(UAnimationStateMachineGraph* SMGraph, const FString& StateName);
|
2026-03-06 15:06:12 -05:00
|
|
|
static UAnimStateTransitionNode* FindTransition(UAnimationStateMachineGraph* SMGraph, const FString& FromStateName, const FString& ToStateName);
|
|
|
|
|
|
2026-03-13 02:09:19 -04:00
|
|
|
// ----- Text formatting -----
|
|
|
|
|
static FString WrapText(const FString& Text, int32 ColLimit, const FString& Prefix);
|
|
|
|
|
|
2026-03-09 03:44:35 -04:00
|
|
|
// ----- Handler discovery -----
|
|
|
|
|
static TArray<UClass*> CollectHandlerClasses();
|
2026-03-13 02:09:19 -04:00
|
|
|
static FString GetHandlerName(UClass* HandlerClass);
|
|
|
|
|
static FString GetHandlerGroup(UClass* HandlerClass);
|
2026-03-19 12:01:38 -04:00
|
|
|
static void PrintHandlerHelp(UClass* HandlerClass);
|
2026-03-08 21:28:47 -04:00
|
|
|
|
2026-03-18 21:48:56 -04:00
|
|
|
// ----- Common Error Reporting -----
|
2026-03-18 23:10:09 -04:00
|
|
|
static bool CheckExactlyOneNamed(int Count, const FString &Kind, const FString &Name);
|
|
|
|
|
static bool CheckExactlyOneNamed(int Count, UClass *Class, const FString &Name);
|
|
|
|
|
static bool CheckExactlyNoneNamed(int Count, const FString &Kind, const FString &Name);
|
|
|
|
|
static bool CheckExactlyNoneNamed(int Count, UClass *Class, const FString &Name);
|
2026-03-18 21:48:56 -04:00
|
|
|
|
2026-03-06 15:06:12 -05:00
|
|
|
};
|
2026-03-15 13:59:09 -04:00
|
|
|
|