More handler wokr

This commit is contained in:
2026-03-12 17:45:27 -04:00
parent 8cf847280c
commit f0c070d4eb
4 changed files with 34 additions and 237 deletions

View File

@@ -1,66 +0,0 @@
#pragma once
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPFetcher.h"
#include "MCPUtils.h"
#include "EdGraph/EdGraphPin.h"
#include "GraphPin_GetDetails.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ============================================================
// HandleGetPinInfo — detailed information about a specific pin
// ============================================================
UCLASS()
class UMCP_GraphPin_GetDetails : public UObject, public IMCPHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Path to the pin, e.g. /Game/Widgets/WB_Hotkeys,node:MyNode,pin:Result"))
FString Pin;
virtual FString GetDescription() const override
{
return TEXT("Get detailed information about a specific pin on a blueprint node, "
"including type, connections, and default values.");
}
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
{
MCPFetcher F(Result);
UEdGraphPin* P = F.Walk(Pin).Cast<UEdGraphPin>();
if (!P) return;
Result.Appendf(TEXT("Direction: %s\n"), P->Direction == EGPD_Input ? TEXT("Input") : TEXT("Output"));
Result.Appendf(TEXT("Type: %s\n"), *MCPUtils::FormatPinType(P));
if (P->PinType.IsArray()) Result.Append(TEXT("Container: Array\n"));
else if (P->PinType.IsSet()) Result.Append(TEXT("Container: Set\n"));
else if (P->PinType.IsMap()) Result.Append(TEXT("Container: Map\n"));
if (P->PinType.bIsReference) Result.Append(TEXT("IsReference: true\n"));
if (P->PinType.bIsConst) Result.Append(TEXT("IsConst: true\n"));
if (!P->DefaultValue.IsEmpty())
Result.Appendf(TEXT("DefaultValue: %s\n"), *P->DefaultValue);
if (!P->DefaultTextValue.IsEmpty())
Result.Appendf(TEXT("DefaultTextValue: %s\n"), *P->DefaultTextValue.ToString());
if (P->DefaultObject)
Result.Appendf(TEXT("DefaultObject: %s\n"), *P->DefaultObject->GetPathName());
// Connected pins
for (UEdGraphPin* Linked : P->LinkedTo)
{
if (!Linked || !Linked->GetOwningNode()) continue;
Result.Appendf(TEXT("Connection: %s :: %s\n"),
*MCPUtils::FormatName(Linked->GetOwningNode()),
*MCPUtils::FormatName(Linked));
}
}
};

View File

