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 "CoreMinimal.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssetFinder.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "Materials/Material.h" #include "Materials/Material.h"
#include "Materials/MaterialInterface.h" #include "Materials/MaterialInterface.h"

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssetFinder.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "Materials/Material.h" #include "Materials/Material.h"
#include "Materials/MaterialFunction.h" #include "Materials/MaterialFunction.h"

View File

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

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssetFinder.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "Materials/Material.h" #include "Materials/Material.h"
#include "Materials/MaterialFunction.h" #include "Materials/MaterialFunction.h"

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssetFinder.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "Materials/Material.h" #include "Materials/Material.h"
#include "Materials/MaterialExpression.h" #include "Materials/MaterialExpression.h"

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssetFinder.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "Materials/Material.h" #include "Materials/Material.h"
#include "Materials/MaterialExpression.h" #include "Materials/MaterialExpression.h"

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssetFinder.h" #include "MCPAssets.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "Materials/Material.h" #include "Materials/Material.h"

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssetFinder.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "Materials/Material.h" #include "Materials/Material.h"
#include "MaterialDomain.h" #include "MaterialDomain.h"

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssetFinder.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "Materials/MaterialFunction.h" #include "Materials/MaterialFunction.h"
#include "Materials/MaterialExpression.h" #include "Materials/MaterialExpression.h"

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssetFinder.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "Materials/Material.h" #include "Materials/Material.h"
#include "Materials/MaterialExpression.h" #include "Materials/MaterialExpression.h"

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssetFinder.h" #include "MCPAssets.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "Materials/Material.h" #include "Materials/Material.h"

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssetFinder.h" #include "MCPAssets.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "Materials/Material.h" #include "Materials/Material.h"

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssetFinder.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "MCPPackageMaker.h" #include "MCPPackageMaker.h"
#include "Animation/AnimBlueprint.h" #include "Animation/AnimBlueprint.h"
@@ -39,14 +39,12 @@ public:
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle(FStringBuilderBase& Result) override
{ {
MCPErrorCallback CB(Result); MCPPackageMaker Maker(AssetPath);
MCPPackageMaker Maker(AssetPath, CB);
if (!Maker.Ok()) return; if (!Maker.Ok()) return;
// Resolve skeleton // Resolve skeleton
MCPAssets<USkeleton> SkeletonAssets; 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(); USkeleton* SkeletonObj = SkeletonAssets.Object();
// Resolve parent class (default: UAnimInstance) // Resolve parent class (default: UAnimInstance)
@@ -64,7 +62,8 @@ public:
} }
if (!Found) 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; ParentClassObj = Found;
} }
@@ -84,7 +83,8 @@ public:
if (!NewAnimBP) if (!NewAnimBP)
{ {
return CB.SetError(TEXT("FKismetEditorUtilities::CreateBlueprint returned null")); UMCPServer::Print(TEXT("ERROR: FKismetEditorUtilities::CreateBlueprint returned null\n"));
return;
} }
// Set target skeleton // Set target skeleton

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssetFinder.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "Animation/AnimBlueprint.h" #include "Animation/AnimBlueprint.h"
#include "AnimGraphNode_Base.h" #include "AnimGraphNode_Base.h"
@@ -30,7 +30,7 @@ public:
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle(FStringBuilderBase& Result) override
{ {
MCPAssets<UAnimBlueprint> Assets; 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(); UAnimBlueprint* AnimBP = Assets.Object();
// Walk all anim nodes to collect slot names // Walk all anim nodes to collect slot names

View File

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

View File

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

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssetFinder.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "AssetToolsModule.h" #include "AssetToolsModule.h"
#include "IAssetTools.h" #include "IAssetTools.h"
@@ -34,7 +34,7 @@ public:
{ {
// Load the asset // Load the asset
MCPAssets<UObject> Assets; 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(); UObject* AssetObj = Assets.Object();
// Parse new path into package path and asset name // Parse new path into package path and asset name

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssetFinder.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "Engine/Blueprint.h" #include "Engine/Blueprint.h"
#include "EdGraph/EdGraph.h" #include "EdGraph/EdGraph.h"
@@ -41,11 +41,11 @@ public:
{ {
// Load both blueprints // Load both blueprints
MCPAssets<UBlueprint> AssetsA; 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(); UBlueprint* BPA = AssetsA.Object();
MCPAssets<UBlueprint> AssetsB; 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(); UBlueprint* BPB = AssetsB.Object();
// Gather graphs, optionally filtering by name // Gather graphs, optionally filtering by name

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssetFinder.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "Engine/Blueprint.h" #include "Engine/Blueprint.h"
#include "EdGraph/EdGraph.h" #include "EdGraph/EdGraph.h"
@@ -35,7 +35,7 @@ public:
{ {
// Load Blueprint // Load Blueprint
MCPAssets<UBlueprint> Assets; 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(); UBlueprint* BP = Assets.Object();
int32 GraphCount = MCPUtils::AllGraphs(BP).Num(); int32 GraphCount = MCPUtils::AllGraphs(BP).Num();

View File

@@ -3,7 +3,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPAssetFinder.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "K2Node_FunctionEntry.h" #include "K2Node_FunctionEntry.h"
#include "K2Node_CustomEvent.h" #include "K2Node_CustomEvent.h"
@@ -39,7 +39,7 @@ public:
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle(FStringBuilderBase& Result) override
{ {
MCPAssets<UBlueprint> Assets; 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(); UBlueprint* BP = Assets.Object();
// Find the entry node (function entry or custom event) // Find the entry node (function entry or custom event)

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssetFinder.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "Engine/UserDefinedEnum.h" #include "Engine/UserDefinedEnum.h"
#include "Kismet2/EnumEditorUtils.h" #include "Kismet2/EnumEditorUtils.h"
@@ -34,9 +34,7 @@ public:
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle(FStringBuilderBase& Result) override
{ {
MCPErrorCallback Error(Result); MCPPackageMaker Maker(AssetPath);
MCPPackageMaker Maker(AssetPath, Error);
if (!Maker.Ok()) return; if (!Maker.Ok()) return;
TArray<FString> EnumValues; TArray<FString> EnumValues;
@@ -47,7 +45,8 @@ public:
} }
if (EnumValues.Num() == 0) 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. // Create the enum using AssetTools.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssetFinder.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "Kismet2/KismetEditorUtilities.h" #include "Kismet2/KismetEditorUtilities.h"
#include "Animation/AnimBlueprint.h" #include "Animation/AnimBlueprint.h"
@@ -56,7 +56,7 @@ public:
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle(FStringBuilderBase& Result) override
{ {
MCPAssets<UAnimBlueprint> Assets; 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(); UAnimBlueprint* AnimBP = Assets.Object();
UAnimationStateMachineGraph* SMGraph = MCPUtils::FindStateMachineGraph(AnimBP, Graph); UAnimationStateMachineGraph* SMGraph = MCPUtils::FindStateMachineGraph(AnimBP, Graph);

View File

@@ -2,7 +2,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "MCPAssetFinder.h" #include "MCPAssets.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "StructUtils/UserDefinedStruct.h" #include "StructUtils/UserDefinedStruct.h"
#include "Kismet2/BlueprintEditorUtils.h" #include "Kismet2/BlueprintEditorUtils.h"
@@ -47,9 +47,7 @@ public:
virtual void Handle(FStringBuilderBase& Result) override virtual void Handle(FStringBuilderBase& Result) override
{ {
MCPErrorCallback Error(Result); MCPPackageMaker Maker(AssetPath);
MCPPackageMaker Maker(AssetPath, Error);
if (!Maker.Ok()) return; if (!Maker.Ok()) return;
// Create the struct using the AssetTools factory. // Create the struct using the AssetTools factory.
@@ -61,11 +59,11 @@ public:
for (const TSharedPtr<FJsonValue>& PropVal : Properties.Array) for (const TSharedPtr<FJsonValue>& PropVal : Properties.Array)
{ {
FStructPropertyEntry Entry; 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; if (Entry.Name.IsEmpty() || Entry.Type.IsEmpty()) continue;
FEdGraphPinType PinType; FEdGraphPinType PinType;
if (!MCPUtils::ResolveTypeFromString(Entry.Type, PinType, Result)) if (!MCPUtils::ResolveTypeFromString(Entry.Type, PinType))
continue; continue;
// Snapshot existing GUIDs so we can find the newly added one. // 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/Blueprint.h"
#include "Engine/World.h" #include "Engine/World.h"
#include "Engine/Level.h" #include "Engine/Level.h"
@@ -46,12 +47,6 @@ MCPAssetsBase& MCPAssetsBase::AllContent()
return *this; return *this;
} }
MCPAssetsBase& MCPAssetsBase::Errors(MCPErrorCallback InCB)
{
ErrorCB = InCB;
return *this;
}
bool MCPAssetsBase::Info() bool MCPAssetsBase::Info()
{ {
@@ -171,5 +166,5 @@ void MCPAssetsBase::SetError(const FString &Msg)
{ {
AssetResults.Empty(); AssetResults.Empty();
UObjectResults.Empty(); UObjectResults.Empty();
ErrorCB.SetError(Msg); UMCPServer::Printf(TEXT("ERROR: %s\n"), *Msg);
} }

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
#include "MCPUtils.h" #include "MCPUtils.h"
#include "MCPServer.h"
#include "MCPHandler.h" #include "MCPHandler.h"
#include "Dom/JsonValue.h" #include "Dom/JsonValue.h"
#include "Serialization/JsonReader.h" #include "Serialization/JsonReader.h"
@@ -90,22 +91,6 @@ extern int32 TrySavePackageSEH(
FSavePackageArgs* SaveArgs, ESavePackageResult* OutResult); FSavePackageArgs* SaveArgs, ESavePackageResult* OutResult);
#endif #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 // Name Formatting
// ============================================================ // ============================================================
@@ -343,12 +328,12 @@ FString MCPUtils::EnumToString(UEnum* Enum, int64 Value, const FString& Prefix)
return Full; 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); OutValue = Enum->GetValueByNameString(Prefix + Str);
if (OutValue == INDEX_NONE) 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 false;
} }
return true; return true;
@@ -541,7 +526,7 @@ UClass* MCPUtils::FindClassByName(const FString& ClassName)
} }
bool MCPUtils::ResolveTypeFromString( bool MCPUtils::ResolveTypeFromString(
const FString& TypeName, FEdGraphPinType& OutPinType, MCPErrorCallback Error) const FString& TypeName, FEdGraphPinType& OutPinType)
{ {
FString TypeLower = TypeName.ToLower(); FString TypeLower = TypeName.ToLower();
@@ -614,7 +599,7 @@ bool MCPUtils::ResolveTypeFromString(
UClass* FoundClass = FindClassByName(ClassName); UClass* FoundClass = FindClassByName(ClassName);
if (!FoundClass) 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; return false;
} }
OutPinType.PinCategory = UEdGraphSchema_K2::PC_Object; OutPinType.PinCategory = UEdGraphSchema_K2::PC_Object;
@@ -626,7 +611,7 @@ bool MCPUtils::ResolveTypeFromString(
UClass* FoundClass = FindClassByName(ClassName); UClass* FoundClass = FindClassByName(ClassName);
if (!FoundClass) 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; return false;
} }
OutPinType.PinCategory = UEdGraphSchema_K2::PC_SoftObject; OutPinType.PinCategory = UEdGraphSchema_K2::PC_SoftObject;
@@ -638,7 +623,7 @@ bool MCPUtils::ResolveTypeFromString(
UClass* FoundClass = FindClassByName(ClassName); UClass* FoundClass = FindClassByName(ClassName);
if (!FoundClass) 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; return false;
} }
OutPinType.PinCategory = UEdGraphSchema_K2::PC_Class; OutPinType.PinCategory = UEdGraphSchema_K2::PC_Class;
@@ -650,7 +635,7 @@ bool MCPUtils::ResolveTypeFromString(
UClass* FoundClass = FindClassByName(ClassName); UClass* FoundClass = FindClassByName(ClassName);
if (!FoundClass) 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; return false;
} }
OutPinType.PinCategory = UEdGraphSchema_K2::PC_SoftClass; OutPinType.PinCategory = UEdGraphSchema_K2::PC_SoftClass;
@@ -662,7 +647,7 @@ bool MCPUtils::ResolveTypeFromString(
UClass* FoundClass = FindClassByName(ClassName); UClass* FoundClass = FindClassByName(ClassName);
if (!FoundClass) 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; return false;
} }
OutPinType.PinCategory = UEdGraphSchema_K2::PC_Interface; OutPinType.PinCategory = UEdGraphSchema_K2::PC_Interface;
@@ -738,9 +723,9 @@ bool MCPUtils::ResolveTypeFromString(
} }
else else
{ {
Error.SetError(FString::Printf( UMCPServer::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)"), 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)); *TypeName);
return false; return false;
} }
} }
@@ -798,54 +783,6 @@ UMaterial* MCPUtils::ReplaceMaterialWithTransientCopy(UMaterial* Material)
return 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) TMap<FMaterialParameterInfo, FMaterialParameterMetadata> MCPUtils::GetMaterialParameters(UMaterialInterface* Material)
{ {
@@ -860,7 +797,7 @@ TMap<FMaterialParameterInfo, FMaterialParameterMetadata> MCPUtils::GetMaterialPa
return Result; 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)) if (Str.Equals(TEXT("Global"), ESearchCase::IgnoreCase))
OutAssociation = GlobalParameter; OutAssociation = GlobalParameter;
@@ -870,7 +807,7 @@ bool MCPUtils::ParseMaterialParameterAssociation(const FString& Str, EMaterialPa
OutAssociation = BlendParameter; OutAssociation = BlendParameter;
else 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 false;
} }
return true; return true;
@@ -984,7 +921,7 @@ UAnimationStateMachineGraph* MCPUtils::FindStateMachineGraph(UBlueprint* BP, con
return nullptr; 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) 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; return nullptr;
} }
@@ -1229,22 +1166,20 @@ FString MCPUtils::SetPropertyFromJson(
bool MCPUtils::PopulateFromJson( bool MCPUtils::PopulateFromJson(
UStruct* StructType, UStruct* StructType,
void* Container, void* Container,
const TSharedPtr<FJsonValue>& JsonValue, const TSharedPtr<FJsonValue>& JsonValue)
MCPErrorCallback Error)
{ {
if (!JsonValue.IsValid() || (JsonValue->Type != EJson::Object)) 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 false;
} }
return PopulateFromJson(StructType, Container, JsonValue->AsObject().Get(), Error); return PopulateFromJson(StructType, Container, JsonValue->AsObject().Get());
} }
bool MCPUtils::PopulateFromJson( bool MCPUtils::PopulateFromJson(
UStruct* StructType, UStruct* StructType,
void* Container, void* Container,
const FJsonObject* Json, const FJsonObject* Json)
MCPErrorCallback Error)
{ {
// Build a set of known property names (as JSON keys) for the unknown-field check. // Build a set of known property names (as JSON keys) for the unknown-field check.
TSet<FString> KnownKeys; TSet<FString> KnownKeys;
@@ -1262,7 +1197,7 @@ bool MCPUtils::PopulateFromJson(
{ {
if (!KnownKeys.Contains(KV.Key)) 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; return false;
} }
} }
@@ -1277,7 +1212,7 @@ bool MCPUtils::PopulateFromJson(
{ {
if (!bOptional) if (!bOptional)
{ {
Error.SetError(FString::Printf(TEXT("Missing required parameter '%s'"), *JsonKey)); UMCPServer::Printf(TEXT("ERROR: Missing required parameter '%s'\n"), *JsonKey);
return false; return false;
} }
continue; continue;
@@ -1286,7 +1221,7 @@ bool MCPUtils::PopulateFromJson(
FString PropError = SetPropertyFromJson(Container, Prop, JsonKey, Json); FString PropError = SetPropertyFromJson(Container, Prop, JsonKey, Json);
if (!PropError.IsEmpty()) if (!PropError.IsEmpty())
{ {
Error.SetError(PropError); UMCPServer::Printf(TEXT("ERROR: %s\n"), *PropError);
return false; return false;
} }
} }
@@ -1369,11 +1304,11 @@ FString MCPUtils::FormatPropertyType(FProperty* Prop)
// FindPropertyByName // FindPropertyByName
// ============================================================ // ============================================================
FProperty* MCPUtils::FindPropertyByName(UObject* Obj, const FString& Name, MCPErrorCallback Error) FProperty* MCPUtils::FindPropertyByName(UObject* Obj, const FString& Name)
{ {
if (!Obj) if (!Obj)
{ {
Error.SetError(TEXT("Object is null")); UMCPServer::Print(TEXT("ERROR: Object is null\n"));
return nullptr; return nullptr;
} }
@@ -1383,14 +1318,14 @@ FProperty* MCPUtils::FindPropertyByName(UObject* Obj, const FString& Name, MCPEr
if (!Identifies(Name, *PropIt)) continue; if (!Identifies(Name, *PropIt)) continue;
if (Found) 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; return nullptr;
} }
Found = *PropIt; Found = *PropIt;
} }
if (!Found) 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; return Found;
} }
@@ -1411,27 +1346,27 @@ FString MCPUtils::GetPropertyValueText(UObject* Container, FProperty* Prop)
// SetPropertyValueText // 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); void* ValuePtr = Prop->ContainerPtrToValuePtr<void>(Container);
const TCHAR* ImportResult = Prop->ImportText_Direct(*Value, ValuePtr, Container, PPF_None); const TCHAR* ImportResult = Prop->ImportText_Direct(*Value, ValuePtr, Container, PPF_None);
if (!ImportResult) if (!ImportResult)
{ {
Error.SetError(FString::Printf(TEXT("Failed to parse '%s' for property '%s' (type: %s)"), UMCPServer::Printf(TEXT("ERROR: Failed to parse '%s' for property '%s' (type: %s)\n"),
*Value, *FormatName(Prop), *Prop->GetCPPType())); *Value, *FormatName(Prop), *Prop->GetCPPType());
return false; return false;
} }
return true; 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); void* ValuePtr = Prop->ContainerPtrToValuePtr<void>(Container);
const TCHAR* ImportResult = Prop->ImportText_Direct(*Value, ValuePtr, Owner, PPF_None); const TCHAR* ImportResult = Prop->ImportText_Direct(*Value, ValuePtr, Owner, PPF_None);
if (!ImportResult) if (!ImportResult)
{ {
Error.SetError(FString::Printf(TEXT("Failed to parse '%s' for property '%s' (type: %s)"), UMCPServer::Printf(TEXT("ERROR: Failed to parse '%s' for property '%s' (type: %s)\n"),
*Value, *FormatName(Prop), *Prop->GetCPPType())); *Value, *FormatName(Prop), *Prop->GetCPPType());
return false; return false;
} }
return true; return true;

