294 lines
12 KiB
C++
294 lines
12 KiB
C++
#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<class T> static const T* EltAsPtr(const TArray<T> &A, const T &Elt) { return &Elt; }
|
|
template<class T> static T* EltAsPtr(TArray<T> &A, T &Elt) { return &Elt; }
|
|
template<class T> static T* EltAsPtr(const TArray<T*> &A, T* &Elt) { return Elt; }
|
|
template<class T> static T* EltAsPtr(TArray<T*> &A, T* &Elt) { return Elt; }
|
|
template<class T> static const T* EltAsPtr(const TArray<const T*> &A, const T* &Elt) { return Elt; }
|
|
template<class T> static const T* EltAsPtr(TArray<const T*> &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<UEdGraph> &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<typename ArrayType>
|
|
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<typename ArrayType>
|
|
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<typename ArrayType>
|
|
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<typename ArrayType>
|
|
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<UEdGraph> &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<typename T>
|
|
static bool Identifies(const FString &ExternalID, T&& Obj)
|
|
{
|
|
return FormatName(std::forward<T>(Obj)).Equals(ExternalID, ESearchCase::IgnoreCase);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////
|
|
|
|
|
|
template<typename T>
|
|
static T* FindExactlyOneNamed(const FString &Name, const TArray<T*> &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<typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>>
|
|
static T* FindExactlyOneNamed(const FString &Name, TArray<T> &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<typename T>
|
|
static bool FindExactlyNoneNamed(const FString &Name, const TArray<T> &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<typename T>
|
|
static FString EnumToString(TEnumAsByte<T> Value)
|
|
{ return EnumToString(StaticEnum<T>(), (int64)Value); }
|
|
|
|
template<typename T>
|
|
static FString EnumToString(T Value)
|
|
{ return EnumToString(StaticEnum<T>(), (int64)Value); }
|
|
|
|
template<typename T>
|
|
static bool StringToEnum(const FString& Str, T& OutValue)
|
|
{ int64 V; if (!StringToEnum(StaticEnum<T>(), Str, V)) return false; OutValue = (T)V; return true; }
|
|
|
|
// ----- Blueprint helpers -----
|
|
static TArray<UEdGraph*> AllGraphs(UBlueprint* BP);
|
|
static TArray<UEdGraphNode*> AllNodes(UBlueprint* BP);
|
|
static TArray<UEdGraphNode*> AllNodes(UEdGraph *Graph);
|
|
static TArray<UBlueprint*> 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<UClass*> 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);
|
|
};
|
|
|