@@ -5,6 +5,7 @@
#include "MCPFetcher.h" #include "MCPFetcher.h"
#include "MCPUtils.h" #include "MCPUtils.h"
#include "EdGraph/EdGraphPin.h" #include "EdGraph/EdGraphPin.h"
#include "EdGraphSchema_K2.h"
#include "GraphPin_SetDefault.generated.h" #include "GraphPin_SetDefault.generated.h"
@@ -17,6 +18,9 @@ struct FSetPinDefaultEntry
{ {
GENERATED_BODY() GENERATED_BODY()
UPROPERTY()
FString Node;
UPROPERTY() UPROPERTY()
FString Pin; FString Pin;
@@ -31,7 +35,10 @@ class UMCP_GraphPin_SetDefault : public UObject, public IMCPHandler
GENERATED_BODY() GENERATED_BODY()
public: public:
UPROPERTY(meta=(Description="Array of {pin, value} objects. Pin is a path like /Game/Foo,graph:EventGraph,node:MyNode,pin:InputPin")) UPROPERTY(meta=(Description="Graph path, e.g. /Game/Foo,graph:EventGraph"))
FString Graph;
UPROPERTY(meta=(Description="Array of {node, pin, value} objects"))
FMCPJsonArray Pins; FMCPJsonArray Pins;
virtual FString GetDescription() const override virtual FString GetDescription() const override
@@ -41,18 +48,24 @@ public:
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
{ {
// Fetch the graph once.
MCPFetcher GraphFetcher(Result);
UEdGraph* GraphObj = GraphFetcher.Walk(Graph).Cast<UEdGraph>();
if (!GraphObj) return;
int32 SuccessCount = 0; int32 SuccessCount = 0;
int32 TotalCount = Pins.Array.Num(); int32 TotalCount = Pins.Array.Num();
TSet<UEdGraphNode*> ModifiedNodes; TSet<UEdGraphNode*> ModifiedNodes;
GraphFetcher.PreEdit();
for (const TSharedPtr<FJsonValue>& PinVal : Pins.Array) for (const TSharedPtr<FJsonValue>& PinVal : Pins.Array)
{ {
FSetPinDefaultEntry Entry; FSetPinDefaultEntry Entry;
if (!MCPUtils::PopulateFromJson(FSetPinDefaultEntry::StaticStruct(), &Entry, PinVal, Result)) if (!MCPUtils::PopulateFromJson(FSetPinDefaultEntry::StaticStruct(), &Entry, PinVal, Result))
continue; continue;
MCPFetcher F(Result); MCPFetcher F(Result, GraphObj);
UEdGraphPin* Pin = F.Walk(Entry.Pin).Cast<UEdGraphPin>(); UEdGraphPin* Pin = F.Node(Entry.Node).Pin(Entry.Pin).Cast<UEdGraphPin>();
if (!Pin) continue; if (!Pin) continue;
UEdGraphNode* Node = Pin->GetOwningNode(); UEdGraphNode* Node = Pin->GetOwningNode();
@@ -63,21 +76,33 @@ public:
continue; continue;
} }
const UEdGraphSchema* Schema = Node->GetGraph()->GetSchema(); const UEdGraphSchema_K2* Schema = Cast<UEdGraphSchema_K2>(Node->GetGraph()->GetSchema());
if (Schema) check(Schema);
Schema->TrySetDefaultValue(*Pin, Entry.Value);
else // Parse and validate the value before applying.
Pin->DefaultValue = Entry.Value; FString UseDefaultValue;
TObjectPtr<UObject> UseDefaultObject = nullptr;
FText UseDefaultText;
Schema->GetPinDefaultValuesFromString(Pin->PinType, Node, Entry.Value, UseDefaultValue, UseDefaultObject, UseDefaultText, false);
FString Error = Schema->IsPinDefaultValid(Pin, UseDefaultValue, UseDefaultObject, UseDefaultText);
if (!Error.IsEmpty())
{
Result.Appendf(TEXT("error: %s: %s\n"), *MCPUtils::FormatName(Pin), *Error);
continue;
}
Pin->Modify();
Schema->TrySetDefaultValue(*Pin, Entry.Value);
SuccessCount++; SuccessCount++;
ModifiedNodes.Add(Node); ModifiedNodes.Add(Node);
F.PostEdit();
} }
for (UEdGraphNode* Node : ModifiedNodes) for (UEdGraphNode* Node : ModifiedNodes)
{ {
Node->ReconstructNode(); Node->ReconstructNode();
} }
GraphFetcher.PostEdit();
Result.Appendf(TEXT("Set %d/%d pin defaults.\n"), SuccessCount, TotalCount); Result.Appendf(TEXT("Set %d/%d pin defaults.\n"), SuccessCount, TotalCount);
} }

View File

@@ -1,48 +0,0 @@
#pragma once
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPFetcher.h"
#include "MCPUtils.h"
#include "Materials/Material.h"
#include "MaterialDomain.h"
#include "Material_DumpProperties.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UMCP_Material_DumpProperties : public UObject, public IMCPHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Material path"))
FString Path;
virtual FString GetDescription() const override
{
return TEXT("List top-level material properties such as domain, blend mode, shading model, and usage flags.");
}
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
{
MCPFetcher F(Result);
UMaterial* Mat = F.Asset(Path).Cast<UMaterial>();
if (!Mat) return;
Result.Appendf(TEXT("Material: %s\n"), *MCPUtils::FormatName(Mat));
Result.Appendf(TEXT(" domain = %s\n"), *MCPUtils::EnumToString(Mat->MaterialDomain, TEXT("MD_")));
Result.Appendf(TEXT(" blendMode = %s\n"), *MCPUtils::EnumToString(Mat->BlendMode, TEXT("BLEND_")));
Result.Appendf(TEXT(" shadingModel = %s\n"), *MCPUtils::EnumToString(Mat->GetShadingModels().GetFirstShadingModel(), TEXT("MSM_")));
Result.Appendf(TEXT(" opacityMaskClipValue = %g\n"), Mat->OpacityMaskClipValue);
Result.Appendf(TEXT(" twoSided = %s\n"), Mat->TwoSided ? TEXT("true") : TEXT("false"));
Result.Appendf(TEXT(" bUsedWithSkeletalMesh = %s\n"), Mat->bUsedWithSkeletalMesh ? TEXT("true") : TEXT("false"));
Result.Appendf(TEXT(" bUsedWithMorphTargets = %s\n"), Mat->bUsedWithMorphTargets ? TEXT("true") : TEXT("false"));
Result.Appendf(TEXT(" bUsedWithNiagaraSprites = %s\n"), Mat->bUsedWithNiagaraSprites ? TEXT("true") : TEXT("false"));
Result.Appendf(TEXT(" ditheredLODTransition = %s\n"), Mat->DitheredLODTransition ? TEXT("true") : TEXT("false"));
Result.Appendf(TEXT(" bAllowNegativeEmissiveColor = %s\n"), Mat->bAllowNegativeEmissiveColor ? TEXT("true") : TEXT("false"));
}
};