View File

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

View File

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

View File

@@ -14,7 +14,7 @@ struct MCPProperty
MCPProperty(FProperty* InProp, void* Container); MCPProperty(FProperty* InProp, void* Container);
FString GetText() const; FString GetText() const;
bool SetText(const FString& Value, MCPErrorCallback Error = nullptr); bool SetText(const FString& Value);
explicit operator bool() const { return Prop != nullptr; } explicit operator bool() const { return Prop != nullptr; }
FProperty* operator->() const { return Prop; } FProperty* operator->() const { return Prop; }
@@ -22,5 +22,5 @@ struct MCPProperty
static TArray<MCPProperty> GetAll(UObject* Obj, EPropertyFlags Flags); static TArray<MCPProperty> GetAll(UObject* Obj, EPropertyFlags Flags);
static TArray<MCPProperty> GetAllSubstring(UObject* Obj, EPropertyFlags Flags, const FString& Substring); static TArray<MCPProperty> GetAllSubstring(UObject* Obj, EPropertyFlags Flags, const FString& Substring);
static TArray<MCPProperty> GetAllExactMatch(UObject* Obj, EPropertyFlags Flags, const FString& Name); 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 // FTickableEditorObject
virtual void Tick(float DeltaTime) override; 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 bool IsTickable() const override;
virtual TStatId GetStatId() 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. */ /** Track an object that has been modified by the current handler. */
static void AddTouchedObject(UObject* Obj) { GMCPServer->Notifier.AddTouchedObject(Obj); } static void AddTouchedObject(UObject* Obj) { GMCPServer->Notifier.AddTouchedObject(Obj); }

