#pragma once #include "CoreMinimal.h" #include "Dom/JsonObject.h" #include "EdGraph/EdGraph.h" #include "EdGraph/EdGraphPin.h" #include "Materials/MaterialInstanceConstant.h" #include "Materials/MaterialExpression.h" #include "MaterialTypes.h" #include "Components/ActorComponent.h" #include "Engine/SCS_Node.h" #include "Engine/MemberReference.h" #include "Engine/Blueprint.h" #include "K2Node_EditablePinBase.h" #include "Components/Widget.h" #include "WingActorComponent.h" struct FEdGraphSchemaAction; class UAnimationStateMachineGraph; class UAnimStateNode; class UAnimStateTransitionNode; class UScriptStruct; class UEnum; struct FBPInterfaceDescription; #include "Engine/World.h" #include "Materials/Material.h" #include "Engine/StaticMesh.h" #include "Engine/SkeletalMesh.h" #include "Animation/AnimSequence.h" #include "Animation/BlendSpace.h" #include "Engine/Texture.h" #include "Materials/MaterialFunction.h" // Stateless utility functions used by MCP handlers and the MCP server. // This is effectively a namespace — all methods are static. class WingUtils { public: // Given an array and a reference to an element of the array, return // a pointer to the element, or if the element is already a pointer, the element. template static const T* EltAsPtr(const TArray &A, const T &Elt) { return &Elt; } template static T* EltAsPtr(TArray &A, T &Elt) { return &Elt; } template static T* EltAsPtr(const TArray &A, T* &Elt) { return Elt; } template static T* EltAsPtr(TArray &A, T* &Elt) { return Elt; } template static const T* EltAsPtr(const TArray &A, const T* &Elt) { return Elt; } template static const T* EltAsPtr(TArray &A, const T* &Elt) { return Elt; } //////////////////////////////////////////////////////// // // GetFName // // For objects whose FormatName uses ExternalizeID, // GetFName returns the raw internal FName. // //////////////////////////////////////////////////////// static FName GetFName(const UActorComponent *C) { return C->GetFName(); } static FName GetFName(const UEdGraph *Graph) { return Graph->GetFName(); } static FName GetFName(const TObjectPtr &Graph) { return Graph->GetFName(); } static FName GetFName(const UEdGraphNode* Node) { return Node->GetFName(); } static FName GetFName(const UEdGraphPin *Pin) { return Pin->GetFName(); } static FName GetFName(const FMemberReference &Ref) { return Ref.GetMemberName(); } static FName GetFName(const FBPVariableDescription &Var) { return Var.VarName; } static FName GetFName(const UMaterialExpression *Expression) { return Expression->GetFName(); } static FName GetFName(const FProperty *Prop) { return Prop->GetFName(); } static FName GetFName(const FUserPinInfo &Pin) { return Pin.PinName; } static FName GetFName(const UWingComponentReference *Ref) { return Ref->VariableName; } static FName GetFName(const UWidget *Widget) { return Widget->GetFName(); } template static auto FindOneWithInternalID(FName InternalID, ArrayType &Array, const TCHAR *Kind) { decltype(EltAsPtr(Array, Array[0])) Result = nullptr; int Count = 0; for (auto &Elt : Array) if (GetFName(Elt) == InternalID) { Count++; Result = EltAsPtr(Array, Elt); } if (!CheckExactlyOneNamed(Count, Kind, InternalID)) Result = nullptr; return Result; } template static auto FindOneWithExternalID(const FString &ExternalID, ArrayType &Array, const TCHAR *Kind) { decltype(EltAsPtr(Array, Array[0])) Result = nullptr; FName InternalID = CheckInternalizeID(ExternalID); if (!InternalID.IsNone()) Result = FindOneWithInternalID(InternalID, Array, Kind); return Result; } template static bool FindNoneWithInternalID(FName InternalID, ArrayType &Array, const TCHAR *Kind) { for (auto &Elt : Array) if (GetFName(Elt) == InternalID) return CheckExactlyNoneNamed(1, Kind, InternalID); return true; } template static bool FindNoneWithExternalID(const FString &ExternalID, ArrayType &Array, const TCHAR *Kind) { FName InternalID = CheckInternalizeID(ExternalID); if (InternalID.IsNone()) return false; return FindNoneWithInternalID(InternalID, Array, Kind); } //////////////////////////////////////////////////////// // // GetPathName // // For objects whose FormatName returns a path name, // GetPathName returns the raw path. // //////////////////////////////////////////////////////// static FString GetPathName(const UWorld *World) { return World->GetPathName(); } static FString GetPathName(const UMaterial *Material) { return Material->GetPathName(); } static FString GetPathName(const UMaterialInstance *MI) { return MI->GetPathName(); } static FString GetPathName(const UMaterialFunction *MF) { return MF->GetPathName(); } static FString GetPathName(const UStaticMesh *Mesh) { return Mesh->GetPathName(); } static FString GetPathName(const USkeletalMesh *Mesh) { return Mesh->GetPathName(); } static FString GetPathName(const UAnimSequence *Anim) { return Anim->GetPathName(); } static FString GetPathName(const UTexture *Texture) { return Texture->GetPathName(); } //////////////////////////////////////////////////////// // // Name Formatting // // The goal here is to centralize the code that outputs // names, and have everybody use it, so that names are // used consistently. When an identifier is output by // FormatName, it's always an ExternalID. // //////////////////////////////////////////////////////// static FString FormatName(const UWorld *World); static FString FormatName(const UBlueprint *BP); static FString FormatName(const UActorComponent *C); static FString FormatName(const USCS_Node *Node); static FString FormatName(const UEdGraph *Graph); static FString FormatName(const TObjectPtr &Graph); static FString FormatName(const UEdGraphNode* Node); static FString FormatName(const UEdGraphPin *Pin); static FString FormatName(const FMemberReference &Ref); static FString FormatName(const FBPVariableDescription &Var); static FString FormatName(const UStruct *Struct); static FString FormatName(const UClass *Class); 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); static FString FormatName(const FProperty *Prop); static FString FormatName(const FUserPinInfo &Pin); static FString FormatName(const FBPInterfaceDescription &IFace); static FString FormatName(const UWingComponentReference *Ref); static FString FormatName(const UWidget *Widget); //////////////////////////////////////////////////////// // Identifies // // Return true if the name matches the formatted name // of the object, using the formatname routines above. //////////////////////////////////////////////////////// template static bool Identifies(const FString &ExternalID, T&& Obj) { return FormatName(std::forward(Obj)).Equals(ExternalID, ESearchCase::IgnoreCase); } //////////////////////////////////////////////////////// template static T* FindExactlyOneNamed(const FString &Name, const TArray &Array, const TCHAR *Kind) { int Count = 0; T* Result = nullptr; for (T* Elt : Array) if (Identifies(Name, Elt)) { Count++; Result = Elt; } if (!CheckExactlyOneNamed(Count, Kind, Name)) return nullptr; return Result; } template>> static T* FindExactlyOneNamed(const FString &Name, TArray &Array, const TCHAR *Kind) { int Count = 0; T* Result = nullptr; for (T& Elt : Array) if (Identifies(Name, Elt)) { Count++; Result = &Elt; } if (!CheckExactlyOneNamed(Count, Kind, Name)) return nullptr; return Result; } template static bool FindExactlyNoneNamed(const FString &Name, const TArray &Array, const TCHAR *Kind) { for (const T& Elt: Array) if (Identifies(Name, Elt)) { return CheckExactlyNoneNamed(1, Kind, Name); } return true; } //////////////////////////////////////////////////////// static FString ExternalizeID(const FString& InternalID); static FString ExternalizeID(FName Name); static FName CheckInternalizeID(const FString &ExternalID); //////////////////////////////////////////////////////// // In Unreal, Menu items tend to be an unpredictable // mix of CamelCase without spaces, and with spaces. // In order to make it so that the LLM doesn't have to remember // which ones have spaces and which ones don't, we standardize // them all to camelcase without spaces. //////////////////////////////////////////////////////// static FString StandardizeMenuItem(const FString &Item); //////////////////////////////////////////////////////// // This routine is used when the LLM is proposing a new // name in order to create a new graph, new node, or // something like that. This verifies that the name is // a valid external ID, it converts it to an internal ID, // and it also verifies that it's a readable internal ID. // If anything goes wrong, it prints an error and returns // empty string. //////////////////////////////////////////////////////// [[nodiscard]] static FString CheckProposedName(const FString &Name); static FString FormatNodeTitle(const UEdGraphNode *Node); // ----- Enum helpers ----- static FString EnumToString(UEnum* Enum, int64 Value); static bool StringToEnum(UEnum* Enum, const FString& Str, int64& OutValue); template static FString EnumToString(TEnumAsByte Value) { return EnumToString(StaticEnum(), (int64)Value); } template static FString EnumToString(T Value) { return EnumToString(StaticEnum(), (int64)Value); } template static bool StringToEnum(const FString& Str, T& OutValue) { int64 V; if (!StringToEnum(StaticEnum(), Str, V)) return false; OutValue = (T)V; return true; } // ----- Blueprint helpers ----- static TArray AllGraphs(UBlueprint* BP); static TArray AllNodes(UBlueprint* BP); static TArray AllNodes(UEdGraph *Graph); static TArray GetAncestorBlueprints(UBlueprint *BP, bool OldestFirst = false); // ----- Material helpers ----- static void EnsureMaterialGraph(UMaterial* Material); // 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); // ----- Anim blueprint helpers ----- static UAnimationStateMachineGraph* FindStateMachineGraph(UBlueprint* BP, const FString& GraphName); static UAnimStateNode* FindStateByName(UAnimationStateMachineGraph* SMGraph, const FString& StateName); static UAnimStateTransitionNode* FindTransition(UAnimationStateMachineGraph* SMGraph, const FString& FromStateName, const FString& ToStateName); // ----- Text formatting ----- static FString WrapText(const FString& Text, int32 ColLimit, const FString& Prefix); // ----- Handler discovery ----- static TArray CollectHandlerClasses(); static FString GetHandlerName(UClass* HandlerClass); static FString GetHandlerGroup(UClass* HandlerClass); // ----- Reparent validation ----- static bool CanReparentBlueprint(UClass* CurrentGenerated, UClass* Proposed); // ----- Common Error Reporting ----- static bool CheckExactlyOneNamed(int Count, const TCHAR *Kind, const FString &Name); static bool CheckExactlyOneNamed(int Count, const TCHAR *Kind, FName Name); static bool CheckExactlyNoneNamed(int Count, const TCHAR *Kind, const FString &Name); static bool CheckExactlyNoneNamed(int Count, const TCHAR *Kind, FName Name); static bool CheckCanRename(UEdGraphNode* Node, const FString &Name); };