View File

@@ -1,114 +0,0 @@
#pragma once
#include "CoreMinimal.h"
#include "MCPHandler.h"
#include "MCPFetcher.h"
#include "MCPUtils.h"
#include "Materials/Material.h"
#include "MaterialDomain.h"
#include "Material_SetProperty.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UMCP_Material_SetProperty : public UObject, public IMCPHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Material path"))
FString Path;
UPROPERTY(meta=(Description="Property name to set (domain, blendMode, twoSided, shadingModel, opacity, opacityMaskClipValue, bUsedWithSkeletalMesh, bUsedWithMorphTargets, bUsedWithNiagaraSprites, ditheredLODTransition, bAllowNegativeEmissiveColor)"))
FString Property;
UPROPERTY(meta=(Description="Value to set"))
FString Value;
virtual FString GetDescription() const override
{
return TEXT("Set a top-level material property such as domain, blend mode, shading model, or usage flags.");
}
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
{
MCPFetcher F(Result);
UMaterial* Mat = F.Asset(Path).Cast<UMaterial>();
if (!Mat) return;
// Parse value and build setter. Validation happens here so we can bail before PreEdit.
TFunction<void()> Setter;
if (Property == TEXT("domain"))
{
EMaterialDomain NewDomain;
if (!MCPUtils::StringToEnum(Value, NewDomain, Result, TEXT("MD_"))) return;
Setter = [Mat, NewDomain]() { Mat->MaterialDomain = NewDomain; };
}
else if (Property == TEXT("blendMode"))
{
EBlendMode NewBlend;
if (!MCPUtils::StringToEnum(Value, NewBlend, Result, TEXT("BLEND_"))) return;
Setter = [Mat, NewBlend]() { Mat->BlendMode = NewBlend; };
}
else if (Property == TEXT("shadingModel"))
{
EMaterialShadingModel NewModel;
if (!MCPUtils::StringToEnum(Value, NewModel, Result, TEXT("MSM_"))) return;
Setter = [Mat, NewModel]() { Mat->SetShadingModel(NewModel); };
}
else if (Property == TEXT("opacity") || Property == TEXT("opacityMaskClipValue"))
{
float FloatVal = FCString::Atof(*Value);
Setter = [Mat, FloatVal]() { Mat->OpacityMaskClipValue = FloatVal; };
}
else if (Property == TEXT("twoSided"))
{
bool bVal; if (!MCPUtils::StringToBool(Value, bVal, Result)) return;
Setter = [Mat, bVal]() { Mat->TwoSided = bVal ? 1 : 0; };
}
else if (Property == TEXT("bUsedWithSkeletalMesh"))
{
bool bVal; if (!MCPUtils::StringToBool(Value, bVal, Result)) return;
Setter = [Mat, bVal]() { Mat->bUsedWithSkeletalMesh = bVal ? 1 : 0; };
}
else if (Property == TEXT("bUsedWithMorphTargets"))
{
bool bVal; if (!MCPUtils::StringToBool(Value, bVal, Result)) return;
Setter = [Mat, bVal]() { Mat->bUsedWithMorphTargets = bVal ? 1 : 0; };
}
else if (Property == TEXT("bUsedWithNiagaraSprites"))
{
bool bVal; if (!MCPUtils::StringToBool(Value, bVal, Result)) return;
Setter = [Mat, bVal]() { Mat->bUsedWithNiagaraSprites = bVal ? 1 : 0; };
}
else if (Property == TEXT("ditheredLODTransition") || Property == TEXT("DitheredLODTransition"))
{
bool bVal; if (!MCPUtils::StringToBool(Value, bVal, Result)) return;
Setter = [Mat, bVal]() { Mat->DitheredLODTransition = bVal ? 1 : 0; };
}
else if (Property == TEXT("bAllowNegativeEmissiveColor"))
{
bool bVal; if (!MCPUtils::StringToBool(Value, bVal, Result)) return;
Setter = [Mat, bVal]() { Mat->bAllowNegativeEmissiveColor = bVal ? 1 : 0; };
}
else
{
Result.Appendf(TEXT("ERROR: Unknown property '%s'. Valid: domain, blendMode, twoSided, shadingModel, "
"opacity, opacityMaskClipValue, bUsedWithSkeletalMesh, bUsedWithMorphTargets, "
"bUsedWithNiagaraSprites, ditheredLODTransition, bAllowNegativeEmissiveColor\n"), *Property);
return;
}
// Apply the change between PreEdit/PostEdit.
F.PreEdit();
Setter();
F.PostEdit();
MCPUtils::SaveMaterialPackage(Mat);
Result.Appendf(TEXT("Set %s on %s\n"), *Property, *MCPUtils::FormatName(Mat));
}
};