General refactoring
This commit is contained in:
@@ -38,18 +38,18 @@ public:
|
|||||||
UObject* AssetObj = Assets.Object();
|
UObject* AssetObj = Assets.Object();
|
||||||
|
|
||||||
// Parse new path into package path and asset name
|
// Parse new path into package path and asset name
|
||||||
FString NewPackagePath, NewAssetName;
|
FString NewPackagePath = FPackageName::GetLongPackagePath(NewPath);
|
||||||
if (!MCPUtils::SplitAssetPath(NewPath, NewPackagePath, NewAssetName))
|
FString NewAssetName = FPackageName::GetShortName(NewPath);
|
||||||
|
if (NewPackagePath.IsEmpty())
|
||||||
{
|
{
|
||||||
// No slash — just a new name, keep the same directory
|
// No slash — just a new name, keep the same directory
|
||||||
FString OldPackagePath, OldAssetName;
|
NewPackagePath = FPackageName::GetLongPackagePath(AssetPath);
|
||||||
if (!MCPUtils::SplitAssetPath(AssetPath, OldPackagePath, OldAssetName))
|
NewAssetName = NewPath;
|
||||||
|
if (NewPackagePath.IsEmpty())
|
||||||
{
|
{
|
||||||
Result.Appendf(TEXT("ERROR: Cannot determine directory from AssetPath '%s'\n"), *AssetPath);
|
Result.Appendf(TEXT("ERROR: Cannot determine directory from AssetPath '%s'\n"), *AssetPath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
NewPackagePath = OldPackagePath;
|
|
||||||
NewAssetName = NewPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform the rename with reference fixup
|
// Perform the rename with reference fixup
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ public:
|
|||||||
|
|
||||||
MCPUtils::PostEdit(Chain);
|
MCPUtils::PostEdit(Chain);
|
||||||
|
|
||||||
bool bSaved = MCPUtils::SaveMaterialPackage(MaterialObj);
|
bool bSaved = MCPUtils::SaveGenericPackage(MaterialObj);
|
||||||
|
|
||||||
Result.Appendf(TEXT("Created %s\n"), *MaterialObj->GetPathName());
|
Result.Appendf(TEXT("Created %s\n"), *MaterialObj->GetPathName());
|
||||||
Result.Appendf(TEXT("Domain: %s\n"), *MCPUtils::EnumToString(MaterialObj->MaterialDomain, TEXT("MD_")));
|
Result.Appendf(TEXT("Domain: %s\n"), *MCPUtils::EnumToString(MaterialObj->MaterialDomain, TEXT("MD_")));
|
||||||
|
|||||||
@@ -30,12 +30,12 @@ public:
|
|||||||
|
|
||||||
for (UClass* Class : MCPUtils::CollectHandlerClasses())
|
for (UClass* Class : MCPUtils::CollectHandlerClasses())
|
||||||
{
|
{
|
||||||
FString ToolName = MCPUtils::GetToolName(Class);
|
FString ToolName = MCPUtils::GetHandlerName(Class);
|
||||||
if (!ToolName.ToLower().Contains(QueryLower))
|
if (!ToolName.ToLower().Contains(QueryLower))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Blank line between groups
|
// Blank line between groups
|
||||||
FString Group = MCPUtils::GetToolGroup(Class);
|
FString Group = MCPUtils::GetHandlerGroup(Class);
|
||||||
if (Group != PrevGroup)
|
if (Group != PrevGroup)
|
||||||
{
|
{
|
||||||
if (!PrevGroup.IsEmpty())
|
if (!PrevGroup.IsEmpty())
|
||||||
|
|||||||
@@ -12,38 +12,6 @@
|
|||||||
#include "K2Node_FunctionEntry.h"
|
#include "K2Node_FunctionEntry.h"
|
||||||
#include "MaterialGraph/MaterialGraphNode.h"
|
#include "MaterialGraph/MaterialGraphNode.h"
|
||||||
|
|
||||||
static FString WrapText(const FString& Text, int32 ColLimit, const FString& Prefix)
|
|
||||||
{
|
|
||||||
FString Clean = Text;
|
|
||||||
Clean.ReplaceInline(TEXT("\r\n"), TEXT("\n"));
|
|
||||||
TArray<FString> Words;
|
|
||||||
Clean.ParseIntoArrayWS(Words);
|
|
||||||
|
|
||||||
TStringBuilder<1024> Result;
|
|
||||||
int32 Col = 0;
|
|
||||||
for (const FString& Word : Words)
|
|
||||||
{
|
|
||||||
if (Col > 0 && Col + 1 + Word.Len() > ColLimit)
|
|
||||||
{
|
|
||||||
Result.Append(TEXT("\n"));
|
|
||||||
Col = 0;
|
|
||||||
}
|
|
||||||
if (Col == 0)
|
|
||||||
{
|
|
||||||
Result.Append(Prefix);
|
|
||||||
Col = Prefix.Len();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Result.Append(TEXT(" "));
|
|
||||||
Col += 1;
|
|
||||||
}
|
|
||||||
Result.Append(Word);
|
|
||||||
Col += Word.Len();
|
|
||||||
}
|
|
||||||
return Result.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
FlxBlueprintExporter::FlxBlueprintExporter(UEdGraph* InGraph)
|
FlxBlueprintExporter::FlxBlueprintExporter(UEdGraph* InGraph)
|
||||||
: Graph(InGraph)
|
: Graph(InGraph)
|
||||||
{
|
{
|
||||||
@@ -347,7 +315,7 @@ void FlxBlueprintExporter::EmitDetails()
|
|||||||
if (Node->IsA<UK2Node_Knot>()) continue;
|
if (Node->IsA<UK2Node_Knot>()) continue;
|
||||||
if (Node->IsA<UEdGraphNode_Comment>()) continue;
|
if (Node->IsA<UEdGraphNode_Comment>()) continue;
|
||||||
if (Node->IsA<UK2Node_VariableGet>()) continue;
|
if (Node->IsA<UK2Node_VariableGet>()) continue;
|
||||||
Details.Appendf(TEXT("details %s\n"), *MCPUtils::FormatName(Node));
|
Details.Appendf(TEXT("\ndetails %s\n"), *MCPUtils::FormatName(Node));
|
||||||
Details.Appendf(TEXT(" pos %d, %d\n"), Node->NodePosX, Node->NodePosY);
|
Details.Appendf(TEXT(" pos %d, %d\n"), Node->NodePosX, Node->NodePosY);
|
||||||
|
|
||||||
EmitMaterialProperties(Node, Details, false);
|
EmitMaterialProperties(Node, Details, false);
|
||||||
@@ -369,7 +337,7 @@ void FlxBlueprintExporter::EmitComments()
|
|||||||
Output.Appendf(TEXT("\ncomment %s:\n"), *MCPUtils::FormatName(CommentNode));
|
Output.Appendf(TEXT("\ncomment %s:\n"), *MCPUtils::FormatName(CommentNode));
|
||||||
|
|
||||||
// Emit wrapped, indented body.
|
// Emit wrapped, indented body.
|
||||||
Output.Append(WrapText(CommentNode->NodeComment, 70, TEXT(" - ")));
|
Output.Append(MCPUtils::WrapText(CommentNode->NodeComment, 70, TEXT(" - ")));
|
||||||
Output.Append(TEXT("\n"));
|
Output.Append(TEXT("\n"));
|
||||||
|
|
||||||
// Find contained nodes.
|
// Find contained nodes.
|
||||||
|
|||||||
@@ -258,7 +258,9 @@ TStatId UMCPServer::GetStatId() const
|
|||||||
FString UMCPServer::HandleRequest(const FString& Line)
|
FString UMCPServer::HandleRequest(const FString& Line)
|
||||||
{
|
{
|
||||||
// Turn the request string into a JSON tree.
|
// Turn the request string into a JSON tree.
|
||||||
TSharedPtr<FJsonObject> Request = MCPUtils::ParseBodyJson(Line);
|
TSharedPtr<FJsonObject> Request;
|
||||||
|
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Line);
|
||||||
|
FJsonSerializer::Deserialize(Reader, Request);
|
||||||
if (!Request.IsValid())
|
if (!Request.IsValid())
|
||||||
{
|
{
|
||||||
return TEXT("Request is not valid JSON");
|
return TEXT("Request is not valid JSON");
|
||||||
@@ -415,6 +417,6 @@ void UMCPServer::BuildMCPHandlerRegistry()
|
|||||||
{
|
{
|
||||||
for (UClass* Class : MCPUtils::CollectHandlerClasses())
|
for (UClass* Class : MCPUtils::CollectHandlerClasses())
|
||||||
{
|
{
|
||||||
MCPHandlerRegistry.FindOrAdd(MCPUtils::GetToolName(Class)) = Class;
|
MCPHandlerRegistry.FindOrAdd(MCPUtils::GetHandlerName(Class)) = Class;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -295,63 +295,63 @@ FString MCPUtils::FormatNodeTitle(const UEdGraphNode *Node)
|
|||||||
// JSON helpers
|
// JSON helpers
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
TSharedPtr<FJsonObject> MCPUtils::ParseBodyJson(const FString& Body)
|
// ============================================================
|
||||||
|
// Text formatting
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
FString MCPUtils::WrapText(const FString& Text, int32 ColLimit, const FString& Prefix)
|
||||||
{
|
{
|
||||||
TSharedPtr<FJsonObject> JsonObj;
|
FString Clean = Text;
|
||||||
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Body);
|
Clean.ReplaceInline(TEXT("\r\n"), TEXT("\n"));
|
||||||
FJsonSerializer::Deserialize(Reader, JsonObj);
|
TArray<FString> Words;
|
||||||
return JsonObj;
|
Clean.ParseIntoArrayWS(Words);
|
||||||
}
|
|
||||||
|
|
||||||
|
TStringBuilder<1024> Result;
|
||||||
FString MCPUtils::UrlDecode(const FString& EncodedString)
|
int32 Col = 0;
|
||||||
{
|
for (const FString& Word : Words)
|
||||||
FString Result;
|
|
||||||
Result.Reserve(EncodedString.Len());
|
|
||||||
|
|
||||||
for (int32 i = 0; i < EncodedString.Len(); ++i)
|
|
||||||
{
|
{
|
||||||
TCHAR C = EncodedString[i];
|
if (Col > 0 && Col + 1 + Word.Len() > ColLimit)
|
||||||
if (C == TEXT('+'))
|
|
||||||
{
|
{
|
||||||
Result += TEXT(' ');
|
Result.Append(TEXT("\n"));
|
||||||
|
Col = 0;
|
||||||
}
|
}
|
||||||
else if (C == TEXT('%') && i + 2 < EncodedString.Len())
|
if (Col == 0)
|
||||||
{
|
{
|
||||||
FString HexStr = EncodedString.Mid(i + 1, 2);
|
Result.Append(Prefix);
|
||||||
int32 HexVal = 0;
|
Col = Prefix.Len();
|
||||||
bool bValid = true;
|
|
||||||
for (TCHAR H : HexStr)
|
|
||||||
{
|
|
||||||
HexVal <<= 4;
|
|
||||||
if (H >= TEXT('0') && H <= TEXT('9'))
|
|
||||||
HexVal += H - TEXT('0');
|
|
||||||
else if (H >= TEXT('a') && H <= TEXT('f'))
|
|
||||||
HexVal += 10 + H - TEXT('a');
|
|
||||||
else if (H >= TEXT('A') && H <= TEXT('F'))
|
|
||||||
HexVal += 10 + H - TEXT('A');
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bValid = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (bValid)
|
|
||||||
{
|
|
||||||
Result += (TCHAR)HexVal;
|
|
||||||
i += 2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Result += C;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Result += C;
|
Result.Append(TEXT(" "));
|
||||||
|
Col += 1;
|
||||||
}
|
}
|
||||||
|
Result.Append(Word);
|
||||||
|
Col += Word.Len();
|
||||||
}
|
}
|
||||||
return Result;
|
return Result.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Enum helpers
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
FString MCPUtils::EnumToString(UEnum* Enum, int64 Value, const FString& Prefix)
|
||||||
|
{
|
||||||
|
FString Full = Enum->GetNameStringByValue(Value);
|
||||||
|
if (!Prefix.IsEmpty() && Full.StartsWith(Prefix))
|
||||||
|
return Full.Mid(Prefix.Len());
|
||||||
|
return Full;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MCPUtils::StringToEnum(UEnum* Enum, const FString& Str, int64& OutValue, MCPErrorCallback Error, const FString& Prefix)
|
||||||
|
{
|
||||||
|
OutValue = Enum->GetValueByNameString(Prefix + Str);
|
||||||
|
if (OutValue == INDEX_NONE)
|
||||||
|
{
|
||||||
|
Error.SetError(FString::Printf(TEXT("Invalid value '%s' for %s"), *Str, *Enum->GetName()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -382,37 +382,6 @@ TArray<UEdGraphNode*> MCPUtils::AllNodes(UBlueprint* BP)
|
|||||||
return Nodes;
|
return Nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
TArray<TSharedPtr<FJsonValue>> MCPUtils::AllGraphNamesJson(UBlueprint* BP)
|
|
||||||
{
|
|
||||||
TArray<TSharedPtr<FJsonValue>> Result;
|
|
||||||
for (UEdGraph* Graph : AllGraphs(BP))
|
|
||||||
Result.Add(MakeShared<FJsonValueString>(FormatName(Graph)));
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
UEdGraphNode* MCPUtils::FindNodeByGuid(
|
|
||||||
UBlueprint* BP, const FString& GuidString, UEdGraph** OutGraph)
|
|
||||||
{
|
|
||||||
FGuid TargetGuid;
|
|
||||||
FGuid::Parse(GuidString, TargetGuid);
|
|
||||||
|
|
||||||
TArray<UEdGraph*> AllGraphs;
|
|
||||||
BP->GetAllGraphs(AllGraphs);
|
|
||||||
|
|
||||||
for (UEdGraph* Graph : AllGraphs)
|
|
||||||
{
|
|
||||||
for (UEdGraphNode* Node : Graph->Nodes)
|
|
||||||
{
|
|
||||||
if (Node && Node->NodeGuid == TargetGuid)
|
|
||||||
{
|
|
||||||
if (OutGraph) *OutGraph = Graph;
|
|
||||||
return Node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MCPUtils::SaveBlueprintPackage(UBlueprint* BP)
|
bool MCPUtils::SaveBlueprintPackage(UBlueprint* BP)
|
||||||
{
|
{
|
||||||
UPackage* Package = BP->GetPackage();
|
UPackage* Package = BP->GetPackage();
|
||||||
@@ -946,12 +915,6 @@ void MCPUtils::FormatMaterialParameter(FStringBuilderBase& Result, const FMateri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MCPUtils::SaveMaterialPackage(UMaterial* Material)
|
|
||||||
{
|
|
||||||
if (!Material) return false;
|
|
||||||
return SaveGenericPackage(Material);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MCPUtils::SaveGenericPackage(UObject* Asset)
|
bool MCPUtils::SaveGenericPackage(UObject* Asset)
|
||||||
{
|
{
|
||||||
if (!Asset) return false;
|
if (!Asset) return false;
|
||||||
@@ -1339,15 +1302,15 @@ TArray<UClass*> MCPUtils::CollectHandlerClasses()
|
|||||||
if (!Class->ImplementsInterface(UMCPHandler::StaticClass())) continue;
|
if (!Class->ImplementsInterface(UMCPHandler::StaticClass())) continue;
|
||||||
Result.Add(Class);
|
Result.Add(Class);
|
||||||
}
|
}
|
||||||
Result.Sort([](UClass& A, UClass& B) { return GetToolName(&A) < GetToolName(&B); });
|
Result.Sort([](UClass& A, UClass& B) { return GetHandlerName(&A) < GetHandlerName(&B); });
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// GetToolName — derive tool name from handler class name
|
// GetHandlerName — derive tool name from handler class name
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
FString MCPUtils::GetToolName(UClass* HandlerClass)
|
FString MCPUtils::GetHandlerName(UClass* HandlerClass)
|
||||||
{
|
{
|
||||||
FString Name = HandlerClass->GetName();
|
FString Name = HandlerClass->GetName();
|
||||||
// Strip "MCP_" prefix
|
// Strip "MCP_" prefix
|
||||||
@@ -1361,10 +1324,10 @@ FString MCPUtils::GetToolName(UClass* HandlerClass)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// GetToolGroup — derive group name from handler class name
|
// GetHandlerGroup — derive group name from handler class name
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
FString MCPUtils::GetToolGroup(UClass* HandlerClass)
|
FString MCPUtils::GetHandlerGroup(UClass* HandlerClass)
|
||||||
{
|
{
|
||||||
FString Name = HandlerClass->GetName();
|
FString Name = HandlerClass->GetName();
|
||||||
// Strip "MCP_" prefix
|
// Strip "MCP_" prefix
|
||||||
@@ -1400,28 +1363,6 @@ FString MCPUtils::FormatPropertyType(FProperty* Prop)
|
|||||||
// GetTemplate
|
// GetTemplate
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
UObject* MCPUtils::GetTemplate(UObject* Obj, MCPErrorCallback Error)
|
|
||||||
{
|
|
||||||
if (!Obj)
|
|
||||||
{
|
|
||||||
Error.SetError(TEXT("Object is null"));
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Blueprint → operate on the Class Default Object.
|
|
||||||
if (UBlueprint* BP = Cast<UBlueprint>(Obj))
|
|
||||||
{
|
|
||||||
if (!BP->GeneratedClass)
|
|
||||||
{
|
|
||||||
Error.SetError(FString::Printf(TEXT("Blueprint '%s' has no GeneratedClass"), *Obj->GetName()));
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return BP->GeneratedClass->GetDefaultObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// FindPropertyByName
|
// FindPropertyByName
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -1525,7 +1466,11 @@ void MCPUtils::FormatCommandHelp(UClass* HandlerClass, FStringBuilderBase& Resul
|
|||||||
const IMCPHandler* Handler = Cast<IMCPHandler>(HandlerClass->GetDefaultObject());
|
const IMCPHandler* Handler = Cast<IMCPHandler>(HandlerClass->GetDefaultObject());
|
||||||
if (!Handler) return;
|
if (!Handler) return;
|
||||||
|
|
||||||
FString ToolName = GetToolName(HandlerClass);
|
FString ToolName = GetHandlerName(HandlerClass);
|
||||||
|
|
||||||
|
Result.Append(TEXT("\n"));
|
||||||
|
Result.Append(WrapText(Handler->GetDescription(), 80, TEXT("// ")));
|
||||||
|
Result.Append(TEXT("\n"));
|
||||||
|
|
||||||
// Command signature line
|
// Command signature line
|
||||||
Result.Append(ToolName);
|
Result.Append(ToolName);
|
||||||
@@ -1540,8 +1485,7 @@ void MCPUtils::FormatCommandHelp(UClass* HandlerClass, FStringBuilderBase& Resul
|
|||||||
}
|
}
|
||||||
Result.Append(TEXT(")\n"));
|
Result.Append(TEXT(")\n"));
|
||||||
|
|
||||||
// Description and parameter details
|
// parameter details
|
||||||
Result.Appendf(TEXT(" %s\n"), *Handler->GetDescription());
|
|
||||||
for (TFieldIterator<FProperty> PropIt(HandlerClass, EFieldIterationFlags::None); PropIt; ++PropIt)
|
for (TFieldIterator<FProperty> PropIt(HandlerClass, EFieldIterationFlags::None); PropIt; ++PropIt)
|
||||||
{
|
{
|
||||||
FProperty* Prop = *PropIt;
|
FProperty* Prop = *PropIt;
|
||||||
|
|||||||
@@ -91,8 +91,6 @@ struct MCPErrorCallback
|
|||||||
|
|
||||||
MCPErrorCallback(std::nullptr_t);
|
MCPErrorCallback(std::nullptr_t);
|
||||||
MCPErrorCallback(FString& OutError);
|
MCPErrorCallback(FString& OutError);
|
||||||
MCPErrorCallback(FJsonObject* Result);
|
|
||||||
MCPErrorCallback(const TSharedRef<FJsonObject>& Result) : MCPErrorCallback(&*Result) {}
|
|
||||||
MCPErrorCallback(FStringBuilderBase& OutResult);
|
MCPErrorCallback(FStringBuilderBase& OutResult);
|
||||||
|
|
||||||
void SetError(const FString& Msg) const { Func(Msg); }
|
void SetError(const FString& Msg) const { Func(Msg); }
|
||||||
@@ -165,66 +163,21 @@ public:
|
|||||||
static FString FormatPinType(UEdGraphPin* Pin);
|
static FString FormatPinType(UEdGraphPin* Pin);
|
||||||
static FString FormatNodeTitle(const UEdGraphNode *Node);
|
static FString FormatNodeTitle(const UEdGraphNode *Node);
|
||||||
|
|
||||||
// ----- Asset path helpers -----
|
|
||||||
// Splits "/Game/Foo/Bar" into PackagePath="/Game/Foo" and AssetName="Bar".
|
|
||||||
// Returns false if the path has no slash or the asset name is empty.
|
|
||||||
static bool SplitAssetPath(const FString& AssetPath, FString& OutPackagePath, FString& OutAssetName)
|
|
||||||
{
|
|
||||||
int32 LastSlash;
|
|
||||||
if (!AssetPath.FindLastChar('/', LastSlash))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
OutPackagePath = AssetPath.Left(LastSlash);
|
|
||||||
OutAssetName = AssetPath.Mid(LastSlash + 1);
|
|
||||||
return !OutAssetName.IsEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----- JSON helpers -----
|
|
||||||
static TSharedPtr<FJsonObject> ParseBodyJson(const FString& Body);
|
|
||||||
static FString UrlDecode(const FString& EncodedString);
|
|
||||||
|
|
||||||
// ----- Enum helpers -----
|
// ----- Enum helpers -----
|
||||||
// Convert enum value to string. If Prefix is specified, strip "Prefix_" from the front.
|
static FString EnumToString(UEnum* Enum, int64 Value, const FString& Prefix = FString());
|
||||||
|
static bool StringToEnum(UEnum* Enum, const FString& Str, int64& OutValue, MCPErrorCallback Error, const FString& Prefix = FString());
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static FString EnumToString(TEnumAsByte<T> Value, const FString& Prefix = FString())
|
static FString EnumToString(TEnumAsByte<T> Value, const FString& Prefix = FString())
|
||||||
{
|
{ return EnumToString(StaticEnum<T>(), (int64)Value, Prefix); }
|
||||||
return EnumToString<T>((T)Value, Prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static FString EnumToString(T Value, const FString& Prefix = FString())
|
static FString EnumToString(T Value, const FString& Prefix = FString())
|
||||||
{
|
{ return EnumToString(StaticEnum<T>(), (int64)Value, Prefix); }
|
||||||
UEnum* Enum = StaticEnum<T>();
|
|
||||||
FString Full = Enum->GetNameStringByValue((int64)Value);
|
|
||||||
if (!Prefix.IsEmpty() && Full.StartsWith(Prefix))
|
|
||||||
return Full.Mid(Prefix.Len());
|
|
||||||
return Full;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert string to enum value. If Prefix is specified, prepend it before lookup.
|
|
||||||
// Returns false and sets error if the string doesn't match any value.
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static bool StringToEnum(const FString& Str, T& OutValue, MCPErrorCallback Error, const FString& Prefix = FString())
|
static bool StringToEnum(const FString& Str, T& OutValue, MCPErrorCallback Error, const FString& Prefix = FString())
|
||||||
{
|
{ int64 V; if (!StringToEnum(StaticEnum<T>(), Str, V, Error, Prefix)) return false; OutValue = (T)V; return true; }
|
||||||
UEnum* Enum = StaticEnum<T>();
|
|
||||||
int64 Value = Enum->GetValueByNameString(Prefix + Str);
|
|
||||||
if (Value == INDEX_NONE)
|
|
||||||
{
|
|
||||||
Error.SetError(FString::Printf(TEXT("Invalid value '%s' for %s"), *Str, *Enum->GetName()));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
OutValue = (T)Value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool StringToBool(const FString& Str, bool& OutValue, MCPErrorCallback Error)
|
|
||||||
{
|
|
||||||
if (Str.Equals(TEXT("true"), ESearchCase::IgnoreCase)) { OutValue = true; return true; }
|
|
||||||
if (Str.Equals(TEXT("false"), ESearchCase::IgnoreCase)) { OutValue = false; return true; }
|
|
||||||
Error.SetError(FString::Printf(TEXT("Invalid bool value '%s' (expected 'true' or 'false')"), *Str));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----- Blueprint helpers -----
|
// ----- Blueprint helpers -----
|
||||||
static TArray<UEdGraph*> AllGraphs(UBlueprint* BP);
|
static TArray<UEdGraph*> AllGraphs(UBlueprint* BP);
|
||||||
@@ -247,17 +200,15 @@ public:
|
|||||||
Result.Add(Typed);
|
Result.Add(Typed);
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
static TArray<TSharedPtr<FJsonValue>> AllGraphNamesJson(UBlueprint* BP);
|
|
||||||
static UEdGraphNode* FindNodeByGuid(UBlueprint* BP, const FString& GuidString, UEdGraph** OutGraph = nullptr);
|
|
||||||
static bool SaveBlueprintPackage(UBlueprint* BP);
|
static bool SaveBlueprintPackage(UBlueprint* BP);
|
||||||
|
|
||||||
// ----- Type resolution -----
|
// ----- Type resolution -----
|
||||||
static UClass* FindClassByName(const FString& ClassName);
|
static UClass* FindClassByName(const FString& ClassName);
|
||||||
static bool ResolveTypeFromString(const FString& TypeName, FEdGraphPinType& OutPinType, MCPErrorCallback Error);
|
static bool ResolveTypeFromString(const FString& TypeName, FEdGraphPinType& OutPinType, MCPErrorCallback Error);
|
||||||
|
static FString FormatPropertyType(FProperty* Prop);
|
||||||
|
|
||||||
// ----- Material helpers -----
|
// ----- Material helpers -----
|
||||||
static void EnsureMaterialGraph(UMaterial* Material);
|
static void EnsureMaterialGraph(UMaterial* Material);
|
||||||
static bool SaveMaterialPackage(UMaterial* Material);
|
|
||||||
static bool SaveGenericPackage(UObject* Asset);
|
static bool SaveGenericPackage(UObject* Asset);
|
||||||
|
|
||||||
// If the material editor has a transient preview copy of this material,
|
// If the material editor has a transient preview copy of this material,
|
||||||
@@ -287,33 +238,27 @@ public:
|
|||||||
static void FormatMaterialParameter(FStringBuilderBase& Result, const FMaterialParameterInfo& Info, const FMaterialParameterMetadata& Meta);
|
static void FormatMaterialParameter(FStringBuilderBase& Result, const FMaterialParameterInfo& Info, const FMaterialParameterMetadata& Meta);
|
||||||
|
|
||||||
// ----- Editable template -----
|
// ----- Editable template -----
|
||||||
// Given an object, returns the appropriate template object for generic
|
|
||||||
// property editing, or nullptr if the type isn't whitelisted.
|
|
||||||
// UBlueprint → CDO; UMaterial, UActorComponent, etc. → as-is.
|
|
||||||
static UObject* GetTemplate(UObject* Obj, MCPErrorCallback Error);
|
|
||||||
static TArray<FProperty*> SearchProperties(UObject* Obj, const FString& Query, EPropertyFlags Flags, bool bLocal);
|
static TArray<FProperty*> SearchProperties(UObject* Obj, const FString& Query, EPropertyFlags Flags, bool bLocal);
|
||||||
|
|
||||||
static FProperty* FindPropertyByName(UObject* Obj, const FString& Name, MCPErrorCallback Error = nullptr);
|
static FProperty* FindPropertyByName(UObject* Obj, const FString& Name, MCPErrorCallback Error = nullptr);
|
||||||
static FString GetPropertyValueText(UObject* Container, FProperty* Prop);
|
static FString GetPropertyValueText(UObject* Container, FProperty* Prop);
|
||||||
static bool SetPropertyValueText(UObject* Container, FProperty* Prop, const FString& Value, MCPErrorCallback Error = nullptr);
|
static bool SetPropertyValueText(UObject* Container, FProperty* Prop, const FString& Value, MCPErrorCallback Error = nullptr);
|
||||||
static bool SetPropertyValueText(void* Container, FProperty* Prop, const FString& Value, UObject* Owner, MCPErrorCallback Error = nullptr);
|
static bool SetPropertyValueText(void* Container, FProperty* Prop, const FString& Value, UObject* Owner, MCPErrorCallback Error = nullptr);
|
||||||
|
|
||||||
// ----- Property population -----
|
// ----- JSON helpers -----
|
||||||
static FString PropertyNameToJsonKey(const FString& PropName);
|
static FString PropertyNameToJsonKey(const FString& PropName);
|
||||||
static bool PopulateFromJson(UStruct* StructType, void* Container, const TSharedPtr<FJsonValue>& JsonValue, MCPErrorCallback Error);
|
static bool PopulateFromJson(UStruct* StructType, void* Container, const TSharedPtr<FJsonValue>& JsonValue, MCPErrorCallback Error);
|
||||||
static bool PopulateFromJson(UStruct* StructType, void* Container, const FJsonObject* Json, MCPErrorCallback Error);
|
static bool PopulateFromJson(UStruct* StructType, void* Container, const FJsonObject* Json, MCPErrorCallback Error);
|
||||||
|
|
||||||
// ----- Handler discovery -----
|
// ----- Text formatting -----
|
||||||
// Collect all concrete IMCPHandler classes, sorted by tool name.
|
static FString WrapText(const FString& Text, int32 ColLimit, const FString& Prefix);
|
||||||
static TArray<UClass*> CollectHandlerClasses();
|
|
||||||
|
|
||||||
// ----- Command help -----
|
// ----- Handler discovery -----
|
||||||
// Derive tool name from handler class name: "MCPHandler_FooBar" → "FooBar"
|
static TArray<UClass*> CollectHandlerClasses();
|
||||||
static FString GetToolName(UClass* HandlerClass);
|
static FString GetHandlerName(UClass* HandlerClass);
|
||||||
static FString GetToolGroup(UClass* HandlerClass);
|
static FString GetHandlerGroup(UClass* HandlerClass);
|
||||||
static FString FormatPropertyType(FProperty* Prop);
|
|
||||||
static void FormatCommandHelp(UClass* HandlerClass, FStringBuilderBase& Result);
|
static void FormatCommandHelp(UClass* HandlerClass, FStringBuilderBase& Result);
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void SanitizeNameInPlace(FString& Name);
|
static void SanitizeNameInPlace(FString& Name);
|
||||||
static void AppendNumericSuffix(FString &Name, int32 N);
|
static void AppendNumericSuffix(FString &Name, int32 N);
|
||||||
|
|||||||
Reference in New Issue
Block a user