Begining refactor of BlueprintMCP
This commit is contained in:
@@ -38,14 +38,8 @@
|
||||
// HandleCreateAnimBlueprint — create a new Animation Blueprint
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleCreateAnimBlueprint(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleCreateAnimBlueprint(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString Name = Json->GetStringField(TEXT("name"));
|
||||
FString PackagePath = Json->GetStringField(TEXT("packagePath"));
|
||||
FString SkeletonName = Json->GetStringField(TEXT("skeleton"));
|
||||
@@ -53,19 +47,19 @@ FString FBlueprintMCPServer::HandleCreateAnimBlueprint(const FString& Body)
|
||||
|
||||
if (Name.IsEmpty() || PackagePath.IsEmpty() || SkeletonName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: name, packagePath, skeleton"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: name, packagePath, skeleton"));
|
||||
}
|
||||
|
||||
if (!PackagePath.StartsWith(TEXT("/Game")))
|
||||
{
|
||||
return MakeErrorJson(TEXT("packagePath must start with '/Game'"));
|
||||
return MakeErrorJson(Result, TEXT("packagePath must start with '/Game'"));
|
||||
}
|
||||
|
||||
// Check if asset already exists
|
||||
FString FullAssetPath = PackagePath / Name;
|
||||
if (FindBlueprintAsset(Name) || FindBlueprintAsset(FullAssetPath))
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Blueprint '%s' already exists. Use a different name or delete the existing asset first."),
|
||||
*Name));
|
||||
}
|
||||
@@ -120,7 +114,7 @@ FString FBlueprintMCPServer::HandleCreateAnimBlueprint(const FString& Body)
|
||||
|
||||
if (!Skeleton)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Skeleton '%s' not found. Provide the skeleton asset name or path. Use '__create_test_skeleton__' for testing."),
|
||||
*SkeletonName));
|
||||
}
|
||||
@@ -147,7 +141,7 @@ FString FBlueprintMCPServer::HandleCreateAnimBlueprint(const FString& Body)
|
||||
UPackage* Package = CreatePackage(*FullPackagePath);
|
||||
if (!Package)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Failed to create package at '%s'"), *FullPackagePath));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Failed to create package at '%s'"), *FullPackagePath));
|
||||
}
|
||||
|
||||
// Create the Animation Blueprint
|
||||
@@ -163,7 +157,7 @@ FString FBlueprintMCPServer::HandleCreateAnimBlueprint(const FString& Body)
|
||||
|
||||
if (!NewAnimBP)
|
||||
{
|
||||
return MakeErrorJson(TEXT("FKismetEditorUtilities::CreateBlueprint returned null for AnimBlueprint"));
|
||||
return MakeErrorJson(Result, TEXT("FKismetEditorUtilities::CreateBlueprint returned null for AnimBlueprint"));
|
||||
}
|
||||
|
||||
// Set target skeleton
|
||||
@@ -195,7 +189,6 @@ FString FBlueprintMCPServer::HandleCreateAnimBlueprint(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Created AnimBlueprint '%s' with %d graphs (saved: %s)"),
|
||||
*Name, GraphNames.Num(), bSaved ? TEXT("true") : TEXT("false"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprintName"), Name);
|
||||
Result->SetStringField(TEXT("packagePath"), PackagePath);
|
||||
@@ -205,7 +198,6 @@ FString FBlueprintMCPServer::HandleCreateAnimBlueprint(const FString& Body)
|
||||
Result->SetBoolField(TEXT("isAnimBlueprint"), true);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
Result->SetArrayField(TEXT("graphs"), GraphNames);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
@@ -267,43 +259,40 @@ static UAnimStateTransitionNode* FindTransition(UAnimationStateMachineGraph* SMG
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FString FBlueprintMCPServer::HandleAddAnimState(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleAddAnimState(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString GraphName = Json->GetStringField(TEXT("graph"));
|
||||
FString StateName = Json->GetStringField(TEXT("stateName"));
|
||||
|
||||
if (BlueprintName.IsEmpty() || GraphName.IsEmpty() || StateName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, graph, stateName"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, graph, stateName"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP) return MakeErrorJson(LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP);
|
||||
if (!AnimBP)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
}
|
||||
|
||||
UAnimationStateMachineGraph* SMGraph = FindStateMachineGraph(BP, GraphName);
|
||||
if (!SMGraph)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("State machine graph '%s' not found"), *GraphName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("State machine graph '%s' not found"), *GraphName));
|
||||
}
|
||||
|
||||
// Check for duplicate state name
|
||||
if (FindStateByName(SMGraph, StateName))
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("State '%s' already exists in graph '%s'"), *StateName, *GraphName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("State '%s' already exists in graph '%s'"), *StateName, *GraphName));
|
||||
}
|
||||
|
||||
// Get position
|
||||
@@ -365,52 +354,47 @@ FString FBlueprintMCPServer::HandleAddAnimState(const FString& Body)
|
||||
FKismetEditorUtilities::CompileBlueprint(AnimBP);
|
||||
bool bSaved = SaveBlueprintPackage(AnimBP);
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("stateName"), StateName);
|
||||
Result->SetStringField(TEXT("graph"), GraphName);
|
||||
Result->SetStringField(TEXT("nodeId"), NewState->NodeGuid.ToString());
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
FString FBlueprintMCPServer::HandleRemoveAnimState(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleRemoveAnimState(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString GraphName = Json->GetStringField(TEXT("graph"));
|
||||
FString StateName = Json->GetStringField(TEXT("stateName"));
|
||||
|
||||
if (BlueprintName.IsEmpty() || GraphName.IsEmpty() || StateName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, graph, stateName"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, graph, stateName"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP) return MakeErrorJson(LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP);
|
||||
if (!AnimBP)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
}
|
||||
|
||||
UAnimationStateMachineGraph* SMGraph = FindStateMachineGraph(BP, GraphName);
|
||||
if (!SMGraph)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("State machine graph '%s' not found"), *GraphName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("State machine graph '%s' not found"), *GraphName));
|
||||
}
|
||||
|
||||
UAnimStateNode* StateNode = FindStateByName(SMGraph, StateName);
|
||||
if (!StateNode)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("State '%s' not found in graph '%s'"), *StateName, *GraphName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("State '%s' not found in graph '%s'"), *StateName, *GraphName));
|
||||
}
|
||||
|
||||
// Collect and remove transitions connected to this state
|
||||
@@ -441,22 +425,14 @@ FString FBlueprintMCPServer::HandleRemoveAnimState(const FString& Body)
|
||||
FKismetEditorUtilities::CompileBlueprint(AnimBP);
|
||||
bool bSaved = SaveBlueprintPackage(AnimBP);
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("removedState"), StateName);
|
||||
Result->SetNumberField(TEXT("removedTransitions"), RemovedTransitions);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
FString FBlueprintMCPServer::HandleAddAnimTransition(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleAddAnimTransition(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString GraphName = Json->GetStringField(TEXT("graph"));
|
||||
FString FromStateName = Json->GetStringField(TEXT("fromState"));
|
||||
@@ -464,35 +440,38 @@ FString FBlueprintMCPServer::HandleAddAnimTransition(const FString& Body)
|
||||
|
||||
if (BlueprintName.IsEmpty() || GraphName.IsEmpty() || FromStateName.IsEmpty() || ToStateName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, graph, fromState, toState"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, graph, fromState, toState"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP) return MakeErrorJson(LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP);
|
||||
if (!AnimBP)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
}
|
||||
|
||||
UAnimationStateMachineGraph* SMGraph = FindStateMachineGraph(BP, GraphName);
|
||||
if (!SMGraph)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("State machine graph '%s' not found"), *GraphName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("State machine graph '%s' not found"), *GraphName));
|
||||
}
|
||||
|
||||
UAnimStateNode* FromState = FindStateByName(SMGraph, FromStateName);
|
||||
if (!FromState)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("From state '%s' not found"), *FromStateName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("From state '%s' not found"), *FromStateName));
|
||||
}
|
||||
|
||||
UAnimStateNode* ToState = FindStateByName(SMGraph, ToStateName);
|
||||
if (!ToState)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("To state '%s' not found"), *ToStateName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("To state '%s' not found"), *ToStateName));
|
||||
}
|
||||
|
||||
// Create transition node
|
||||
@@ -529,7 +508,6 @@ FString FBlueprintMCPServer::HandleAddAnimTransition(const FString& Body)
|
||||
FKismetEditorUtilities::CompileBlueprint(AnimBP);
|
||||
bool bSaved = SaveBlueprintPackage(AnimBP);
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("fromState"), FromStateName);
|
||||
Result->SetStringField(TEXT("toState"), ToStateName);
|
||||
@@ -538,17 +516,10 @@ FString FBlueprintMCPServer::HandleAddAnimTransition(const FString& Body)
|
||||
Result->SetNumberField(TEXT("priorityOrder"), TransNode->PriorityOrder);
|
||||
Result->SetBoolField(TEXT("bBidirectional"), TransNode->Bidirectional);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
FString FBlueprintMCPServer::HandleSetTransitionRule(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleSetTransitionRule(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString GraphName = Json->GetStringField(TEXT("graph"));
|
||||
FString FromStateName = Json->GetStringField(TEXT("fromState"));
|
||||
@@ -556,29 +527,32 @@ FString FBlueprintMCPServer::HandleSetTransitionRule(const FString& Body)
|
||||
|
||||
if (BlueprintName.IsEmpty() || GraphName.IsEmpty() || FromStateName.IsEmpty() || ToStateName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, graph, fromState, toState"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, graph, fromState, toState"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP) return MakeErrorJson(LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP);
|
||||
if (!AnimBP)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
}
|
||||
|
||||
UAnimationStateMachineGraph* SMGraph = FindStateMachineGraph(BP, GraphName);
|
||||
if (!SMGraph)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("State machine graph '%s' not found"), *GraphName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("State machine graph '%s' not found"), *GraphName));
|
||||
}
|
||||
|
||||
UAnimStateTransitionNode* TransNode = FindTransition(SMGraph, FromStateName, ToStateName);
|
||||
if (!TransNode)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Transition from '%s' to '%s' not found in graph '%s'"),
|
||||
*FromStateName, *ToStateName, *GraphName));
|
||||
}
|
||||
@@ -614,14 +588,13 @@ FString FBlueprintMCPServer::HandleSetTransitionRule(const FString& Body)
|
||||
|
||||
if (ChangedCount == 0)
|
||||
{
|
||||
return MakeErrorJson(TEXT("No properties to update. Provide at least one of: crossfadeDuration, blendMode, priorityOrder, logicType, bBidirectional"));
|
||||
return MakeErrorJson(Result, TEXT("No properties to update. Provide at least one of: crossfadeDuration, blendMode, priorityOrder, logicType, bBidirectional"));
|
||||
}
|
||||
|
||||
// Compile and save
|
||||
FKismetEditorUtilities::CompileBlueprint(AnimBP);
|
||||
bool bSaved = SaveBlueprintPackage(AnimBP);
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("fromState"), FromStateName);
|
||||
Result->SetStringField(TEXT("toState"), ToStateName);
|
||||
@@ -632,38 +605,34 @@ FString FBlueprintMCPServer::HandleSetTransitionRule(const FString& Body)
|
||||
Result->SetNumberField(TEXT("logicType"), (int32)TransNode->LogicType.GetValue());
|
||||
Result->SetBoolField(TEXT("bBidirectional"), TransNode->Bidirectional);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Tier 3: AnimGraph Blend Tree Mutation
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleAddAnimNode(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleAddAnimNode(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString GraphName = Json->GetStringField(TEXT("graph"));
|
||||
FString NodeType = Json->GetStringField(TEXT("nodeType"));
|
||||
|
||||
if (BlueprintName.IsEmpty() || NodeType.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, nodeType"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, nodeType"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP) return MakeErrorJson(LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP);
|
||||
if (!AnimBP)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
}
|
||||
|
||||
// Find target graph (default to AnimGraph if not specified)
|
||||
@@ -686,7 +655,7 @@ FString FBlueprintMCPServer::HandleAddAnimNode(const FString& Body)
|
||||
|
||||
if (!TargetGraph)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Graph '%s' not found"), *GraphName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Graph '%s' not found"), *GraphName));
|
||||
}
|
||||
|
||||
int32 PosX = Json->HasField(TEXT("posX")) ? (int32)Json->GetNumberField(TEXT("posX")) : 0;
|
||||
@@ -766,14 +735,14 @@ FString FBlueprintMCPServer::HandleAddAnimNode(const FString& Body)
|
||||
}
|
||||
else
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Unsupported nodeType '%s'. Supported: SequencePlayer, BlendSpacePlayer, StateMachine"),
|
||||
*NodeType));
|
||||
}
|
||||
|
||||
if (!NewNode)
|
||||
{
|
||||
return MakeErrorJson(TEXT("Failed to create anim node"));
|
||||
return MakeErrorJson(Result, TEXT("Failed to create anim node"));
|
||||
}
|
||||
|
||||
NewNode->NodePosX = PosX;
|
||||
@@ -785,7 +754,6 @@ FString FBlueprintMCPServer::HandleAddAnimNode(const FString& Body)
|
||||
FKismetEditorUtilities::CompileBlueprint(AnimBP);
|
||||
bool bSaved = SaveBlueprintPackage(AnimBP);
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("nodeType"), NodeType);
|
||||
Result->SetStringField(TEXT("nodeId"), NewNode->NodeGuid.ToString());
|
||||
@@ -803,28 +771,20 @@ FString FBlueprintMCPServer::HandleAddAnimNode(const FString& Body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Tier 4: Advanced Operations
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleAddStateMachine(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleAddStateMachine(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString MachineName = Json->GetStringField(TEXT("name"));
|
||||
|
||||
if (BlueprintName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required field: blueprint"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required field: blueprint"));
|
||||
}
|
||||
|
||||
// Default name
|
||||
@@ -843,21 +803,11 @@ FString FBlueprintMCPServer::HandleAddStateMachine(const FString& Body)
|
||||
if (Json->HasField(TEXT("posY")))
|
||||
ForwardJson->SetNumberField(TEXT("posY"), Json->GetNumberField(TEXT("posY")));
|
||||
|
||||
FString ForwardBody;
|
||||
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&ForwardBody);
|
||||
FJsonSerializer::Serialize(ForwardJson, Writer);
|
||||
|
||||
return HandleAddAnimNode(ForwardBody);
|
||||
HandleAddAnimNode(&*ForwardJson, Result);
|
||||
}
|
||||
|
||||
FString FBlueprintMCPServer::HandleSetStateAnimation(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleSetStateAnimation(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString GraphName = Json->GetStringField(TEXT("graph"));
|
||||
FString StateName = Json->GetStringField(TEXT("stateName"));
|
||||
@@ -865,35 +815,38 @@ FString FBlueprintMCPServer::HandleSetStateAnimation(const FString& Body)
|
||||
|
||||
if (BlueprintName.IsEmpty() || GraphName.IsEmpty() || StateName.IsEmpty() || AnimAssetName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, graph, stateName, animationAsset"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, graph, stateName, animationAsset"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP) return MakeErrorJson(LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP);
|
||||
if (!AnimBP)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
}
|
||||
|
||||
UAnimationStateMachineGraph* SMGraph = FindStateMachineGraph(BP, GraphName);
|
||||
if (!SMGraph)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("State machine graph '%s' not found"), *GraphName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("State machine graph '%s' not found"), *GraphName));
|
||||
}
|
||||
|
||||
UAnimStateNode* StateNode = FindStateByName(SMGraph, StateName);
|
||||
if (!StateNode)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("State '%s' not found in graph '%s'"), *StateName, *GraphName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("State '%s' not found in graph '%s'"), *StateName, *GraphName));
|
||||
}
|
||||
|
||||
UEdGraph* InnerGraph = StateNode->GetBoundGraph();
|
||||
if (!InnerGraph)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("State '%s' has no bound graph"), *StateName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("State '%s' has no bound graph"), *StateName));
|
||||
}
|
||||
|
||||
// Find the animation asset
|
||||
@@ -913,7 +866,7 @@ FString FBlueprintMCPServer::HandleSetStateAnimation(const FString& Body)
|
||||
|
||||
if (!AnimSeq)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Animation asset '%s' not found"), *AnimAssetName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Animation asset '%s' not found"), *AnimAssetName));
|
||||
}
|
||||
|
||||
// Find existing SequencePlayer or create one
|
||||
@@ -943,37 +896,32 @@ FString FBlueprintMCPServer::HandleSetStateAnimation(const FString& Body)
|
||||
FKismetEditorUtilities::CompileBlueprint(AnimBP);
|
||||
bool bSaved = SaveBlueprintPackage(AnimBP);
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("stateName"), StateName);
|
||||
Result->SetStringField(TEXT("animationAsset"), AnimSeq->GetName());
|
||||
Result->SetBoolField(TEXT("createdNewNode"), bCreatedNew);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
FString FBlueprintMCPServer::HandleListAnimSlots(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleListAnimSlots(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
if (BlueprintName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required field: blueprint"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required field: blueprint"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP) return MakeErrorJson(LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP);
|
||||
if (!AnimBP)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
}
|
||||
|
||||
// Walk all anim nodes to collect slot names
|
||||
@@ -1008,36 +956,31 @@ FString FBlueprintMCPServer::HandleListAnimSlots(const FString& Body)
|
||||
SlotsArr.Add(MakeShared<FJsonValueString>(Slot));
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetArrayField(TEXT("slots"), SlotsArr);
|
||||
Result->SetNumberField(TEXT("count"), SlotsArr.Num());
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
FString FBlueprintMCPServer::HandleListSyncGroups(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleListSyncGroups(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
if (BlueprintName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required field: blueprint"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required field: blueprint"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP) return MakeErrorJson(LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP);
|
||||
if (!AnimBP)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
}
|
||||
|
||||
// Walk all anim nodes to collect sync group names
|
||||
@@ -1072,38 +1015,30 @@ FString FBlueprintMCPServer::HandleListSyncGroups(const FString& Body)
|
||||
GroupsArr.Add(MakeShared<FJsonValueString>(Group));
|
||||
}
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blueprint"), BlueprintName);
|
||||
Result->SetArrayField(TEXT("syncGroups"), GroupsArr);
|
||||
Result->SetNumberField(TEXT("count"), GroupsArr.Num());
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleCreateBlendSpace — create a new 2D Blend Space asset
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleCreateBlendSpace(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleCreateBlendSpace(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString Name = Json->GetStringField(TEXT("name"));
|
||||
FString PackagePath = Json->GetStringField(TEXT("packagePath"));
|
||||
FString SkeletonName = Json->GetStringField(TEXT("skeleton"));
|
||||
|
||||
if (Name.IsEmpty() || PackagePath.IsEmpty() || SkeletonName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: name, packagePath, skeleton"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: name, packagePath, skeleton"));
|
||||
}
|
||||
|
||||
if (!PackagePath.StartsWith(TEXT("/Game")))
|
||||
{
|
||||
return MakeErrorJson(TEXT("packagePath must start with '/Game'"));
|
||||
return MakeErrorJson(Result, TEXT("packagePath must start with '/Game'"));
|
||||
}
|
||||
|
||||
// Check if asset already exists
|
||||
@@ -1116,7 +1051,7 @@ FString FBlueprintMCPServer::HandleCreateBlendSpace(const FString& Body)
|
||||
{
|
||||
if (Asset.AssetName.ToString() == Name || Asset.GetObjectPathString() == FullAssetPath)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Blend Space '%s' already exists. Use a different name or delete the existing asset first."),
|
||||
*Name));
|
||||
}
|
||||
@@ -1171,7 +1106,7 @@ FString FBlueprintMCPServer::HandleCreateBlendSpace(const FString& Body)
|
||||
|
||||
if (!Skeleton)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(
|
||||
return MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Skeleton '%s' not found. Provide the skeleton asset name or path. Use '__create_test_skeleton__' for testing."),
|
||||
*SkeletonName));
|
||||
}
|
||||
@@ -1184,14 +1119,14 @@ FString FBlueprintMCPServer::HandleCreateBlendSpace(const FString& Body)
|
||||
UPackage* Package = CreatePackage(*FullPackagePath);
|
||||
if (!Package)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Failed to create package at '%s'"), *FullPackagePath));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Failed to create package at '%s'"), *FullPackagePath));
|
||||
}
|
||||
|
||||
// Create the Blend Space
|
||||
UBlendSpace* NewBS = NewObject<UBlendSpace>(Package, FName(*Name), RF_Public | RF_Standalone);
|
||||
if (!NewBS)
|
||||
{
|
||||
return MakeErrorJson(TEXT("Failed to create Blend Space object"));
|
||||
return MakeErrorJson(Result, TEXT("Failed to create Blend Space object"));
|
||||
}
|
||||
|
||||
// Set skeleton
|
||||
@@ -1204,30 +1139,22 @@ FString FBlueprintMCPServer::HandleCreateBlendSpace(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Created Blend Space '%s' (saved: %s)"),
|
||||
*Name, bSaved ? TEXT("true") : TEXT("false"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("assetPath"), FullAssetPath);
|
||||
Result->SetStringField(TEXT("skeleton"), Skeleton->GetName());
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleSetBlendSpaceSamples — add animation samples to a Blend Space
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleSetBlendSpaceSamples(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleSetBlendSpaceSamples(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlendSpaceName = Json->GetStringField(TEXT("blendSpace"));
|
||||
if (BlendSpaceName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required field: blendSpace"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required field: blendSpace"));
|
||||
}
|
||||
|
||||
// Load the blend space
|
||||
@@ -1263,7 +1190,7 @@ FString FBlueprintMCPServer::HandleSetBlendSpaceSamples(const FString& Body)
|
||||
|
||||
if (!BS)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Blend Space '%s' not found"), *BlendSpaceName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Blend Space '%s' not found"), *BlendSpaceName));
|
||||
}
|
||||
|
||||
// Set axis parameters
|
||||
@@ -1366,26 +1293,18 @@ FString FBlueprintMCPServer::HandleSetBlendSpaceSamples(const FString& Body)
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Set %d samples on Blend Space '%s' (saved: %s)"),
|
||||
SamplesSet, *BS->GetName(), bSaved ? TEXT("true") : TEXT("false"));
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("blendSpace"), BS->GetPathName());
|
||||
Result->SetNumberField(TEXT("samplesSet"), SamplesSet);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HandleSetStateBlendSpace — place a BlendSpacePlayer in a state
|
||||
// ============================================================
|
||||
|
||||
FString FBlueprintMCPServer::HandleSetStateBlendSpace(const FString& Body)
|
||||
void FBlueprintMCPServer::HandleSetStateBlendSpace(const FJsonObject* Json, FJsonObject* Result)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Json = ParseBodyJson(Body);
|
||||
if (!Json.IsValid())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Invalid JSON body"));
|
||||
}
|
||||
|
||||
FString BlueprintName = Json->GetStringField(TEXT("blueprint"));
|
||||
FString GraphName = Json->GetStringField(TEXT("graph"));
|
||||
FString StateName = Json->GetStringField(TEXT("stateName"));
|
||||
@@ -1395,35 +1314,38 @@ FString FBlueprintMCPServer::HandleSetStateBlendSpace(const FString& Body)
|
||||
|
||||
if (BlueprintName.IsEmpty() || GraphName.IsEmpty() || StateName.IsEmpty() || BlendSpaceName.IsEmpty())
|
||||
{
|
||||
return MakeErrorJson(TEXT("Missing required fields: blueprint, graph, stateName, blendSpace"));
|
||||
return MakeErrorJson(Result, TEXT("Missing required fields: blueprint, graph, stateName, blendSpace"));
|
||||
}
|
||||
|
||||
FString LoadError;
|
||||
UBlueprint* BP = LoadBlueprintByName(BlueprintName, LoadError);
|
||||
if (!BP) return MakeErrorJson(LoadError);
|
||||
if (!BP)
|
||||
{
|
||||
return MakeErrorJson(Result, LoadError);
|
||||
}
|
||||
|
||||
UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP);
|
||||
if (!AnimBP)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("'%s' is not an Animation Blueprint"), *BlueprintName));
|
||||
}
|
||||
|
||||
UAnimationStateMachineGraph* SMGraph = FindStateMachineGraph(BP, GraphName);
|
||||
if (!SMGraph)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("State machine graph '%s' not found"), *GraphName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("State machine graph '%s' not found"), *GraphName));
|
||||
}
|
||||
|
||||
UAnimStateNode* StateNode = FindStateByName(SMGraph, StateName);
|
||||
if (!StateNode)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("State '%s' not found in graph '%s'"), *StateName, *GraphName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("State '%s' not found in graph '%s'"), *StateName, *GraphName));
|
||||
}
|
||||
|
||||
UEdGraph* InnerGraph = StateNode->GetBoundGraph();
|
||||
if (!InnerGraph)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("State '%s' has no bound graph"), *StateName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("State '%s' has no bound graph"), *StateName));
|
||||
}
|
||||
|
||||
// Find the blend space asset
|
||||
@@ -1459,7 +1381,7 @@ FString FBlueprintMCPServer::HandleSetStateBlendSpace(const FString& Body)
|
||||
|
||||
if (!BlendSpaceAsset)
|
||||
{
|
||||
return MakeErrorJson(FString::Printf(TEXT("Blend Space '%s' not found"), *BlendSpaceName));
|
||||
return MakeErrorJson(Result, FString::Printf(TEXT("Blend Space '%s' not found"), *BlendSpaceName));
|
||||
}
|
||||
|
||||
// Find existing BlendSpacePlayer or create one
|
||||
@@ -1609,11 +1531,9 @@ FString FBlueprintMCPServer::HandleSetStateBlendSpace(const FString& Body)
|
||||
FKismetEditorUtilities::CompileBlueprint(AnimBP);
|
||||
bool bSaved = SaveBlueprintPackage(AnimBP);
|
||||
|
||||
TSharedRef<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
Result->SetBoolField(TEXT("success"), true);
|
||||
Result->SetStringField(TEXT("stateName"), StateName);
|
||||
Result->SetStringField(TEXT("blendSpace"), BlendSpaceAsset->GetName());
|
||||
Result->SetStringField(TEXT("nodeId"), BSNode->NodeGuid.ToString());
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
return JsonToString(Result);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user