More MCP refactoring
This commit is contained in:
@@ -332,6 +332,21 @@ public:
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
USTRUCT()
|
||||||
|
struct FBlendSpaceSampleEntry
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
FString AnimationAsset;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
float X = 0.0f;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
float Y = 0.0f;
|
||||||
|
};
|
||||||
|
|
||||||
UCLASS(meta=(ToolName="set_blend_space_sample_points"))
|
UCLASS(meta=(ToolName="set_blend_space_sample_points"))
|
||||||
class UMCPHandler_SetBlendSpaceSamples : public UObject, public IMCPHandler
|
class UMCPHandler_SetBlendSpaceSamples : public UObject, public IMCPHandler
|
||||||
{
|
{
|
||||||
@@ -410,22 +425,18 @@ public:
|
|||||||
|
|
||||||
for (const TSharedPtr<FJsonValue>& SampleVal : Samples.Array)
|
for (const TSharedPtr<FJsonValue>& SampleVal : Samples.Array)
|
||||||
{
|
{
|
||||||
const TSharedPtr<FJsonObject>& SampleObj = SampleVal->AsObject();
|
FBlendSpaceSampleEntry Entry;
|
||||||
if (!SampleObj.IsValid()) continue;
|
if (!MCPUtils::PopulateFromJson(FBlendSpaceSampleEntry::StaticStruct(), &Entry, SampleVal, Result)) return;
|
||||||
|
|
||||||
FString AnimAssetName = SampleObj->GetStringField(TEXT("animationAsset"));
|
|
||||||
float X = (float)SampleObj->GetNumberField(TEXT("x"));
|
|
||||||
float Y = (float)SampleObj->GetNumberField(TEXT("y"));
|
|
||||||
|
|
||||||
UAnimSequence* AnimSeq = nullptr;
|
UAnimSequence* AnimSeq = nullptr;
|
||||||
if (!AnimAssetName.IsEmpty())
|
if (!Entry.AnimationAsset.IsEmpty())
|
||||||
{
|
{
|
||||||
MCPAssets<UAnimSequence> AnimAssets;
|
MCPAssets<UAnimSequence> AnimAssets;
|
||||||
if (AnimAssets.Exact(AnimAssetName).Load())
|
if (AnimAssets.Exact(Entry.AnimationAsset).Load())
|
||||||
AnimSeq = AnimAssets.Object();
|
AnimSeq = AnimAssets.Object();
|
||||||
}
|
}
|
||||||
|
|
||||||
FVector SampleValue(X, Y, 0.0f);
|
FVector SampleValue(Entry.X, Entry.Y, 0.0f);
|
||||||
if (AnimSeq)
|
if (AnimSeq)
|
||||||
{
|
{
|
||||||
BS->AddSample(AnimSeq, SampleValue);
|
BS->AddSample(AnimSeq, SampleValue);
|
||||||
|
|||||||
@@ -16,6 +16,18 @@
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
USTRUCT()
|
||||||
|
struct FDispatcherParamEntry
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
FString Name;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
FString Type;
|
||||||
|
};
|
||||||
|
|
||||||
UCLASS(meta=(ToolName="add_event_dispatcher"))
|
UCLASS(meta=(ToolName="add_event_dispatcher"))
|
||||||
class UMCPHandler_AddEventDispatcher : public UObject, public IMCPHandler
|
class UMCPHandler_AddEventDispatcher : public UObject, public IMCPHandler
|
||||||
{
|
{
|
||||||
@@ -117,23 +129,19 @@ public:
|
|||||||
|
|
||||||
for (const TSharedPtr<FJsonValue>& ParamVal : Parameters.Array)
|
for (const TSharedPtr<FJsonValue>& ParamVal : Parameters.Array)
|
||||||
{
|
{
|
||||||
if (!ParamVal.IsValid() || ParamVal->Type != EJson::Object) continue;
|
FDispatcherParamEntry Entry;
|
||||||
TSharedPtr<FJsonObject> ParamObj = ParamVal->AsObject();
|
if (!MCPUtils::PopulateFromJson(FDispatcherParamEntry::StaticStruct(), &Entry, ParamVal, Result)) return;
|
||||||
|
if (Entry.Name.IsEmpty() || Entry.Type.IsEmpty()) continue;
|
||||||
FString ParamName = ParamObj->GetStringField(TEXT("name"));
|
|
||||||
FString ParamType = ParamObj->GetStringField(TEXT("type"));
|
|
||||||
|
|
||||||
if (ParamName.IsEmpty() || ParamType.IsEmpty()) continue;
|
|
||||||
|
|
||||||
FEdGraphPinType PinType;
|
FEdGraphPinType PinType;
|
||||||
if (!MCPUtils::ResolveTypeFromString(ParamType, PinType, Result))
|
if (!MCPUtils::ResolveTypeFromString(Entry.Type, PinType, Result))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
EntryNode->CreateUserDefinedPin(FName(*ParamName), PinType, EGPD_Output);
|
EntryNode->CreateUserDefinedPin(FName(*Entry.Name), PinType, EGPD_Output);
|
||||||
|
|
||||||
TSharedRef<FJsonObject> ParamJson = MakeShared<FJsonObject>();
|
TSharedRef<FJsonObject> ParamJson = MakeShared<FJsonObject>();
|
||||||
ParamJson->SetStringField(TEXT("name"), ParamName);
|
ParamJson->SetStringField(TEXT("name"), Entry.Name);
|
||||||
ParamJson->SetStringField(TEXT("type"), ParamType);
|
ParamJson->SetStringField(TEXT("type"), Entry.Type);
|
||||||
AddedParamsJson.Add(MakeShared<FJsonValueObject>(ParamJson));
|
AddedParamsJson.Add(MakeShared<FJsonValueObject>(ParamJson));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,12 +103,7 @@ public:
|
|||||||
Results.Add(MakeShared<FJsonValueObject>(EntryResult));
|
Results.Add(MakeShared<FJsonValueObject>(EntryResult));
|
||||||
|
|
||||||
FMoveNodeEntry Entry;
|
FMoveNodeEntry Entry;
|
||||||
FString PopulateError = MCPUtils::PopulateFromJson(FMoveNodeEntry::StaticStruct(), &Entry, NodeVal);
|
if (!MCPUtils::PopulateFromJson(FMoveNodeEntry::StaticStruct(), &Entry, NodeVal, &*EntryResult)) continue;
|
||||||
if (!PopulateError.IsEmpty())
|
|
||||||
{
|
|
||||||
EntryResult->SetStringField(TEXT("error"), PopulateError);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
UEdGraphNode* Node = MCPUtils::FindNodeByGuid(BP, Entry.Node);
|
UEdGraphNode* Node = MCPUtils::FindNodeByGuid(BP, Entry.Node);
|
||||||
if (!Node)
|
if (!Node)
|
||||||
@@ -362,12 +357,7 @@ public:
|
|||||||
Results.Add(MakeShared<FJsonValueObject>(EntryResult));
|
Results.Add(MakeShared<FJsonValueObject>(EntryResult));
|
||||||
|
|
||||||
FSpawnNodeEntry Entry;
|
FSpawnNodeEntry Entry;
|
||||||
FString PopulateError = MCPUtils::PopulateFromJson(FSpawnNodeEntry::StaticStruct(), &Entry, NodeVal);
|
if (!MCPUtils::PopulateFromJson(FSpawnNodeEntry::StaticStruct(), &Entry, NodeVal, &*EntryResult)) continue;
|
||||||
if (!PopulateError.IsEmpty())
|
|
||||||
{
|
|
||||||
EntryResult->SetStringField(TEXT("error"), PopulateError);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the spawner by exact full name
|
// Find the spawner by exact full name
|
||||||
TArray<UBlueprintNodeSpawner*> Matches = MCPUtils::SearchNodeSpawners(Entry.ActionName, 0, /*ExactMatch=*/true, TargetGraph);
|
TArray<UBlueprintNodeSpawner*> Matches = MCPUtils::SearchNodeSpawners(Entry.ActionName, 0, /*ExactMatch=*/true, TargetGraph);
|
||||||
|
|||||||
@@ -63,12 +63,7 @@ public:
|
|||||||
Results.Add(MakeShared<FJsonValueObject>(EntryResult));
|
Results.Add(MakeShared<FJsonValueObject>(EntryResult));
|
||||||
|
|
||||||
FSetPinDefaultEntry Entry;
|
FSetPinDefaultEntry Entry;
|
||||||
FString PopulateError = MCPUtils::PopulateFromJson(FSetPinDefaultEntry::StaticStruct(), &Entry, PinVal);
|
if (!MCPUtils::PopulateFromJson(FSetPinDefaultEntry::StaticStruct(), &Entry, PinVal, &*EntryResult)) continue;
|
||||||
if (!PopulateError.IsEmpty())
|
|
||||||
{
|
|
||||||
EntryResult->SetStringField(TEXT("error"), PopulateError);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
MCPAssets<UBlueprint> Assets;
|
MCPAssets<UBlueprint> Assets;
|
||||||
if (!Assets.Scan<UBlueprint>().Scan<UWorld>().Exact(Entry.Blueprint).Errors(&*EntryResult).ENone().ETwo().Load())
|
if (!Assets.Scan<UBlueprint>().Scan<UWorld>().Exact(Entry.Blueprint).Errors(&*EntryResult).ENone().ETwo().Load())
|
||||||
@@ -192,12 +187,7 @@ public:
|
|||||||
Results.Add(MakeShared<FJsonValueObject>(EntryResult));
|
Results.Add(MakeShared<FJsonValueObject>(EntryResult));
|
||||||
|
|
||||||
FConnectPinsEntry Entry;
|
FConnectPinsEntry Entry;
|
||||||
FString PopulateError = MCPUtils::PopulateFromJson(FConnectPinsEntry::StaticStruct(), &Entry, ConnVal);
|
if (!MCPUtils::PopulateFromJson(FConnectPinsEntry::StaticStruct(), &Entry, ConnVal, &*EntryResult)) continue;
|
||||||
if (!PopulateError.IsEmpty())
|
|
||||||
{
|
|
||||||
EntryResult->SetStringField(TEXT("error"), PopulateError);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
UEdGraph* SourceGraph = nullptr;
|
UEdGraph* SourceGraph = nullptr;
|
||||||
UEdGraphNode* SourceNode = MCPUtils::FindNodeByGuid(BP, Entry.SourceNode, &SourceGraph);
|
UEdGraphNode* SourceNode = MCPUtils::FindNodeByGuid(BP, Entry.SourceNode, &SourceGraph);
|
||||||
@@ -319,12 +309,7 @@ public:
|
|||||||
Results.Add(MakeShared<FJsonValueObject>(EntryResult));
|
Results.Add(MakeShared<FJsonValueObject>(EntryResult));
|
||||||
|
|
||||||
FDisconnectPinEntry Entry;
|
FDisconnectPinEntry Entry;
|
||||||
FString PopulateError = MCPUtils::PopulateFromJson(FDisconnectPinEntry::StaticStruct(), &Entry, DiscVal);
|
if (!MCPUtils::PopulateFromJson(FDisconnectPinEntry::StaticStruct(), &Entry, DiscVal, &*EntryResult)) continue;
|
||||||
if (!PopulateError.IsEmpty())
|
|
||||||
{
|
|
||||||
EntryResult->SetStringField(TEXT("error"), PopulateError);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
UEdGraphNode* Node = MCPUtils::FindNodeByGuid(BP, Entry.Node);
|
UEdGraphNode* Node = MCPUtils::FindNodeByGuid(BP, Entry.Node);
|
||||||
if (!Node)
|
if (!Node)
|
||||||
|
|||||||
@@ -21,6 +21,18 @@
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
USTRUCT()
|
||||||
|
struct FStructPropertyEntry
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
FString Name;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
FString Type;
|
||||||
|
};
|
||||||
|
|
||||||
UCLASS(meta=(ToolName="create_struct_asset"))
|
UCLASS(meta=(ToolName="create_struct_asset"))
|
||||||
class UMCPHandler_CreateStruct : public UObject, public IMCPHandler
|
class UMCPHandler_CreateStruct : public UObject, public IMCPHandler
|
||||||
{
|
{
|
||||||
@@ -77,18 +89,15 @@ public:
|
|||||||
int32 PropsAdded = 0;
|
int32 PropsAdded = 0;
|
||||||
for (const TSharedPtr<FJsonValue>& PropVal : Properties.Array)
|
for (const TSharedPtr<FJsonValue>& PropVal : Properties.Array)
|
||||||
{
|
{
|
||||||
TSharedPtr<FJsonObject> PropObj = PropVal->AsObject();
|
FStructPropertyEntry Entry;
|
||||||
if (!PropObj) continue;
|
if (!MCPUtils::PopulateFromJson(FStructPropertyEntry::StaticStruct(), &Entry, PropVal, Result)) return;
|
||||||
|
if (Entry.Name.IsEmpty() || Entry.Type.IsEmpty()) continue;
|
||||||
FString PropName = PropObj->GetStringField(TEXT("name"));
|
|
||||||
FString PropType = PropObj->GetStringField(TEXT("type"));
|
|
||||||
if (PropName.IsEmpty() || PropType.IsEmpty()) continue;
|
|
||||||
|
|
||||||
FEdGraphPinType PinType;
|
FEdGraphPinType PinType;
|
||||||
FString TypeError;
|
FString TypeError;
|
||||||
if (!MCPUtils::ResolveTypeFromString(PropType, PinType, TypeError))
|
if (!MCPUtils::ResolveTypeFromString(Entry.Type, PinType, TypeError))
|
||||||
{
|
{
|
||||||
UE_LOG(LogTemp, Warning, TEXT("BlueprintMCP: Could not resolve type '%s' for property '%s': %s"), *PropType, *PropName, *TypeError);
|
UE_LOG(LogTemp, Warning, TEXT("BlueprintMCP: Could not resolve type '%s' for property '%s': %s"), *Entry.Type, *Entry.Name, *TypeError);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +123,7 @@ public:
|
|||||||
}
|
}
|
||||||
if (NewPropGuid.IsValid())
|
if (NewPropGuid.IsValid())
|
||||||
{
|
{
|
||||||
FStructureEditorUtils::RenameVariable(NewStruct, NewPropGuid, PropName);
|
FStructureEditorUtils::RenameVariable(NewStruct, NewPropGuid, Entry.Name);
|
||||||
}
|
}
|
||||||
PropsAdded++;
|
PropsAdded++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -261,15 +261,10 @@ void FMCPServer::DispatchToolCall(const FString& ToolName, const FJsonObject* Pa
|
|||||||
|
|
||||||
TStrongObjectPtr<UObject> HandlerObj(NewObject<UObject>(GetTransientPackage(), *HandlerClass));
|
TStrongObjectPtr<UObject> HandlerObj(NewObject<UObject>(GetTransientPackage(), *HandlerClass));
|
||||||
IMCPHandler* Handler = Cast<IMCPHandler>(HandlerObj.Get());
|
IMCPHandler* Handler = Cast<IMCPHandler>(HandlerObj.Get());
|
||||||
FString PopulateError = MCPUtils::PopulateFromJson(HandlerObj->GetClass(), HandlerObj.Get(), Params);
|
if (MCPUtils::PopulateFromJson(HandlerObj->GetClass(), HandlerObj.Get(), Params, Result))
|
||||||
if (PopulateError.IsEmpty())
|
|
||||||
{
|
{
|
||||||
Handler->Handle(Params, Result);
|
Handler->Handle(Params, Result);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
MCPUtils::MakeErrorJson(Result, PopulateError);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bIsMutation && GEditor)
|
if (bIsMutation && GEditor)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1427,22 +1427,25 @@ FString MCPUtils::SetPropertyFromJson(
|
|||||||
*FieldName, *Prop->GetCPPType());
|
*FieldName, *Prop->GetCPPType());
|
||||||
}
|
}
|
||||||
|
|
||||||
FString 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))
|
||||||
{
|
{
|
||||||
return TEXT("Expected a JSON object");
|
Error.SetError(TEXT("Expected a JSON object"));
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return PopulateFromJson(StructType, Container, JsonValue->AsObject().Get());
|
return PopulateFromJson(StructType, Container, JsonValue->AsObject().Get(), Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
FString 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;
|
||||||
@@ -1460,7 +1463,8 @@ FString MCPUtils::PopulateFromJson(
|
|||||||
{
|
{
|
||||||
if (!KnownKeys.Contains(KV.Key))
|
if (!KnownKeys.Contains(KV.Key))
|
||||||
{
|
{
|
||||||
return FString::Printf(TEXT("Unknown parameter '%s'"), *KV.Key);
|
Error.SetError(FString::Printf(TEXT("Unknown parameter '%s'"), *KV.Key));
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1474,17 +1478,19 @@ FString MCPUtils::PopulateFromJson(
|
|||||||
{
|
{
|
||||||
if (!bOptional)
|
if (!bOptional)
|
||||||
{
|
{
|
||||||
return FString::Printf(TEXT("Missing required parameter '%s'"), *JsonKey);
|
Error.SetError(FString::Printf(TEXT("Missing required parameter '%s'"), *JsonKey));
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
FString Error = SetPropertyFromJson(Container, Prop, JsonKey, Json);
|
FString PropError = SetPropertyFromJson(Container, Prop, JsonKey, Json);
|
||||||
if (!Error.IsEmpty())
|
if (!PropError.IsEmpty())
|
||||||
{
|
{
|
||||||
return Error;
|
Error.SetError(PropError);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return FString();
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,8 +149,8 @@ public:
|
|||||||
|
|
||||||
// ----- Property population -----
|
// ----- Property population -----
|
||||||
static FString PropertyNameToJsonKey(const FString& PropName);
|
static FString PropertyNameToJsonKey(const FString& PropName);
|
||||||
static FString PopulateFromJson(UStruct* StructType, void* Container, const TSharedPtr<FJsonValue>& JsonValue);
|
static bool PopulateFromJson(UStruct* StructType, void* Container, const TSharedPtr<FJsonValue>& JsonValue, MCPErrorCallback Error);
|
||||||
static FString PopulateFromJson(UStruct* StructType, void* Container, const FJsonObject* Json);
|
static bool PopulateFromJson(UStruct* StructType, void* Container, const FJsonObject* Json, MCPErrorCallback Error);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static FString SetPropertyFromJson(void* Container, FProperty* Prop, const FString& FieldName, const FJsonObject* Json);
|
static FString SetPropertyFromJson(void* Container, FProperty* Prop, const FString& FieldName, const FJsonObject* Json);
|
||||||
|
|||||||
Reference in New Issue
Block a user