Lots of refactoring

This commit is contained in:
2026-03-13 14:26:04 -04:00
parent e3b5d32345
commit 7cfe73eca8
65 changed files with 246 additions and 364 deletions

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "MCPPackageMaker.h"
#include "Animation/AnimBlueprint.h"
@@ -39,14 +39,12 @@ public:
virtual void Handle(FStringBuilderBase& Result) override
{
MCPErrorCallback CB(Result);
MCPPackageMaker Maker(AssetPath, CB);
MCPPackageMaker Maker(AssetPath);
if (!Maker.Ok()) return;
// Resolve skeleton
MCPAssets<USkeleton> SkeletonAssets;
if (!SkeletonAssets.Exact(Skeleton).Errors(CB).ENone().ETwo().Load()) return;
if (!SkeletonAssets.Exact(Skeleton).ENone().ETwo().Load()) return;
USkeleton* SkeletonObj = SkeletonAssets.Object();
// Resolve parent class (default: UAnimInstance)
@@ -64,7 +62,8 @@ public:
}
if (!Found)
{
return CB.SetError(FString::Printf(TEXT("Parent class '%s' not found (must derive from AnimInstance)"), *ParentClass));
UMCPServer::Printf(TEXT("ERROR: Parent class '%s' not found (must derive from AnimInstance)\n"), *ParentClass);
return;
}
ParentClassObj = Found;
}
@@ -84,7 +83,8 @@ public:
if (!NewAnimBP)
{
return CB.SetError(TEXT("FKismetEditorUtilities::CreateBlueprint returned null"));
UMCPServer::Print(TEXT("ERROR: FKismetEditorUtilities::CreateBlueprint returned null\n"));
return;
}
// Set target skeleton

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "Animation/AnimBlueprint.h"
#include "AnimGraphNode_Base.h"
@@ -30,7 +30,7 @@ public:
virtual void Handle(FStringBuilderBase& Result) override
{
MCPAssets<UAnimBlueprint> Assets;
if (!Assets.Exact(Blueprint).Errors(Result).ENone().ETwo().Load()) return;
if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return;
UAnimBlueprint* AnimBP = Assets.Object();
// Walk all anim nodes to collect slot names

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPFetcher.h"
#include "MCPUtils.h"
#include "Animation/AnimSequence.h"
@@ -69,7 +69,7 @@ public:
{
// Load the blend space
MCPAssets<UBlendSpace> Assets;
if (!Assets.Exact(BlendSpace).Errors(Result).ENone().ETwo().Load()) return;
if (!Assets.Exact(BlendSpace).ENone().ETwo().Load()) return;
UBlendSpace* BS = Assets.Object();
// Set axis parameters
@@ -101,13 +101,13 @@ public:
for (const TSharedPtr<FJsonValue>& SampleVal : Samples.Array)
{
FBlendSpaceSampleEntry Entry;
if (!MCPUtils::PopulateFromJson(FBlendSpaceSampleEntry::StaticStruct(), &Entry, SampleVal, Result)) return;
if (!MCPUtils::PopulateFromJson(FBlendSpaceSampleEntry::StaticStruct(), &Entry, SampleVal)) return;
UAnimSequence* AnimSeq = nullptr;
if (!Entry.AnimationAsset.IsEmpty())
{
MCPAssets<UAnimSequence> AnimAssets;
if (!AnimAssets.Exact(Entry.AnimationAsset).Errors(Result).ENone().Load()) return;
if (!AnimAssets.Exact(Entry.AnimationAsset).ENone().Load()) return;
AnimSeq = AnimAssets.Object();
}

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "Misc/PackageName.h"
#include "AssetRegistry/AssetRegistryModule.h"

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "AssetToolsModule.h"
#include "IAssetTools.h"
@@ -34,7 +34,7 @@ public:
{
// Load the asset
MCPAssets<UObject> Assets;
if (!Assets.Exact(AssetPath).AllContent().Errors(Result).ENone().ETwo().Load()) return;
if (!Assets.Exact(AssetPath).AllContent().ENone().ETwo().Load()) return;
UObject* AssetObj = Assets.Object();
// Parse new path into package path and asset name

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "Asset_Search.generated.h"
@@ -58,7 +58,7 @@ public:
Assets.Substring(Query);
}
Assets.AllContent().Limit(Limit).Errors(Result).Info();
Assets.AllContent().Limit(Limit).Info();
const TArray<FAssetData>& AllData = Assets.AllData();
for (const FAssetData& Data : AllData)

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "MCPPackageMaker.h"
#include "Animation/Skeleton.h"
@@ -33,14 +33,12 @@ public:
virtual void Handle(FStringBuilderBase& Result) override
{
MCPErrorCallback CB(Result);
MCPPackageMaker Maker(AssetPath, CB);
MCPPackageMaker Maker(AssetPath);
if (!Maker.Ok()) return;
// Resolve skeleton.
MCPAssets<USkeleton> SkeletonAssets;
if (!SkeletonAssets.Exact(Skeleton).Errors(CB).ENone().ETwo().Load()) return;
if (!SkeletonAssets.Exact(Skeleton).ENone().ETwo().Load()) return;
USkeleton* SkeletonObj = SkeletonAssets.Object();
// Create the package and Blend Space.

View File

@@ -2,8 +2,9 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "MCPServer.h"
#include "Engine/Blueprint.h"
#include "Engine/SimpleConstructionScript.h"
#include "Engine/SCS_Node.h"
@@ -42,15 +43,14 @@ public:
virtual void Handle(FStringBuilderBase& Result) override
{
MCPAssets<UBlueprint> Assets;
if (!Assets.Exact(Blueprint).Errors(Result).ENone().ETwo().Load()) return;
if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return;
UBlueprint* BP = Assets.Object();
USimpleConstructionScript* SCS = BP->SimpleConstructionScript;
if (!SCS)
{
MCPErrorCallback(Result).SetError(FString::Printf(
TEXT("Blueprint '%s' does not have a SimpleConstructionScript (not an Actor Blueprint)"),
*MCPUtils::FormatName(BP)));
UMCPServer::Printf(TEXT("ERROR: Blueprint '%s' does not have a SimpleConstructionScript (not an Actor Blueprint)\n"),
*MCPUtils::FormatName(BP));
return;
}
@@ -61,9 +61,8 @@ public:
if (Existing && Existing->ComponentTemplate &&
MCPUtils::Identifies(Component, Existing->ComponentTemplate))
{
MCPErrorCallback(Result).SetError(FString::Printf(
TEXT("A component named '%s' already exists in Blueprint '%s'"),
*Component, *MCPUtils::FormatName(BP)));
UMCPServer::Printf(TEXT("ERROR: A component named '%s' already exists in Blueprint '%s'\n"),
*Component, *MCPUtils::FormatName(BP));
return;
}
}
@@ -72,13 +71,12 @@ public:
UClass* ComponentClassObj = MCPUtils::FindClassByName(ComponentClass);
if (!ComponentClassObj || !ComponentClassObj->IsChildOf(UActorComponent::StaticClass()))
{
MCPErrorCallback(Result).SetError(FString::Printf(
TEXT("Component class '%s' not found or is not a subclass of UActorComponent. "
"Common classes: StaticMeshComponent, SkeletalMeshComponent, AudioComponent, "
"SceneComponent, BoxCollisionComponent, SphereCollisionComponent, CapsuleComponent, "
"ArrowComponent, ChildActorComponent, SpotLightComponent, PointLightComponent, "
"WidgetComponent, BillboardComponent"),
*ComponentClass));
UMCPServer::Printf(TEXT("ERROR: Component class '%s' not found or is not a subclass of UActorComponent. "
"Common classes: StaticMeshComponent, SkeletalMeshComponent, AudioComponent, "
"SceneComponent, BoxCollisionComponent, SphereCollisionComponent, CapsuleComponent, "
"ArrowComponent, ChildActorComponent, SpotLightComponent, PointLightComponent, "
"WidgetComponent, BillboardComponent\n"),
*ComponentClass);
return;
}
@@ -98,9 +96,8 @@ public:
if (!ParentSCSNode)
{
MCPErrorCallback(Result).SetError(FString::Printf(
TEXT("Parent component '%s' not found in Blueprint '%s'"),
*ParentComponent, *MCPUtils::FormatName(BP)));
UMCPServer::Printf(TEXT("ERROR: Parent component '%s' not found in Blueprint '%s'\n"),
*ParentComponent, *MCPUtils::FormatName(BP));
return;
}
}
@@ -109,9 +106,8 @@ public:
USCS_Node* NewNode = SCS->CreateNode(ComponentClassObj, FName(*Component));
if (!NewNode)
{
MCPErrorCallback(Result).SetError(FString::Printf(
TEXT("Failed to create SCS node for component '%s' with class '%s'"),
*Component, *MCPUtils::FormatName(ComponentClassObj)));
UMCPServer::Printf(TEXT("ERROR: Failed to create SCS node for component '%s' with class '%s'\n"),
*Component, *MCPUtils::FormatName(ComponentClassObj));
return;
}

View File

@@ -123,11 +123,11 @@ public:
for (const TSharedPtr<FJsonValue>& ParamVal : Parameters.Array)
{
FDispatcherParamEntry Entry;
if (!MCPUtils::PopulateFromJson(FDispatcherParamEntry::StaticStruct(), &Entry, ParamVal, Result)) return;
if (!MCPUtils::PopulateFromJson(FDispatcherParamEntry::StaticStruct(), &Entry, ParamVal)) return;
if (Entry.Name.IsEmpty() || Entry.Type.IsEmpty()) continue;
FEdGraphPinType PinType;
if (!MCPUtils::ResolveTypeFromString(Entry.Type, PinType, Result))
if (!MCPUtils::ResolveTypeFromString(Entry.Type, PinType))
return;
EntryNode->CreateUserDefinedPin(FName(*Entry.Name), PinType, EGPD_Output);

View File

@@ -2,8 +2,9 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "MCPServer.h"
#include "Engine/Blueprint.h"
#include "EdGraph/EdGraph.h"
#include "EdGraph/EdGraphPin.h"
@@ -44,12 +45,12 @@ public:
virtual void Handle(FStringBuilderBase& Result) override
{
MCPAssets<UBlueprint> Assets;
if (!Assets.Exact(Blueprint).Errors(Result).ENone().ETwo().Load()) return;
if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return;
UBlueprint* BP = Assets.Object();
// Resolve param type
FEdGraphPinType PinType;
if (!MCPUtils::ResolveTypeFromString(ParamType, PinType, Result))
if (!MCPUtils::ResolveTypeFromString(ParamType, PinType))
return;
// Find the entry node using 3 strategies
@@ -101,22 +102,21 @@ public:
if (!EntryNode)
{
// Build a helpful error listing available functions, events, and dispatchers
MCPErrorCallback(Result).SetError(FString::Printf(
TEXT("Function/event/dispatcher '%s' not found. Available:\n"), *FunctionName));
UMCPServer::Printf(TEXT("ERROR: Function/event/dispatcher '%s' not found. Available:\n"), *FunctionName);
for (UEdGraph* Graph : BP->FunctionGraphs)
{
if (Graph) Result.Appendf(TEXT(" function: %s\n"), *MCPUtils::FormatName(Graph));
if (Graph) UMCPServer::Printf(TEXT(" function: %s\n"), *MCPUtils::FormatName(Graph));
}
for (UK2Node_CustomEvent* CE : MCPUtils::AllNodes<UK2Node_CustomEvent>(BP))
{
Result.Appendf(TEXT(" custom event: %s\n"), *CE->CustomFunctionName.ToString());
UMCPServer::Printf(TEXT(" custom event: %s\n"), *CE->CustomFunctionName.ToString());
}
TSet<FName> DelegateNames;
FBlueprintEditorUtils::GetDelegateNameList(BP, DelegateNames);
for (const FName& DN : DelegateNames)
{
Result.Appendf(TEXT(" dispatcher: %s\n"), *DN.ToString());
UMCPServer::Printf(TEXT(" dispatcher: %s\n"), *DN.ToString());
}
return;
}
@@ -126,8 +126,7 @@ public:
{
if (Existing.IsValid() && Existing->PinName.ToString().Equals(ParamName, ESearchCase::IgnoreCase))
{
MCPErrorCallback(Result).SetError(FString::Printf(
TEXT("Parameter '%s' already exists on '%s'"), *ParamName, *FunctionName));
UMCPServer::Printf(TEXT("ERROR: Parameter '%s' already exists on '%s'\n"), *ParamName, *FunctionName);
return;
}
}

View File

@@ -2,8 +2,9 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "MCPServer.h"
#include "Engine/Blueprint.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "UObject/UObjectIterator.h"
@@ -35,7 +36,7 @@ public:
virtual void Handle(FStringBuilderBase& Result) override
{
MCPAssets<UBlueprint> Assets;
if (!Assets.Exact(Blueprint).Errors(Result).ENone().ETwo().Load()) return;
if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return;
UBlueprint* BP = Assets.Object();
// Resolve the interface class
@@ -47,9 +48,8 @@ public:
{
if (IfaceDesc.Interface == InterfaceClass)
{
MCPErrorCallback(Result).SetError(FString::Printf(
TEXT("Interface '%s' is already implemented by this Blueprint."),
*MCPUtils::FormatName(InterfaceClass)));
UMCPServer::Printf(TEXT("ERROR: Interface '%s' is already implemented by this Blueprint.\n"),
*MCPUtils::FormatName(InterfaceClass));
return;
}
}
@@ -58,9 +58,8 @@ public:
bool bAdded = FBlueprintEditorUtils::ImplementNewInterface(BP, InterfacePath);
if (!bAdded)
{
MCPErrorCallback(Result).SetError(FString::Printf(
TEXT("ImplementNewInterface failed for '%s'."),
*MCPUtils::FormatName(InterfaceClass)));
UMCPServer::Printf(TEXT("ERROR: ImplementNewInterface failed for '%s'.\n"),
*MCPUtils::FormatName(InterfaceClass));
return;
}
@@ -95,7 +94,7 @@ private:
// Strategy 2: Try loading as a Blueprint Interface asset
MCPAssets<UBlueprint> IfaceAssets;
if (!IfaceAssets.Exact(Name).AllContent().Errors(Result).ETwo().Load()) return nullptr;
if (!IfaceAssets.Exact(Name).AllContent().ETwo().Load()) return nullptr;
if (!IfaceAssets.Objects().IsEmpty())
{
UClass* GenClass = IfaceAssets.Object()->GeneratedClass;
@@ -103,9 +102,8 @@ private:
return GenClass;
}
MCPErrorCallback(Result).SetError(FString::Printf(
TEXT("Interface '%s' not found. Provide a Blueprint Interface asset name (e.g. 'BPI_MyInterface') or a native UInterface class name."),
*Name));
UMCPServer::Printf(TEXT("ERROR: Interface '%s' not found. Provide a Blueprint Interface asset name (e.g. 'BPI_MyInterface') or a native UInterface class name.\n"),
*Name);
return nullptr;
}
};

View File

@@ -2,8 +2,9 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "MCPServer.h"
#include "Engine/Blueprint.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Blueprint_AddVariable.generated.h"
@@ -45,7 +46,7 @@ public:
virtual void Handle(FStringBuilderBase& Result) override
{
MCPAssets<UBlueprint> Assets;
if (!Assets.Exact(Blueprint).Errors(Result).ENone().ETwo().Load()) return;
if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return;
UBlueprint* BP = Assets.Object();
// Check for duplicate variable name
@@ -54,15 +55,14 @@ public:
{
if (Var.VarName == VarFName)
{
MCPErrorCallback(Result).SetError(FString::Printf(
TEXT("Variable '%s' already exists in %s"), *VariableName, *MCPUtils::FormatName(BP)));
UMCPServer::Printf(TEXT("ERROR: Variable '%s' already exists in %s\n"), *VariableName, *MCPUtils::FormatName(BP));
return;
}
}
// Resolve the type
FEdGraphPinType PinType;
if (!MCPUtils::ResolveTypeFromString(VariableType, PinType, Result))
if (!MCPUtils::ResolveTypeFromString(VariableType, PinType))
return;
if (IsArray)
@@ -71,8 +71,7 @@ public:
// Add the variable
if (!FBlueprintEditorUtils::AddMemberVariable(BP, VarFName, PinType, DefaultValue))
{
MCPErrorCallback(Result).SetError(FString::Printf(
TEXT("Failed to add variable '%s' to %s"), *VariableName, *MCPUtils::FormatName(BP)));
UMCPServer::Printf(TEXT("ERROR: Failed to add variable '%s' to %s\n"), *VariableName, *MCPUtils::FormatName(BP));
return;
}

View File

@@ -2,8 +2,9 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "MCPServer.h"
#include "Engine/Blueprint.h"
#include "EdGraph/EdGraph.h"
#include "EdGraph/EdGraphPin.h"
@@ -47,12 +48,12 @@ public:
virtual void Handle(FStringBuilderBase& Result) override
{
MCPAssets<UBlueprint> Assets;
if (!Assets.Exact(Blueprint).Errors(Result).ENone().ETwo().Load()) return;
if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return;
UBlueprint* BP = Assets.Object();
// Resolve the new type using the shared resolver (supports primitives, structs, enums, and object references)
FEdGraphPinType NewPinType;
if (!MCPUtils::ResolveTypeFromString(NewType, NewPinType, Result))
if (!MCPUtils::ResolveTypeFromString(NewType, NewPinType))
return;
// Find the entry node: K2Node_FunctionEntry in a function graph,
@@ -84,12 +85,11 @@ public:
if (!EntryNode)
{
MCPErrorCallback(Result).SetError(FString::Printf(
TEXT("Function or custom event '%s' not found. Available:"), *FunctionName));
UMCPServer::Printf(TEXT("ERROR: Function or custom event '%s' not found. Available:\n"), *FunctionName);
for (UK2Node_FunctionEntry* FE : MCPUtils::AllNodes<UK2Node_FunctionEntry>(BP))
Result.Appendf(TEXT(" function: %s\n"), *MCPUtils::FormatName(FE->GetGraph()));
UMCPServer::Printf(TEXT(" function: %s\n"), *MCPUtils::FormatName(FE->GetGraph()));
for (UK2Node_CustomEvent* CE : MCPUtils::AllNodes<UK2Node_CustomEvent>(BP))
Result.Appendf(TEXT(" event: %s\n"), *MCPUtils::FormatName(static_cast<UEdGraphNode*>(CE)));
UMCPServer::Printf(TEXT(" event: %s\n"), *MCPUtils::FormatName(static_cast<UEdGraphNode*>(CE)));
return;
}
@@ -106,11 +106,10 @@ public:
if (!FoundPinInfo)
{
MCPErrorCallback(Result).SetError(FString::Printf(
TEXT("Parameter '%s' not found. Available:"), *ParamName));
UMCPServer::Printf(TEXT("ERROR: Parameter '%s' not found. Available:\n"), *ParamName);
for (const TSharedPtr<FUserPinInfo>& PinInfo : EntryNode->UserDefinedPins)
if (PinInfo.IsValid())
Result.Appendf(TEXT(" %s\n"), *PinInfo->PinName.ToString());
UMCPServer::Printf(TEXT(" %s\n"), *PinInfo->PinName.ToString());
return;
}

View File

@@ -3,7 +3,7 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPFetcher.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "Engine/Blueprint.h"
#include "EdGraph/EdGraphPin.h"
@@ -81,7 +81,7 @@ public:
ResolveInput = TypeCategory + TEXT(":") + NewType;
}
if (!MCPUtils::ResolveTypeFromString(ResolveInput, NewPinType, Result))
if (!MCPUtils::ResolveTypeFromString(ResolveInput, NewPinType))
return;
// List affected nodes (get/set nodes for this variable)

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "Engine/Blueprint.h"
#include "Kismet2/KismetEditorUtilities.h"
@@ -88,7 +88,7 @@ public:
virtual void Handle(FStringBuilderBase& Result) override
{
MCPAssets<UBlueprint> Finder;
Finder.Scan<UBlueprint>().Scan<UWorld>().Errors(Result);
Finder.Scan<UBlueprint>().Scan<UWorld>();
if (!Blueprint.IsEmpty())
{
if (!Finder.Exact(Blueprint).ENone().ETwo().Info()) return;

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "MCPPackageMaker.h"
#include "Engine/Blueprint.h"
@@ -36,9 +36,7 @@ public:
virtual void Handle(FStringBuilderBase& Result) override
{
MCPErrorCallback Error(Result);
MCPPackageMaker Maker(AssetPath, Error);
MCPPackageMaker Maker(AssetPath);
if (!Maker.Ok()) return;
// Resolve parent class — try C++ class first, then Blueprint asset
@@ -47,23 +45,23 @@ public:
if (!ParentClassObj)
{
MCPAssets<UBlueprint> ParentAssets;
if (!ParentAssets.Exact(ParentClass).AllContent().Errors(Error).ETwo().Load()) return;
if (!ParentAssets.Exact(ParentClass).AllContent().ETwo().Load()) return;
if (!ParentAssets.Objects().IsEmpty() && ParentAssets.Object()->GeneratedClass)
ParentClassObj = ParentAssets.Object()->GeneratedClass;
}
if (!ParentClassObj)
{
return Error.SetError(FString::Printf(
TEXT("Could not find parent class '%s'. Provide a C++ class name (e.g. 'Actor', 'Pawn') or Blueprint name."),
*ParentClass));
UMCPServer::Printf(TEXT("ERROR: Could not find parent class '%s'. Provide a C++ class name (e.g. 'Actor', 'Pawn') or Blueprint name.\n"),
*ParentClass);
return;
}
// Map blueprintType string to EBlueprintType
EBlueprintType BlueprintTypeEnum = BPTYPE_Normal;
if (!BlueprintType.IsEmpty())
{
if (!MCPUtils::StringToEnum(BlueprintType, BlueprintTypeEnum, Error, TEXT("BPTYPE_"))) return;
if (!MCPUtils::StringToEnum(BlueprintType, BlueprintTypeEnum, TEXT("BPTYPE_"))) return;
}
// For Interface type, parent must be UInterface
@@ -86,7 +84,8 @@ public:
if (!NewBP)
{
return Error.SetError(TEXT("FKismetEditorUtilities::CreateBlueprint returned null"));
UMCPServer::Print(TEXT("ERROR: FKismetEditorUtilities::CreateBlueprint returned null\n"));
return;
}
// Compile and save

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "Engine/Blueprint.h"
#include "EdGraph/EdGraph.h"
@@ -41,11 +41,11 @@ public:
{
// Load both blueprints
MCPAssets<UBlueprint> AssetsA;
if (!AssetsA.Exact(BlueprintA).Errors(Result).ENone().ETwo().Load()) return;
if (!AssetsA.Exact(BlueprintA).ENone().ETwo().Load()) return;
UBlueprint* BPA = AssetsA.Object();
MCPAssets<UBlueprint> AssetsB;
if (!AssetsB.Exact(BlueprintB).Errors(Result).ENone().ETwo().Load()) return;
if (!AssetsB.Exact(BlueprintB).ENone().ETwo().Load()) return;
UBlueprint* BPB = AssetsB.Object();
// Gather graphs, optionally filtering by name

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "Engine/Blueprint.h"
#include "EdGraph/EdGraph.h"
@@ -35,7 +35,7 @@ public:
{
// Load Blueprint
MCPAssets<UBlueprint> Assets;
if (!Assets.Exact(Blueprint).Errors(Result).ENone().ETwo().Load()) return;
if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return;
UBlueprint* BP = Assets.Object();
int32 GraphCount = MCPUtils::AllGraphs(BP).Num();

View File

@@ -3,7 +3,7 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPFetcher.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "K2Node_FunctionEntry.h"
#include "K2Node_CustomEvent.h"
@@ -39,7 +39,7 @@ public:
virtual void Handle(FStringBuilderBase& Result) override
{
MCPAssets<UBlueprint> Assets;
if (!Assets.Exact(Blueprint).Errors(Result).ENone().ETwo().Load()) return;
if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return;
UBlueprint* BP = Assets.Object();
// Find the entry node (function entry or custom event)

View File

@@ -2,8 +2,9 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "MCPServer.h"
#include "Engine/Blueprint.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Blueprint_RemoveInterface.generated.h"
@@ -37,7 +38,7 @@ public:
virtual void Handle(FStringBuilderBase& Result) override
{
MCPAssets<UBlueprint> Assets;
if (!Assets.Exact(Blueprint).Errors(Result).ENone().ETwo().Load()) return;
if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return;
UBlueprint* BP = Assets.Object();
// Find the interface by name
@@ -54,12 +55,11 @@ public:
if (!FoundInterface)
{
MCPErrorCallback(Result).SetError(FString::Printf(
TEXT("Interface '%s' not found. Implemented interfaces: "), *InterfaceName));
UMCPServer::Printf(TEXT("ERROR: Interface '%s' not found. Implemented interfaces:\n"), *InterfaceName);
for (const FBPInterfaceDescription& IfaceDesc : BP->ImplementedInterfaces)
{
if (IfaceDesc.Interface)
Result.Appendf(TEXT(" %s\n"), *MCPUtils::FormatName(IfaceDesc.Interface));
UMCPServer::Printf(TEXT(" %s\n"), *MCPUtils::FormatName(IfaceDesc.Interface));
}
return;
}

View File

@@ -3,8 +3,9 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPFetcher.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "MCPServer.h"
#include "Engine/Blueprint.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Kismet2/KismetEditorUtilities.h"
@@ -36,7 +37,7 @@ public:
{
// Load Blueprint
MCPAssets<UBlueprint> Assets;
if (!Assets.Exact(Blueprint).Errors(Result).ENone().ETwo().Load()) return;
if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return;
UBlueprint* BP = Assets.Object();
FString OldParentName = BP->ParentClass ? MCPUtils::FormatName(BP->ParentClass) : TEXT("None");
@@ -47,16 +48,15 @@ public:
if (!NewParentClassObj)
{
MCPAssets<UBlueprint> ParentAssets;
if (!ParentAssets.Exact(NewParentClass).AllContent().Errors(Result).ETwo().Load()) return;
if (!ParentAssets.Exact(NewParentClass).AllContent().ETwo().Load()) return;
if (!ParentAssets.Objects().IsEmpty() && ParentAssets.Object()->GeneratedClass)
NewParentClassObj = ParentAssets.Object()->GeneratedClass;
}
if (!NewParentClassObj)
{
MCPErrorCallback(Result).SetError(FString::Printf(
TEXT("Could not find class '%s'. Provide a C++ class name or Blueprint name."),
*NewParentClass));
UMCPServer::Printf(TEXT("ERROR: Could not find class '%s'. Provide a C++ class name or Blueprint name.\n"),
*NewParentClass);
return;
}

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "Engine/Blueprint.h"
#include "Engine/World.h"
@@ -39,7 +39,7 @@ public:
virtual void Handle(FStringBuilderBase& Result) override
{
MCPAssets<UObject> Assets;
Assets.NoScans().Substring(Query).Limit(500).Errors(Result);
Assets.NoScans().Substring(Query).Limit(500);
if (IncludeRegular) Assets.Scan<UBlueprint>();
if (IncludeLevel) Assets.Scan<UWorld>();
Assets.Info();

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "Engine/Blueprint.h"
#include "Engine/World.h"

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "Engine/UserDefinedEnum.h"
#include "Kismet2/EnumEditorUtils.h"
@@ -34,9 +34,7 @@ public:
virtual void Handle(FStringBuilderBase& Result) override
{
MCPErrorCallback Error(Result);
MCPPackageMaker Maker(AssetPath, Error);
MCPPackageMaker Maker(AssetPath);
if (!Maker.Ok()) return;
TArray<FString> EnumValues;
@@ -47,7 +45,8 @@ public:
}
if (EnumValues.Num() == 0)
{
return Error.SetError(TEXT("Values must be a non-empty array of strings"));
UMCPServer::Print(TEXT("ERROR: Values must be a non-empty array of strings\n"));
return;
}
// Create the enum using AssetTools.

View File

@@ -61,7 +61,7 @@ public:
for (const TSharedPtr<FJsonValue>& NodeVal : Nodes.Array)
{
FSpawnNodeEntry Entry;
if (!MCPUtils::PopulateFromJson(FSpawnNodeEntry::StaticStruct(), &Entry, NodeVal, Result))
if (!MCPUtils::PopulateFromJson(FSpawnNodeEntry::StaticStruct(), &Entry, NodeVal))
continue;
// Find the action by exact full name

View File

@@ -4,6 +4,7 @@
#include "MCPHandler.h"
#include "MCPFetcher.h"
#include "MCPUtils.h"
#include "MCPServer.h"
#include "EdGraph/EdGraph.h"
#include "EdGraph/EdGraphNode.h"
#include "MaterialGraph/MaterialGraphNode.h"
@@ -43,10 +44,9 @@ public:
if (!FoundNode->CanUserDeleteNode())
{
MCPErrorCallback Error(Result);
return Error.SetError(FString::Printf(
TEXT("Cannot delete node '%s' in graph '%s' — it is not deletable."),
*NodeTitle, *GraphName));
UMCPServer::Printf(TEXT("ERROR: Cannot delete node '%s' in graph '%s' — it is not deletable.\n"),
*NodeTitle, *GraphName);
return;
}
if (Cast<UMaterialGraphNode>(FoundNode))

View File

@@ -95,12 +95,12 @@ public:
UEdGraphNode* Node = F.Node(Entry.Node).Cast<UEdGraphNode>();
if (!Node) return;
MCPProperty P = MCPProperty::GetOneExactMatch(Node, CPF_Edit, Entry.Name, Result);
MCPProperty P = MCPProperty::GetOneExactMatch(Node, CPF_Edit, Entry.Name);
if (!P) return;
UMCPServer::AddTouchedObject(Node);
if (!P.SetText(Entry.Value, Result))
if (!P.SetText(Entry.Value))
return;
}
@@ -126,7 +126,7 @@ public:
for (const TSharedPtr<FJsonValue>& PinVal : Pins.Array)
{
FSetNodeDefaultEntry Entry;
if (!MCPUtils::PopulateFromJson(FSetNodeDefaultEntry::StaticStruct(), &Entry, PinVal, Result))
if (!MCPUtils::PopulateFromJson(FSetNodeDefaultEntry::StaticStruct(), &Entry, PinVal))
continue;
if (K2Schema)

View File

@@ -57,7 +57,7 @@ public:
for (const TSharedPtr<FJsonValue>& NodeVal : Nodes.Array)
{
FMoveNodeEntry Entry;
if (!MCPUtils::PopulateFromJson(FMoveNodeEntry::StaticStruct(), &Entry, NodeVal, Result)) continue;
if (!MCPUtils::PopulateFromJson(FMoveNodeEntry::StaticStruct(), &Entry, NodeVal)) continue;
MCPFetcher FN(BP);
UEdGraphNode* Node = FN.Node(Entry.Node).Cast<UEdGraphNode>();

View File

@@ -57,7 +57,7 @@ public:
for (const TSharedPtr<FJsonValue>& ConnVal : Connections.Array)
{
FConnectPinsEntry Entry;
if (!MCPUtils::PopulateFromJson(FConnectPinsEntry::StaticStruct(), &Entry, ConnVal, Result))
if (!MCPUtils::PopulateFromJson(FConnectPinsEntry::StaticStruct(), &Entry, ConnVal))
continue;
MCPFetcher FS(G);

View File

@@ -57,7 +57,7 @@ public:
for (const TSharedPtr<FJsonValue>& DiscVal : Disconnections.Array)
{
FDisconnectPinEntry Entry;
if (!MCPUtils::PopulateFromJson(FDisconnectPinEntry::StaticStruct(), &Entry, DiscVal, Result)) continue;
if (!MCPUtils::PopulateFromJson(FDisconnectPinEntry::StaticStruct(), &Entry, DiscVal)) continue;
MCPFetcher FP(G);
UEdGraphPin* Pin = FP.Walk(Entry.Pin).Cast<UEdGraphPin>();

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "Materials/MaterialFunction.h"
#include "Factories/MaterialFunctionFactoryNew.h"
@@ -33,9 +33,7 @@ public:
virtual void Handle(FStringBuilderBase& Result) override
{
MCPErrorCallback Error(Result);
MCPPackageMaker Maker(AssetPath, Error);
MCPPackageMaker Maker(AssetPath);
if (!Maker.Ok()) return;
// Create via IAssetTools + factory.

View File

@@ -44,7 +44,7 @@ public:
// Parse the association string.
EMaterialParameterAssociation Association;
if (!MCPUtils::ParseMaterialParameterAssociation(ParameterAssociation, Association, Result))
if (!MCPUtils::ParseMaterialParameterAssociation(ParameterAssociation, Association))
return;
FMaterialParameterInfo ParamID(*Parameter, Association, ParameterLayer);

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "Materials/Material.h"
#include "Materials/MaterialInterface.h"
@@ -35,9 +35,7 @@ public:
virtual void Handle(FStringBuilderBase& Result) override
{
MCPErrorCallback Error(Result);
MCPPackageMaker Maker(AssetPath, Error);
MCPPackageMaker Maker(AssetPath);
if (!Maker.Ok()) return;
// Load parent material -- try as Material first, then as Material Instance.

View File

@@ -48,7 +48,7 @@ public:
// Parse the association string.
EMaterialParameterAssociation Association;
if (!MCPUtils::ParseMaterialParameterAssociation(ParameterAssociation, Association, Result))
if (!MCPUtils::ParseMaterialParameterAssociation(ParameterAssociation, Association))
return;
// Build the parameter ID to look up.

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "Materials/Material.h"
#include "MaterialDomain.h"
@@ -40,23 +40,21 @@ public:
virtual void Handle(FStringBuilderBase& Result) override
{
MCPErrorCallback Error(Result);
MCPPackageMaker Maker(AssetPath, Error);
MCPPackageMaker Maker(AssetPath);
if (!Maker.Ok()) return;
// Parse optional enum properties before creating the asset.
EMaterialDomain ParsedDomain = MD_Surface;
if (!Domain.IsEmpty())
{
if (!MCPUtils::StringToEnum(Domain, ParsedDomain, Error, TEXT("MD_")))
if (!MCPUtils::StringToEnum(Domain, ParsedDomain, TEXT("MD_")))
return;
}
EBlendMode ParsedBlendMode = BLEND_Opaque;
if (!BlendMode.IsEmpty())
{
if (!MCPUtils::StringToEnum(BlendMode, ParsedBlendMode, Error, TEXT("BLEND_")))
if (!MCPUtils::StringToEnum(BlendMode, ParsedBlendMode, TEXT("BLEND_")))
return;
}

View File

@@ -35,7 +35,7 @@ public:
UObject* Template = F.Walk(Path).Template().Cast<UObject>();
if (!Template) return;
MCPProperty P = MCPProperty::GetOneExactMatch(Template, CPF_Edit, Property, Result);
MCPProperty P = MCPProperty::GetOneExactMatch(Template, CPF_Edit, Property);
if (!P) return;
Result.Append(P.GetText());

View File

@@ -47,7 +47,7 @@ public:
TArray<TPair<MCPProperty, FString>> Resolved;
for (const auto& Pair : Properties.Json->Values)
{
MCPProperty P = MCPProperty::GetOneExactMatch(Template, CPF_Edit, Pair.Key, Result);
MCPProperty P = MCPProperty::GetOneExactMatch(Template, CPF_Edit, Pair.Key);
if (!P) return;
FString ValueStr;
@@ -63,7 +63,7 @@ public:
int32 SuccessCount = 0;
for (auto& [P, ValueStr] : Resolved)
{
if (!P.SetText(ValueStr, Result))
if (!P.SetText(ValueStr))
continue;
SuccessCount++;
}

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPFetcher.h"
#include "MCPUtils.h"
#include "EdGraph/EdGraph.h"
@@ -65,7 +65,7 @@ public:
}
// Check for duplicate state name
if (MCPUtils::FindStateByName(SMGraph, StateName, nullptr))
if (MCPUtils::FindStateByName(SMGraph, StateName))
{
Result.Appendf(TEXT("ERROR: State '%s' already exists in %s\n"), *StateName, *MCPUtils::FormatName(SMGraph));
return;
@@ -94,7 +94,7 @@ public:
if (!AnimationAsset.IsEmpty() && NewState->GetBoundGraph())
{
MCPAssets<UAnimSequence> AnimAssets;
if (!AnimAssets.Exact(AnimationAsset).Errors(Result).ENone().ETwo().Load()) return;
if (!AnimAssets.Exact(AnimationAsset).ENone().ETwo().Load()) return;
UAnimGraphNode_SequencePlayer* SeqNode = NewObject<UAnimGraphNode_SequencePlayer>(NewState->GetBoundGraph());
SeqNode->CreateNewGuid();

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "Animation/AnimBlueprint.h"
@@ -51,7 +51,7 @@ public:
virtual void Handle(FStringBuilderBase& Result) override
{
MCPAssets<UAnimBlueprint> Assets;
if (!Assets.Exact(Blueprint).Errors(Result).ENone().ETwo().Load()) return;
if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return;
UAnimBlueprint* AnimBP = Assets.Object();
UAnimationStateMachineGraph* SMGraph = MCPUtils::FindStateMachineGraph(AnimBP, Graph);
@@ -61,10 +61,10 @@ public:
return;
}
UAnimStateNode* FromStateNode = MCPUtils::FindStateByName(SMGraph, FromState, Result);
UAnimStateNode* FromStateNode = MCPUtils::FindStateByName(SMGraph, FromState);
if (!FromStateNode) return;
UAnimStateNode* ToStateNode = MCPUtils::FindStateByName(SMGraph, ToState, Result);
UAnimStateNode* ToStateNode = MCPUtils::FindStateByName(SMGraph, ToState);
if (!ToStateNode) return;
// Create transition node

View File

@@ -52,7 +52,7 @@ public:
}
// Find the state node
UAnimStateNode* StateNode = MCPUtils::FindStateByName(SMGraph, StateName, Result);
UAnimStateNode* StateNode = MCPUtils::FindStateByName(SMGraph, StateName);
if (!StateNode) return;
// Collect and remove transitions connected to this state

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPFetcher.h"
#include "MCPUtils.h"
#include "EdGraph/EdGraph.h"
@@ -59,7 +59,7 @@ public:
}
// Find the target state
UAnimStateNode* StateNode = MCPUtils::FindStateByName(SMGraph, StateName, Result);
UAnimStateNode* StateNode = MCPUtils::FindStateByName(SMGraph, StateName);
if (!StateNode) return;
UEdGraph* InnerGraph = StateNode->GetBoundGraph();
@@ -71,7 +71,7 @@ public:
// Find the animation asset
MCPAssets<UAnimSequence> AnimAssets;
if (!AnimAssets.Exact(AnimationAsset).Errors(Result).ENone().ETwo().Load()) return;
if (!AnimAssets.Exact(AnimationAsset).ENone().ETwo().Load()) return;
UAnimSequence* AnimSeq = AnimAssets.Object();
// Find existing SequencePlayer or create one

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPFetcher.h"
#include "MCPUtils.h"
#include "EdGraph/EdGraph.h"
@@ -57,14 +57,14 @@ public:
{
// Load the anim blueprint
MCPAssets<UAnimBlueprint> Assets;
if (!Assets.Exact(Blueprint).Errors(Result).ENone().ETwo().Load()) return;
if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return;
UAnimBlueprint* AnimBP = Assets.Object();
// Find the state machine graph and state
UAnimationStateMachineGraph* SMGraph = MCPUtils::FindStateMachineGraph(AnimBP, Graph);
if (!SMGraph) { Result.Appendf(TEXT("ERROR: State machine graph '%s' not found\n"), *Graph); return; }
UAnimStateNode* StateNode = MCPUtils::FindStateByName(SMGraph, StateName, Result);
UAnimStateNode* StateNode = MCPUtils::FindStateByName(SMGraph, StateName);
if (!StateNode) return;
UEdGraph* InnerGraph = StateNode->GetBoundGraph();
@@ -72,7 +72,7 @@ public:
// Load the blend space asset
MCPAssets<UBlendSpace> BlendSpaceAssets;
if (!BlendSpaceAssets.Exact(BlendSpace).Errors(Result).ENone().ETwo().Load()) return;
if (!BlendSpaceAssets.Exact(BlendSpace).ENone().ETwo().Load()) return;
UBlendSpace* BlendSpaceAsset = BlendSpaceAssets.Object();
// Find existing BlendSpacePlayer or create one

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "Animation/AnimBlueprint.h"
@@ -56,7 +56,7 @@ public:
virtual void Handle(FStringBuilderBase& Result) override
{
MCPAssets<UAnimBlueprint> Assets;
if (!Assets.Exact(Blueprint).Errors(Result).ENone().ETwo().Load()) return;
if (!Assets.Exact(Blueprint).ENone().ETwo().Load()) return;
UAnimBlueprint* AnimBP = Assets.Object();
UAnimationStateMachineGraph* SMGraph = MCPUtils::FindStateMachineGraph(AnimBP, Graph);

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPUtils.h"
#include "StructUtils/UserDefinedStruct.h"
#include "Kismet2/BlueprintEditorUtils.h"
@@ -47,9 +47,7 @@ public:
virtual void Handle(FStringBuilderBase& Result) override
{
MCPErrorCallback Error(Result);
MCPPackageMaker Maker(AssetPath, Error);
MCPPackageMaker Maker(AssetPath);
if (!Maker.Ok()) return;
// Create the struct using the AssetTools factory.
@@ -61,11 +59,11 @@ public:
for (const TSharedPtr<FJsonValue>& PropVal : Properties.Array)
{
FStructPropertyEntry Entry;
if (!MCPUtils::PopulateFromJson(FStructPropertyEntry::StaticStruct(), &Entry, PropVal, Result)) return;
if (!MCPUtils::PopulateFromJson(FStructPropertyEntry::StaticStruct(), &Entry, PropVal)) return;
if (Entry.Name.IsEmpty() || Entry.Type.IsEmpty()) continue;
FEdGraphPinType PinType;
if (!MCPUtils::ResolveTypeFromString(Entry.Type, PinType, Result))
if (!MCPUtils::ResolveTypeFromString(Entry.Type, PinType))
continue;
// Snapshot existing GUIDs so we can find the newly added one.

View File

@@ -1,4 +1,5 @@
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "MCPServer.h"
#include "Engine/Blueprint.h"
#include "Engine/World.h"
#include "Engine/Level.h"
@@ -46,12 +47,6 @@ MCPAssetsBase& MCPAssetsBase::AllContent()
return *this;
}
MCPAssetsBase& MCPAssetsBase::Errors(MCPErrorCallback InCB)
{
ErrorCB = InCB;
return *this;
}
bool MCPAssetsBase::Info()
{
@@ -171,5 +166,5 @@ void MCPAssetsBase::SetError(const FString &Msg)
{
AssetResults.Empty();
UObjectResults.Empty();
ErrorCB.SetError(Msg);
UMCPServer::Printf(TEXT("ERROR: %s\n"), *Msg);
}

View File

@@ -1,5 +1,6 @@
#include "MCPProperty.h"
#include "MCPUtils.h"
#include "MCPServer.h"
#include "Materials/MaterialExpression.h"
#include "MaterialGraph/MaterialGraphNode.h"
@@ -14,14 +15,14 @@ FString MCPProperty::GetText() const
return Result;
}
bool MCPProperty::SetText(const FString& Value, MCPErrorCallback Error)
bool MCPProperty::SetText(const FString& Value)
{
void* ValuePtr = Prop->ContainerPtrToValuePtr<void>(Container);
const TCHAR* ImportResult = Prop->ImportText_Direct(*Value, ValuePtr, nullptr, PPF_None);
if (!ImportResult)
{
Error.SetError(FString::Printf(TEXT("Failed to parse '%s' for property '%s' (type: %s)"),
*Value, *MCPUtils::FormatName(Prop), *Prop->GetCPPType()));
UMCPServer::Printf(TEXT("ERROR: Failed to parse '%s' for property '%s' (type: %s)\n"),
*Value, *MCPUtils::FormatName(Prop), *Prop->GetCPPType());
return false;
}
@@ -82,19 +83,19 @@ TArray<MCPProperty> MCPProperty::GetAllExactMatch(UObject* Obj, EPropertyFlags F
return Result;
}
MCPProperty MCPProperty::GetOneExactMatch(UObject* Obj, EPropertyFlags Flags, const FString& Name, MCPErrorCallback Error)
MCPProperty MCPProperty::GetOneExactMatch(UObject* Obj, EPropertyFlags Flags, const FString& Name)
{
TArray<MCPProperty> Matches = GetAllExactMatch(Obj, Flags, Name);
if (Matches.Num() == 0)
{
Error.SetError(FString::Printf(TEXT("Property '%s' not found on %s"),
*Name, *MCPUtils::FormatName(Obj->GetClass())));
UMCPServer::Printf(TEXT("ERROR: Property '%s' not found on %s\n"),
*Name, *MCPUtils::FormatName(Obj->GetClass()));
return MCPProperty();
}
if (Matches.Num() > 1)
{
Error.SetError(FString::Printf(TEXT("Ambiguous property '%s' on %s"),
*Name, *MCPUtils::FormatName(Obj->GetClass())));
UMCPServer::Printf(TEXT("ERROR: Ambiguous property '%s' on %s\n"),
*Name, *MCPUtils::FormatName(Obj->GetClass()));
return MCPProperty();
}
return Matches[0];

View File

@@ -2,7 +2,7 @@
#include "MCPHandler.h"
#include "LogCapture.h"
#include "MCPUtils.h"
#include "MCPAssetFinder.h"
#include "MCPAssets.h"
#include "UObject/StrongObjectPtr.h"
#include "Materials/MaterialExpression.h"
#include "AssetRegistry/AssetRegistryModule.h"
@@ -289,12 +289,14 @@ FString UMCPServer::HandleRequest(const FString& Line)
IMCPHandler* Handler = Cast<IMCPHandler>(HandlerObj.Get());
// Populate the handler object with the request parameters.
TStringBuilder<4096> PopulateError;
if (!MCPUtils::PopulateFromJson(HandlerObj->GetClass(), HandlerObj.Get(), &*Request, PopulateError))
HandlerOutput.Reset();
if (!MCPUtils::PopulateFromJson(HandlerObj->GetClass(), HandlerObj.Get(), &*Request))
{
PopulateError.Append(TEXT("\nUsage:\n"));
MCPUtils::FormatCommandHelp(*HandlerClass, PopulateError);
return PopulateError.ToString();
HandlerOutput.Append(TEXT("\nUsage:\n"));
MCPUtils::FormatCommandHelp(*HandlerClass, HandlerOutput);
FString Result = HandlerOutput.ToString();
HandlerOutput.Reset();
return Result;
}
// Invoke the handler with log capture.

View File

@@ -1,4 +1,5 @@
#include "MCPUtils.h"
#include "MCPServer.h"
#include "MCPHandler.h"
#include "Dom/JsonValue.h"
#include "Serialization/JsonReader.h"
@@ -90,22 +91,6 @@ extern int32 TrySavePackageSEH(
FSavePackageArgs* SaveArgs, ESavePackageResult* OutResult);
#endif
// ============================================================
// MCPErrorCallback
// ============================================================
MCPErrorCallback::MCPErrorCallback(std::nullptr_t)
: Func([](const FString&) {})
{}
MCPErrorCallback::MCPErrorCallback(FString& OutError)
: Func([&OutError](const FString& Msg) { OutError = Msg; })
{}
MCPErrorCallback::MCPErrorCallback(FStringBuilderBase& OutResult)
: Func([&OutResult](const FString& Msg) { OutResult.Appendf(TEXT("ERROR: %s\n"), *Msg); })
{}
// ============================================================
// Name Formatting
// ============================================================
@@ -343,12 +328,12 @@ FString MCPUtils::EnumToString(UEnum* Enum, int64 Value, const FString& Prefix)
return Full;
}
bool MCPUtils::StringToEnum(UEnum* Enum, const FString& Str, int64& OutValue, MCPErrorCallback Error, const FString& Prefix)
bool MCPUtils::StringToEnum(UEnum* Enum, const FString& Str, int64& OutValue, 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()));
UMCPServer::Printf(TEXT("ERROR: Invalid value '%s' for %s\n"), *Str, *Enum->GetName());
return false;
}
return true;
@@ -541,7 +526,7 @@ UClass* MCPUtils::FindClassByName(const FString& ClassName)
}
bool MCPUtils::ResolveTypeFromString(
const FString& TypeName, FEdGraphPinType& OutPinType, MCPErrorCallback Error)
const FString& TypeName, FEdGraphPinType& OutPinType)
{
FString TypeLower = TypeName.ToLower();
@@ -614,7 +599,7 @@ bool MCPUtils::ResolveTypeFromString(
UClass* FoundClass = FindClassByName(ClassName);
if (!FoundClass)
{
Error.SetError(FString::Printf(TEXT("Class '%s' not found for object reference type"), *ClassName));
UMCPServer::Printf(TEXT("ERROR: Class '%s' not found for object reference type\n"), *ClassName);
return false;
}
OutPinType.PinCategory = UEdGraphSchema_K2::PC_Object;
@@ -626,7 +611,7 @@ bool MCPUtils::ResolveTypeFromString(
UClass* FoundClass = FindClassByName(ClassName);
if (!FoundClass)
{
Error.SetError(FString::Printf(TEXT("Class '%s' not found for soft object reference type"), *ClassName));
UMCPServer::Printf(TEXT("ERROR: Class '%s' not found for soft object reference type\n"), *ClassName);
return false;
}
OutPinType.PinCategory = UEdGraphSchema_K2::PC_SoftObject;
@@ -638,7 +623,7 @@ bool MCPUtils::ResolveTypeFromString(
UClass* FoundClass = FindClassByName(ClassName);
if (!FoundClass)
{
Error.SetError(FString::Printf(TEXT("Class '%s' not found for class reference type (TSubclassOf)"), *ClassName));
UMCPServer::Printf(TEXT("ERROR: Class '%s' not found for class reference type (TSubclassOf)\n"), *ClassName);
return false;
}
OutPinType.PinCategory = UEdGraphSchema_K2::PC_Class;
@@ -650,7 +635,7 @@ bool MCPUtils::ResolveTypeFromString(
UClass* FoundClass = FindClassByName(ClassName);
if (!FoundClass)
{
Error.SetError(FString::Printf(TEXT("Class '%s' not found for soft class reference type"), *ClassName));
UMCPServer::Printf(TEXT("ERROR: Class '%s' not found for soft class reference type\n"), *ClassName);
return false;
}
OutPinType.PinCategory = UEdGraphSchema_K2::PC_SoftClass;
@@ -662,7 +647,7 @@ bool MCPUtils::ResolveTypeFromString(
UClass* FoundClass = FindClassByName(ClassName);
if (!FoundClass)
{
Error.SetError(FString::Printf(TEXT("Class '%s' not found for interface reference type"), *ClassName));
UMCPServer::Printf(TEXT("ERROR: Class '%s' not found for interface reference type\n"), *ClassName);
return false;
}
OutPinType.PinCategory = UEdGraphSchema_K2::PC_Interface;
@@ -738,9 +723,9 @@ bool MCPUtils::ResolveTypeFromString(
}
else
{
Error.SetError(FString::Printf(
TEXT("Unknown type '%s'. Use: bool, int, float, string, name, text, byte, vector, rotator, transform, object, a struct/enum name (e.g. FVector, EMyEnum), or colon syntax for references (object:Actor, softobject:Actor, class:Actor, softclass:Actor, interface:MyInterface)"),
*TypeName));
UMCPServer::Printf(
TEXT("ERROR: Unknown type '%s'. Use: bool, int, float, string, name, text, byte, vector, rotator, transform, object, a struct/enum name (e.g. FVector, EMyEnum), or colon syntax for references (object:Actor, softobject:Actor, class:Actor, softclass:Actor, interface:MyInterface)\n"),
*TypeName);
return false;
}
}
@@ -798,54 +783,6 @@ UMaterial* MCPUtils::ReplaceMaterialWithTransientCopy(UMaterial* Material)
return Material;
}
// ============================================================
// PreEdit / PostEdit
// ============================================================
void MCPUtils::PreEdit(const TArray<UObject*>& Objects)
{
for (UObject* Obj : Objects)
Obj->PreEditChange(nullptr);
}
void MCPUtils::PostEdit(const TArray<UObject*>& Objects)
{
TSet<UEdGraphNode*> Nodes;
TSet<UEdGraph*> Graphs;
TSet<UMaterial*> Materials;
TSet<UBlueprint*> Blueprints;
for (int32 i = Objects.Num() - 1; i >= 0; --i)
{
UObject* Obj = Objects[i];
Obj->PostEditChange();
Obj->MarkPackageDirty();
if (UEdGraphNode* Node = Cast<UEdGraphNode>(Obj))
Nodes.Add(Node);
if (UEdGraph* Graph = Cast<UEdGraph>(Obj))
Graphs.Add(Graph);
if (UBlueprint* BP = Cast<UBlueprint>(Obj))
Blueprints.Add(BP);
if (UMaterialInterface* MatIface = Cast<UMaterialInterface>(Obj))
if (UMaterial* BaseMat = MatIface->GetMaterial())
Materials.Add(BaseMat);
}
for (UEdGraphNode* Node : Nodes)
Node->ReconstructNode();
for (UEdGraph* Graph : Graphs)
Graph->NotifyGraphChanged();
for (UMaterial *Material : Materials)
UMaterialEditingLibrary::RebuildMaterialInstanceEditors(Material);
for (UBlueprint *Blueprint : Blueprints)
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint);
if (GEditor)
GEditor->RedrawAllViewports();
}
TMap<FMaterialParameterInfo, FMaterialParameterMetadata> MCPUtils::GetMaterialParameters(UMaterialInterface* Material)
{
@@ -860,7 +797,7 @@ TMap<FMaterialParameterInfo, FMaterialParameterMetadata> MCPUtils::GetMaterialPa
return Result;
}
bool MCPUtils::ParseMaterialParameterAssociation(const FString& Str, EMaterialParameterAssociation& OutAssociation, MCPErrorCallback Error)
bool MCPUtils::ParseMaterialParameterAssociation(const FString& Str, EMaterialParameterAssociation& OutAssociation)
{
if (Str.Equals(TEXT("Global"), ESearchCase::IgnoreCase))
OutAssociation = GlobalParameter;
@@ -870,7 +807,7 @@ bool MCPUtils::ParseMaterialParameterAssociation(const FString& Str, EMaterialPa
OutAssociation = BlendParameter;
else
{
Error.SetError(FString::Printf(TEXT("Invalid ParameterAssociation '%s' (expected 'Global', 'Layer', or 'Blend')"), *Str));
UMCPServer::Printf(TEXT("ERROR: Invalid ParameterAssociation '%s' (expected 'Global', 'Layer', or 'Blend')\n"), *Str);
return false;
}
return true;
@@ -984,7 +921,7 @@ UAnimationStateMachineGraph* MCPUtils::FindStateMachineGraph(UBlueprint* BP, con
return nullptr;
}
UAnimStateNode* MCPUtils::FindStateByName(UAnimationStateMachineGraph* SMGraph, const FString& StateName, MCPErrorCallback Error)
UAnimStateNode* MCPUtils::FindStateByName(UAnimationStateMachineGraph* SMGraph, const FString& StateName)
{
for (UEdGraphNode* Node : SMGraph->Nodes)
{
@@ -996,7 +933,7 @@ UAnimStateNode* MCPUtils::FindStateByName(UAnimationStateMachineGraph* SMGraph,
}
}
}
Error.SetError(FString::Printf(TEXT("State '%s' not found in graph '%s'"), *StateName, *SMGraph->GetName()));
UMCPServer::Printf(TEXT("ERROR: State '%s' not found in graph '%s'\n"), *StateName, *SMGraph->GetName());
return nullptr;
}
@@ -1229,22 +1166,20 @@ FString MCPUtils::SetPropertyFromJson(
bool MCPUtils::PopulateFromJson(
UStruct* StructType,
void* Container,
const TSharedPtr<FJsonValue>& JsonValue,
MCPErrorCallback Error)
const TSharedPtr<FJsonValue>& JsonValue)
{
if (!JsonValue.IsValid() || (JsonValue->Type != EJson::Object))
{
Error.SetError(TEXT("Expected a JSON object"));
UMCPServer::Print(TEXT("ERROR: Expected a JSON object\n"));
return false;
}
return PopulateFromJson(StructType, Container, JsonValue->AsObject().Get(), Error);
return PopulateFromJson(StructType, Container, JsonValue->AsObject().Get());
}
bool MCPUtils::PopulateFromJson(
UStruct* StructType,
void* Container,
const FJsonObject* Json,
MCPErrorCallback Error)
const FJsonObject* Json)
{
// Build a set of known property names (as JSON keys) for the unknown-field check.
TSet<FString> KnownKeys;
@@ -1262,7 +1197,7 @@ bool MCPUtils::PopulateFromJson(
{
if (!KnownKeys.Contains(KV.Key))
{
Error.SetError(FString::Printf(TEXT("Unknown parameter '%s'"), *KV.Key));
UMCPServer::Printf(TEXT("ERROR: Unknown parameter '%s'\n"), *KV.Key);
return false;
}
}
@@ -1277,7 +1212,7 @@ bool MCPUtils::PopulateFromJson(
{
if (!bOptional)
{
Error.SetError(FString::Printf(TEXT("Missing required parameter '%s'"), *JsonKey));
UMCPServer::Printf(TEXT("ERROR: Missing required parameter '%s'\n"), *JsonKey);
return false;
}
continue;
@@ -1286,7 +1221,7 @@ bool MCPUtils::PopulateFromJson(
FString PropError = SetPropertyFromJson(Container, Prop, JsonKey, Json);
if (!PropError.IsEmpty())
{
Error.SetError(PropError);
UMCPServer::Printf(TEXT("ERROR: %s\n"), *PropError);
return false;
}
}
@@ -1369,11 +1304,11 @@ FString MCPUtils::FormatPropertyType(FProperty* Prop)
// FindPropertyByName
// ============================================================
FProperty* MCPUtils::FindPropertyByName(UObject* Obj, const FString& Name, MCPErrorCallback Error)
FProperty* MCPUtils::FindPropertyByName(UObject* Obj, const FString& Name)
{
if (!Obj)
{
Error.SetError(TEXT("Object is null"));
UMCPServer::Print(TEXT("ERROR: Object is null\n"));
return nullptr;
}
@@ -1383,14 +1318,14 @@ FProperty* MCPUtils::FindPropertyByName(UObject* Obj, const FString& Name, MCPEr
if (!Identifies(Name, *PropIt)) continue;
if (Found)
{
Error.SetError(FString::Printf(TEXT("Ambiguous property '%s' on %s"), *Name, *FormatName(Obj->GetClass())));
UMCPServer::Printf(TEXT("ERROR: Ambiguous property '%s' on %s\n"), *Name, *FormatName(Obj->GetClass()));
return nullptr;
}
Found = *PropIt;
}
if (!Found)
Error.SetError(FString::Printf(TEXT("Property '%s' not found on %s"), *Name, *FormatName(Obj->GetClass())));
UMCPServer::Printf(TEXT("ERROR: Property '%s' not found on %s\n"), *Name, *FormatName(Obj->GetClass()));
return Found;
}
@@ -1411,27 +1346,27 @@ FString MCPUtils::GetPropertyValueText(UObject* Container, FProperty* Prop)
// SetPropertyValueText
// ============================================================
bool MCPUtils::SetPropertyValueText(UObject* Container, FProperty* Prop, const FString& Value, MCPErrorCallback Error)
bool MCPUtils::SetPropertyValueText(UObject* Container, FProperty* Prop, const FString& Value)
{
void* ValuePtr = Prop->ContainerPtrToValuePtr<void>(Container);
const TCHAR* ImportResult = Prop->ImportText_Direct(*Value, ValuePtr, Container, PPF_None);
if (!ImportResult)
{
Error.SetError(FString::Printf(TEXT("Failed to parse '%s' for property '%s' (type: %s)"),
*Value, *FormatName(Prop), *Prop->GetCPPType()));
UMCPServer::Printf(TEXT("ERROR: Failed to parse '%s' for property '%s' (type: %s)\n"),
*Value, *FormatName(Prop), *Prop->GetCPPType());
return false;
}
return true;
}
bool MCPUtils::SetPropertyValueText(void* Container, FProperty* Prop, const FString& Value, UObject* Owner, MCPErrorCallback Error)
bool MCPUtils::SetPropertyValueText(void* Container, FProperty* Prop, const FString& Value, UObject* Owner)
{
void* ValuePtr = Prop->ContainerPtrToValuePtr<void>(Container);
const TCHAR* ImportResult = Prop->ImportText_Direct(*Value, ValuePtr, Owner, PPF_None);
if (!ImportResult)
{
Error.SetError(FString::Printf(TEXT("Failed to parse '%s' for property '%s' (type: %s)"),
*Value, *FormatName(Prop), *Prop->GetCPPType()));
UMCPServer::Printf(TEXT("ERROR: Failed to parse '%s' for property '%s' (type: %s)\n"),
*Value, *FormatName(Prop), *Prop->GetCPPType());
return false;
}
return true;

View File

@@ -60,12 +60,7 @@ struct FARFilter;
// Assets.EAny() - it's an error if anything is found
// Assets.ETwo() - it's an error if two or more are found
//
// Errors can be stored in variables, or in string builders,
// or in json trees. This example tells it to put error
// messages into a string variable:
//
// FString ErrorMessage;
// Assets.Errors(ErrorMessage)
// Errors are reported via UMCPServer::Printf.
//
// Once the Assets object is configured, it's time to scan
// the assets. Use 'Info' if you just want to see
@@ -87,7 +82,7 @@ struct FARFilter;
//
// MCPAssets configuration methods can be chained:
//
// Assets.Limit(100).Errors(ErrMsg).ENone().ETwo();
// Assets.Limit(100).ENone().ETwo();
//
////////////////////////////////////////////////////////////
@@ -102,7 +97,6 @@ public:
MCPAssetsBase& Limit(int32 Count) { MaxResults = Count; return *this; }
MCPAssetsBase& NoDerived();
MCPAssetsBase& AllContent();
MCPAssetsBase& Errors(MCPErrorCallback InCB);
MCPAssetsBase& EAny() { bErrorIfAny = true; return *this; }
MCPAssetsBase& ENone() { bErrorIfNone = true; return *this; }
MCPAssetsBase& ETwo() { bErrorIfTwo = true; return *this; }
@@ -135,7 +129,6 @@ protected:
bool bErrorIfNone = false;
bool bErrorIfTwo = false;
int32 MaxResults = 50;
MCPErrorCallback ErrorCB = MCPErrorCallback(nullptr);
};

View File

@@ -1,7 +1,7 @@
#pragma once
#include "CoreMinimal.h"
#include "MCPUtils.h"
#include "MCPServer.h"
#include "UObject/Package.h"
#include "AssetToolsModule.h"
#include "IAssetTools.h"
@@ -12,14 +12,13 @@
class MCPPackageMaker
{
public:
MCPPackageMaker(const FString& InFullPath, MCPErrorCallback InError)
MCPPackageMaker(const FString& InFullPath)
: FullPath(InFullPath)
, Error(InError)
{
// Path must start with /Game.
if (!FullPath.StartsWith(TEXT("/Game")))
{
Error.SetError(FString::Printf(TEXT("Package path '%s' must start with '/Game'"), *FullPath));
UMCPServer::Printf(TEXT("ERROR: Package path '%s' must start with '/Game'\n"), *FullPath);
bError = true;
return;
}
@@ -27,7 +26,7 @@ public:
// Check for an existing asset at this path.
if (FindObject<UPackage>(nullptr, *FullPath))
{
Error.SetError(FString::Printf(TEXT("An asset already exists at '%s'"), *FullPath));
UMCPServer::Printf(TEXT("ERROR: An asset already exists at '%s'\n"), *FullPath);
bError = true;
return;
}
@@ -41,7 +40,7 @@ public:
Pkg = CreatePackage(*FullPath);
if (!Pkg)
{
Error.SetError(FString::Printf(TEXT("Failed to create package at '%s'"), *FullPath));
UMCPServer::Printf(TEXT("ERROR: Failed to create package at '%s'\n"), *FullPath);
bError = true;
return false;
}
@@ -63,7 +62,7 @@ public:
AssetClass* Result = Cast<AssetClass>(NewAsset);
if (!Result)
{
Error.SetError(FString::Printf(TEXT("Failed to create asset at '%s'"), *FullPath));
UMCPServer::Printf(TEXT("ERROR: Failed to create asset at '%s'\n"), *FullPath);
bError = true;
}
return Result;
@@ -71,7 +70,6 @@ public:
private:
FString FullPath;
MCPErrorCallback Error;
UPackage* Pkg = nullptr;
bool bError = false;
};

View File

@@ -14,7 +14,7 @@ struct MCPProperty
MCPProperty(FProperty* InProp, void* Container);
FString GetText() const;
bool SetText(const FString& Value, MCPErrorCallback Error = nullptr);
bool SetText(const FString& Value);
explicit operator bool() const { return Prop != nullptr; }
FProperty* operator->() const { return Prop; }
@@ -22,5 +22,5 @@ struct MCPProperty
static TArray<MCPProperty> GetAll(UObject* Obj, EPropertyFlags Flags);
static TArray<MCPProperty> GetAllSubstring(UObject* Obj, EPropertyFlags Flags, const FString& Substring);
static TArray<MCPProperty> GetAllExactMatch(UObject* Obj, EPropertyFlags Flags, const FString& Name);
static MCPProperty GetOneExactMatch(UObject* Obj, EPropertyFlags Flags, const FString& Name, MCPErrorCallback Error);
static MCPProperty GetOneExactMatch(UObject* Obj, EPropertyFlags Flags, const FString& Name);
};

View File

@@ -39,12 +39,12 @@ public:
// FTickableEditorObject
virtual void Tick(float DeltaTime) override;
/** Static entry point for commandlet mode (no FTickableEditorObject). */
static void TickServer(float DeltaTime);
virtual bool IsTickable() const override;
virtual TStatId GetStatId() const override;
/** Tick entry point for commandlet mode (no FTickableEditorObject). */
static void TickServer(float DeltaTime);
/** Track an object that has been modified by the current handler. */
static void AddTouchedObject(UObject* Obj) { GMCPServer->Notifier.AddTouchedObject(Obj); }

View File

@@ -30,20 +30,6 @@ class UEnum;
struct FMemberReference;
struct FBPVariableDescription;
// ----- Error callback -----
struct MCPErrorCallback
{
TFunction<void(const FString&)> Func;
MCPErrorCallback(std::nullptr_t);
MCPErrorCallback(FString& OutError);
MCPErrorCallback(FStringBuilderBase& OutResult);
void SetError(const FString& Msg) const { Func(Msg); }
};
// Stateless utility functions used by MCP handlers and the MCP server.
// This is effectively a namespace — all methods are static.
class MCPUtils
@@ -113,7 +99,7 @@ public:
// ----- Enum helpers -----
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());
static bool StringToEnum(UEnum* Enum, const FString& Str, int64& OutValue, const FString& Prefix = FString());
template<typename T>
static FString EnumToString(TEnumAsByte<T> Value, const FString& Prefix = FString())
@@ -124,8 +110,8 @@ public:
{ return EnumToString(StaticEnum<T>(), (int64)Value, Prefix); }
template<typename T>
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; }
static bool StringToEnum(const FString& Str, T& OutValue, const FString& Prefix = FString())
{ int64 V; if (!StringToEnum(StaticEnum<T>(), Str, V, Prefix)) return false; OutValue = (T)V; return true; }
// ----- Blueprint helpers -----
static TArray<UEdGraph*> AllGraphs(UBlueprint* BP);
@@ -152,7 +138,7 @@ public:
// ----- Type resolution -----
static UClass* FindClassByName(const FString& ClassName);
static bool ResolveTypeFromString(const FString& TypeName, FEdGraphPinType& OutPinType, MCPErrorCallback Error);
static bool ResolveTypeFromString(const FString& TypeName, FEdGraphPinType& OutPinType);
static FString FormatPropertyType(FProperty* Prop);
// ----- Material helpers -----
@@ -166,36 +152,29 @@ public:
// ----- Anim blueprint helpers -----
static UAnimationStateMachineGraph* FindStateMachineGraph(UBlueprint* BP, const FString& GraphName);
static UAnimStateNode* FindStateByName(UAnimationStateMachineGraph* SMGraph, const FString& StateName, MCPErrorCallback Error);
static UAnimStateNode* FindStateByName(UAnimationStateMachineGraph* SMGraph, const FString& StateName);
static UAnimStateTransitionNode* FindTransition(UAnimationStateMachineGraph* SMGraph, const FString& FromStateName, const FString& ToStateName);
// ----- Graph actions (node spawning) -----
static FString ActionFullName(const TSharedPtr<FEdGraphSchemaAction>& Action);
static TArray<TSharedPtr<FEdGraphSchemaAction>> SearchGraphActions(UEdGraph* Graph, const FString& Query, int32 MaxResults = 0, bool ExactMatch = false);
// ----- Pre/Post edit -----
// Call before and after modifying objects. Walks the list looking for
// known parent types (UMaterial, UBlueprint, etc.) and issues the
// appropriate notifications.
static void PreEdit(const TArray<UObject*>& Objects);
static void PostEdit(const TArray<UObject*>& Objects);
// ----- Material Parameters -----
static TMap<FMaterialParameterInfo, FMaterialParameterMetadata> GetMaterialParameters(UMaterialInterface* Material);
static bool ParseMaterialParameterAssociation(const FString& Str, EMaterialParameterAssociation& OutAssociation, MCPErrorCallback Error = nullptr);
static bool ParseMaterialParameterAssociation(const FString& Str, EMaterialParameterAssociation& OutAssociation);
static void FormatMaterialParameter(FStringBuilderBase& Result, const FMaterialParameterInfo& Info, const FMaterialParameterMetadata& Meta);
// ----- Editable template -----
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);
static FString GetPropertyValueText(UObject* Container, FProperty* Prop);
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(UObject* Container, FProperty* Prop, const FString& Value);
static bool SetPropertyValueText(void* Container, FProperty* Prop, const FString& Value, UObject* Owner);
// ----- JSON helpers -----
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 FJsonObject* Json, MCPErrorCallback Error);
static bool PopulateFromJson(UStruct* StructType, void* Container, const TSharedPtr<FJsonValue>& JsonValue);
static bool PopulateFromJson(UStruct* StructType, void* Container, const FJsonObject* Json);
// ----- Text formatting -----
static FString WrapText(const FString& Text, int32 ColLimit, const FString& Prefix);