View File

@@ -30,20 +30,6 @@ class UEnum;
struct FMemberReference; struct FMemberReference;
struct FBPVariableDescription; 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. // Stateless utility functions used by MCP handlers and the MCP server.
// This is effectively a namespace — all methods are static. // This is effectively a namespace — all methods are static.
class MCPUtils class MCPUtils
@@ -113,7 +99,7 @@ public:
// ----- Enum helpers ----- // ----- Enum helpers -----
static FString EnumToString(UEnum* Enum, int64 Value, const FString& Prefix = FString()); 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> template<typename T>
static FString EnumToString(TEnumAsByte<T> Value, const FString& Prefix = FString()) static FString EnumToString(TEnumAsByte<T> Value, const FString& Prefix = FString())
@@ -124,8 +110,8 @@ public:
{ return EnumToString(StaticEnum<T>(), (int64)Value, Prefix); } { return EnumToString(StaticEnum<T>(), (int64)Value, Prefix); }
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, const FString& Prefix = FString())
{ int64 V; if (!StringToEnum(StaticEnum<T>(), Str, V, Error, Prefix)) return false; OutValue = (T)V; return true; } { int64 V; if (!StringToEnum(StaticEnum<T>(), Str, V, Prefix)) return false; OutValue = (T)V; return true; }
// ----- Blueprint helpers ----- // ----- Blueprint helpers -----
static TArray<UEdGraph*> AllGraphs(UBlueprint* BP); static TArray<UEdGraph*> AllGraphs(UBlueprint* BP);
@@ -152,7 +138,7 @@ public:
// ----- 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);
static FString FormatPropertyType(FProperty* Prop); static FString FormatPropertyType(FProperty* Prop);
// ----- Material helpers ----- // ----- Material helpers -----
@@ -166,36 +152,29 @@ public:
// ----- Anim blueprint helpers ----- // ----- Anim blueprint helpers -----
static UAnimationStateMachineGraph* FindStateMachineGraph(UBlueprint* BP, const FString& GraphName); 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); static UAnimStateTransitionNode* FindTransition(UAnimationStateMachineGraph* SMGraph, const FString& FromStateName, const FString& ToStateName);
// ----- Graph actions (node spawning) ----- // ----- Graph actions (node spawning) -----
static FString ActionFullName(const TSharedPtr<FEdGraphSchemaAction>& Action); static FString ActionFullName(const TSharedPtr<FEdGraphSchemaAction>& Action);
static TArray<TSharedPtr<FEdGraphSchemaAction>> SearchGraphActions(UEdGraph* Graph, const FString& Query, int32 MaxResults = 0, bool ExactMatch = false); 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 ----- // ----- Material Parameters -----
static TMap<FMaterialParameterInfo, FMaterialParameterMetadata> GetMaterialParameters(UMaterialInterface* Material); 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); static void FormatMaterialParameter(FStringBuilderBase& Result, const FMaterialParameterInfo& Info, const FMaterialParameterMetadata& Meta);
// ----- Editable template ----- // ----- Editable template -----
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);
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);
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);
// ----- JSON helpers ----- // ----- 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);
static bool PopulateFromJson(UStruct* StructType, void* Container, const FJsonObject* Json, MCPErrorCallback Error); static bool PopulateFromJson(UStruct* StructType, void* Container, const FJsonObject* Json);
// ----- Text formatting ----- // ----- Text formatting -----
static FString WrapText(const FString& Text, int32 ColLimit, const FString& Prefix); static FString WrapText(const FString& Text, int32 ColLimit, const FString& Prefix);