diff --git a/Plugins/UEWingman/Deprecated/AnimBlueprint_Create.h b/Plugins/UEWingman/Deprecated/AnimBlueprint_Create.h deleted file mode 100644 index 198102fc..00000000 --- a/Plugins/UEWingman/Deprecated/AnimBlueprint_Create.h +++ /dev/null @@ -1,106 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "WingServer.h" -#include "WingHandler.h" -#include "WingFetcher.h" -#include "WingUtils.h" -#include "WingPackageMaker.h" -#include "Animation/AnimBlueprint.h" -#include "Animation/AnimBlueprintGeneratedClass.h" -#include "Animation/AnimInstance.h" -#include "Animation/Skeleton.h" -#include "Kismet2/KismetEditorUtilities.h" -#include "AnimBlueprint_Create.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS() -class UWing_AnimBlueprint_Create : public UObject, public IWingHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Full asset path for the new Animation Blueprint (e.g. '/Game/AnimBP/ABP_Character')")) - FString AssetPath; - - UPROPERTY(meta=(Description="Skeleton asset package path")) - FString Skeleton; - - UPROPERTY(meta=(Optional, Description="Parent class name (default: AnimInstance)")) - FString ParentClass; - - virtual FString GetDescription() const override - { - return TEXT("Create a new Animation Blueprint asset with a specified skeleton."); - } - - virtual void Handle() override - { - WingPackageMaker Maker(AssetPath); - if (!Maker.Ok()) return; - - // Resolve skeleton - WingFetcher SkeletonFetcher; - USkeleton* SkeletonObj = SkeletonFetcher.Asset(Skeleton).Cast(); - if (!SkeletonObj) return; - - // Resolve parent class (default: UAnimInstance) - UClass* ParentClassObj = UAnimInstance::StaticClass(); - if (!ParentClass.IsEmpty() && !ParentClass.Equals(TEXT("AnimInstance"), ESearchCase::IgnoreCase)) - { - UClass* Found = nullptr; - for (TObjectIterator It; It; ++It) - { - if (It->IsChildOf(UAnimInstance::StaticClass()) && WingUtils::Identifies(ParentClass, *It)) - { - Found = *It; - break; - } - } - if (!Found) - { - UWingServer::Printf(TEXT("ERROR: Parent class '%s' not found (must derive from AnimInstance)\n"), *ParentClass); - return; - } - ParentClassObj = Found; - } - - // Create the package and Animation Blueprint - if (!Maker.Make()) return; - - UAnimBlueprint* NewAnimBP = CastChecked( - FKismetEditorUtilities::CreateBlueprint( - ParentClassObj, - Maker.Package(), - FName(*Maker.Name()), - BPTYPE_Normal, - UAnimBlueprint::StaticClass(), - UAnimBlueprintGeneratedClass::StaticClass() - )); - - if (!NewAnimBP) - { - UWingServer::Print(TEXT("ERROR: FKismetEditorUtilities::CreateBlueprint returned null\n")); - return; - } - - // Set target skeleton - NewAnimBP->TargetSkeleton = SkeletonObj; - - // Compile - FKismetEditorUtilities::CompileBlueprint(NewAnimBP); - - UWingServer::Printf(TEXT("Created: %s\n"), *AssetPath); - UWingServer::Printf(TEXT("ParentClass: %s\n"), *WingUtils::FormatName(ParentClassObj)); - - TArray Graphs = WingUtils::AllGraphs(NewAnimBP); - for (UEdGraph* Graph : Graphs) - { - UWingServer::Printf(TEXT("Graph: %s\n"), *WingUtils::FormatName(Graph)); - } - } -}; diff --git a/Plugins/UEWingman/Deprecated/AnimBlueprint_ListSyncGroups.h b/Plugins/UEWingman/Deprecated/AnimBlueprint_ListSyncGroups.h deleted file mode 100644 index c950e927..00000000 --- a/Plugins/UEWingman/Deprecated/AnimBlueprint_ListSyncGroups.h +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "WingServer.h" -#include "WingHandler.h" -#include "WingFetcher.h" -#include "WingUtils.h" -#include "Animation/AnimBlueprint.h" -#include "AnimGraphNode_Base.h" -#include "AnimBlueprint_ListSyncGroups.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS() -class UWing_AnimBlueprint_ListSyncGroups : public UObject, public IWingHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Path to an Animation Blueprint, e.g. /Game/Foo/ABP_Character")) - FString Blueprint; - - virtual FString GetDescription() const override - { - return TEXT("List all sync group names used in an Animation Blueprint."); - } - - virtual void Handle() override - { - WingFetcher F; - UAnimBlueprint* AnimBP = F.Walk(Blueprint).Cast(); - if (!AnimBP) return; - - // Walk all anim nodes to collect sync group names - TSet SyncGroupNames; - for (UAnimGraphNode_Base* AnimNode : WingUtils::AllNodes(AnimBP)) - { - for (TFieldIterator PropIt(AnimNode->GetClass()); PropIt; ++PropIt) - { - if (PropIt->GetName().Contains(TEXT("SyncGroup")) || PropIt->GetName().Contains(TEXT("GroupName"))) - { - FName GroupValue = PropIt->GetPropertyValue_InContainer(AnimNode); - if (!GroupValue.IsNone()) - { - SyncGroupNames.Add(GroupValue.ToString()); - } - } - } - } - - if (SyncGroupNames.Num() == 0) - { - UWingServer::Print(TEXT("No sync groups found.\n")); - return; - } - - for (const FString& Group : SyncGroupNames) - { - UWingServer::Printf(TEXT("%s\n"), *Group); - } - } -}; diff --git a/Plugins/UEWingman/Deprecated/AnimBlueprint_SetBlendSpaceSamples.h b/Plugins/UEWingman/Deprecated/AnimBlueprint_SetBlendSpaceSamples.h deleted file mode 100644 index d9958691..00000000 --- a/Plugins/UEWingman/Deprecated/AnimBlueprint_SetBlendSpaceSamples.h +++ /dev/null @@ -1,131 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "WingServer.h" -#include "WingHandler.h" -#include "WingFetcher.h" -#include "WingJson.h" -#include "WingUtils.h" -#include "Animation/AnimSequence.h" -#include "Animation/BlendSpace.h" -#include "AnimBlueprint_SetBlendSpaceSamples.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -USTRUCT() -struct FBlendSpaceSampleEntry -{ - GENERATED_BODY() - - UPROPERTY() - FString AnimationAsset; - - UPROPERTY() - float X = 0.0f; - - UPROPERTY() - float Y = 0.0f; -}; - -UCLASS() -class UWing_AnimBlueprint_SetBlendSpaceSamples : public UObject, public IWingHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Blend Space package path")) - FString BlendSpace; - - UPROPERTY(meta=(Optional, Description="Display name for the X axis")) - FString AxisXName; - - UPROPERTY(meta=(Optional, Description="Display name for the Y axis")) - FString AxisYName; - - UPROPERTY(meta=(Optional, Description="Minimum value for X axis")) - float AxisXMin = 0.0f; - - UPROPERTY(meta=(Optional, Description="Maximum value for X axis")) - float AxisXMax = 0.0f; - - UPROPERTY(meta=(Optional, Description="Minimum value for Y axis")) - float AxisYMin = 0.0f; - - UPROPERTY(meta=(Optional, Description="Maximum value for Y axis")) - float AxisYMax = 0.0f; - - UPROPERTY(meta=(Optional, Description="Array of sample points, each with animationAsset, x, y")) - FWingJsonArray Samples; - - virtual FString GetDescription() const override - { - return TEXT("Set axis parameters and animation sample points on a Blend Space. " - "Replaces all existing samples."); - } - - virtual void Handle() override - { - // Load the blend space - WingFetcher F; - UBlendSpace* BS = F.Asset(BlendSpace).Cast(); - if (!BS) return; - - // Set axis parameters - const FBlendParameter& ParamX = BS->GetBlendParameter(0); - const FBlendParameter& ParamY = BS->GetBlendParameter(1); - - // We need to modify BlendParameters directly — use const_cast since there's no setter API - FBlendParameter& MutableParamX = const_cast(ParamX); - FBlendParameter& MutableParamY = const_cast(ParamY); - - if (!AxisXName.IsEmpty()) MutableParamX.DisplayName = AxisXName; - if (AxisXMin != 0.0f) MutableParamX.Min = AxisXMin; - if (AxisXMax != 0.0f) MutableParamX.Max = AxisXMax; - - if (!AxisYName.IsEmpty()) MutableParamY.DisplayName = AxisYName; - if (AxisYMin != 0.0f) MutableParamY.Min = AxisYMin; - if (AxisYMax != 0.0f) MutableParamY.Max = AxisYMax; - - // Clear existing samples (delete from end to start) - int32 NumExisting = BS->GetNumberOfBlendSamples(); - for (int32 i = NumExisting - 1; i >= 0; --i) - { - BS->DeleteSample(i); - } - - // Add new samples - int32 SamplesSet = 0; - - for (const TSharedPtr& SampleVal : Samples.Array) - { - FBlendSpaceSampleEntry Entry; - if (!WingJson::PopulateFromJson(FBlendSpaceSampleEntry::StaticStruct(), &Entry, SampleVal)) return; - - UAnimSequence* AnimSeq = nullptr; - if (!Entry.AnimationAsset.IsEmpty()) - { - WingFetcher F2; - AnimSeq = F2.Asset(Entry.AnimationAsset).Cast(); - if (!AnimSeq) return; - } - - FVector SampleValue(Entry.X, Entry.Y, 0.0f); - if (AnimSeq) - { - BS->AddSample(AnimSeq, SampleValue); - } - else - { - BS->AddSample(SampleValue); - } - SamplesSet++; - } - - BS->ValidateSampleData(); - - UWingServer::Printf(TEXT("Set %d samples on %s\n"), SamplesSet, *WingUtils::FormatName(BS)); - } -}; diff --git a/Plugins/UEWingman/Deprecated/BlendSpace_Create.h b/Plugins/UEWingman/Deprecated/BlendSpace_Create.h deleted file mode 100644 index c9599024..00000000 --- a/Plugins/UEWingman/Deprecated/BlendSpace_Create.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "WingServer.h" -#include "WingHandler.h" -#include "WingFetcher.h" -#include "WingUtils.h" -#include "WingPackageMaker.h" -#include "Animation/Skeleton.h" -#include "Animation/BlendSpace.h" -#include "BlendSpace_Create.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS() -class UWing_BlendSpace_Create : public UObject, public IWingHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Full asset path for the new Blend Space (e.g. '/Game/BlendSpaces/BS_Locomotion')")) - FString AssetPath; - - UPROPERTY(meta=(Description="Skeleton asset package path")) - FString Skeleton; - - virtual FString GetDescription() const override - { - return TEXT("Create a new 2D Blend Space asset with a specified skeleton."); - } - - virtual void Handle() override - { - WingPackageMaker Maker(AssetPath); - if (!Maker.Ok()) return; - - // Resolve skeleton. - WingFetcher SkeletonFetcher; - USkeleton* SkeletonObj = SkeletonFetcher.Asset(Skeleton).Cast(); - if (!SkeletonObj) return; - - // Create the package and Blend Space. - if (!Maker.Make()) return; - UBlendSpace* NewBS = NewObject(Maker.Package(), FName(*Maker.Name()), RF_Public | RF_Standalone); - if (!NewBS) - { - UWingServer::Print(TEXT("ERROR: Failed to create Blend Space object\n")); - return; - } - - // Set skeleton. - NewBS->SetSkeleton(SkeletonObj); - - NewBS->MarkPackageDirty(); - - UWingServer::Printf(TEXT("Created %s\n"), *NewBS->GetPathName()); - UWingServer::Printf(TEXT("Skeleton: %s\n"), *SkeletonObj->GetPathName()); - } -}; diff --git a/Plugins/UEWingman/Deprecated/BlueprintExportSubsystem.cpp b/Plugins/UEWingman/Deprecated/BlueprintExportSubsystem.cpp deleted file mode 100644 index 239b4a03..00000000 --- a/Plugins/UEWingman/Deprecated/BlueprintExportSubsystem.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "BlueprintExportSubsystem.h" -#include "BlueprintExporter.h" -#include "Engine/Blueprint.h" -#include "EdGraph/EdGraph.h" -#include "UObject/Package.h" -#include "Exporters/Exporter.h" -#include "UnrealExporter.h" -#include "Misc/FileHelper.h" -#include "Misc/Paths.h" - -void UBlueprintExportSubsystem::Initialize(FSubsystemCollectionBase& Collection) -{ - Super::Initialize(Collection); - - OnAssetSavedHandle = UPackage::PackageSavedWithContextEvent.AddUObject( - this, &UBlueprintExportSubsystem::OnAssetSaved); -} - -void UBlueprintExportSubsystem::Deinitialize() -{ - UPackage::PackageSavedWithContextEvent.Remove(OnAssetSavedHandle); - Super::Deinitialize(); -} - -void UBlueprintExportSubsystem::OnAssetSaved(const FString& PackageFilename, UPackage* Package, FObjectPostSaveContext Context) -{ - if (!Package) return; - - FString PkDir = FPaths::ProjectDir() / TEXT("Saved") / TEXT("BlueprintExports") / FPaths::GetBaseFilename(PackageFilename); - IFileManager::Get().DeleteDirectory(*PkDir, false, true); - - // Export the whole package in both formats for comparison. - { - FStringOutputDevice Archive; - const FExportObjectInnerContext InnerContext; - UExporter::ExportToOutputDevice(&InnerContext, Package, nullptr, Archive, TEXT("copy"), 0); - FFileHelper::SaveStringToFile(Archive, *(PkDir / TEXT("COPY_DUMP.txt"))); - } - { - FStringOutputDevice Archive; - const FExportObjectInnerContext InnerContext; - UExporter::ExportToOutputDevice(&InnerContext, Package, nullptr, Archive, TEXT("t3d"), 0); - FFileHelper::SaveStringToFile(Archive, *(PkDir / TEXT("T3D_DUMP.txt"))); - } - - TArray AllObjects; - GetObjectsWithPackage(Package, AllObjects); - for (UObject *Obj : AllObjects) - { - if (UBlueprint *BP = Cast(Obj)) - { - FString BPDir = PkDir / BP->GetName(); - TArray AllGraphs; - BP->GetAllGraphs(AllGraphs); - for (UEdGraph* Graph : AllGraphs) - { - MCPGraphExport Exporter(Graph); - - FString FilePath = BPDir / Graph->GetName() + TEXT(".txt"); - FString DetailsPath = BPDir / TEXT("DETAILS") / Graph->GetName() + TEXT(".txt"); - FFileHelper::SaveStringToFile(Exporter.GetOutput(), *FilePath); - FFileHelper::SaveStringToFile(Exporter.GetDetails(), *DetailsPath); - UE_LOG(LogTemp, Warning, TEXT("Blueprint export: %s"), *FilePath); - } - } - } -} diff --git a/Plugins/UEWingman/Deprecated/BlueprintExportSubsystem.h b/Plugins/UEWingman/Deprecated/BlueprintExportSubsystem.h deleted file mode 100644 index 1216638e..00000000 --- a/Plugins/UEWingman/Deprecated/BlueprintExportSubsystem.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "EditorSubsystem.h" -#include "UObject/ObjectSaveContext.h" -#include "BlueprintExportSubsystem.generated.h" - -/** - * Editor subsystem that exports blueprint text files whenever an asset is saved. - */ -UCLASS() -class UBlueprintExportSubsystem : public UEditorSubsystem -{ - GENERATED_BODY() - -public: - virtual void Initialize(FSubsystemCollectionBase& Collection) override; - virtual void Deinitialize() override; - -private: - void OnAssetSaved(const FString& PackageFilename, UPackage* Package, FObjectPostSaveContext Context); - FDelegateHandle OnAssetSavedHandle; -}; diff --git a/Plugins/UEWingman/Deprecated/BlueprintSerialization.cpp b/Plugins/UEWingman/Deprecated/BlueprintSerialization.cpp deleted file mode 100644 index e91023fc..00000000 --- a/Plugins/UEWingman/Deprecated/BlueprintSerialization.cpp +++ /dev/null @@ -1,450 +0,0 @@ -#include "MCPUtils.h" -#include "Dom/JsonValue.h" -#include "Engine/Blueprint.h" -#include "EdGraph/EdGraph.h" -#include "EdGraph/EdGraphNode.h" -#include "EdGraph/EdGraphPin.h" -#include "K2Node_CallFunction.h" -#include "K2Node_Event.h" -#include "K2Node_CustomEvent.h" -#include "K2Node_FunctionEntry.h" -#include "K2Node_EditablePinBase.h" -#include "K2Node_VariableGet.h" -#include "K2Node_VariableSet.h" -#include "K2Node_MacroInstance.h" -#include "K2Node_DynamicCast.h" -#include "K2Node_CallParentFunction.h" -#include "K2Node_IfThenElse.h" - -// Animation Blueprint support -#include "Animation/AnimBlueprint.h" -#include "AnimGraphNode_StateMachine.h" -#include "AnimGraphNode_AssetPlayerBase.h" -#include "AnimGraphNode_SequencePlayer.h" -#include "AnimGraphNode_BlendSpacePlayer.h" -#include "AnimGraphNode_Base.h" -#include "AnimStateNode.h" -#include "AnimStateTransitionNode.h" -#include "AnimStateConduitNode.h" -#include "AnimStateEntryNode.h" -#include "AnimationStateMachineGraph.h" - -// Material support -#include "Materials/MaterialExpression.h" -#include "Materials/MaterialExpressionScalarParameter.h" -#include "Materials/MaterialExpressionVectorParameter.h" -#include "Materials/MaterialExpressionTextureSampleParameter2D.h" -#include "Materials/MaterialExpressionStaticSwitchParameter.h" -#include "Materials/MaterialExpressionConstant.h" -#include "Materials/MaterialExpressionConstant3Vector.h" -#include "Materials/MaterialExpressionConstant4Vector.h" -#include "Materials/MaterialExpressionTextureSample.h" -#include "Materials/MaterialExpressionTextureCoordinate.h" -#include "Materials/MaterialExpressionComponentMask.h" -#include "Materials/MaterialExpressionCustom.h" -#include "Materials/MaterialExpressionFunctionInput.h" -#include "Materials/MaterialExpressionFunctionOutput.h" -#include "Materials/MaterialExpressionMaterialFunctionCall.h" -#include "MaterialGraph/MaterialGraphNode.h" - -TSharedRef MCPUtils::SerializeBlueprint(UBlueprint* BP) -{ - TSharedRef J = MakeShared(); - J->SetStringField(TEXT("name"), BP->GetName()); - J->SetStringField(TEXT("path"), BP->GetPackage()->GetName()); - J->SetStringField(TEXT("parentClass"), BP->ParentClass ? BP->ParentClass->GetName() : TEXT("None")); - J->SetStringField(TEXT("blueprintType"), - StaticEnum()->GetNameStringByValue((int64)BP->BlueprintType)); - - // Animation Blueprint detection - if (UAnimBlueprint* AnimBP = Cast(BP)) - { - J->SetBoolField(TEXT("isAnimBlueprint"), true); - if (AnimBP->TargetSkeleton) - { - J->SetStringField(TEXT("targetSkeleton"), AnimBP->TargetSkeleton->GetName()); - J->SetStringField(TEXT("targetSkeletonPath"), AnimBP->TargetSkeleton->GetPathName()); - } - } - - // Variables - TArray> Vars; - for (const FBPVariableDescription& V : BP->NewVariables) - { - TSharedRef VJ = MakeShared(); - VJ->SetStringField(TEXT("name"), V.VarName.ToString()); - VJ->SetStringField(TEXT("type"), V.VarType.PinCategory.ToString()); - if (V.VarType.PinSubCategoryObject.IsValid()) - VJ->SetStringField(TEXT("subtype"), V.VarType.PinSubCategoryObject->GetName()); - VJ->SetBoolField(TEXT("isArray"), V.VarType.IsArray()); - VJ->SetBoolField(TEXT("isSet"), V.VarType.IsSet()); - VJ->SetBoolField(TEXT("isMap"), V.VarType.IsMap()); - VJ->SetStringField(TEXT("category"), V.Category.ToString()); - VJ->SetStringField(TEXT("defaultValue"), V.DefaultValue); - Vars.Add(MakeShared(VJ)); - } - J->SetArrayField(TEXT("variables"), Vars); - - // Interfaces - TArray> Ifaces; - for (const FBPInterfaceDescription& I : BP->ImplementedInterfaces) - { - if (I.Interface) - Ifaces.Add(MakeShared(I.Interface->GetName())); - } - J->SetArrayField(TEXT("interfaces"), Ifaces); - - return J; -} - -TSharedPtr MCPUtils::SerializeNode(UEdGraphNode* Node) -{ - TSharedRef NJ = MakeShared(); - NJ->SetStringField(TEXT("id"), Node->NodeGuid.ToString()); - NJ->SetStringField(TEXT("class"), Node->GetClass()->GetName()); - NJ->SetStringField(TEXT("title"), Node->GetNodeTitle(ENodeTitleType::FullTitle).ToString()); - if (!Node->NodeComment.IsEmpty()) - NJ->SetStringField(TEXT("comment"), Node->NodeComment); - NJ->SetNumberField(TEXT("posX"), Node->NodePosX); - NJ->SetNumberField(TEXT("posY"), Node->NodePosY); - - // Material graph node — extract UMaterialExpression data - if (UMaterialGraphNode* MatNode = Cast(Node)) - { - NJ->SetStringField(TEXT("nodeType"), TEXT("MaterialExpression")); - if (MatNode->MaterialExpression) - { - TSharedPtr ExprJson = SerializeMaterialExpression(MatNode->MaterialExpression); - if (ExprJson.IsValid()) - { - NJ->SetObjectField(TEXT("expression"), ExprJson); - } - } - } - // Animation Blueprint node types - else if (auto* SMNode = Cast(Node)) - { - NJ->SetStringField(TEXT("nodeType"), TEXT("AnimStateMachine")); - if (SMNode->EditorStateMachineGraph) - { - NJ->SetStringField(TEXT("stateMachineName"), SMNode->EditorStateMachineGraph->GetName()); - int32 StateCount = 0, TransitionCount = 0; - for (UEdGraphNode* SubNode : SMNode->EditorStateMachineGraph->Nodes) - { - if (Cast(SubNode)) StateCount++; - else if (Cast(SubNode)) TransitionCount++; - } - NJ->SetNumberField(TEXT("stateCount"), StateCount); - NJ->SetNumberField(TEXT("transitionCount"), TransitionCount); - } - } - else if (auto* SeqPlayer = Cast(Node)) - { - NJ->SetStringField(TEXT("nodeType"), TEXT("AnimSequencePlayer")); - if (UAnimationAsset* Asset = SeqPlayer->GetAnimationAsset()) - { - NJ->SetStringField(TEXT("animationAsset"), Asset->GetName()); - NJ->SetStringField(TEXT("animationAssetPath"), Asset->GetPathName()); - } - } - else if (auto* BSPlayer = Cast(Node)) - { - NJ->SetStringField(TEXT("nodeType"), TEXT("AnimBlendSpacePlayer")); - if (UAnimationAsset* Asset = BSPlayer->GetAnimationAsset()) - { - NJ->SetStringField(TEXT("blendSpaceAsset"), Asset->GetName()); - NJ->SetStringField(TEXT("blendSpaceAssetPath"), Asset->GetPathName()); - } - } - else if (auto* AssetPlayer = Cast(Node)) - { - NJ->SetStringField(TEXT("nodeType"), TEXT("AnimAssetPlayer")); - if (UAnimationAsset* Asset = AssetPlayer->GetAnimationAsset()) - { - NJ->SetStringField(TEXT("animationAsset"), Asset->GetName()); - NJ->SetStringField(TEXT("animationAssetPath"), Asset->GetPathName()); - } - } - else if (Cast(Node)) - { - NJ->SetStringField(TEXT("nodeType"), TEXT("AnimNode")); - } - else if (auto* StateNode = Cast(Node)) - { - NJ->SetStringField(TEXT("nodeType"), TEXT("AnimState")); - NJ->SetStringField(TEXT("stateName"), StateNode->GetStateName()); - NJ->SetBoolField(TEXT("bAlwaysResetOnEntry"), StateNode->bAlwaysResetOnEntry); - } - else if (auto* TransNode = Cast(Node)) - { - NJ->SetStringField(TEXT("nodeType"), TEXT("AnimTransition")); - if (UAnimStateNode* FromState = Cast(TransNode->GetPreviousState())) - { - NJ->SetStringField(TEXT("fromState"), FromState->GetStateName()); - } - if (UAnimStateNode* ToState = Cast(TransNode->GetNextState())) - { - NJ->SetStringField(TEXT("toState"), ToState->GetStateName()); - } - NJ->SetNumberField(TEXT("crossfadeDuration"), TransNode->CrossfadeDuration); - NJ->SetNumberField(TEXT("blendMode"), (int32)TransNode->BlendMode); - NJ->SetNumberField(TEXT("priorityOrder"), TransNode->PriorityOrder); - NJ->SetNumberField(TEXT("logicType"), (int32)TransNode->LogicType.GetValue()); - NJ->SetBoolField(TEXT("bBidirectional"), TransNode->Bidirectional); - } - else if (Cast(Node)) - { - NJ->SetStringField(TEXT("nodeType"), TEXT("AnimConduit")); - } - else if (Cast(Node)) - { - NJ->SetStringField(TEXT("nodeType"), TEXT("AnimStateEntry")); - } - // K2Node specifics — check CallParentFunction before CallFunction (inheritance) - else if (auto* CPF = Cast(Node)) - { - NJ->SetStringField(TEXT("functionName"), CPF->FunctionReference.GetMemberName().ToString()); - if (CPF->FunctionReference.GetMemberParentClass()) - NJ->SetStringField(TEXT("targetClass"), CPF->FunctionReference.GetMemberParentClass()->GetName()); - NJ->SetStringField(TEXT("nodeType"), TEXT("CallParentFunction")); - } - else if (auto* CF = Cast(Node)) - { - NJ->SetStringField(TEXT("functionName"), CF->FunctionReference.GetMemberName().ToString()); - if (CF->FunctionReference.GetMemberParentClass()) - NJ->SetStringField(TEXT("targetClass"), CF->FunctionReference.GetMemberParentClass()->GetName()); - } - else if (auto* FE = Cast(Node)) - { - NJ->SetStringField(TEXT("nodeType"), TEXT("FunctionEntry")); - - // Serialize UserDefinedPins (parameter names and types) - TArray> ParamArr; - for (const TSharedPtr& PinInfo : FE->UserDefinedPins) - { - if (!PinInfo.IsValid()) continue; - TSharedRef ParamJ = MakeShared(); - ParamJ->SetStringField(TEXT("name"), PinInfo->PinName.ToString()); - FString ParamType = PinInfo->PinType.PinCategory.ToString(); - ParamJ->SetStringField(TEXT("type"), ParamType); - if (PinInfo->PinType.PinSubCategoryObject.IsValid()) - ParamJ->SetStringField(TEXT("subtype"), PinInfo->PinType.PinSubCategoryObject->GetName()); - else if (ParamType == TEXT("None") || ParamType.IsEmpty()) - ParamJ->SetBoolField(TEXT("typeUnknown"), true); - ParamArr.Add(MakeShared(ParamJ)); - } - NJ->SetArrayField(TEXT("parameters"), ParamArr); - } - else if (auto* Ev = Cast(Node)) - { - NJ->SetStringField(TEXT("eventName"), Ev->EventReference.GetMemberName().ToString()); - NJ->SetStringField(TEXT("nodeType"), Ev->bOverrideFunction ? TEXT("OverrideEvent") : TEXT("Event")); - } - else if (auto* CE = Cast(Node)) - { - NJ->SetStringField(TEXT("eventName"), CE->CustomFunctionName.ToString()); - NJ->SetStringField(TEXT("nodeType"), TEXT("CustomEvent")); - - // Serialize UserDefinedPins (parameter names and types) - TArray> ParamArr; - for (const TSharedPtr& PinInfo : CE->UserDefinedPins) - { - if (!PinInfo.IsValid()) continue; - TSharedRef ParamJ = MakeShared(); - ParamJ->SetStringField(TEXT("name"), PinInfo->PinName.ToString()); - FString ParamType = PinInfo->PinType.PinCategory.ToString(); - ParamJ->SetStringField(TEXT("type"), ParamType); - if (PinInfo->PinType.PinSubCategoryObject.IsValid()) - ParamJ->SetStringField(TEXT("subtype"), PinInfo->PinType.PinSubCategoryObject->GetName()); - else if (ParamType == TEXT("None") || ParamType.IsEmpty()) - ParamJ->SetBoolField(TEXT("typeUnknown"), true); - ParamArr.Add(MakeShared(ParamJ)); - } - NJ->SetArrayField(TEXT("parameters"), ParamArr); - } - else if (auto* VG = Cast(Node)) - { - NJ->SetStringField(TEXT("variableName"), VG->GetVarName().ToString()); - NJ->SetStringField(TEXT("nodeType"), TEXT("VariableGet")); - } - else if (auto* VS = Cast(Node)) - { - NJ->SetStringField(TEXT("variableName"), VS->GetVarName().ToString()); - NJ->SetStringField(TEXT("nodeType"), TEXT("VariableSet")); - } - else if (auto* MI = Cast(Node)) - { - if (MI->GetMacroGraph()) - NJ->SetStringField(TEXT("macroName"), MI->GetMacroGraph()->GetName()); - NJ->SetStringField(TEXT("nodeType"), TEXT("MacroInstance")); - } - else if (auto* DC = Cast(Node)) - { - if (DC->TargetType) - NJ->SetStringField(TEXT("castTarget"), DC->TargetType->GetName()); - NJ->SetStringField(TEXT("nodeType"), TEXT("DynamicCast")); - } - else if (Cast(Node)) - { - NJ->SetStringField(TEXT("nodeType"), TEXT("Branch")); - } - - // Pins - TArray> Pins; - for (UEdGraphPin* Pin : Node->Pins) - { - if (!Pin || Pin->bHidden) continue; - TSharedPtr PJ = SerializePin(Pin); - if (PJ.IsValid()) - Pins.Add(MakeShared(PJ.ToSharedRef())); - } - NJ->SetArrayField(TEXT("pins"), Pins); - return NJ; -} - -TSharedPtr MCPUtils::SerializePin(UEdGraphPin* Pin) -{ - TSharedRef PJ = MakeShared(); - PJ->SetStringField(TEXT("name"), Pin->PinName.ToString()); - PJ->SetStringField(TEXT("direction"), Pin->Direction == EGPD_Input ? TEXT("Input") : TEXT("Output")); - PJ->SetStringField(TEXT("type"), Pin->PinType.PinCategory.ToString()); - if (Pin->PinType.PinSubCategoryObject.IsValid()) - PJ->SetStringField(TEXT("subtype"), Pin->PinType.PinSubCategoryObject->GetName()); - if (!Pin->DefaultValue.IsEmpty()) - PJ->SetStringField(TEXT("defaultValue"), Pin->DefaultValue); - - if (Pin->LinkedTo.Num() > 0) - { - TArray> Conns; - for (UEdGraphPin* Linked : Pin->LinkedTo) - { - if (!Linked || !Linked->GetOwningNode()) continue; - TSharedRef CJ = MakeShared(); - CJ->SetStringField(TEXT("nodeId"), Linked->GetOwningNode()->NodeGuid.ToString()); - CJ->SetStringField(TEXT("pinName"), Linked->PinName.ToString()); - Conns.Add(MakeShared(CJ)); - } - PJ->SetArrayField(TEXT("connections"), Conns); - } - return PJ; -} - -TSharedPtr MCPUtils::SerializeMaterialExpression(UMaterialExpression* Expression) -{ - if (!Expression) return nullptr; - - TSharedRef EJ = MakeShared(); - EJ->SetStringField(TEXT("class"), Expression->GetClass()->GetName()); - EJ->SetStringField(TEXT("name"), Expression->GetName()); - EJ->SetStringField(TEXT("description"), Expression->GetDescription()); - EJ->SetNumberField(TEXT("posX"), Expression->MaterialExpressionEditorX); - EJ->SetNumberField(TEXT("posY"), Expression->MaterialExpressionEditorY); - - if (auto* SP = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("ScalarParameter")); - EJ->SetStringField(TEXT("parameterName"), SP->ParameterName.ToString()); - EJ->SetNumberField(TEXT("defaultValue"), SP->DefaultValue); - EJ->SetStringField(TEXT("group"), SP->Group.ToString()); - } - else if (auto* VP = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("VectorParameter")); - EJ->SetStringField(TEXT("parameterName"), VP->ParameterName.ToString()); - TSharedRef DefVal = MakeShared(); - DefVal->SetNumberField(TEXT("r"), VP->DefaultValue.R); - DefVal->SetNumberField(TEXT("g"), VP->DefaultValue.G); - DefVal->SetNumberField(TEXT("b"), VP->DefaultValue.B); - DefVal->SetNumberField(TEXT("a"), VP->DefaultValue.A); - EJ->SetObjectField(TEXT("defaultValue"), DefVal); - EJ->SetStringField(TEXT("group"), VP->Group.ToString()); - } - else if (auto* TP = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("TextureSampleParameter2D")); - EJ->SetStringField(TEXT("parameterName"), TP->ParameterName.ToString()); - if (TP->Texture) - EJ->SetStringField(TEXT("texture"), TP->Texture->GetPathName()); - EJ->SetStringField(TEXT("group"), TP->Group.ToString()); - } - else if (auto* SSP = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("StaticSwitchParameter")); - EJ->SetStringField(TEXT("parameterName"), SSP->ParameterName.ToString()); - EJ->SetBoolField(TEXT("defaultValue"), SSP->DefaultValue); - EJ->SetStringField(TEXT("group"), SSP->Group.ToString()); - } - else if (auto* SC = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("Constant")); - EJ->SetNumberField(TEXT("value"), SC->R); - } - else if (auto* C3 = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("Constant3Vector")); - TSharedRef Val = MakeShared(); - Val->SetNumberField(TEXT("r"), C3->Constant.R); - Val->SetNumberField(TEXT("g"), C3->Constant.G); - Val->SetNumberField(TEXT("b"), C3->Constant.B); - EJ->SetObjectField(TEXT("value"), Val); - } - else if (auto* C4 = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("Constant4Vector")); - TSharedRef Val = MakeShared(); - Val->SetNumberField(TEXT("r"), C4->Constant.R); - Val->SetNumberField(TEXT("g"), C4->Constant.G); - Val->SetNumberField(TEXT("b"), C4->Constant.B); - Val->SetNumberField(TEXT("a"), C4->Constant.A); - EJ->SetObjectField(TEXT("value"), Val); - } - else if (auto* TS = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("TextureSample")); - if (TS->Texture) - EJ->SetStringField(TEXT("texture"), TS->Texture->GetPathName()); - } - else if (auto* TC = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("TextureCoordinate")); - EJ->SetNumberField(TEXT("coordinateIndex"), TC->CoordinateIndex); - EJ->SetNumberField(TEXT("uTiling"), TC->UTiling); - EJ->SetNumberField(TEXT("vTiling"), TC->VTiling); - } - else if (auto* CM = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("ComponentMask")); - EJ->SetBoolField(TEXT("r"), CM->R != 0); - EJ->SetBoolField(TEXT("g"), CM->G != 0); - EJ->SetBoolField(TEXT("b"), CM->B != 0); - EJ->SetBoolField(TEXT("a"), CM->A != 0); - } - else if (auto* Custom = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("Custom")); - EJ->SetStringField(TEXT("code"), Custom->Code); - EJ->SetStringField(TEXT("outputType"), StaticEnum()->GetNameStringByValue((int64)Custom->OutputType)); - } - else if (auto* FI = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("FunctionInput")); - EJ->SetStringField(TEXT("inputName"), FI->InputName.ToString()); - } - else if (auto* FO = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("FunctionOutput")); - EJ->SetStringField(TEXT("outputName"), FO->OutputName.ToString()); - } - else if (auto* MFC = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("MaterialFunctionCall")); - if (MFC->MaterialFunction) - EJ->SetStringField(TEXT("functionName"), MFC->MaterialFunction->GetName()); - } - else - { - EJ->SetStringField(TEXT("expressionType"), Expression->GetClass()->GetName()); - } - - return EJ; -} diff --git a/Plugins/UEWingman/Deprecated/Blueprint_Diff.h b/Plugins/UEWingman/Deprecated/Blueprint_Diff.h deleted file mode 100644 index 2264f245..00000000 --- a/Plugins/UEWingman/Deprecated/Blueprint_Diff.h +++ /dev/null @@ -1,242 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "WingServer.h" -#include "WingHandler.h" -#include "WingFetcher.h" -#include "WingUtils.h" -#include "Engine/Blueprint.h" -#include "EdGraph/EdGraph.h" -#include "EdGraph/EdGraphNode.h" -#include "EdGraph/EdGraphPin.h" -#include "Blueprint_Diff.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS() -class UWing_Blueprint_Diff : public UObject, public IWingHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="First blueprint package path")) - FString BlueprintA; - - UPROPERTY(meta=(Description="Second blueprint package path")) - FString BlueprintB; - - UPROPERTY(meta=(Optional, Description="Filter to a specific graph name")) - FString Graph; - - virtual FString GetDescription() const override - { - return TEXT("Structural diff between two different Blueprints. Compares nodes, " - "connections, and variables across graphs. Use for comparing variants, " - "finding divergence after copy-paste, or auditing consistency."); - } - - virtual void Handle() override - { - // Load both blueprints - WingFetcher FA; - UBlueprint* BPA = FA.Asset(BlueprintA).Cast(); - if (!BPA) return; - - WingFetcher FB; - UBlueprint* BPB = FB.Asset(BlueprintB).Cast(); - if (!BPB) return; - - // Gather graphs, optionally filtering by name - auto GatherGraphs = [this](UBlueprint* BP) -> TArray - { - TArray Graphs; - for (UEdGraph* G : BP->UbergraphPages) - { - if (!G) continue; - if (!Graph.IsEmpty() && !WingUtils::Identifies(Graph, G)) continue; - Graphs.Add(G); - } - for (UEdGraph* G : BP->FunctionGraphs) - { - if (!G) continue; - if (!Graph.IsEmpty() && !WingUtils::Identifies(Graph, G)) continue; - Graphs.Add(G); - } - return Graphs; - }; - - TArray GraphsA = GatherGraphs(BPA); - TArray GraphsB = GatherGraphs(BPB); - - // Build graph name maps - TMap GraphMapA, GraphMapB; - for (UEdGraph* G : GraphsA) GraphMapA.Add(WingUtils::FormatName(G), G); - for (UEdGraph* G : GraphsB) GraphMapB.Add(WingUtils::FormatName(G), G); - - // Find all unique graph names - TSet AllGraphNames; - for (auto& Pair : GraphMapA) AllGraphNames.Add(Pair.Key); - for (auto& Pair : GraphMapB) AllGraphNames.Add(Pair.Key); - - int32 TotalDiffs = 0; - - for (const FString& GraphName : AllGraphNames) - { - UEdGraph** pGA = GraphMapA.Find(GraphName); - UEdGraph** pGB = GraphMapB.Find(GraphName); - - if (!pGA) - { - UWingServer::Printf(TEXT("Graph %s: only in B (%d nodes)\n"), *GraphName, (*pGB)->Nodes.Num()); - TotalDiffs++; - continue; - } - if (!pGB) - { - UWingServer::Printf(TEXT("Graph %s: only in A (%d nodes)\n"), *GraphName, (*pGA)->Nodes.Num()); - TotalDiffs++; - continue; - } - - // Both exist -- compare nodes - UEdGraph* GA = *pGA; - UEdGraph* GB = *pGB; - - // Build node title maps for matching - TMap> NodesA, NodesB; - for (UEdGraphNode* N : GA->Nodes) - { - if (!N) continue; - NodesA.FindOrAdd(WingUtils::FormatName(N)).Add(N); - } - for (UEdGraphNode* N : GB->Nodes) - { - if (!N) continue; - NodesB.FindOrAdd(WingUtils::FormatName(N)).Add(N); - } - - // Nodes only in A - TArray OnlyInA; - for (auto& Pair : NodesA) - { - int32 CountA = Pair.Value.Num(); - int32 CountB = 0; - if (auto* pArr = NodesB.Find(Pair.Key)) CountB = pArr->Num(); - if (CountA > CountB) - OnlyInA.Add(FString::Printf(TEXT(" %s (x%d)"), *Pair.Key, CountA - CountB)); - } - - // Nodes only in B - TArray OnlyInB; - for (auto& Pair : NodesB) - { - int32 CountB = Pair.Value.Num(); - int32 CountA = 0; - if (auto* pArr = NodesA.Find(Pair.Key)) CountA = pArr->Num(); - if (CountB > CountA) - OnlyInB.Add(FString::Printf(TEXT(" %s (x%d)"), *Pair.Key, CountB - CountA)); - } - - // Connection diff - auto MakeConnKey = [](UEdGraphPin* SrcPin, UEdGraphPin* TgtPin) -> FString - { - return FString::Printf(TEXT("%s|%s|%s|%s"), - *WingUtils::FormatName(SrcPin->GetOwningNode()), *WingUtils::FormatName(SrcPin), - *WingUtils::FormatName(TgtPin->GetOwningNode()), *WingUtils::FormatName(TgtPin)); - }; - - auto GatherConnections = [&MakeConnKey](UEdGraph* G) -> TSet - { - TSet Conns; - for (UEdGraphNode* N : G->Nodes) - { - if (!N) continue; - for (UEdGraphPin* Pin : N->Pins) - { - if (!Pin || Pin->Direction != EGPD_Output) continue; - for (UEdGraphPin* Linked : Pin->LinkedTo) - { - if (!Linked || !Linked->GetOwningNode()) continue; - Conns.Add(MakeConnKey(Pin, Linked)); - } - } - } - return Conns; - }; - - TSet ConnectionsA = GatherConnections(GA); - TSet ConnectionsB = GatherConnections(GB); - - TArray ConnsOnlyInA, ConnsOnlyInB; - for (const FString& Key : ConnectionsA) - if (!ConnectionsB.Contains(Key)) - ConnsOnlyInA.Add(FString::Printf(TEXT(" %s"), *Key)); - for (const FString& Key : ConnectionsB) - if (!ConnectionsA.Contains(Key)) - ConnsOnlyInB.Add(FString::Printf(TEXT(" %s"), *Key)); - - bool bIdentical = OnlyInA.IsEmpty() && OnlyInB.IsEmpty() && ConnsOnlyInA.IsEmpty() && ConnsOnlyInB.IsEmpty(); - - if (bIdentical) - { - UWingServer::Printf(TEXT("Graph %s: identical (%d nodes)\n"), *GraphName, GA->Nodes.Num()); - continue; - } - - TotalDiffs++; - UWingServer::Printf(TEXT("Graph %s: different (A=%d nodes, B=%d nodes)\n"), *GraphName, GA->Nodes.Num(), GB->Nodes.Num()); - - if (!OnlyInA.IsEmpty()) - { - UWingServer::Print(TEXT(" Nodes only in A:\n")); - for (const FString& Line : OnlyInA) UWingServer::Printf(TEXT(" %s\n"), *Line); - } - if (!OnlyInB.IsEmpty()) - { - UWingServer::Print(TEXT(" Nodes only in B:\n")); - for (const FString& Line : OnlyInB) UWingServer::Printf(TEXT(" %s\n"), *Line); - } - if (!ConnsOnlyInA.IsEmpty()) - { - UWingServer::Print(TEXT(" Connections only in A:\n")); - for (const FString& Line : ConnsOnlyInA) UWingServer::Printf(TEXT(" %s\n"), *Line); - } - if (!ConnsOnlyInB.IsEmpty()) - { - UWingServer::Print(TEXT(" Connections only in B:\n")); - for (const FString& Line : ConnsOnlyInB) UWingServer::Printf(TEXT(" %s\n"), *Line); - } - } - - // Compare variables - TSet VarNamesA, VarNamesB; - for (const FBPVariableDescription& V : BPA->NewVariables) VarNamesA.Add(WingUtils::FormatName(V)); - for (const FBPVariableDescription& V : BPB->NewVariables) VarNamesB.Add(WingUtils::FormatName(V)); - - TArray VarsOnlyInA, VarsOnlyInB; - for (const FString& Name : VarNamesA) - if (!VarNamesB.Contains(Name)) - VarsOnlyInA.Add(Name); - for (const FString& Name : VarNamesB) - if (!VarNamesA.Contains(Name)) - VarsOnlyInB.Add(Name); - - if (!VarsOnlyInA.IsEmpty()) - { - UWingServer::Print(TEXT("Variables only in A:\n")); - for (const FString& Name : VarsOnlyInA) UWingServer::Printf(TEXT(" %s\n"), *Name); - TotalDiffs += VarsOnlyInA.Num(); - } - if (!VarsOnlyInB.IsEmpty()) - { - UWingServer::Print(TEXT("Variables only in B:\n")); - for (const FString& Name : VarsOnlyInB) UWingServer::Printf(TEXT(" %s\n"), *Name); - TotalDiffs += VarsOnlyInB.Num(); - } - - UWingServer::Printf(TEXT("Total differences: %d\n"), TotalDiffs); - } -}; diff --git a/Plugins/UEWingman/Deprecated/Blueprint_Search.h b/Plugins/UEWingman/Deprecated/Blueprint_Search.h deleted file mode 100644 index ee4b12e2..00000000 --- a/Plugins/UEWingman/Deprecated/Blueprint_Search.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "MCPServer.h" -#include "MCPHandler.h" -#include "MCPAssets.h" -#include "MCPUtils.h" -#include "MCPTypes.h" -#include "Engine/Blueprint.h" -#include "Engine/World.h" -#include "Blueprint_Search.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS() -class UMCP_Blueprint_Search : public UObject, public IMCPHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Optional, Description="Substring filter for blueprint name or path")) - FString Query; - - UPROPERTY(meta=(Optional, Description="Filter by parent class name (exact match, case-insensitive)")) - FString ParentClass; - - virtual FString GetDescription() const override - { - return TEXT("List all Blueprint assets in the project, with optional filtering by name, parent class, or type."); - } - - virtual void Handle() override - { - MCPAssets Assets; - Assets.Scan().Substring(Query).Limit(500); - if (!Assets.Info()) return; - - UClass *Parent = nullptr; - if (!ParentClass.IsEmpty()) - { - Parent = UMCPTypes::TextToOneObjectType(ParentClass); - if (!Parent) return; - } - - int32 Count = 0; - for (const FAssetData& Asset : Assets.AllData()) - { - // Extract parent class name from asset tags - FString ParentClassName; - Asset.GetTagValue(FName(TEXT("ParentClass")), ParentClassName); - int32 DotIndex; - if (ParentClassName.FindLastChar('.', DotIndex)) - { - ParentClassName = ParentClassName.Mid(DotIndex + 1); - } - ParentClassName.RemoveFromEnd(TEXT("'")); - - // Apply parent class filter - if (!ParentClass.IsEmpty()) - { - if (!ParentClassName.Equals(ParentClass, ESearchCase::IgnoreCase)) - { - continue; - } - } - - UMCPServer::Printf(TEXT("%30s %s\n"), *ParentClassName, *Asset.PackageName.ToString()); - Count++; - } - - if (Count == 0) - { - UMCPServer::Print(TEXT("No blueprint assets found.\n")); - } - } -}; diff --git a/Plugins/UEWingman/Deprecated/Blueprint_SearchContents.h b/Plugins/UEWingman/Deprecated/Blueprint_SearchContents.h deleted file mode 100644 index 75533945..00000000 --- a/Plugins/UEWingman/Deprecated/Blueprint_SearchContents.h +++ /dev/null @@ -1,127 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "MCPServer.h" -#include "MCPHandler.h" -#include "MCPAssets.h" -#include "MCPUtils.h" -#include "Engine/Blueprint.h" -#include "Engine/World.h" -#include "Engine/Level.h" -#include "Engine/LevelScriptBlueprint.h" -#include "EdGraph/EdGraphNode.h" -#include "K2Node_CallFunction.h" -#include "K2Node_Event.h" -#include "K2Node_CustomEvent.h" -#include "K2Node_VariableGet.h" -#include "K2Node_VariableSet.h" -#include "Blueprint_SearchContents.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS() -class UMCP_Blueprint_SearchContents : public UObject, public IMCPHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Search query string to match against node titles, function names, event names, and variable names")) - FString Query; - - UPROPERTY(meta=(Optional, Description="Filter results to blueprints whose path contains this substring")) - FString Path; - - UPROPERTY(meta=(Optional, Description="Maximum number of results to return (default 50, max 200)")) - int32 MaxResults = 0; - - virtual FString GetDescription() const override - { - return TEXT("Search across all Blueprint graphs for nodes matching a query string."); - } - - virtual void Handle() override - { - int32 Limit = (MaxResults > 0) ? FMath::Clamp(MaxResults, 1, 200) : 50; - int32 Count = 0; - - // Search one blueprint's nodes for the query string. - auto SearchBlueprint = [&](UBlueprint* BP, bool bIsLevelBP) - { - for (UEdGraphNode* Node : MCPUtils::AllNodes(BP)) - { - if (Count >= Limit) return; - - FString Title = Node->GetNodeTitle(ENodeTitleType::FullTitle).ToString(); - - FString FuncName, EventName, VarName; - if (auto* CF = Cast(Node)) - { - FuncName = CF->FunctionReference.GetMemberName().ToString(); - } - else if (auto* Ev = Cast(Node)) - { - EventName = Ev->EventReference.GetMemberName().ToString(); - } - else if (auto* CE = Cast(Node)) - { - EventName = CE->CustomFunctionName.ToString(); - } - else if (auto* VG = Cast(Node)) - { - VarName = VG->GetVarName().ToString(); - } - else if (auto* VS = Cast(Node)) - { - VarName = VS->GetVarName().ToString(); - } - - bool bMatch = Title.Contains(Query, ESearchCase::IgnoreCase) || - (!FuncName.IsEmpty() && FuncName.Contains(Query, ESearchCase::IgnoreCase)) || - (!EventName.IsEmpty() && EventName.Contains(Query, ESearchCase::IgnoreCase)) || - (!VarName.IsEmpty() && VarName.Contains(Query, ESearchCase::IgnoreCase)); - - if (!bMatch) continue; - - Count++; - UMCPServer::Printf(TEXT("blueprint: %s\n"), *MCPUtils::FormatName(BP)); - UMCPServer::Printf(TEXT(" graph: %s\n"), *MCPUtils::FormatName(Node->GetGraph())); - UMCPServer::Printf(TEXT(" node: %s\n"), *MCPUtils::FormatName(Node)); - UMCPServer::Printf(TEXT(" class: %s\n"), *MCPUtils::FormatName(Node->GetClass())); - if (!FuncName.IsEmpty()) UMCPServer::Printf(TEXT(" function: %s\n"), *FuncName); - if (!EventName.IsEmpty()) UMCPServer::Printf(TEXT(" event: %s\n"), *EventName); - if (!VarName.IsEmpty()) UMCPServer::Printf(TEXT(" variable: %s\n"), *VarName); - if (bIsLevelBP) UMCPServer::Print(TEXT(" level-blueprint: true\n")); - UMCPServer::Print(TEXT("\n")); - } - }; - - // Search regular blueprints - MCPAssets AllBlueprints; - if (!Path.IsEmpty()) AllBlueprints.Substring(Path); - AllBlueprints.Load(); - for (UBlueprint* BP : AllBlueprints.Objects()) - { - if (Count >= Limit) break; - SearchBlueprint(BP, false); - } - - // Search level blueprints - MCPAssets AllWorlds; - if (!Path.IsEmpty()) AllWorlds.Substring(Path); - AllWorlds.Load(); - for (UWorld* World : AllWorlds.Objects()) - { - if (Count >= Limit) break; - if (!World || !World->PersistentLevel) continue; - ULevelScriptBlueprint* LevelBP = World->PersistentLevel->GetLevelScriptBlueprint(false); - if (!LevelBP) continue; - SearchBlueprint(LevelBP, true); - } - - UMCPServer::Printf(TEXT("Results: %d\n"), Count); - if (Count >= Limit) UMCPServer::Printf(TEXT("(limit %d reached, use MaxResults to increase)\n"), Limit); - } -}; diff --git a/Plugins/UEWingman/Deprecated/Class_Search.h b/Plugins/UEWingman/Deprecated/Class_Search.h deleted file mode 100644 index e478c0cd..00000000 --- a/Plugins/UEWingman/Deprecated/Class_Search.h +++ /dev/null @@ -1,112 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "WingServer.h" -#include "WingHandler.h" -#include "WingUtils.h" -#include "UObject/UObjectIterator.h" -#include "Class_Search.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -// ============================================================ -// HandleListClasses — discover available UClasses -// ============================================================ - -UCLASS() -class UWing_Class_Search : public UObject, public IWingHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Optional, Description="Substring filter for class names")) - FString Query; - - UPROPERTY(meta=(Optional, Description="Parent class name to restrict results to subclasses")) - FString ParentClass; - - UPROPERTY(meta=(Optional, Description="Maximum number of results to return (1-500, default 100)")) - int32 Limit = 100; - - virtual FString GetDescription() const override - { - return TEXT("Search for available UClasses by name substring and/or parent class. " - "Returns class names, parent class, package, and flags."); - } - - virtual void Handle() override - { - Limit = FMath::Clamp(Limit, 1, 500); - - UClass* ParentClassObj = nullptr; - if (!ParentClass.IsEmpty()) - { - for (TObjectIterator It; It; ++It) - { - if (WingUtils::Identifies(ParentClass, *It)) - { - ParentClassObj = *It; - break; - } - } - if (!ParentClassObj) - { - UWingServer::Printf(TEXT("Error: Parent class '%s' not found\n"), *ParentClass); - return; - } - } - - TArray Matches; - int32 TotalMatched = 0; - - for (TObjectIterator It; It; ++It) - { - UClass* Class = *It; - if (!Class) continue; - if (Class->HasAnyClassFlags(CLASS_Deprecated | CLASS_NewerVersionExists)) continue; - if (ParentClassObj && !Class->IsChildOf(ParentClassObj)) continue; - - FString ClassName = WingUtils::FormatName(Class); - if (!Query.IsEmpty() && !ClassName.Contains(Query, ESearchCase::IgnoreCase)) continue; - - TotalMatched++; - if (Matches.Num() < Limit) - { - Matches.Add(Class); - } - } - - UWingServer::Printf(TEXT("Found %d classes"), TotalMatched); - if (TotalMatched > Limit) - { - UWingServer::Printf(TEXT(" (showing %d)"), Limit); - } - UWingServer::Print(TEXT("\n")); - - for (UClass* Class : Matches) - { - UWingServer::Printf(TEXT(" %s"), *WingUtils::FormatName(Class)); - - // Flags - TStringBuilder<64> Flags; - if (Class->HasAnyClassFlags(CLASS_Abstract)) Flags.Append(TEXT(" Abstract")); - if (Class->HasAnyClassFlags(CLASS_Interface)) Flags.Append(TEXT(" Interface")); - if (Class->HasAnyClassFlags(CLASS_MinimalAPI)) Flags.Append(TEXT(" MinimalAPI")); - if (Class->ClassGeneratedBy) Flags.Append(TEXT(" Blueprint")); - if (Flags.Len() > 0) - { - UWingServer::Printf(TEXT(" [%s]"), Flags.ToString() + 1); // skip leading space - } - - if (Class->GetSuperClass()) - { - UWingServer::Printf(TEXT(" : %s"), *WingUtils::FormatName(Class->GetSuperClass())); - } - - UWingServer::Print(TEXT("\n")); - } - } -}; diff --git a/Plugins/UEWingman/Deprecated/Class_ShowProperties.h b/Plugins/UEWingman/Deprecated/Class_ShowProperties.h deleted file mode 100644 index 9840504b..00000000 --- a/Plugins/UEWingman/Deprecated/Class_ShowProperties.h +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "WingServer.h" -#include "WingHandler.h" -#include "WingTypes.h" -#include "WingUtils.h" -#include "Class_ShowProperties.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS() -class UWing_Class_ShowProperties : public UObject, public IWingHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Class name to list properties for")) - FString ClassName; - - UPROPERTY(meta=(Optional, Description="Substring filter for property names")) - FString Query; - - virtual FString GetDescription() const override - { - return TEXT("List properties on a UClass, including type, owning class, and property flags."); - } - - virtual void Handle() override - { - UClass* FoundClass = UWingTypes::TextToOneObjectType(ClassName); - if (!FoundClass) return; - - UWingServer::Printf(TEXT("Properties of %s:\n"), *WingUtils::FormatName(FoundClass)); - - int32 Count = 0; - for (TFieldIterator PropIt(FoundClass); PropIt; ++PropIt) - { - FProperty* Prop = *PropIt; - if (!Prop) continue; - - FString PropName = Prop->GetName(); - - if (!Query.IsEmpty() && !PropName.Contains(Query, ESearchCase::IgnoreCase)) - continue; - - // Build compact flags string - TStringBuilder<256> Flags; - if (Prop->HasAnyPropertyFlags(CPF_BlueprintVisible)) Flags.Append(TEXT(" BlueprintVisible")); - if (Prop->HasAnyPropertyFlags(CPF_BlueprintReadOnly)) Flags.Append(TEXT(" BlueprintReadOnly")); - if (Prop->HasAnyPropertyFlags(CPF_Edit)) Flags.Append(TEXT(" EditAnywhere")); - if (Prop->HasAnyPropertyFlags(CPF_EditConst)) Flags.Append(TEXT(" VisibleOnly")); - if (Prop->HasAnyPropertyFlags(CPF_Config)) Flags.Append(TEXT(" Config")); - if (Prop->HasAnyPropertyFlags(CPF_SaveGame)) Flags.Append(TEXT(" SaveGame")); - if (Prop->HasAnyPropertyFlags(CPF_Transient)) Flags.Append(TEXT(" Transient")); - if (Prop->HasAnyPropertyFlags(CPF_RepNotify)) Flags.Append(TEXT(" RepNotify")); - - UClass* OwnerClass = Prop->GetOwnerClass(); - UWingServer::Printf(TEXT(" %s %s"), *UWingTypes::TypeToText(Prop), *PropName); - if (OwnerClass && OwnerClass != FoundClass) - UWingServer::Printf(TEXT(" [%s]"), *WingUtils::FormatName(OwnerClass)); - if (Flags.Len() > 0) - UWingServer::Printf(TEXT(" (%s)"), Flags.ToString() + 1); // skip leading space - UWingServer::Print(TEXT("\n")); - Count++; - } - - if (Count == 0) - { - UWingServer::Print(TEXT("No properties found.\n")); - } - } -}; diff --git a/Plugins/UEWingman/Deprecated/Enum_Create.h b/Plugins/UEWingman/Deprecated/Enum_Create.h deleted file mode 100644 index 8dd64c16..00000000 --- a/Plugins/UEWingman/Deprecated/Enum_Create.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "WingServer.h" -#include "WingHandler.h" -#include "WingUtils.h" -#include "Engine/UserDefinedEnum.h" -#include "Kismet2/EnumEditorUtils.h" -#include "Factories/EnumFactory.h" -#include "WingPackageMaker.h" -#include "Enum_Create.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS() -class UWing_Enum_Create : public UObject, public IWingHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Full package path for the new enum (e.g. '/Game/DataTypes/E_MyEnum')")) - FString AssetPath; - - UPROPERTY(meta=(Description="Array of enum value names")) - FWingJsonArray Values; - - virtual FString GetDescription() const override - { - return TEXT("Create a new UserDefinedEnum asset with the specified values."); - } - - virtual void Handle() override - { - WingPackageMaker Maker(AssetPath); - if (!Maker.Ok()) return; - - TArray EnumValues; - for (const TSharedPtr& Val : Values.Array) - { - FString Str = Val->AsString(); - if (!Str.IsEmpty()) EnumValues.Add(Str); - } - if (EnumValues.Num() == 0) - { - UWingServer::Print(TEXT("ERROR: Values must be a non-empty array of strings\n")); - return; - } - - // Create the enum using AssetTools. - UUserDefinedEnum* NewEnum = Maker.CreateAsset(); - if (!NewEnum) return; - - // Add enum values — UUserDefinedEnum starts with a MAX value. - // We need to add entries before MAX. - for (int32 i = 0; i < EnumValues.Num(); ++i) - { - FEnumEditorUtils::AddNewEnumeratorForUserDefinedEnum(NewEnum); - int32 NewIndex = NewEnum->NumEnums() - 2; - FEnumEditorUtils::SetEnumeratorDisplayName(NewEnum, NewIndex, FText::FromString(EnumValues[i])); - } - - UWingServer::Printf(TEXT("Created %s with %d values\n"), *NewEnum->GetPathName(), EnumValues.Num()); - } -}; diff --git a/Plugins/UEWingman/Deprecated/Enum_Modify.h b/Plugins/UEWingman/Deprecated/Enum_Modify.h deleted file mode 100644 index e69de29b..00000000 diff --git a/Plugins/UEWingman/Deprecated/MaterialFunction_Create.h b/Plugins/UEWingman/Deprecated/MaterialFunction_Create.h deleted file mode 100644 index 42aae2e6..00000000 --- a/Plugins/UEWingman/Deprecated/MaterialFunction_Create.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "WingServer.h" -#include "WingHandler.h" -#include "WingUtils.h" -#include "Materials/MaterialFunction.h" -#include "Factories/MaterialFunctionFactoryNew.h" -#include "WingPackageMaker.h" -#include "MaterialFunction_Create.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS() -class UWing_MaterialFunction_Create : public UObject, public IWingHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Full asset path for the new material function (e.g. '/Game/Materials/MF_MyFunc')")) - FString AssetPath; - - UPROPERTY(meta=(Optional, Description="Description for the material function")) - FString Description; - - virtual FString GetDescription() const override - { - return TEXT("Create a new UMaterialFunction asset with an optional description."); - } - - virtual void Handle() override - { - WingPackageMaker Maker(AssetPath); - if (!Maker.Ok()) return; - - // Create via IAssetTools + factory. - UMaterialFunction* MF = Maker.CreateAsset(); - if (!MF) return; - - // Set optional description. - if (!Description.IsEmpty()) - MF->Description = Description; - - UWingServer::Printf(TEXT("Created %s\n"), *MF->GetPathName()); - } -}; diff --git a/Plugins/UEWingman/Deprecated/Material_ReparentInstance.h b/Plugins/UEWingman/Deprecated/Material_ReparentInstance.h deleted file mode 100644 index 93dd7d6a..00000000 --- a/Plugins/UEWingman/Deprecated/Material_ReparentInstance.h +++ /dev/null @@ -1,95 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "MCPHandler.h" -#include "MCPAssets.h" -#include "MCPUtils.h" -#include "Materials/Material.h" -#include "Materials/MaterialInterface.h" -#include "Materials/MaterialInstanceConstant.h" -#include "Material_ReparentInstance.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS() -class UMCP_Material_ReparentInstance : public UObject, public IMCPHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Material Instance name or path to reparent")) - FString MaterialInstance; - - UPROPERTY(meta=(Description="New parent material name or path (Material or Material Instance)")) - FString NewParent; - - UPROPERTY(meta=(Optional, Description="If true, validate without applying changes")) - bool DryRun = false; - - virtual FString GetDescription() const override - { - return TEXT("Change the parent material of a Material Instance. " - "Validates against circular parent chains."); - } - - virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override - { - // Load the Material Instance - MCPAssets Assets; - if (!Assets.Exact(MaterialInstance).Errors(Result).ENone().ETwo().Load()) return; - UMaterialInstanceConstant* MI = Assets.Object(); - - // Load new parent (Material or MaterialInstance) - MCPAssets ParentAssets; - ParentAssets.NoScans(); - ParentAssets.Scan(); - ParentAssets.Scan(); - if (!ParentAssets.Exact(NewParent).Errors(Result).ENone().ETwo().Load()) return; - UMaterialInterface* NewParentObj = ParentAssets.Object(); - - // Prevent circular parenting - UMaterialInterface* Check = NewParentObj; - while (Check) - { - if (Check == MI) - { - Result.Appendf(TEXT("ERROR: Reparenting to '%s' would create a circular parent chain.\n"), - *NameOf(NewParentObj)); - return; - } - UMaterialInstanceConstant* CheckMI = Cast(Check); - if (!CheckMI) break; - Check = CheckMI->Parent; - } - - FString OldParentName = MI->Parent ? NameOf(MI->Parent) : TEXT("None"); - - if (DryRun) - { - Result.Appendf(TEXT("[DRY RUN] Would reparent %s: %s -> %s\n"), - *MCPUtils::FormatName(MI), *OldParentName, *NameOf(NewParentObj)); - return; - } - - MCPUtils::PreEdit({MI}); - MI->Parent = NewParentObj; - MCPUtils::PostEdit({MI}); - MCPUtils::SaveGenericPackage(MI); - - Result.Appendf(TEXT("Reparented %s: %s -> %s\n"), - *MCPUtils::FormatName(MI), *OldParentName, *NameOf(NewParentObj)); - } - -private: - FString NameOf(UMaterialInterface* Obj) - { - if (UMaterial* M = Cast(Obj)) - return MCPUtils::FormatName(M); - if (UMaterialInstance* MI = Cast(Obj)) - return MCPUtils::FormatName(MI); - return Obj->GetPathName(); - } -}; diff --git a/Plugins/UEWingman/Deprecated/OldSearchNodeSpawners.cpp b/Plugins/UEWingman/Deprecated/OldSearchNodeSpawners.cpp deleted file mode 100644 index 8aebfa47..00000000 --- a/Plugins/UEWingman/Deprecated/OldSearchNodeSpawners.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// This code was extracted from git commit 0e79b02^ (the commit before it was -// replaced with GetGraphContextActions). Original file was: -// Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPUtils.cpp -// -// It uses FBlueprintActionDatabase to search for spawnable node types, -// which is the correct approach for Blueprint (K2) graphs. - -#include "BlueprintActionDatabase.h" -#include "BlueprintNodeSpawner.h" - -FString MCPUtils::NodeSpawnerFullName(UBlueprintNodeSpawner* Spawner) -{ - const FBlueprintActionUiSpec& UiSpec = Spawner->PrimeDefaultUiSpec(); - FString Category = UiSpec.Category.ToString(); - FString MenuName = UiSpec.MenuName.ToString(); - if (Category.IsEmpty()) - { - return MenuName; - } - return Category + TEXT("|") + MenuName; -} - -TArray MCPUtils::SearchNodeSpawners(const FString& Query, int32 MaxResults, bool ExactMatch, UEdGraph* GraphFilter) -{ - return Result; -} diff --git a/Plugins/UEWingman/Deprecated/OldWingUtils.cpp b/Plugins/UEWingman/Deprecated/OldWingUtils.cpp deleted file mode 100644 index 586de583..00000000 --- a/Plugins/UEWingman/Deprecated/OldWingUtils.cpp +++ /dev/null @@ -1,867 +0,0 @@ -#include "WingUtils.h" -#include "WingJson.h" -#include "WingTypes.h" -#include "WingServer.h" -#include "WingHandler.h" -#include "Engine/Blueprint.h" -#include "Engine/MemberReference.h" -#include "Engine/World.h" -#include "Components/ActorComponent.h" -#include "Engine/SCS_Node.h" -#include "EdGraph/EdGraph.h" -#include "EdGraph/EdGraphNode.h" -#include "EdGraph/EdGraphPin.h" -#include "K2Node_EditablePinBase.h" -#include "EdGraph/EdGraphSchema.h" -#include "Kismet2/BlueprintEditorUtils.h" -#include "Kismet2/KismetEditorUtilities.h" -#include "UObject/SavePackage.h" -#include "UObject/UObjectIterator.h" -#include "UObject/UnrealType.h" -#include "Misc/Paths.h" -#include "Misc/PackageName.h" - -// Animation Blueprint support -#include "AnimStateNode.h" -#include "AnimStateTransitionNode.h" -#include "AnimationStateMachineGraph.h" - -// Material support -#include "Materials/Material.h" -#include "Materials/MaterialExpression.h" -#include "Materials/MaterialFunction.h" -#include "Materials/MaterialInstanceConstant.h" -#include "MaterialGraph/MaterialGraph.h" -#include "MaterialGraph/MaterialGraphSchema.h" -#include "IMaterialEditor.h" -#include "Subsystems/AssetEditorSubsystem.h" - -// Mesh, animation, texture support -#include "Engine/StaticMesh.h" -#include "Engine/SkeletalMesh.h" -#include "Animation/AnimSequence.h" -#include "Animation/BlendSpace.h" -#include "Engine/Texture.h" - -// SEH support (Windows only) — defined in BlueprintWingServer.cpp -#if PLATFORM_WINDOWS -extern int32 TryCompileBlueprintSEH(UBlueprint* BP, EBlueprintCompileOptions Opts); -extern int32 TrySavePackageSEH( - UPackage* Package, UObject* Asset, const TCHAR* Filename, - FSavePackageArgs* SaveArgs, ESavePackageResult* OutResult); -#endif - -// ============================================================ -// Name sanitization -// -// Our parsers reserve certain punctuation marks for parsing -// types, paths, and the like. For example: Array. -// We therefore cannot allow those specific punctuation marks -// in names. We replace them with similar-looking unicode -// characters. -// ============================================================ - -void WingUtils::SanitizeNameInPlace(FString &Name) -{ - int32 Dst = 0; - for (int32 Src = 0; Src < Name.Len(); Src++) - { - TCHAR c = Name[Src]; - if (c < 0x20 || c == 0x7F) continue; - if (c == ' ') c=L'·'; - if (c == '<') c=L'◁'; - if (c == '>') c=L'▷'; - if (c == ',') c=L'·'; - Name[Dst++] = c; - } - if (Dst == 0) Name[Dst++] = L'·'; - Name.LeftInline(Dst); -} - -FString WingUtils::SanitizeName(const FString &Name) -{ - FString Result = Name; - SanitizeNameInPlace(Result); - return Result; -} - -FString WingUtils::SanitizeName(FName Name) -{ - FString Result = Name.ToString(); - SanitizeNameInPlace(Result); - return Result; -} - -// ============================================================ -// Name Lookup -// ============================================================ - -FString WingUtils::FormatName(const UWorld *World) -{ - return World->GetPathName(); -} - -FString WingUtils::FormatName(const UBlueprint *BP) -{ - return UWingTypes::TypeToTextOrDie(BP); -} - -FString WingUtils::FormatName(const UActorComponent *C) -{ - return SanitizeName(C->GetName()); -} - -FString WingUtils::FormatName(const USCS_Node *Node) -{ - return SanitizeName(Node->GetVariableName()); -} - -FString WingUtils::FormatName(const UEdGraph *Graph) -{ - return SanitizeName(Graph->GetName()); -} - -FString WingUtils::FormatName(const UEdGraphNode* Node) -{ - return SanitizeName(Node->GetName()); -} - -FString WingUtils::FormatName(const UEdGraphPin *Pin) -{ - return SanitizeName(Pin->GetName()); -} - -FString WingUtils::FormatName(const FMemberReference &Ref) -{ - return SanitizeName(Ref.GetMemberName()); -} - -FString WingUtils::FormatName(const FBPVariableDescription &Var) -{ - return SanitizeName(Var.VarName); -} - -FString WingUtils::FormatName(const UStruct *Struct) -{ - if (Cast(Struct) || Cast(Struct)) - return UWingTypes::TypeToTextOrDie(Struct); - return SanitizeName(Struct->GetName()); -} - -FString WingUtils::FormatName(const UClass *Class) -{ - return UWingTypes::TypeToTextOrDie(Class); -} - -FString WingUtils::FormatName(const UMaterial *Material) -{ - return Material->GetPathName(); -} - -FString WingUtils::FormatName(const UMaterialInstance *MaterialInstance) -{ - return MaterialInstance->GetPathName(); -} - -FString WingUtils::FormatName(const UMaterialFunction *MaterialFunction) -{ - return MaterialFunction->GetPathName(); -} - -FString WingUtils::FormatName(const UMaterialExpression *Expression) -{ - return SanitizeName(Expression->GetName()); -} - -FString WingUtils::FormatName(const UStaticMesh *Mesh) -{ - return Mesh->GetPathName(); -} - -FString WingUtils::FormatName(const USkeletalMesh *Mesh) -{ - return Mesh->GetPathName(); -} - -FString WingUtils::FormatName(const UAnimSequence *Anim) -{ - return Anim->GetPathName(); -} - -FString WingUtils::FormatName(const UBlendSpace *BlendSpace) -{ - return BlendSpace->GetPathName(); -} - -FString WingUtils::FormatName(const UTexture *Texture) -{ - return Texture->GetPathName(); -} - -FString WingUtils::FormatName(const UScriptStruct *Struct) -{ - return UWingTypes::TypeToTextOrDie(Struct); -} - -FString WingUtils::FormatName(const UEnum *Enum) -{ - return UWingTypes::TypeToTextOrDie(Enum); -} - -FString WingUtils::FormatName(const FProperty *Prop) -{ - return SanitizeName(Prop->GetName()); -} - -FString WingUtils::FormatName(const FUserPinInfo &Pin) -{ - return SanitizeName(Pin.PinName); -} - -// ============================================================ -// Formatting other things -// ============================================================ - -FString WingUtils::FormatNodeTitle(const UEdGraphNode *Node) -{ - FString Title = Node->GetNodeTitle(ENodeTitleType::FullTitle).ToString(); - int32 NewlineIdx; - if (Title.FindChar(TEXT('\n'), NewlineIdx)) - Title.LeftInline(NewlineIdx); - return Title; -} - -// ============================================================ -// JSON helpers -// ============================================================ - -// ============================================================ -// Text formatting -// ============================================================ - -FString WingUtils::WrapText(const FString& Text, int32 ColLimit, const FString& Prefix) -{ - FString Clean = Text; - Clean.ReplaceInline(TEXT("\r\n"), TEXT("\n")); - TArray Words; - Clean.ParseIntoArrayWS(Words); - - TStringBuilder<1024> Result; - int32 Col = 0; - for (const FString& Word : Words) - { - if (Col > 0 && Col + 1 + Word.Len() > ColLimit) - { - Result.Append(TEXT("\n")); - Col = 0; - } - if (Col == 0) - { - Result.Append(Prefix); - Col = Prefix.Len(); - } - else - { - Result.Append(TEXT(" ")); - Col += 1; - } - Result.Append(Word); - Col += Word.Len(); - } - return Result.ToString(); -} - -// ============================================================ -// Enum helpers -// ============================================================ - -FString WingUtils::EnumToString(UEnum* Enum, int64 Value, const FString& Prefix) -{ - FString Full = Enum->GetNameStringByValue(Value); - if (!Prefix.IsEmpty() && Full.StartsWith(Prefix)) - return Full.Mid(Prefix.Len()); - return Full; -} - -bool WingUtils::StringToEnum(UEnum* Enum, const FString& Str, int64& OutValue, const FString& Prefix) -{ - OutValue = Enum->GetValueByNameString(Prefix + Str); - if (OutValue == INDEX_NONE) - { - UWingServer::Printf(TEXT("ERROR: Invalid value '%s' for %s\n"), *Str, *Enum->GetName()); - return false; - } - return true; -} - - -// ============================================================ -// Common Error Reporting -// ============================================================ - -bool WingUtils::CheckExactlyOneNamed(int Count, const FString &Kind, const FString &Name) -{ - if (Count == 0) - { - UWingServer::Printf(TEXT("Could not find a %s named %s.\n"), *Kind, *Name); - return false; - } - if (Count > 1) - { - UWingServer::Printf(TEXT("More than one %s named %s\n"), *Kind, *Name); - return false; - } - return true; -} - -bool WingUtils::CheckExactlyOneNamed(int Count, UClass *Class, const FString &Name) -{ - return CheckExactlyOneNamed(Count, Class->GetName(), Name); -} - -bool WingUtils::CheckExactlyNoneNamed(int Count, const FString &Kind, const FString &Name) -{ - if (Count > 0) - { - UWingServer::Printf(TEXT("A %s named %s already exists."), *Kind, *Name); - return false; - } - return true; -} - -bool WingUtils::CheckExactlyNoneNamed(int Count, UClass *Class, const FString &Name) -{ - return CheckExactlyNoneNamed(Count, Class->GetName(), Name); -} - -// ============================================================ -// Blueprint helpers -// ============================================================ - -TArray WingUtils::AllGraphs(UBlueprint* BP) -{ - TArray Graphs; - BP->GetAllGraphs(Graphs); - return Graphs; -} - - -TArray WingUtils::AllNodes(UBlueprint* BP) -{ - TArray Nodes; - for (UEdGraph* Graph : AllGraphs(BP)) - Nodes.Append(Graph->Nodes); - return Nodes; -} - -bool WingUtils::SaveBlueprintPackage(UBlueprint* BP) -{ - UPackage* Package = BP->GetPackage(); - - // 1. Build absolute package filename — use .umap for map packages, .uasset otherwise - FString PackageExtension = Package->ContainsMap() - ? FPackageName::GetMapPackageExtension() - : FPackageName::GetAssetPackageExtension(); - FString PackageFilename = FPackageName::LongPackageNameToFilename( - Package->GetName(), PackageExtension); - PackageFilename = FPaths::ConvertRelativePathToFull(PackageFilename); - UE_LOG(LogTemp, Display, TEXT("UEWingman: Save target: %s"), *PackageFilename); - - // 2. Phase 1: Try explicit compilation (same flags as UCompileAllBlueprintsCommandlet) - bool bCompiled = false; - { - EBlueprintCompileOptions CompileOpts = - EBlueprintCompileOptions::SkipSave | - EBlueprintCompileOptions::BatchCompile | - EBlueprintCompileOptions::SkipGarbageCollection | - EBlueprintCompileOptions::SkipFiBSearchMetaUpdate; - - UE_LOG(LogTemp, Display, TEXT("UEWingman: Phase 1: Attempting explicit compilation...")); - -#if PLATFORM_WINDOWS - int32 CompileResult = TryCompileBlueprintSEH(BP, CompileOpts); - if (CompileResult == 0) - { - bCompiled = (BP->Status == BS_UpToDate); - UE_LOG(LogTemp, Display, TEXT("UEWingman: Compilation %s (status=%d)"), - bCompiled ? TEXT("succeeded") : TEXT("completed with warnings"), (int32)BP->Status); - } - else - { - UE_LOG(LogTemp, Warning, TEXT("UEWingman: Compilation crashed (SEH), proceeding uncompiled")); - } -#else - FKismetEditorUtilities::CompileBlueprint(BP, CompileOpts, nullptr); - bCompiled = (BP->Status == BS_UpToDate); -#endif - } - - // 3. Phase 2: Set guards for save - uint8 OldRegen = BP->bIsRegeneratingOnLoad; - BP->bIsRegeneratingOnLoad = true; - - EBlueprintStatus OldStatus = (EBlueprintStatus)(uint8)BP->Status; - if (!bCompiled) - { - // Tell PreSave the BP is up-to-date so it doesn't try to compile - BP->Status = BS_UpToDate; - } - - // 4. Clear read-only attribute if present (source control or LFS may set this) - if (FPlatformFileManager::Get().GetPlatformFile().IsReadOnly(*PackageFilename)) - { - UE_LOG(LogTemp, Display, TEXT("UEWingman: Clearing read-only attribute on %s"), *PackageFilename); - FPlatformFileManager::Get().GetPlatformFile().SetReadOnly(*PackageFilename, false); - } - - // 5. Phase 3: Save with SAVE_NoError + SEH protection - FSavePackageArgs SaveArgs; - SaveArgs.TopLevelFlags = RF_Public | RF_Standalone; - SaveArgs.SaveFlags = SAVE_NoError; - - // For level blueprints (map packages), the base object should be the UWorld, not the BP - bool bIsMapPackage = Package->ContainsMap(); - UObject* BaseObject = BP; - if (bIsMapPackage) - { - // Find the UWorld in this package — it's the actual asset for .umap files - UWorld* World = FindObject(Package, *Package->GetName().Mid(Package->GetName().Find(TEXT("/"), ESearchCase::IgnoreCase, ESearchDir::FromEnd) + 1)); - if (!World) - { - // Fallback: iterate the package to find any UWorld - ForEachObjectWithPackage(Package, [&World](UObject* Obj) { - if (UWorld* W = Cast(Obj)) - { - World = W; - return false; // stop - } - return true; // continue - }); - } - if (World) - { - BaseObject = World; - UE_LOG(LogTemp, Display, TEXT("UEWingman: Map package detected — saving UWorld '%s'"), *World->GetName()); - } - else - { - UE_LOG(LogTemp, Warning, TEXT("UEWingman: Map package detected but no UWorld found — saving with BP as base")); - } - } - - ESavePackageResult SaveResult = ESavePackageResult::Error; - - UE_LOG(LogTemp, Display, TEXT("UEWingman: Phase 3: Calling UPackage::Save (compiled=%s, isMap=%s)..."), - bCompiled ? TEXT("yes") : TEXT("no"), bIsMapPackage ? TEXT("yes") : TEXT("no")); - -#if PLATFORM_WINDOWS - int32 SEHCode = TrySavePackageSEH(Package, BaseObject, *PackageFilename, &SaveArgs, &SaveResult); - if (SEHCode != 0) - { - UE_LOG(LogTemp, Error, TEXT("UEWingman: UPackage::Save CRASHED (SEH exception caught)")); - } -#else - FSavePackageResultStruct Result = UPackage::Save(Package, BaseObject, *PackageFilename, SaveArgs); - SaveResult = Result.Result; -#endif - - // 6. Restore guards - BP->bIsRegeneratingOnLoad = OldRegen; - if (!bCompiled) - { - BP->Status = (TEnumAsByte)OldStatus; - } - - bool bSuccess = (SaveResult == ESavePackageResult::Success); - UE_LOG(LogTemp, Display, TEXT("UEWingman: SaveBlueprintPackage — %s for '%s' (compiled=%s, result=%d)"), - bSuccess ? TEXT("SUCCEEDED") : TEXT("FAILED"), - *BP->GetName(), bCompiled ? TEXT("yes") : TEXT("no"), (int32)SaveResult); - - return bSuccess; - -} -// ============================================================ -// Material helpers -// ============================================================ - -void WingUtils::EnsureMaterialGraph(UMaterial* Material) -{ - if (!Material) return; - if (!Material->MaterialGraph) - { - // In commandlet/headless mode the MaterialGraph is not auto-created. - // Replicate what the Material Editor does on open (MaterialEditor.cpp:619). - Material->MaterialGraph = CastChecked( - FBlueprintEditorUtils::CreateNewGraph( - Material, NAME_None, - UMaterialGraph::StaticClass(), - UMaterialGraphSchema::StaticClass())); - Material->MaterialGraph->Material = Material; - Material->MaterialGraph->RebuildGraph(); - } -} - -UMaterial* WingUtils::ReplaceMaterialWithTransientCopy(UMaterial* Material) -{ - if (!Material) return nullptr; - - // Already a preview material — nothing to do. - if (Material->GetOutermost() == GetTransientPackage()) - return Material; - - // If the material editor has a transient preview copy open, get it - // via the editor API. This follows the same pattern as Epic's - // MaterialEditingLibrary (FindMaterialEditorForAsset). - UAssetEditorSubsystem* Sub = GEditor->GetEditorSubsystem(); - IAssetEditorInstance* EditorInstance = Sub ? Sub->FindEditorForAsset(Material, false) : nullptr; - if (EditorInstance) - { - // This is a weird hack. We know that the IAssetEditorInstance for a material - // is always going to be an FMaterialEditor, which conforms to IMaterialEditor. - // If that weren't the case, this unsafe code would crash hard. However, - // lots of places in unreal use this same unsafe pattern. - IMaterialEditor* MatEditor = static_cast(EditorInstance); - UMaterialInterface* Edited = MatEditor->GetMaterialInterface(); - if (UMaterial* EditedMat = Cast(Edited)) - return EditedMat; - } - - return Material; -} - -bool WingUtils::SaveGenericPackage(UObject* Asset) -{ - if (!Asset) return false; - UPackage* Package = Asset->GetPackage(); - UE_LOG(LogTemp, Display, TEXT("UEWingman: SaveGenericPackage — begin for '%s'"), *Asset->GetName()); - - FString PackageFilename = FPackageName::LongPackageNameToFilename( - Package->GetName(), FPackageName::GetAssetPackageExtension()); - PackageFilename = FPaths::ConvertRelativePathToFull(PackageFilename); - - if (FPlatformFileManager::Get().GetPlatformFile().IsReadOnly(*PackageFilename)) - { - FPlatformFileManager::Get().GetPlatformFile().SetReadOnly(*PackageFilename, false); - } - - FSavePackageArgs SaveArgs; - SaveArgs.TopLevelFlags = RF_Public | RF_Standalone; - SaveArgs.SaveFlags = SAVE_NoError; - - ESavePackageResult SaveResult = ESavePackageResult::Error; -#if PLATFORM_WINDOWS - int32 SEHCode = TrySavePackageSEH(Package, Asset, *PackageFilename, &SaveArgs, &SaveResult); - if (SEHCode != 0) - { - UE_LOG(LogTemp, Error, TEXT("UEWingman: SaveGenericPackage CRASHED (SEH exception)")); - } -#else - FSavePackageResultStruct Result = UPackage::Save(Package, Asset, *PackageFilename, SaveArgs); - SaveResult = Result.Result; -#endif - - bool bSuccess = (SaveResult == ESavePackageResult::Success); - UE_LOG(LogTemp, Display, TEXT("UEWingman: SaveGenericPackage — %s for '%s'"), - bSuccess ? TEXT("SUCCEEDED") : TEXT("FAILED"), *Asset->GetName()); - return bSuccess; -} - - -// ============================================================ -// Anim blueprint helpers -// ============================================================ - -UAnimationStateMachineGraph* WingUtils::FindStateMachineGraph(UBlueprint* BP, const FString& GraphName) -{ - TArray AllGraphs; - BP->GetAllGraphs(AllGraphs); - for (UEdGraph* Graph : AllGraphs) - { - if (UAnimationStateMachineGraph* SMGraph = Cast(Graph)) - { - if (SMGraph->GetName() == GraphName) - { - return SMGraph; - } - } - } - return nullptr; -} - -UAnimStateNode* WingUtils::FindStateByName(UAnimationStateMachineGraph* SMGraph, const FString& StateName) -{ - for (UEdGraphNode* Node : SMGraph->Nodes) - { - if (UAnimStateNode* StateNode = Cast(Node)) - { - if (StateNode->GetStateName() == StateName) - { - return StateNode; - } - } - } - UWingServer::Printf(TEXT("ERROR: State '%s' not found in graph '%s'\n"), *StateName, *SMGraph->GetName()); - return nullptr; -} - -UAnimStateTransitionNode* WingUtils::FindTransition(UAnimationStateMachineGraph* SMGraph, - const FString& FromStateName, const FString& ToStateName) -{ - for (UEdGraphNode* Node : SMGraph->Nodes) - { - if (UAnimStateTransitionNode* TransNode = Cast(Node)) - { - UAnimStateNode* FromState = Cast(TransNode->GetPreviousState()); - UAnimStateNode* ToState = Cast(TransNode->GetNextState()); - if (FromState && ToState && - (FromState->GetStateName() == FromStateName) && - (ToState->GetStateName() == ToStateName)) - { - return TransNode; - } - } - } - return nullptr; -} - -// ============================================================ -// Graph actions (node spawning) -// ============================================================ - -FString WingUtils::ActionFullName(const TSharedPtr& Action) -{ - FString Category = Action->GetCategory().ToString(); - FString MenuName = Action->GetMenuDescription().ToString(); - if (Category.IsEmpty()) - return MenuName; - return Category + TEXT("|") + MenuName; -} - -TArray> WingUtils::SearchGraphActions(UEdGraph* Graph, const FString& Query, int32 MaxResults, bool ExactMatch) -{ - FString QueryLower = Query.ToLower(); - TArray> Result; - - FGraphContextMenuBuilder ContextMenuBuilder(Graph); - Graph->GetSchema()->GetGraphContextActions(ContextMenuBuilder); - - for (int32 i = 0; i < ContextMenuBuilder.GetNumActions(); i++) - { - TSharedPtr Action = ContextMenuBuilder.GetSchemaAction(i); - if (!Action.IsValid()) continue; - - FString FullName = ActionFullName(Action); - if (FullName.IsEmpty()) continue; - - if (ExactMatch) - { - if (FullName.ToLower() != QueryLower) - continue; - } - else - { - FString Keywords = Action->GetKeywords().ToString(); - if (!FullName.ToLower().Contains(QueryLower) && !Keywords.ToLower().Contains(QueryLower)) - continue; - } - - Result.Add(Action); - if ((MaxResults > 0) && (Result.Num() >= MaxResults)) - break; - } - - return Result; -} - -// ============================================================ -// PopulateFromJson — fill a USTRUCT from a JSON object -// ============================================================ - -// ============================================================ -// CollectHandlerClasses — find all concrete IWingHandler classes -// ============================================================ - -TArray WingUtils::CollectHandlerClasses() -{ - TArray Result; - for (TObjectIterator It; It; ++It) - { - UClass* Class = *It; - if (Class->HasAnyClassFlags(CLASS_Abstract)) continue; - if (!Class->ImplementsInterface(UWingHandler::StaticClass())) continue; - Result.Add(Class); - } - Result.Sort([](UClass& A, UClass& B) { return GetHandlerName(&A) < GetHandlerName(&B); }); - return Result; -} - -// ============================================================ -// GetHandlerName — derive tool name from handler class name -// ============================================================ - -FString WingUtils::GetHandlerName(UClass* HandlerClass) -{ - FString Name = HandlerClass->GetName(); - // Strip "Wing_" prefix - Name.RemoveFromStart(TEXT("Wing_")); - return Name; -} - -// ============================================================ -// GetHandlerGroup — derive group name from handler class name -// ============================================================ - -FString WingUtils::GetHandlerGroup(UClass* HandlerClass) -{ - FString Name = HandlerClass->GetName(); - // Strip "Wing_" prefix - Name.RemoveFromStart(TEXT("Wing_")); - // Everything before the underscore is the group - int32 UnderscoreIdx; - if (Name.FindChar(TEXT('_'), UnderscoreIdx)) - return Name.Left(UnderscoreIdx); - return Name; -} - -// ============================================================ -// GetTemplate -// ============================================================ - -// ============================================================ -// FindPropertyByName -// ============================================================ - -FProperty* WingUtils::FindPropertyByName(UObject* Obj, const FString& Name) -{ - if (!Obj) - { - UWingServer::Print(TEXT("ERROR: Object is null\n")); - return nullptr; - } - - FProperty* Found = nullptr; - for (TFieldIterator PropIt(Obj->GetClass()); PropIt; ++PropIt) - { - if (!Identifies(Name, *PropIt)) continue; - if (Found) - { - UWingServer::Printf(TEXT("ERROR: Ambiguous property '%s' on %s\n"), *Name, *FormatName(Obj->GetClass())); - return nullptr; - } - Found = *PropIt; - } - - if (!Found) - UWingServer::Printf(TEXT("ERROR: Property '%s' not found on %s\n"), *Name, *FormatName(Obj->GetClass())); - - return Found; -} - -// ============================================================ -// GetPropertyValueText -// ============================================================ - -FString WingUtils::GetPropertyValueText(UObject* Container, FProperty* Prop) -{ - FString Result; - void* ValuePtr = Prop->ContainerPtrToValuePtr(Container); - Prop->ExportTextItem_Direct(Result, ValuePtr, nullptr, Container, PPF_None); - return Result; -} - -// ============================================================ -// SetPropertyValueText -// ============================================================ - -bool WingUtils::SetPropertyValueText(UObject* Container, FProperty* Prop, const FString& Value) -{ - void* ValuePtr = Prop->ContainerPtrToValuePtr(Container); - const TCHAR* ImportResult = Prop->ImportText_Direct(*Value, ValuePtr, Container, PPF_None); - if (!ImportResult) - { - UWingServer::Printf(TEXT("ERROR: Failed to parse '%s' for property '%s' (type: %s)\n"), - *Value, *FormatName(Prop), *Prop->GetCPPType()); - return false; - } - return true; -} - -bool WingUtils::SetPropertyValueText(void* Container, FProperty* Prop, const FString& Value, UObject* Owner) -{ - void* ValuePtr = Prop->ContainerPtrToValuePtr(Container); - const TCHAR* ImportResult = Prop->ImportText_Direct(*Value, ValuePtr, Owner, PPF_None); - if (!ImportResult) - { - UWingServer::Printf(TEXT("ERROR: Failed to parse '%s' for property '%s' (type: %s)\n"), - *Value, *FormatName(Prop), *Prop->GetCPPType()); - return false; - } - return true; -} - -// ============================================================ -// SearchProperties -// ============================================================ - -TArray WingUtils::SearchProperties(UObject* Obj, const FString& Query, EPropertyFlags Flags, bool bLocal) -{ - TArray Result; - if (!Obj) return Result; - UClass* ObjClass = Obj->GetClass(); - for (TFieldIterator PropIt(ObjClass); PropIt; ++PropIt) - { - FProperty* Prop = *PropIt; - if (!Prop) continue; - if (Flags != 0 && !Prop->HasAnyPropertyFlags(Flags)) continue; - if (bLocal && Prop->GetOwnerStruct() != ObjClass) continue; - if (!Query.IsEmpty() && !FormatName(Prop).Contains(Query, ESearchCase::IgnoreCase)) - continue; - Result.Add(Prop); - } - return Result; -} - -// ============================================================ -// FormatCommandHelp — verbose description of one handler command -// ============================================================ - -void WingUtils::FormatCommandHelp(UClass* HandlerClass) -{ - const IWingHandler* Handler = Cast(HandlerClass->GetDefaultObject()); - if (!Handler) return; - - FString ToolName = GetHandlerName(HandlerClass); - - UWingServer::Print(TEXT("\n")); - UWingServer::Print(WrapText(Handler->GetDescription(), 80, TEXT("// "))); - UWingServer::Print(TEXT("\n")); - - // Command signature line - UWingServer::Print(ToolName); - UWingServer::Print(TEXT("(")); - bool bFirst = true; - for (TFieldIterator PropIt(HandlerClass, EFieldIterationFlags::None); PropIt; ++PropIt) - { - if (!bFirst) UWingServer::Print(TEXT(",")); - bFirst = false; - if (PropIt->HasMetaData(TEXT("Optional"))) UWingServer::Print(TEXT("?")); - UWingServer::Print(PropIt->GetName()); - } - UWingServer::Print(TEXT(")\n")); - - // parameter details - for (TFieldIterator PropIt(HandlerClass, EFieldIterationFlags::None); PropIt; ++PropIt) - { - FProperty* Prop = *PropIt; - FString Name = Prop->GetName(); - FString Type = UWingTypes::TypeToText(Prop); - bool bOptional = Prop->HasMetaData(TEXT("Optional")); - const FString& Desc = Prop->GetMetaData(TEXT("Description")); - - UWingServer::Printf(TEXT(" %s %s%s"), - *Type, *Name, bOptional ? TEXT(" (optional)") : TEXT("")); - if (!Desc.IsEmpty()) - UWingServer::Printf(TEXT(" — %s"), *Desc); - UWingServer::Print(TEXT("\n")); - } -} diff --git a/Plugins/UEWingman/Deprecated/StateMachine_AddState.h b/Plugins/UEWingman/Deprecated/StateMachine_AddState.h deleted file mode 100644 index 80f8276f..00000000 --- a/Plugins/UEWingman/Deprecated/StateMachine_AddState.h +++ /dev/null @@ -1,116 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "WingServer.h" -#include "WingHandler.h" -#include "WingFetcher.h" -#include "WingUtils.h" -#include "EdGraph/EdGraph.h" -#include "Kismet2/KismetEditorUtilities.h" -#include "Animation/AnimBlueprint.h" -#include "Animation/AnimSequence.h" -#include "AnimGraphNode_SequencePlayer.h" -#include "AnimStateNode.h" -#include "AnimationStateMachineGraph.h" -#include "StateMachine_AddState.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS() -class UWing_StateMachine_AddState : public UObject, public IWingHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Animation Blueprint package path")) - FString Blueprint; - - UPROPERTY(meta=(Description="State machine graph name")) - FString Graph; - - UPROPERTY(meta=(Description="Name for the new state")) - FString StateName; - - UPROPERTY(meta=(Optional, Description="X position of the new state node")) - int32 PosX = 200; - - UPROPERTY(meta=(Optional, Description="Y position of the new state node")) - int32 PosY = 0; - - UPROPERTY(meta=(Optional, Description="Animation asset package path to assign to the state")) - FString AnimationAsset; - - virtual FString GetDescription() const override - { - return TEXT("Add a new state to an animation state machine graph. " - "Optionally assign an animation asset to the state."); - } - - virtual void Handle() override - { - // Resolve the anim blueprint - WingFetcher F; - UAnimBlueprint* AnimBP = F.Walk(Blueprint).Cast(); - if (!AnimBP) return; - - // Find the state machine graph - UAnimationStateMachineGraph* SMGraph = WingUtils::FindStateMachineGraph(AnimBP, Graph); - if (!SMGraph) - { - UWingServer::Printf(TEXT("ERROR: State machine graph '%s' not found in %s\n"), *Graph, *WingUtils::FormatName(AnimBP)); - return; - } - - // Check for duplicate state name - if (WingUtils::FindStateByName(SMGraph, StateName)) - { - UWingServer::Printf(TEXT("ERROR: State '%s' already exists in %s\n"), *StateName, *WingUtils::FormatName(SMGraph)); - return; - } - - // Create the state node - UAnimStateNode* NewState = NewObject(SMGraph); - NewState->CreateNewGuid(); - NewState->NodePosX = PosX; - NewState->NodePosY = PosY; - - // Set the state name via the bound graph - NewState->PostPlacedNewNode(); - NewState->AllocateDefaultPins(); - - // Rename the bound graph to set the state name - if (NewState->GetBoundGraph()) - { - NewState->GetBoundGraph()->Rename(*StateName, nullptr); - } - - SMGraph->AddNode(NewState, false, false); - NewState->SetFlags(RF_Transactional); - - // Optionally set animation asset - if (!AnimationAsset.IsEmpty() && NewState->GetBoundGraph()) - { - WingFetcher F2; - UAnimSequence* AnimSeq = F2.Asset(AnimationAsset).Cast(); - if (!AnimSeq) return; - - UAnimGraphNode_SequencePlayer* SeqNode = NewObject(NewState->GetBoundGraph()); - SeqNode->CreateNewGuid(); - SeqNode->PostPlacedNewNode(); - SeqNode->AllocateDefaultPins(); - SeqNode->SetAnimationAsset(AnimSeq); - SeqNode->NodePosX = 0; - SeqNode->NodePosY = 0; - NewState->GetBoundGraph()->AddNode(SeqNode, false, false); - } - - // Compile - FKismetEditorUtilities::CompileBlueprint(AnimBP); - - UWingServer::Printf(TEXT("Created state '%s' in %s\n"), *StateName, *WingUtils::FormatName(SMGraph)); - UWingServer::Printf(TEXT(" node: %s\n"), *WingUtils::FormatName(NewState)); - } -}; diff --git a/Plugins/UEWingman/Deprecated/StateMachine_AddTransition.h b/Plugins/UEWingman/Deprecated/StateMachine_AddTransition.h deleted file mode 100644 index 6deae007..00000000 --- a/Plugins/UEWingman/Deprecated/StateMachine_AddTransition.h +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "WingServer.h" -#include "WingHandler.h" -#include "WingFetcher.h" -#include "WingUtils.h" -#include "Kismet2/KismetEditorUtilities.h" -#include "Animation/AnimBlueprint.h" -#include "AnimStateNode.h" -#include "AnimStateTransitionNode.h" -#include "AnimationStateMachineGraph.h" -#include "StateMachine_AddTransition.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS() -class UWing_StateMachine_AddTransition : public UObject, public IWingHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Animation Blueprint package path")) - FString Blueprint; - - UPROPERTY(meta=(Description="State machine graph name")) - FString Graph; - - UPROPERTY(meta=(Description="Name of the source state")) - FString FromState; - - UPROPERTY(meta=(Description="Name of the target state")) - FString ToState; - - UPROPERTY(meta=(Optional, Description="Crossfade duration in seconds")) - float CrossfadeDuration = 0.0f; - - UPROPERTY(meta=(Optional, Description="Transition priority order")) - int32 Priority = 0; - - UPROPERTY(meta=(Optional, Description="Whether the transition is bidirectional")) - bool BBidirectional = false; - - virtual FString GetDescription() const override - { - return TEXT("Add a transition between two states in an animation state machine graph."); - } - - virtual void Handle() override - { - WingFetcher F; - UAnimBlueprint* AnimBP = F.Asset(Blueprint).Cast(); - if (!AnimBP) return; - - UAnimationStateMachineGraph* SMGraph = WingUtils::FindStateMachineGraph(AnimBP, Graph); - if (!SMGraph) - { - UWingServer::Printf(TEXT("ERROR: State machine graph '%s' not found in '%s'\n"), *Graph, *WingUtils::FormatName(AnimBP)); - return; - } - - UAnimStateNode* FromStateNode = WingUtils::FindStateByName(SMGraph, FromState); - if (!FromStateNode) return; - - UAnimStateNode* ToStateNode = WingUtils::FindStateByName(SMGraph, ToState); - if (!ToStateNode) return; - - // Create transition node - UAnimStateTransitionNode* TransNode = NewObject(SMGraph); - TransNode->CreateNewGuid(); - TransNode->PostPlacedNewNode(); - TransNode->AllocateDefaultPins(); - - // Position between the two states - TransNode->NodePosX = (FromStateNode->NodePosX + ToStateNode->NodePosX) / 2; - TransNode->NodePosY = (FromStateNode->NodePosY + ToStateNode->NodePosY) / 2; - - SMGraph->AddNode(TransNode, false, false); - TransNode->SetFlags(RF_Transactional); - - // Connect: FromState output -> Transition input, Transition output -> ToState input - TransNode->CreateConnections(FromStateNode, ToStateNode); - - // Set optional properties - TransNode->CrossfadeDuration = CrossfadeDuration; - TransNode->PriorityOrder = Priority; - TransNode->Bidirectional = BBidirectional; - - // Compile - FKismetEditorUtilities::CompileBlueprint(AnimBP); - - UWingServer::Printf(TEXT("Created transition %s -> %s: %s\n"), - *FromState, *ToState, *WingUtils::FormatName(TransNode)); - } -}; diff --git a/Plugins/UEWingman/Deprecated/StateMachine_RemoveState.h b/Plugins/UEWingman/Deprecated/StateMachine_RemoveState.h deleted file mode 100644 index f5e4436e..00000000 --- a/Plugins/UEWingman/Deprecated/StateMachine_RemoveState.h +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "WingServer.h" -#include "WingHandler.h" -#include "WingFetcher.h" -#include "WingUtils.h" -#include "Kismet2/KismetEditorUtilities.h" -#include "Animation/AnimBlueprint.h" -#include "AnimStateNode.h" -#include "AnimStateTransitionNode.h" -#include "AnimationStateMachineGraph.h" -#include "StateMachine_RemoveState.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS() -class UWing_StateMachine_RemoveState : public UObject, public IWingHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Path to the state machine graph, e.g. /Game/MyAnimBP,graph:StateMachine")) - FString Graph; - - UPROPERTY(meta=(Description="Name of the state to remove")) - FString StateName; - - virtual FString GetDescription() const override - { - return TEXT("Remove a state and its connected transitions from an animation state machine graph."); - } - - virtual void Handle() override - { - // Fetch the state machine graph via WingFetcher - WingFetcher F; - F.Walk(Graph); - if (!F.Ok()) return; - - UAnimationStateMachineGraph* SMGraph = F.Cast(); - if (!SMGraph) return; - - // Find the owning AnimBlueprint for compile/save - UBlueprint* BP = Cast(SMGraph->GetOuter()->GetOuter()); - if (!BP) - { - UWingServer::Print(TEXT("ERROR: Could not find owning blueprint.\n")); - return; - } - - // Find the state node - UAnimStateNode* StateNode = WingUtils::FindStateByName(SMGraph, StateName); - if (!StateNode) return; - - // Collect and remove transitions connected to this state - int32 RemovedTransitions = 0; - for (UEdGraphNode* Node : TArray(SMGraph->Nodes)) - { - UAnimStateTransitionNode* TransNode = Cast(Node); - if (!TransNode) continue; - if (TransNode->GetPreviousState() != StateNode && TransNode->GetNextState() != StateNode) continue; - TransNode->BreakAllNodeLinks(); - SMGraph->RemoveNode(TransNode); - RemovedTransitions++; - } - - // Remove the state - StateNode->BreakAllNodeLinks(); - SMGraph->RemoveNode(StateNode); - - // Compile - FKismetEditorUtilities::CompileBlueprint(BP); - - UWingServer::Printf(TEXT("Removed state %s and %d transition(s).\n"), - *WingUtils::FormatName(StateNode), RemovedTransitions); - } -}; diff --git a/Plugins/UEWingman/Deprecated/StateMachine_SetAnimation.h b/Plugins/UEWingman/Deprecated/StateMachine_SetAnimation.h deleted file mode 100644 index c922ce8f..00000000 --- a/Plugins/UEWingman/Deprecated/StateMachine_SetAnimation.h +++ /dev/null @@ -1,108 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "WingServer.h" -#include "WingHandler.h" -#include "WingFetcher.h" -#include "WingUtils.h" -#include "EdGraph/EdGraph.h" -#include "EdGraph/EdGraphNode.h" -#include "Kismet2/KismetEditorUtilities.h" -#include "Animation/AnimBlueprint.h" -#include "Animation/AnimSequence.h" -#include "AnimGraphNode_SequencePlayer.h" -#include "AnimStateNode.h" -#include "AnimationStateMachineGraph.h" -#include "StateMachine_SetAnimation.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS() -class UWing_StateMachine_SetAnimation : public UObject, public IWingHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Animation Blueprint package path")) - FString Blueprint; - - UPROPERTY(meta=(Description="State machine graph name")) - FString Graph; - - UPROPERTY(meta=(Description="Name of the state to modify")) - FString StateName; - - UPROPERTY(meta=(Description="Animation asset package path to assign")) - FString AnimationAsset; - - virtual FString GetDescription() const override - { - return TEXT("Set or replace the animation sequence played by a state in an animation state machine."); - } - - virtual void Handle() override - { - // Resolve the anim blueprint - WingFetcher F; - UAnimBlueprint* AnimBP = F.Walk(Blueprint).Cast(); - if (!AnimBP) return; - - // Find the state machine graph - UAnimationStateMachineGraph* SMGraph = WingUtils::FindStateMachineGraph(AnimBP, Graph); - if (!SMGraph) - { - UWingServer::Printf(TEXT("ERROR: State machine graph '%s' not found in %s\n"), *Graph, *WingUtils::FormatName(AnimBP)); - return; - } - - // Find the target state - UAnimStateNode* StateNode = WingUtils::FindStateByName(SMGraph, StateName); - if (!StateNode) return; - - UEdGraph* InnerGraph = StateNode->GetBoundGraph(); - if (!InnerGraph) - { - UWingServer::Printf(TEXT("ERROR: State '%s' has no bound graph\n"), *StateName); - return; - } - - // Find the animation asset - WingFetcher F2; - UAnimSequence* AnimSeq = F2.Asset(AnimationAsset).Cast(); - if (!AnimSeq) return; - - // Find existing SequencePlayer or create one - UAnimGraphNode_SequencePlayer* SeqNode = nullptr; - for (UEdGraphNode* Node : InnerGraph->Nodes) - { - SeqNode = Cast(Node); - if (SeqNode) break; - } - - bool bCreatedNew = false; - if (!SeqNode) - { - SeqNode = NewObject(InnerGraph); - SeqNode->CreateNewGuid(); - SeqNode->PostPlacedNewNode(); - SeqNode->AllocateDefaultPins(); - SeqNode->NodePosX = 0; - SeqNode->NodePosY = 0; - InnerGraph->AddNode(SeqNode, false, false); - bCreatedNew = true; - } - - SeqNode->SetAnimationAsset(AnimSeq); - - // Compile - FKismetEditorUtilities::CompileBlueprint(AnimBP); - - if (bCreatedNew) - UWingServer::Printf(TEXT("Created sequence player in state '%s', assigned %s\n"), *StateName, *WingUtils::FormatName(AnimSeq)); - else - UWingServer::Printf(TEXT("Updated sequence player in state '%s' to %s\n"), *StateName, *WingUtils::FormatName(AnimSeq)); - } -}; diff --git a/Plugins/UEWingman/Deprecated/StateMachine_SetBlendSpace.h b/Plugins/UEWingman/Deprecated/StateMachine_SetBlendSpace.h deleted file mode 100644 index e1c72d06..00000000 --- a/Plugins/UEWingman/Deprecated/StateMachine_SetBlendSpace.h +++ /dev/null @@ -1,217 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "WingServer.h" -#include "WingHandler.h" -#include "WingFetcher.h" -#include "WingUtils.h" -#include "EdGraph/EdGraph.h" -#include "EdGraph/EdGraphNode.h" -#include "EdGraph/EdGraphPin.h" -#include "Kismet2/KismetEditorUtilities.h" -#include "Animation/AnimBlueprint.h" -#include "Animation/BlendSpace.h" -#include "AnimGraphNode_BlendSpacePlayer.h" -#include "EdGraphSchema_K2.h" -#include "AnimStateNode.h" -#include "AnimationStateMachineGraph.h" -#include "K2Node_VariableGet.h" -#include "StateMachine_SetBlendSpace.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS() -class UWing_StateMachine_SetBlendSpace : public UObject, public IWingHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Animation Blueprint package path")) - FString Blueprint; - - UPROPERTY(meta=(Description="State machine graph name")) - FString Graph; - - UPROPERTY(meta=(Description="Name of the state to modify")) - FString StateName; - - UPROPERTY(meta=(Description="Blend Space asset package path")) - FString BlendSpace; - - UPROPERTY(meta=(Optional, Description="Blueprint variable name to wire to the X axis input")) - FString XVariable; - - UPROPERTY(meta=(Optional, Description="Blueprint variable name to wire to the Y axis input")) - FString YVariable; - - virtual FString GetDescription() const override - { - return TEXT("Place a BlendSpacePlayer in a state's inner graph, connect it to the output pose, " - "and optionally wire blueprint variables to the X and Y axis inputs."); - } - - virtual void Handle() override - { - // Load the anim blueprint - WingFetcher F; - UAnimBlueprint* AnimBP = F.Asset(Blueprint).Cast(); - if (!AnimBP) return; - - // Find the state machine graph and state - UAnimationStateMachineGraph* SMGraph = WingUtils::FindStateMachineGraph(AnimBP, Graph); - if (!SMGraph) { UWingServer::Printf(TEXT("ERROR: State machine graph '%s' not found\n"), *Graph); return; } - - UAnimStateNode* StateNode = WingUtils::FindStateByName(SMGraph, StateName); - if (!StateNode) return; - - UEdGraph* InnerGraph = StateNode->GetBoundGraph(); - if (!InnerGraph) { UWingServer::Printf(TEXT("ERROR: State '%s' has no bound graph\n"), *StateName); return; } - - // Load the blend space asset - WingFetcher F2; - UBlendSpace* BlendSpaceAsset = F2.Asset(BlendSpace).Cast(); - if (!BlendSpaceAsset) return; - - // Find existing BlendSpacePlayer or create one - UAnimGraphNode_BlendSpacePlayer* BSNode = nullptr; - for (UEdGraphNode* Node : InnerGraph->Nodes) - { - BSNode = Cast(Node); - if (BSNode) break; - } - - if (!BSNode) - { - BSNode = NewObject(InnerGraph); - BSNode->CreateNewGuid(); - BSNode->PostPlacedNewNode(); - BSNode->AllocateDefaultPins(); - BSNode->NodePosX = 0; - BSNode->NodePosY = 0; - InnerGraph->AddNode(BSNode, false, false); - } - - BSNode->SetAnimationAsset(BlendSpaceAsset); - - // Connect BlendSpacePlayer output to the Output Animation Pose node - ConnectToOutputPose(BSNode, InnerGraph); - - // Wire X and Y variables if provided - WireVariable(AnimBP, InnerGraph, BSNode, XVariable, TEXT("X")); - WireVariable(AnimBP, InnerGraph, BSNode, YVariable, TEXT("Y")); - - // Compile - FKismetEditorUtilities::CompileBlueprint(AnimBP); - - UWingServer::Printf(TEXT("BlendSpacePlayer %s placed in state %s\n"), - *WingUtils::FormatName(BSNode), *StateName); - } - -private: - void ConnectToOutputPose(UAnimGraphNode_BlendSpacePlayer* BSNode, UEdGraph* InnerGraph) - { - // Find the result node (AnimGraphNode_Root or AnimGraphNode_StateResult) - UEdGraphNode* ResultNode = nullptr; - for (UEdGraphNode* Node : InnerGraph->Nodes) - { - if (Node->GetClass()->GetName().Contains(TEXT("AnimGraphNode_Root")) || - Node->GetClass()->GetName().Contains(TEXT("AnimGraphNode_StateResult"))) - { - ResultNode = Node; - break; - } - } - if (!ResultNode) return; - - // Find the pose output pin on BlendSpacePlayer and input pin on result node - UEdGraphPin* BSOutputPin = nullptr; - for (UEdGraphPin* Pin : BSNode->Pins) - { - if (Pin && Pin->Direction == EGPD_Output && Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Struct) - { - BSOutputPin = Pin; - break; - } - } - - UEdGraphPin* ResultInputPin = nullptr; - for (UEdGraphPin* Pin : ResultNode->Pins) - { - if (Pin && Pin->Direction == EGPD_Input && Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Struct) - { - ResultInputPin = Pin; - break; - } - } - - if (!BSOutputPin || !ResultInputPin) return; - - ResultInputPin->BreakAllPinLinks(); - const UEdGraphSchema* Schema = InnerGraph->GetSchema(); - if (Schema) - Schema->TryCreateConnection(BSOutputPin, ResultInputPin); - } - - void WireVariable(UAnimBlueprint* AnimBP, UEdGraph* InnerGraph, - UAnimGraphNode_BlendSpacePlayer* BSNode, const FString& VarName, - const TCHAR* PinName) - { - if (VarName.IsEmpty()) return; - - // Verify the variable exists in the blueprint - FName VarFName(*VarName); - bool bVarFound = false; - for (FBPVariableDescription& Var : AnimBP->NewVariables) - { - if (Var.VarName == VarFName) - { - bVarFound = true; - break; - } - } - if (!bVarFound) - { - if (UClass* GenClass = AnimBP->SkeletonGeneratedClass) - { - if (GenClass->FindPropertyByName(VarFName)) - bVarFound = true; - } - } - if (!bVarFound) - { - UWingServer::Printf(TEXT("WARNING: Variable '%s' not found, skipping %s wire\n"), *VarName, PinName); - return; - } - - // Create a VariableGet node - UK2Node_VariableGet* GetNode = NewObject(InnerGraph); - GetNode->VariableReference.SetSelfMember(VarFName); - GetNode->NodePosX = BSNode->NodePosX - 250; - GetNode->NodePosY = BSNode->NodePosY; - InnerGraph->AddNode(GetNode, false, false); - GetNode->AllocateDefaultPins(); - - // Find the variable output pin - UEdGraphPin* VarOutPin = nullptr; - for (UEdGraphPin* Pin : GetNode->Pins) - { - if (Pin && Pin->Direction == EGPD_Output && Pin->PinName == VarFName) - { - VarOutPin = Pin; - break; - } - } - - UEdGraphPin* TargetPin = BSNode->FindPin(FName(PinName)); - - if (VarOutPin && TargetPin) - { - const UEdGraphSchema* Schema = InnerGraph->GetSchema(); - if (Schema) - Schema->TryCreateConnection(VarOutPin, TargetPin); - } - } -}; diff --git a/Plugins/UEWingman/Deprecated/StateMachine_SetTransitionRule.h b/Plugins/UEWingman/Deprecated/StateMachine_SetTransitionRule.h deleted file mode 100644 index 40d5c419..00000000 --- a/Plugins/UEWingman/Deprecated/StateMachine_SetTransitionRule.h +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "WingServer.h" -#include "WingHandler.h" -#include "WingFetcher.h" -#include "WingUtils.h" -#include "Kismet2/KismetEditorUtilities.h" -#include "Animation/AnimBlueprint.h" -#include "AnimStateTransitionNode.h" -#include "AnimationStateMachineGraph.h" -#include "StateMachine_SetTransitionRule.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS() -class UWing_StateMachine_SetTransitionRule : public UObject, public IWingHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Animation Blueprint package path")) - FString Blueprint; - - UPROPERTY(meta=(Description="State machine graph name")) - FString Graph; - - UPROPERTY(meta=(Description="Name of the source state")) - FString FromState; - - UPROPERTY(meta=(Description="Name of the target state")) - FString ToState; - - UPROPERTY(meta=(Optional, Description="Crossfade duration in seconds")) - float CrossfadeDuration = 0.0f; - - UPROPERTY(meta=(Optional, Description="Blend mode (as integer enum value)")) - int32 BlendMode = 0; - - UPROPERTY(meta=(Optional, Description="Transition priority order")) - int32 PriorityOrder = 0; - - UPROPERTY(meta=(Optional, Description="Logic type (as integer enum value)")) - int32 LogicType = 0; - - UPROPERTY(meta=(Optional, Description="Whether the transition is bidirectional")) - bool BBidirectional = false; - - virtual FString GetDescription() const override - { - return TEXT("Update properties on an existing transition between two states in an animation state machine."); - } - - virtual void Handle() override - { - WingFetcher F; - UAnimBlueprint* AnimBP = F.Asset(Blueprint).Cast(); - if (!AnimBP) return; - - UAnimationStateMachineGraph* SMGraph = WingUtils::FindStateMachineGraph(AnimBP, Graph); - if (!SMGraph) - { - UWingServer::Printf(TEXT("ERROR: State machine graph '%s' not found in '%s'\n"), *Graph, *WingUtils::FormatName(AnimBP)); - return; - } - - UAnimStateTransitionNode* TransNode = WingUtils::FindTransition(SMGraph, FromState, ToState); - if (!TransNode) - { - UWingServer::Printf(TEXT("ERROR: Transition from '%s' to '%s' not found in graph '%s'\n"), - *FromState, *ToState, *Graph); - return; - } - - // Apply properties - TransNode->CrossfadeDuration = CrossfadeDuration; - TransNode->BlendMode = (EAlphaBlendOption)BlendMode; - TransNode->PriorityOrder = PriorityOrder; - TransNode->LogicType = (ETransitionLogicType::Type)LogicType; - TransNode->Bidirectional = BBidirectional; - - // Compile - FKismetEditorUtilities::CompileBlueprint(AnimBP); - - UWingServer::Printf(TEXT("Updated transition %s -> %s: %s\n"), - *FromState, *ToState, *WingUtils::FormatName(TransNode)); - } -}; diff --git a/Plugins/UEWingman/Deprecated/Struct_Create.h b/Plugins/UEWingman/Deprecated/Struct_Create.h deleted file mode 100644 index 0ce794b0..00000000 --- a/Plugins/UEWingman/Deprecated/Struct_Create.h +++ /dev/null @@ -1,95 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "WingServer.h" -#include "WingHandler.h" -#include "WingTypes.h" -#include "WingJson.h" -#include "WingUtils.h" -#include "StructUtils/UserDefinedStruct.h" -#include "Kismet2/BlueprintEditorUtils.h" -#include "UserDefinedStructure/UserDefinedStructEditorData.h" -#include "Factories/StructureFactory.h" -#include "WingPackageMaker.h" -#include "Struct_Create.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -USTRUCT() -struct FStructPropertyEntry -{ - GENERATED_BODY() - - UPROPERTY() - FString Name; - - UPROPERTY() - FString Type; -}; - -UCLASS() -class UWing_Struct_Create : public UObject, public IWingHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Full asset path for the new struct (e.g. '/Game/DataTypes/S_MyStruct')")) - FString AssetPath; - - UPROPERTY(meta=(Optional, Description="Array of initial properties, each with 'name' and 'type' fields")) - FWingJsonArray Properties; - - virtual FString GetDescription() const override - { - return TEXT("Create a new UserDefinedStruct asset with optional initial properties."); - } - - virtual void Handle() override - { - WingPackageMaker Maker(AssetPath); - if (!Maker.Ok()) return; - - // Create the struct using the AssetTools factory. - UUserDefinedStruct* NewStruct = Maker.CreateAsset(); - if (!NewStruct) return; - - // Add properties if specified. - int32 PropsAdded = 0; - for (const TSharedPtr& PropVal : Properties.Array) - { - FStructPropertyEntry Entry; - if (!WingJson::PopulateFromJson(FStructPropertyEntry::StaticStruct(), &Entry, PropVal)) return; - if (Entry.Name.IsEmpty() || Entry.Type.IsEmpty()) continue; - - FEdGraphPinType PinType; - if (!UWingTypes::TextToType(Entry.Type, PinType)) - continue; - - // Snapshot existing GUIDs so we can find the newly added one. - TSet ExistingGuids; - for (const FStructVariableDescription& Var : FStructureEditorUtils::GetVarDesc(NewStruct)) - ExistingGuids.Add(Var.VarGuid); - - if (!FStructureEditorUtils::AddVariable(NewStruct, PinType)) - continue; - - // Find the new variable by diffing GUID sets. - for (const FStructVariableDescription& Var : FStructureEditorUtils::GetVarDesc(NewStruct)) - { - if (!ExistingGuids.Contains(Var.VarGuid)) - { - FStructureEditorUtils::RenameVariable(NewStruct, Var.VarGuid, Entry.Name); - break; - } - } - PropsAdded++; - } - - UWingServer::Printf(TEXT("Created %s\n"), *NewStruct->GetPathName()); - if (PropsAdded > 0) - UWingServer::Printf(TEXT("Properties added: %d\n"), PropsAdded); - } -}; diff --git a/Plugins/UEWingman/Deprecated/Struct_Modify.h b/Plugins/UEWingman/Deprecated/Struct_Modify.h deleted file mode 100644 index e69de29b..00000000 diff --git a/Plugins/UEWingman/Deprecated/UMCPHandler_AddMaterialExpression.h b/Plugins/UEWingman/Deprecated/UMCPHandler_AddMaterialExpression.h deleted file mode 100644 index 4534c051..00000000 --- a/Plugins/UEWingman/Deprecated/UMCPHandler_AddMaterialExpression.h +++ /dev/null @@ -1,169 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "MCPHandler.h" -#include "MCPAssets.h" -#include "MCPUtils.h" -#include "Materials/Material.h" -#include "Materials/MaterialFunction.h" -#include "Materials/MaterialExpression.h" -#include "MaterialGraph/MaterialGraph.h" -#include "MaterialGraph/MaterialGraphNode.h" -#include "UObject/UObjectIterator.h" -#include "UMCPHandler_AddMaterialExpression.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS() -class UMCPHandler_AddMaterialExpression : public UObject, public IMCPHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Optional, Description="Material name or package path (specify this or materialFunction, not both)")) - FString Material; - - UPROPERTY(meta=(Optional, Description="Material function name or package path (specify this or material, not both)")) - FString MaterialFunction; - - UPROPERTY(meta=(Description="Expression class name without 'MaterialExpression' prefix (e.g. 'Constant', 'ScalarParameter', 'Add', 'Multiply', 'Lerp')")) - FString ExpressionClass; - - UPROPERTY(meta=(Optional, Description="X position in the material graph editor")) - int32 PosX = 0; - - UPROPERTY(meta=(Optional, Description="Y position in the material graph editor")) - int32 PosY = 0; - - virtual FString GetDescription() const override - { - return TEXT("Add a new expression node to a material or material function graph."); - } - - virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override - { - if (Material.IsEmpty() && MaterialFunction.IsEmpty()) - { - Result.Append(TEXT("ERROR: Specify 'material' or 'materialFunction'\n")); - return; - } - if (!Material.IsEmpty() && !MaterialFunction.IsEmpty()) - { - Result.Append(TEXT("ERROR: Specify 'material' or 'materialFunction', not both\n")); - return; - } - - // Resolve the expression class - UClass* ExprClass = ResolveExpressionClass(Result); - if (!ExprClass) return; - - // Load material or material function - UMaterial* MaterialObj = nullptr; - UMaterialFunction* MatFunc = nullptr; - UObject* Owner = nullptr; - - if (!MaterialFunction.IsEmpty()) - { - MCPAssets Assets; - if (!Assets.Exact(MaterialFunction).Errors(Result).ENone().ETwo().Load()) return; - MatFunc = Assets.Object(); - Owner = MatFunc; - } - else - { - MCPAssets Assets; - if (!Assets.Exact(Material).Errors(Result).ENone().ETwo().Load()) return; - MaterialObj = Assets.Object(); - Owner = MaterialObj; - } - - // Ensure the MaterialGraph exists (commandlet mode doesn't auto-create it) - if (MaterialObj) MCPUtils::EnsureMaterialGraph(MaterialObj); - - // Create the expression - UMaterialExpression* NewExpr = NewObject(Owner, ExprClass); - if (!NewExpr) - { - Result.Append(TEXT("ERROR: Failed to create material expression object\n")); - return; - } - NewExpr->MaterialExpressionEditorX = PosX; - NewExpr->MaterialExpressionEditorY = PosY; - - if (MaterialObj) - { - MaterialObj->GetExpressionCollection().AddExpression(NewExpr); - if (MaterialObj->MaterialGraph) - MaterialObj->MaterialGraph->RebuildGraph(); - MaterialObj->PreEditChange(nullptr); - MaterialObj->PostEditChange(); - MaterialObj->MarkPackageDirty(); - } - else - { - MatFunc->GetExpressionCollection().AddExpression(NewExpr); - MatFunc->PreEditChange(nullptr); - MatFunc->PostEditChange(); - MatFunc->MarkPackageDirty(); - } - - // Save - bool bSaved = MaterialObj - ? MCPUtils::SaveMaterialPackage(MaterialObj) - : MCPUtils::SaveGenericPackage(MatFunc); - - // Output - Result.Appendf(TEXT("Added %s\n"), *MCPUtils::FormatName(NewExpr)); - - // Find the graph node GUID (only for materials with a material graph) - if (MaterialObj && MaterialObj->MaterialGraph) - { - for (UEdGraphNode* Node : MaterialObj->MaterialGraph->Nodes) - { - UMaterialGraphNode* MatNode = Cast(Node); - if (MatNode && MatNode->MaterialExpression == NewExpr) - { - Result.Appendf(TEXT("NodeId: %s\n"), *Node->NodeGuid.ToString()); - break; - } - } - } - - if (!bSaved) - Result.Append(TEXT("WARNING: Failed to save package\n")); - } - -private: - UClass* ResolveExpressionClass(FStringBuilderBase& Result) - { - // Convenience aliases - static TMap Aliases = { - {TEXT("Lerp"), TEXT("LinearInterpolate")}, - }; - - FString LookupName = ExpressionClass; - if (const FString* Alias = Aliases.Find(ExpressionClass)) - LookupName = *Alias; - - FString FullClassName = FString::Printf(TEXT("MaterialExpression%s"), *LookupName); - for (TObjectIterator It; It; ++It) - { - if (It->GetName() == FullClassName && It->IsChildOf(UMaterialExpression::StaticClass())) - { - if (It->HasAnyClassFlags(CLASS_Abstract)) - { - Result.Appendf(TEXT("ERROR: Expression class '%s' is abstract\n"), *ExpressionClass); - return nullptr; - } - return *It; - } - } - - Result.Appendf(TEXT("ERROR: Unknown expression class '%s'. Use the UMaterialExpression subclass name without the 'MaterialExpression' prefix (e.g. 'Constant', 'ScalarParameter', 'Add', 'Multiply', 'Lerp')\n"), - *ExpressionClass); - return nullptr; - } -}; diff --git a/Plugins/UEWingman/Deprecated/UMCPHandler_AddStructField.h b/Plugins/UEWingman/Deprecated/UMCPHandler_AddStructField.h deleted file mode 100644 index 99d321ed..00000000 --- a/Plugins/UEWingman/Deprecated/UMCPHandler_AddStructField.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "MCPHandler.h" -#include "MCPFetcher.h" -#include "MCPTypes.h" -#include "MCPUtils.h" -#include "StructUtils/UserDefinedStruct.h" -#include "UserDefinedStructure/UserDefinedStructEditorData.h" -#include "UMCPHandler_AddStructField.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS(meta=(Group="Unclassified")) -class UMCPHandler_AddStructField : public UObject, public IMCPHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Package path of the struct asset")) - FString Struct; - - UPROPERTY(meta=(Description="Name for the new field")) - FString Name; - - UPROPERTY(meta=(Description="Type for the new field (e.g. 'int32', 'FString', 'FVector')")) - FString Type; - - virtual FString GetDescription() const override - { - return TEXT("Add a new field to a UserDefinedStruct asset."); - } - - virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override - { - // Find the struct - MCPFetcher F(Result); - UUserDefinedStruct* S = F.Walk(Struct).Cast(); - if (!S) return; - - // Resolve type - FEdGraphPinType PinType; - if (!UMCPTypes::TextToType(Type, PinType, Result)) - return; - - // Snapshot existing GUIDs so we can find the newly added one - TSet ExistingGuids; - for (const FStructVariableDescription& Var : FStructureEditorUtils::GetVarDesc(S)) - ExistingGuids.Add(Var.VarGuid); - - if (!FStructureEditorUtils::AddVariable(S, PinType)) - { - Result.Append(TEXT("ERROR: Failed to add field to struct.\n")); - return; - } - - // Find the new variable by diffing GUID sets and rename it - for (const FStructVariableDescription& Var : FStructureEditorUtils::GetVarDesc(S)) - { - if (!ExistingGuids.Contains(Var.VarGuid)) - { - FStructureEditorUtils::RenameVariable(S, Var.VarGuid, Name); - break; - } - } - - MCPUtils::SaveGenericPackage(S); - Result.Appendf(TEXT("Added field: %s %s\n"), *Type, *Name); - } -}; diff --git a/Plugins/UEWingman/Deprecated/UMCPHandler_BlueprintSearchTypeUsage.h b/Plugins/UEWingman/Deprecated/UMCPHandler_BlueprintSearchTypeUsage.h deleted file mode 100644 index 32d67dc4..00000000 --- a/Plugins/UEWingman/Deprecated/UMCPHandler_BlueprintSearchTypeUsage.h +++ /dev/null @@ -1,229 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "MCPHandler.h" -#include "MCPAssets.h" -#include "MCPUtils.h" -#include "Engine/Blueprint.h" -#include "Engine/World.h" -#include "Engine/Level.h" -#include "Engine/LevelScriptBlueprint.h" -#include "EdGraph/EdGraph.h" -#include "EdGraph/EdGraphNode.h" -#include "K2Node_CallFunction.h" -#include "K2Node_Event.h" -#include "K2Node_CustomEvent.h" -#include "K2Node_VariableGet.h" -#include "K2Node_VariableSet.h" -#include "K2Node_BreakStruct.h" -#include "K2Node_MakeStruct.h" -#include "K2Node_FunctionEntry.h" -#include "K2Node_EditablePinBase.h" -#include "AssetRegistry/IAssetRegistry.h" -#include "UMCPHandler_BlueprintSearchTypeUsage.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -// ============================================================ -// HandleSearchByType — find all usages of a type across blueprints -// ============================================================ - -UCLASS(meta=(Group="Blueprint")) -class UMCPHandler_BlueprintSearchTypeUsage : public UObject, public IMCPHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Type name to search for (e.g. 'FVector', 'MyStruct'). F/E/U prefix is stripped for matching.")) - FString TypeName; - - UPROPERTY(meta=(Optional, Description="Filter to blueprints whose name or path contains this substring")) - FString Query; - - UPROPERTY(meta=(Optional, Description="Maximum number of results to return (default 200, max 500)")) - int32 MaxResults = 0; - - virtual FString GetDescription() const override - { - return TEXT("Search all Blueprints for usages of a specific type in variables, function parameters, struct nodes, and pin connections."); - } - - virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override - { - FString DecodedTypeName = MCPUtils::UrlDecode(TypeName); - FString FilterStr = Query.IsEmpty() ? FString() : MCPUtils::UrlDecode(Query); - - int32 EffectiveMaxResults = (MaxResults > 0) ? FMath::Clamp(MaxResults, 1, 500) : 200; - - // Strip F/E/U prefix for comparison - FString TypeNameNoPrefix = DecodedTypeName; - if (TypeNameNoPrefix.StartsWith(TEXT("F")) || TypeNameNoPrefix.StartsWith(TEXT("E")) || TypeNameNoPrefix.StartsWith(TEXT("U"))) - { - TypeNameNoPrefix = TypeNameNoPrefix.Mid(1); - } - - auto MatchesType = [&DecodedTypeName, &TypeNameNoPrefix](const FString& TestType) -> bool - { - return TestType.Equals(DecodedTypeName, ESearchCase::IgnoreCase) || - TestType.Equals(TypeNameNoPrefix, ESearchCase::IgnoreCase); - }; - - int32 ResultCount = 0; - - // Helper: get subtype name from a pin type - auto GetSubtype = [](const FEdGraphPinType& PinType) -> FString - { - if (PinType.PinSubCategoryObject.IsValid()) - return PinType.PinSubCategoryObject->GetName(); - return FString(); - }; - - // Helper: check if a pin type matches - auto PinTypeMatches = [&](const FEdGraphPinType& PinType) -> bool - { - return MatchesType(GetSubtype(PinType)) || MatchesType(PinType.PinCategory.ToString()); - }; - - // Lambda that searches a single Blueprint for type usages - auto SearchOneBlueprint = [&](UBlueprint* BP, bool bIsLevel) - { - FString BPName = MCPUtils::FormatName(BP); - - // Check variables - for (const FBPVariableDescription& Var : BP->NewVariables) - { - if (ResultCount >= EffectiveMaxResults) return; - if (!PinTypeMatches(Var.VarType)) continue; - - Result.Appendf(TEXT("variable %s in %s: %s %s\n"), - *MCPUtils::FormatName(Var), *BPName, - *Var.VarType.PinCategory.ToString(), *GetSubtype(Var.VarType)); - ResultCount++; - } - - // Check graphs for function/event params, struct nodes, and pin connections - for (UEdGraphNode* Node : MCPUtils::AllNodes(BP)) - { - if (ResultCount >= EffectiveMaxResults) return; - - // Check FunctionEntry parameters - if (auto* FuncEntry = Cast(Node)) - { - for (const TSharedPtr& PinInfo : FuncEntry->UserDefinedPins) - { - if (!PinInfo.IsValid()) continue; - if (!PinTypeMatches(PinInfo->PinType)) continue; - - Result.Appendf(TEXT("func-param %s.%s in %s: %s %s\n"), - *MCPUtils::FormatName(Node->GetGraph()), *PinInfo->PinName.ToString(), - *BPName, - *PinInfo->PinType.PinCategory.ToString(), *GetSubtype(PinInfo->PinType)); - ResultCount++; - } - } - else if (auto* CustomEvent = Cast(Node)) - { - for (const TSharedPtr& PinInfo : CustomEvent->UserDefinedPins) - { - if (!PinInfo.IsValid()) continue; - if (!PinTypeMatches(PinInfo->PinType)) continue; - - Result.Appendf(TEXT("event-param %s.%s in %s: %s %s\n"), - *MCPUtils::FormatName(Node), *PinInfo->PinName.ToString(), - *BPName, - *PinInfo->PinType.PinCategory.ToString(), *GetSubtype(PinInfo->PinType)); - ResultCount++; - } - } - // Check Break/Make struct nodes - else if (auto* BreakNode = Cast(Node)) - { - if (BreakNode->StructType && MatchesType(BreakNode->StructType->GetName())) - { - Result.Appendf(TEXT("break-struct %s in %s graph %s\n"), - *BreakNode->StructType->GetName(), *BPName, - *MCPUtils::FormatName(Node->GetGraph())); - ResultCount++; - } - } - else if (auto* MakeNode = Cast(Node)) - { - if (MakeNode->StructType && MatchesType(MakeNode->StructType->GetName())) - { - Result.Appendf(TEXT("make-struct %s in %s graph %s\n"), - *MakeNode->StructType->GetName(), *BPName, - *MCPUtils::FormatName(Node->GetGraph())); - ResultCount++; - } - } - - // Check pin connections carrying the type - for (UEdGraphPin* Pin : Node->Pins) - { - if (!Pin || Pin->bHidden || ResultCount >= EffectiveMaxResults) continue; - if (Pin->LinkedTo.Num() == 0) continue; - if (!PinTypeMatches(Pin->PinType)) continue; - - Result.Appendf(TEXT("pin %s.%s in %s graph %s: %s %s (%d connections)\n"), - *MCPUtils::FormatName(Node), *MCPUtils::FormatName(Pin), - *BPName, *MCPUtils::FormatName(Node->GetGraph()), - *Pin->PinType.PinCategory.ToString(), *GetSubtype(Pin->PinType), - Pin->LinkedTo.Num()); - ResultCount++; - } - } - }; - - MCPAssets AllBlueprints; - AllBlueprints.Info(); - MCPAssets AllWorlds; - AllWorlds.Info(); - - // Search regular blueprints - for (const FAssetData& Asset : AllBlueprints.AllData()) - { - if (ResultCount >= EffectiveMaxResults) break; - - FString AssetPath = Asset.PackageName.ToString(); - FString AssetName = Asset.AssetName.ToString(); - - if (!FilterStr.IsEmpty() && !AssetName.Contains(FilterStr, ESearchCase::IgnoreCase) && - !AssetPath.Contains(FilterStr, ESearchCase::IgnoreCase)) - { - continue; - } - - UBlueprint* BP = Cast(const_cast(Asset).GetAsset()); - if (!BP) continue; - - SearchOneBlueprint(BP, false); - } - - // Search level blueprints from maps - for (const FAssetData& MapAsset : AllWorlds.AllData()) - { - if (ResultCount >= EffectiveMaxResults) break; - - FString AssetPath = MapAsset.PackageName.ToString(); - FString MapName = MapAsset.AssetName.ToString(); - - if (!FilterStr.IsEmpty() && !MapName.Contains(FilterStr, ESearchCase::IgnoreCase) && - !AssetPath.Contains(FilterStr, ESearchCase::IgnoreCase)) - { - continue; - } - - UWorld* World = Cast(MapAsset.GetAsset()); - if (!World || !World->PersistentLevel) continue; - ULevelScriptBlueprint* LevelBP = World->PersistentLevel->GetLevelScriptBlueprint(false); - if (!LevelBP) continue; - - SearchOneBlueprint(LevelBP, true); - } - - Result.Appendf(TEXT("\n%d results\n"), ResultCount); - } -}; diff --git a/Plugins/UEWingman/Deprecated/UMCPHandler_ConnectMaterialExpressionPins.h b/Plugins/UEWingman/Deprecated/UMCPHandler_ConnectMaterialExpressionPins.h deleted file mode 100644 index 8f655b6d..00000000 --- a/Plugins/UEWingman/Deprecated/UMCPHandler_ConnectMaterialExpressionPins.h +++ /dev/null @@ -1,188 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "MCPHandler.h" -#include "MCPAssets.h" -#include "MCPUtils.h" -#include "Materials/Material.h" -#include "Materials/MaterialFunction.h" -#include "Materials/MaterialExpression.h" -#include "MaterialGraph/MaterialGraph.h" -#include "EdGraph/EdGraph.h" -#include "EdGraph/EdGraphNode.h" -#include "UMCPHandler_ConnectMaterialExpressionPins.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -USTRUCT() -struct FConnectMaterialPinsEntry -{ - GENERATED_BODY() - - UPROPERTY() - FString SourceNode; - - UPROPERTY() - FString SourcePin; - - UPROPERTY() - FString TargetNode; - - UPROPERTY() - FString TargetPin; -}; - - -UCLASS(meta=(Group="Unclassified")) -class UMCPHandler_ConnectMaterialExpressionPins : public UObject, public IMCPHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Optional, Description="Material name or package path (specify this or materialFunction)")) - FString Material; - - UPROPERTY(meta=(Optional, Description="Material function name or package path (specify this or material)")) - FString MaterialFunction; - - UPROPERTY(meta=(Description="Array of {sourceNode, sourcePin, targetNode, targetPin} objects")) - FMCPJsonArray Connections; - - virtual FString GetDescription() const override - { - return TEXT("Connect pins between nodes in a material or material function graph. Supports batching."); - } - - virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override - { - if (Material.IsEmpty() && MaterialFunction.IsEmpty()) - { - Result.Append(TEXT("ERROR: Specify 'material' or 'materialFunction'.\n")); - return; - } - - // Load material or material function - UMaterial* MaterialObj = nullptr; - UMaterialFunction* MatFunc = nullptr; - - if (!MaterialFunction.IsEmpty()) - { - MCPAssets Assets; - if (!Assets.Exact(MaterialFunction).Errors(Result).ENone().ETwo().Load()) return; - MatFunc = Assets.Object(); - } - else - { - MCPAssets Assets; - if (!Assets.Exact(Material).Errors(Result).ENone().ETwo().Load()) return; - MaterialObj = Assets.Object(); - } - - if (MaterialObj) MCPUtils::EnsureMaterialGraph(MaterialObj); - UEdGraph* Graph = MaterialObj ? (UEdGraph*)MaterialObj->MaterialGraph : (MatFunc ? MatFunc->MaterialGraph : nullptr); - if (!Graph) - { - Result.Appendf(TEXT("ERROR: %s has no material graph.\n"), - MaterialObj ? *MCPUtils::FormatName(MaterialObj) : *MCPUtils::FormatName(MatFunc)); - return; - } - - int32 SuccessCount = 0; - const UEdGraphSchema* Schema = Graph->GetSchema(); - - for (const TSharedPtr& ConnVal : Connections.Array) - { - FConnectMaterialPinsEntry Entry; - if (!MCPUtils::PopulateFromJson(FConnectMaterialPinsEntry::StaticStruct(), &Entry, ConnVal, MCPErrorCallback(Result))) - continue; - - // Find source node - UEdGraphNode* SrcNode = FindNodeByName(Graph, Entry.SourceNode); - if (!SrcNode) - { - Result.Appendf(TEXT("ERROR: Source node '%s' not found.\n"), *Entry.SourceNode); - continue; - } - - // Find target node - UEdGraphNode* TgtNode = FindNodeByName(Graph, Entry.TargetNode); - if (!TgtNode) - { - Result.Appendf(TEXT("ERROR: Target node '%s' not found.\n"), *Entry.TargetNode); - continue; - } - - // Find source pin - UEdGraphPin* SrcPin = FindPinByName(SrcNode, Entry.SourcePin); - if (!SrcPin) - { - Result.Appendf(TEXT("ERROR: Pin '%s' not found on %s. Available:"), - *Entry.SourcePin, *MCPUtils::FormatName(SrcNode)); - for (UEdGraphPin* P : SrcNode->Pins) - if (P) Result.Appendf(TEXT(" %s"), *MCPUtils::FormatName(P)); - Result.Append(TEXT("\n")); - continue; - } - - // Find target pin - UEdGraphPin* TgtPin = FindPinByName(TgtNode, Entry.TargetPin); - if (!TgtPin) - { - Result.Appendf(TEXT("ERROR: Pin '%s' not found on %s. Available:"), - *Entry.TargetPin, *MCPUtils::FormatName(TgtNode)); - for (UEdGraphPin* P : TgtNode->Pins) - if (P) Result.Appendf(TEXT(" %s"), *MCPUtils::FormatName(P)); - Result.Append(TEXT("\n")); - continue; - } - - // Check compatibility - const FPinConnectionResponse Response = Schema->CanCreateConnection(SrcPin, TgtPin); - if (Response.Response == CONNECT_RESPONSE_DISALLOW) - { - Result.Appendf(TEXT("ERROR: Cannot connect %s.%s -> %s.%s: %s\n"), - *MCPUtils::FormatName(SrcNode), *MCPUtils::FormatName(SrcPin), - *MCPUtils::FormatName(TgtNode), *MCPUtils::FormatName(TgtPin), - *Response.Message.ToString()); - continue; - } - - Schema->TryCreateConnection(SrcPin, TgtPin); - SuccessCount++; - } - - if (SuccessCount > 0) - { - UObject* Asset = MaterialObj ? (UObject*)MaterialObj : (UObject*)MatFunc; - Asset->PreEditChange(nullptr); - Asset->PostEditChange(); - bool bSaved = MaterialObj ? MCPUtils::SaveMaterialPackage(MaterialObj) : MCPUtils::SaveGenericPackage(MatFunc); - Result.Appendf(TEXT("Connected %d/%d. Saved: %s\n"), - SuccessCount, Connections.Array.Num(), bSaved ? TEXT("yes") : TEXT("no")); - } - else - { - Result.Appendf(TEXT("0/%d connections succeeded.\n"), Connections.Array.Num()); - } - } - -private: - UEdGraphNode* FindNodeByName(UEdGraph* Graph, const FString& Name) - { - for (UEdGraphNode* Node : Graph->Nodes) - if (Node && MCPUtils::Identifies(Name, Node)) - return Node; - return nullptr; - } - - UEdGraphPin* FindPinByName(UEdGraphNode* Node, const FString& Name) - { - for (UEdGraphPin* Pin : Node->Pins) - if (Pin && MCPUtils::Identifies(Name, Pin)) - return Pin; - return nullptr; - } -}; diff --git a/Plugins/UEWingman/Deprecated/UMCPHandler_DeleteMaterialExpression.h b/Plugins/UEWingman/Deprecated/UMCPHandler_DeleteMaterialExpression.h deleted file mode 100644 index 9506b299..00000000 --- a/Plugins/UEWingman/Deprecated/UMCPHandler_DeleteMaterialExpression.h +++ /dev/null @@ -1,126 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "MCPHandler.h" -#include "MCPAssets.h" -#include "MCPUtils.h" -#include "Materials/Material.h" -#include "Materials/MaterialExpression.h" -#include "Materials/MaterialFunction.h" -#include "MaterialGraph/MaterialGraph.h" -#include "MaterialGraph/MaterialGraphNode.h" -#include "UMCPHandler_DeleteMaterialExpression.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS(meta=(Group="Unclassified")) -class UMCPHandler_DeleteMaterialExpression : public UObject, public IMCPHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Optional, Description="Material name or package path (specify this or materialFunction)")) - FString Material; - - UPROPERTY(meta=(Optional, Description="Material function name or package path (specify this or material)")) - FString MaterialFunction; - - UPROPERTY(meta=(Description="Expression name (use FormatName from DumpMaterial output)")) - FString Node; - - UPROPERTY(meta=(Optional, Description="If true, preview the change without applying it")) - bool DryRun = false; - - virtual FString GetDescription() const override - { - return TEXT("Remove an expression node from a material or material function graph."); - } - - virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override - { - if (Material.IsEmpty() && MaterialFunction.IsEmpty()) - { - MCPErrorCallback(Result).SetError(TEXT("Specify 'material' or 'materialFunction'.")); - return; - } - - // Load material or material function - UMaterial* MaterialObj = nullptr; - UMaterialFunction* MatFunc = nullptr; - - if (!MaterialFunction.IsEmpty()) - { - MCPAssets MFAssets; - if (!MFAssets.Exact(MaterialFunction).Errors(Result).ENone().ETwo().Load()) return; - MatFunc = MFAssets.Object(); - } - else - { - MCPAssets MatAssets; - if (!MatAssets.Exact(Material).Errors(Result).ENone().ETwo().Load()) return; - MaterialObj = MatAssets.Object(); - } - - if (MaterialObj) MCPUtils::EnsureMaterialGraph(MaterialObj); - UEdGraph* Graph = MaterialObj ? (UEdGraph*)MaterialObj->MaterialGraph : (MatFunc ? MatFunc->MaterialGraph : nullptr); - if (!Graph) - { - MCPErrorCallback(Result).SetError(TEXT("Asset has no material graph.")); - return; - } - - // Find node by name - UMaterialGraphNode* TargetMatNode = nullptr; - for (UEdGraphNode* GraphNode : Graph->Nodes) - { - if (!GraphNode) continue; - UMaterialGraphNode* MatNode = Cast(GraphNode); - if (!MatNode || !MatNode->MaterialExpression) continue; - if (MCPUtils::Identifies(Node, MatNode->MaterialExpression)) - { - TargetMatNode = MatNode; - break; - } - } - - if (!TargetMatNode) - { - MCPErrorCallback(Result).SetError(FString::Printf(TEXT("Expression '%s' not found."), *Node)); - return; - } - - FString ExprName = MCPUtils::FormatName(TargetMatNode->MaterialExpression); - - if (DryRun) - { - Result.Appendf(TEXT("DryRun: would delete %s\n"), *ExprName); - return; - } - - // Remove the expression - UMaterialExpression* ExprToRemove = TargetMatNode->MaterialExpression; - if (MaterialObj) - MaterialObj->GetExpressionCollection().RemoveExpression(ExprToRemove); - else - MatFunc->GetExpressionCollection().RemoveExpression(ExprToRemove); - ExprToRemove->MarkAsGarbage(); - - // Rebuild graph - Graph->NotifyGraphChanged(); - - UObject* Asset = MaterialObj ? (UObject*)MaterialObj : (UObject*)MatFunc; - Asset->PreEditChange(nullptr); - Asset->PostEditChange(); - Asset->MarkPackageDirty(); - - // Save - bool bSaved = MaterialObj ? MCPUtils::SaveMaterialPackage(MaterialObj) : MCPUtils::SaveGenericPackage(MatFunc); - - Result.Appendf(TEXT("Deleted %s"), *ExprName); - if (!bSaved) Result.Append(TEXT(" (save failed)")); - Result.Append(TEXT("\n")); - } -}; diff --git a/Plugins/UEWingman/Deprecated/UMCPHandler_DescribeMaterialInEnglish.h b/Plugins/UEWingman/Deprecated/UMCPHandler_DescribeMaterialInEnglish.h deleted file mode 100644 index 28bedea1..00000000 --- a/Plugins/UEWingman/Deprecated/UMCPHandler_DescribeMaterialInEnglish.h +++ /dev/null @@ -1,188 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "MCPHandler.h" -#include "MCPAssets.h" -#include "MCPUtils.h" -#include "Materials/Material.h" -#include "Materials/MaterialExpression.h" -#include "Materials/MaterialExpressionScalarParameter.h" -#include "Materials/MaterialExpressionVectorParameter.h" -#include "Materials/MaterialExpressionTextureObjectParameter.h" -#include "Materials/MaterialExpressionTextureSampleParameter2D.h" -#include "Materials/MaterialExpressionStaticSwitchParameter.h" -#include "Materials/MaterialExpressionConstant.h" -#include "Materials/MaterialExpressionConstant3Vector.h" -#include "Materials/MaterialExpressionConstant4Vector.h" -#include "Materials/MaterialExpressionTextureSample.h" -#include "Materials/MaterialExpressionMaterialFunctionCall.h" -#include "Materials/MaterialFunction.h" -#include "MaterialGraph/MaterialGraph.h" -#include "MaterialGraph/MaterialGraphNode.h" -#include "MaterialGraph/MaterialGraphNode_Root.h" -#include "MaterialGraph/MaterialGraphSchema.h" -#include "EdGraph/EdGraph.h" -#include "EdGraph/EdGraphNode.h" -#include "UMCPHandler_DescribeMaterialInEnglish.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS(meta=(Group="Unclassified")) -class UMCPHandler_DescribeMaterialInEnglish : public UObject, public IMCPHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Material name or package path")) - FString Material; - - virtual FString GetDescription() const override - { - return TEXT("Generate a human-readable description of a material by tracing its expression graph from the root node inputs."); - } - - virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override - { - MCPAssets Assets; - if (!Assets.Exact(Material).Errors(Result).ENone().ETwo().Load()) return; - UMaterial* MaterialObj = Assets.Object(); - - // Ensure material graph is built - MCPUtils::EnsureMaterialGraph(MaterialObj); - if (!MaterialObj->MaterialGraph) - { - MCPErrorCallback(Result).SetError(TEXT("Could not build MaterialGraph for this material")); - return; - } - - // Find root node - UMaterialGraphNode_Root* RootNode = nullptr; - for (UEdGraphNode* Node : MaterialObj->MaterialGraph->Nodes) - { - RootNode = Cast(Node); - if (RootNode) break; - } - if (!RootNode) - { - MCPErrorCallback(Result).SetError(TEXT("Could not find root node in material graph")); - return; - } - - // Recursive helper: trace backwards from a pin and build a description string - TFunction TracePin = [&TracePin](UEdGraphPin* Pin, int32 Depth) -> FString - { - if (!Pin || Depth > 10) - return TEXT("(unknown)"); - - if (Pin->LinkedTo.Num() == 0) - { - if (!Pin->DefaultValue.IsEmpty()) - return FString::Printf(TEXT("(default: %s)"), *Pin->DefaultValue); - return TEXT("(unconnected)"); - } - - TArray Sources; - for (UEdGraphPin* LinkedPin : Pin->LinkedTo) - { - if (!LinkedPin || !LinkedPin->GetOwningNode()) continue; - - UEdGraphNode* SourceNode = LinkedPin->GetOwningNode(); - FString NodeDesc; - - UMaterialGraphNode* MatNode = Cast(SourceNode); - if (!MatNode) - { - NodeDesc = MCPUtils::FormatName(SourceNode); - Sources.Add(NodeDesc); - continue; - } - - UMaterialExpression* Expr = MatNode->MaterialExpression; - if (!Expr) - { - NodeDesc = TEXT("(null expression)"); - } - else if (auto* SP = Cast(Expr)) - { - NodeDesc = FString::Printf(TEXT("ScalarParam \"%s\" (default: %.4f)"), *SP->ParameterName.ToString(), SP->DefaultValue); - } - else if (auto* VP = Cast(Expr)) - { - NodeDesc = FString::Printf(TEXT("VectorParam \"%s\" (default: R=%.2f G=%.2f B=%.2f A=%.2f)"), - *VP->ParameterName.ToString(), VP->DefaultValue.R, VP->DefaultValue.G, VP->DefaultValue.B, VP->DefaultValue.A); - } - else if (auto* TP = Cast(Expr)) - { - FString TexName = TP->Texture ? MCPUtils::FormatName(TP->Texture) : TEXT("None"); - NodeDesc = FString::Printf(TEXT("TextureParam \"%s\" (%s)"), *TP->ParameterName.ToString(), *TexName); - } - else if (auto* SSP = Cast(Expr)) - { - NodeDesc = FString::Printf(TEXT("StaticSwitchParam \"%s\" (default: %s)"), - *SSP->ParameterName.ToString(), SSP->DefaultValue ? TEXT("true") : TEXT("false")); - } - else if (auto* SC = Cast(Expr)) - { - NodeDesc = FString::Printf(TEXT("Constant(%.4f)"), SC->R); - } - else if (auto* C3 = Cast(Expr)) - { - NodeDesc = FString::Printf(TEXT("Constant3(R=%.2f G=%.2f B=%.2f)"), C3->Constant.R, C3->Constant.G, C3->Constant.B); - } - else if (auto* C4 = Cast(Expr)) - { - NodeDesc = FString::Printf(TEXT("Constant4(R=%.2f G=%.2f B=%.2f A=%.2f)"), C4->Constant.R, C4->Constant.G, C4->Constant.B, C4->Constant.A); - } - else if (auto* TS = Cast(Expr)) - { - FString TexName = TS->Texture ? MCPUtils::FormatName(TS->Texture) : TEXT("None"); - NodeDesc = FString::Printf(TEXT("TextureSample(%s)"), *TexName); - } - else if (auto* MFC = Cast(Expr)) - { - FString FuncName = MFC->MaterialFunction ? MFC->MaterialFunction->GetPathName() : TEXT("None"); - NodeDesc = FString::Printf(TEXT("FunctionCall(%s)"), *FuncName); - } - else - { - NodeDesc = MCPUtils::FormatName(Expr); - } - - // Recurse into input pins - TArray InputDescs; - for (UEdGraphPin* InputPin : SourceNode->Pins) - { - if (!InputPin || InputPin->Direction != EGPD_Input || InputPin->LinkedTo.Num() == 0) continue; - InputDescs.Add(TracePin(InputPin, Depth + 1)); - } - if (InputDescs.Num() > 0) - { - NodeDesc += TEXT(" <- (") + FString::Join(InputDescs, TEXT(", ")) + TEXT(")"); - } - - Sources.Add(NodeDesc); - } - - if (Sources.Num() == 1) - return Sources[0]; - - return TEXT("(") + FString::Join(Sources, TEXT(", ")) + TEXT(")"); - }; - - // Trace each connected root input and output plain text - Result.Appendf(TEXT("Material: %s\n\n"), *MCPUtils::FormatName(MaterialObj)); - - for (UEdGraphPin* Pin : RootNode->Pins) - { - if (!Pin || Pin->Direction != EGPD_Input) continue; - if (Pin->LinkedTo.Num() == 0) continue; - - FString PinName = MCPUtils::FormatName(Pin); - FString Description = TracePin(Pin, 0); - Result.Appendf(TEXT("%s <- %s\n"), *PinName, *Description); - } - } -}; diff --git a/Plugins/UEWingman/Deprecated/UMCPHandler_DisconnectMaterialExpressionPin.h b/Plugins/UEWingman/Deprecated/UMCPHandler_DisconnectMaterialExpressionPin.h deleted file mode 100644 index cea26ac1..00000000 --- a/Plugins/UEWingman/Deprecated/UMCPHandler_DisconnectMaterialExpressionPin.h +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "MCPHandler.h" -#include "MCPAssets.h" -#include "MCPFetcher.h" -#include "MCPUtils.h" -#include "Materials/Material.h" -#include "Materials/MaterialExpression.h" -#include "Materials/MaterialFunction.h" -#include "MaterialGraph/MaterialGraph.h" -#include "EdGraph/EdGraph.h" -#include "EdGraph/EdGraphNode.h" -#include "EdGraph/EdGraphPin.h" -#include "UMCPHandler_DisconnectMaterialExpressionPin.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS(meta=(Group="Unclassified")) -class UMCPHandler_DisconnectMaterialExpressionPin : public UObject, public IMCPHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Optional, Description="Material name or package path (specify this or materialFunction)")) - FString Material; - - UPROPERTY(meta=(Optional, Description="Material function name or package path (specify this or material)")) - FString MaterialFunction; - - UPROPERTY(meta=(Description="Node name (use FormatName-style identifier)")) - FString Node; - - UPROPERTY(meta=(Description="Pin name to disconnect")) - FString PinName; - - virtual FString GetDescription() const override - { - return TEXT("Break all connections on a specific pin in a material or material function graph."); - } - - virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override - { - if (Material.IsEmpty() && MaterialFunction.IsEmpty()) - { - Result.Append(TEXT("ERROR: Specify 'material' or 'materialFunction'.\n")); - return; - } - - // Load material or material function - UMaterial* MaterialObj = nullptr; - UMaterialFunction* MatFunc = nullptr; - - if (!MaterialFunction.IsEmpty()) - { - MCPAssets Assets; - if (!Assets.Exact(MaterialFunction).Errors(Result).ENone().ETwo().Load()) return; - MatFunc = Assets.Object(); - } - else - { - MCPAssets Assets; - if (!Assets.Exact(Material).Errors(Result).ENone().ETwo().Load()) return; - MaterialObj = Assets.Object(); - } - - if (MaterialObj) MCPUtils::EnsureMaterialGraph(MaterialObj); - UEdGraph* Graph = MaterialObj ? (UEdGraph*)MaterialObj->MaterialGraph : (MatFunc ? MatFunc->MaterialGraph : nullptr); - if (!Graph) - { - Result.Appendf(TEXT("ERROR: %s has no material graph.\n"), - MaterialObj ? *MCPUtils::FormatName(MaterialObj) : *MCPUtils::FormatName(MatFunc)); - return; - } - - // Find node and pin via MCPFetcher - MCPFetcher F(Result, Graph); - UEdGraphPin* Pin = F.Node(Node).Pin(PinName).Cast(); - if (!Pin) return; - - int32 BrokenCount = Pin->LinkedTo.Num(); - Pin->BreakAllPinLinks(); - - UObject* Asset = MaterialObj ? (UObject*)MaterialObj : (UObject*)MatFunc; - Asset->PreEditChange(nullptr); - Asset->PostEditChange(); - - bool bSaved = MaterialObj ? MCPUtils::SaveMaterialPackage(MaterialObj) : MCPUtils::SaveGenericPackage(MatFunc); - - Result.Appendf(TEXT("Disconnected %d link(s) from %s on %s.\n"), - BrokenCount, *MCPUtils::FormatName(Pin), *MCPUtils::FormatName(Pin->GetOwningNode())); - if (!bSaved) - Result.Append(TEXT("WARNING: Failed to save package.\n")); - } -}; diff --git a/Plugins/UEWingman/Deprecated/UMCPHandler_DumpMaterial.h b/Plugins/UEWingman/Deprecated/UMCPHandler_DumpMaterial.h deleted file mode 100644 index 19aeec31..00000000 --- a/Plugins/UEWingman/Deprecated/UMCPHandler_DumpMaterial.h +++ /dev/null @@ -1,228 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "MCPHandler.h" -#include "MCPAssets.h" -#include "MCPUtils.h" -#include "Materials/Material.h" -#include "MaterialDomain.h" -#include "Materials/MaterialInstanceConstant.h" -#include "Materials/MaterialExpression.h" -#include "Materials/MaterialExpressionScalarParameter.h" -#include "Materials/MaterialExpressionVectorParameter.h" -#include "Materials/MaterialExpressionTextureSampleParameter2D.h" -#include "Materials/MaterialExpressionStaticSwitchParameter.h" -#include "Materials/MaterialExpressionTextureSample.h" -#include "Engine/Texture.h" -#include "UMCPHandler_DumpMaterial.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS(meta=(Group="Unclassified")) -class UMCPHandler_DumpMaterial : public UObject, public IMCPHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Material or MaterialInstance name or package path")) - FString Material; - - virtual FString GetDescription() const override - { - return TEXT("Get detailed info about a material or material instance, including parameters, usage flags, and referenced textures."); - } - - virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override - { - MCPAssets Assets; - Assets.NoScans(); - Assets.Scan(); - Assets.Scan(); - if (!Assets.Exact(Material).Errors(Result).ENone().ETwo().Load()) return; - UMaterialInterface* LoadedObj = Assets.Object(); - - if (UMaterial* Mat = Cast(LoadedObj)) - { - EmitMaterial(Mat, Result); - return; - } - - if (UMaterialInstanceConstant* MI = Cast(LoadedObj)) - { - EmitMaterialInstance(MI, Result); - return; - } - - Result.Appendf(TEXT("ERROR: Loaded object is %s, expected Material or MaterialInstance.\n"), - *LoadedObj->GetClass()->GetName()); - } - -private: - void EmitMaterial(UMaterial* Mat, FStringBuilderBase& Result) - { - 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("TwoSided: %s\n"), Mat->IsTwoSided() ? TEXT("true") : TEXT("false")); - - // Shading models - FMaterialShadingModelField SMField = Mat->GetShadingModels(); - const UEnum* SMEnum = StaticEnum(); - TArray SMNames; - for (int32 i = 0; i < SMEnum->NumEnums() - 1; ++i) - { - EMaterialShadingModel SM = (EMaterialShadingModel)SMEnum->GetValueByIndex(i); - if (SMField.HasShadingModel(SM)) - SMNames.Add(SMEnum->GetNameStringByIndex(i)); - } - Result.Appendf(TEXT("ShadingModels: %s\n"), *FString::Join(SMNames, TEXT(", "))); - - // Parameters - auto Expressions = Mat->GetExpressions(); - bool bHasParams = false; - for (UMaterialExpression* Expr : Expressions) - { - if (!Expr) continue; - - if (auto* SP = Cast(Expr)) - { - if (!bHasParams) { Result.Append(TEXT("\nParameters:\n")); bHasParams = true; } - Result.Appendf(TEXT(" Scalar \"%s\" = %g"), *SP->ParameterName.ToString(), SP->DefaultValue); - if (!SP->Group.IsNone()) Result.Appendf(TEXT(" [%s]"), *SP->Group.ToString()); - Result.Append(TEXT("\n")); - } - else if (auto* VP = Cast(Expr)) - { - if (!bHasParams) { Result.Append(TEXT("\nParameters:\n")); bHasParams = true; } - Result.Appendf(TEXT(" Vector \"%s\" = (%.3f, %.3f, %.3f, %.3f)"), - *VP->ParameterName.ToString(), - VP->DefaultValue.R, VP->DefaultValue.G, VP->DefaultValue.B, VP->DefaultValue.A); - if (!VP->Group.IsNone()) Result.Appendf(TEXT(" [%s]"), *VP->Group.ToString()); - Result.Append(TEXT("\n")); - } - else if (auto* TP = Cast(Expr)) - { - if (!bHasParams) { Result.Append(TEXT("\nParameters:\n")); bHasParams = true; } - Result.Appendf(TEXT(" Texture \"%s\" = %s"), - *TP->ParameterName.ToString(), - TP->Texture ? *MCPUtils::FormatName(TP->Texture) : TEXT("None")); - if (!TP->Group.IsNone()) Result.Appendf(TEXT(" [%s]"), *TP->Group.ToString()); - Result.Append(TEXT("\n")); - } - else if (auto* SSP = Cast(Expr)) - { - if (!bHasParams) { Result.Append(TEXT("\nParameters:\n")); bHasParams = true; } - Result.Appendf(TEXT(" StaticSwitch \"%s\" = %s"), - *SSP->ParameterName.ToString(), - SSP->DefaultValue ? TEXT("true") : TEXT("false")); - if (!SSP->Group.IsNone()) Result.Appendf(TEXT(" [%s]"), *SSP->Group.ToString()); - Result.Append(TEXT("\n")); - } - } - - // Referenced textures - auto RefTexObjs = Mat->GetReferencedTextures(); - bool bHasTextures = false; - for (const TObjectPtr& TexObj : RefTexObjs) - { - if (!TexObj) continue; - if (!bHasTextures) { Result.Append(TEXT("\nReferenced Textures:\n")); bHasTextures = true; } - if (UTexture* Tex = Cast(TexObj.Get())) - Result.Appendf(TEXT(" %s\n"), *MCPUtils::FormatName(Tex)); - else - Result.Appendf(TEXT(" %s\n"), *TexObj->GetPathName()); - } - - // Usage flags — only print enabled ones - Result.Append(TEXT("\nUsage Flags:")); - bool bAnyUsage = false; - auto EmitFlag = [&](bool bSet, const TCHAR* Name) { - if (bSet) { Result.Appendf(TEXT(" %s"), Name); bAnyUsage = true; } - }; - EmitFlag(Mat->bUsedWithSkeletalMesh, TEXT("SkeletalMesh")); - EmitFlag(Mat->bUsedWithMorphTargets, TEXT("MorphTargets")); - EmitFlag(Mat->bUsedWithNiagaraSprites, TEXT("NiagaraSprites")); - EmitFlag(Mat->bUsedWithParticleSprites, TEXT("ParticleSprites")); - EmitFlag(Mat->bUsedWithStaticLighting, TEXT("StaticLighting")); - if (!bAnyUsage) Result.Append(TEXT(" (none)")); - Result.Append(TEXT("\n")); - - // Stats - Result.Appendf(TEXT("Expressions: %d\n"), Expressions.Num()); - int32 TextureSampleCount = 0; - for (UMaterialExpression* Expr : Expressions) - if (Expr && Expr->IsA()) - TextureSampleCount++; - Result.Appendf(TEXT("TextureSamples: %d\n"), TextureSampleCount); - if (Mat->MaterialGraph) - Result.Appendf(TEXT("GraphNodes: %d\n"), Mat->MaterialGraph->Nodes.Num()); - - // Additional settings — only print non-default values - if (Mat->OpacityMaskClipValue != 0.3333f) - Result.Appendf(TEXT("OpacityMaskClipValue: %g\n"), Mat->OpacityMaskClipValue); - if (Mat->DitheredLODTransition) - Result.Append(TEXT("DitheredLODTransition: true\n")); - if (Mat->bAllowNegativeEmissiveColor) - Result.Append(TEXT("AllowNegativeEmissiveColor: true\n")); - } - - void EmitMaterialInstance(UMaterialInstanceConstant* MI, FStringBuilderBase& Result) - { - Result.Appendf(TEXT("MaterialInstance: %s\n"), *MCPUtils::FormatName(MI)); - if (MI->Parent) - { - if (UMaterial* ParentMat = Cast(MI->Parent)) - Result.Appendf(TEXT("Parent: %s\n"), *MCPUtils::FormatName(ParentMat)); - else if (UMaterialInstance* ParentMI = Cast(MI->Parent)) - Result.Appendf(TEXT("Parent: %s\n"), *MCPUtils::FormatName(ParentMI)); - else - Result.Appendf(TEXT("Parent: %s\n"), *MI->Parent->GetPathName()); - } - - // Overridden parameters - bool bHasParams = false; - auto EnsureHeader = [&]() { - if (!bHasParams) { Result.Append(TEXT("\nOverridden Parameters:\n")); bHasParams = true; } - }; - - for (const FScalarParameterValue& P : MI->ScalarParameterValues) - { - EnsureHeader(); - Result.Appendf(TEXT(" Scalar \"%s\" = %g\n"), *P.ParameterInfo.Name.ToString(), P.ParameterValue); - } - - for (const FVectorParameterValue& P : MI->VectorParameterValues) - { - EnsureHeader(); - Result.Appendf(TEXT(" Vector \"%s\" = (%.3f, %.3f, %.3f, %.3f)\n"), - *P.ParameterInfo.Name.ToString(), - P.ParameterValue.R, P.ParameterValue.G, P.ParameterValue.B, P.ParameterValue.A); - } - - for (const FTextureParameterValue& P : MI->TextureParameterValues) - { - EnsureHeader(); - if (P.ParameterValue) - { - Result.Appendf(TEXT(" Texture \"%s\" = %s\n"), - *P.ParameterInfo.Name.ToString(), *MCPUtils::FormatName(P.ParameterValue)); - } - else - { - Result.Appendf(TEXT(" Texture \"%s\" = None\n"), *P.ParameterInfo.Name.ToString()); - } - } - - for (const FStaticSwitchParameter& P : MI->GetStaticParameters().StaticSwitchParameters) - { - EnsureHeader(); - Result.Appendf(TEXT(" StaticSwitch \"%s\" = %s%s\n"), - *P.ParameterInfo.Name.ToString(), - P.Value ? TEXT("true") : TEXT("false"), - P.bOverride ? TEXT("") : TEXT(" (not overridden)")); - } - } -}; diff --git a/Plugins/UEWingman/Deprecated/UMCPHandler_DumpMaterialFunction.h b/Plugins/UEWingman/Deprecated/UMCPHandler_DumpMaterialFunction.h deleted file mode 100644 index 23d68f70..00000000 --- a/Plugins/UEWingman/Deprecated/UMCPHandler_DumpMaterialFunction.h +++ /dev/null @@ -1,147 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "MCPHandler.h" -#include "MCPAssets.h" -#include "MCPUtils.h" -#include "Materials/MaterialFunction.h" -#include "Materials/MaterialExpression.h" -#include "Materials/MaterialExpressionFunctionInput.h" -#include "Materials/MaterialExpressionFunctionOutput.h" -#include "Materials/MaterialExpressionMaterialFunctionCall.h" -#include "Materials/MaterialExpressionScalarParameter.h" -#include "Materials/MaterialExpressionVectorParameter.h" -#include "Materials/MaterialExpressionTextureSampleParameter2D.h" -#include "Materials/MaterialExpressionStaticSwitchParameter.h" -#include "Materials/MaterialExpressionConstant.h" -#include "Materials/MaterialExpressionConstant3Vector.h" -#include "Materials/MaterialExpressionConstant4Vector.h" -#include "Materials/MaterialExpressionTextureObjectParameter.h" -#include "Materials/MaterialExpressionTextureSample.h" -#include "Materials/MaterialExpressionTextureCoordinate.h" -#include "Materials/MaterialExpressionComponentMask.h" -#include "Materials/MaterialExpressionCustom.h" -#include "Engine/Texture.h" -#include "UMCPHandler_DumpMaterialFunction.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS(meta=(Group="Unclassified")) -class UMCPHandler_DumpMaterialFunction : public UObject, public IMCPHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="MaterialFunction name or package path")) - FString MaterialFunction; - - virtual FString GetDescription() const override - { - return TEXT("Get detailed info about a material function, including its inputs, outputs, and expressions."); - } - - virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override - { - MCPAssets Assets; - if (!Assets.Exact(MaterialFunction).Errors(Result).ENone().ETwo().Load()) return; - UMaterialFunction* MF = Assets.Object(); - - Result.Appendf(TEXT("MaterialFunction: %s\n"), *MCPUtils::FormatName(MF)); - - FString Desc = MF->GetDescription(); - if (!Desc.IsEmpty()) - Result.Appendf(TEXT("Description: %s\n"), *Desc); - - auto Expressions = MF->GetExpressions(); - Result.Appendf(TEXT("Expressions: %d\n"), Expressions.Num()); - - // Inputs and outputs - bool bHasInputs = false; - bool bHasOutputs = false; - for (UMaterialExpression* Expr : Expressions) - { - if (!Expr) continue; - if (auto* FI = Cast(Expr)) - { - if (!bHasInputs) { Result.Append(TEXT("\nInputs:\n")); bHasInputs = true; } - Result.Appendf(TEXT(" %s\n"), *MCPUtils::FormatName(Expr)); - } - else if (auto* FO = Cast(Expr)) - { - if (!bHasOutputs) { Result.Append(TEXT("\nOutputs:\n")); bHasOutputs = true; } - Result.Appendf(TEXT(" %s\n"), *MCPUtils::FormatName(Expr)); - } - } - - // All expressions - Result.Append(TEXT("\nExpression List:\n")); - for (UMaterialExpression* Expr : Expressions) - { - if (!Expr) continue; - Result.Appendf(TEXT(" %s"), *MCPUtils::FormatName(Expr)); - EmitExpressionDetails(Expr, Result); - Result.Append(TEXT("\n")); - } - } - -private: - void EmitExpressionDetails(UMaterialExpression* Expr, FStringBuilderBase& Result) - { - if (auto* SP = Cast(Expr)) - { - Result.Appendf(TEXT(" default=%g"), SP->DefaultValue); - if (!SP->Group.IsNone()) Result.Appendf(TEXT(" [%s]"), *SP->Group.ToString()); - } - else if (auto* VP = Cast(Expr)) - { - Result.Appendf(TEXT(" default=(%.3f, %.3f, %.3f, %.3f)"), - VP->DefaultValue.R, VP->DefaultValue.G, VP->DefaultValue.B, VP->DefaultValue.A); - if (!VP->Group.IsNone()) Result.Appendf(TEXT(" [%s]"), *VP->Group.ToString()); - } - else if (auto* TP = Cast(Expr)) - { - Result.Appendf(TEXT(" texture=%s"), - TP->Texture ? *MCPUtils::FormatName(TP->Texture) : TEXT("None")); - if (!TP->Group.IsNone()) Result.Appendf(TEXT(" [%s]"), *TP->Group.ToString()); - } - else if (auto* SSP = Cast(Expr)) - { - Result.Appendf(TEXT(" default=%s"), SSP->DefaultValue ? TEXT("true") : TEXT("false")); - if (!SSP->Group.IsNone()) Result.Appendf(TEXT(" [%s]"), *SSP->Group.ToString()); - } - else if (auto* SC = Cast(Expr)) - { - Result.Appendf(TEXT(" value=%g"), SC->R); - } - else if (auto* C3 = Cast(Expr)) - { - Result.Appendf(TEXT(" value=(%.3f, %.3f, %.3f)"), C3->Constant.R, C3->Constant.G, C3->Constant.B); - } - else if (auto* C4 = Cast(Expr)) - { - Result.Appendf(TEXT(" value=(%.3f, %.3f, %.3f, %.3f)"), - C4->Constant.R, C4->Constant.G, C4->Constant.B, C4->Constant.A); - } - else if (auto* FC = Cast(Expr)) - { - if (FC->MaterialFunction) - Result.Appendf(TEXT(" calls=%s"), *FC->MaterialFunction->GetPathName()); - } - else if (auto* TS = Cast(Expr)) - { - if (TS->Texture) - Result.Appendf(TEXT(" texture=%s"), *MCPUtils::FormatName(TS->Texture)); - } - else if (auto* TC = Cast(Expr)) - { - Result.Appendf(TEXT(" index=%d tiling=(%.1f, %.1f)"), TC->CoordinateIndex, TC->UTiling, TC->VTiling); - } - else if (auto* Custom = Cast(Expr)) - { - Result.Appendf(TEXT(" code_len=%d"), Custom->Code.Len()); - } - } -}; diff --git a/Plugins/UEWingman/Deprecated/UMCPHandler_RemoveStructField.h b/Plugins/UEWingman/Deprecated/UMCPHandler_RemoveStructField.h deleted file mode 100644 index a245dfcb..00000000 --- a/Plugins/UEWingman/Deprecated/UMCPHandler_RemoveStructField.h +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "MCPHandler.h" -#include "MCPFetcher.h" -#include "MCPUtils.h" -#include "StructUtils/UserDefinedStruct.h" -#include "UserDefinedStructure/UserDefinedStructEditorData.h" -#include "UMCPHandler_RemoveStructField.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS(meta=(Group="Unclassified")) -class UMCPHandler_RemoveStructField : public UObject, public IMCPHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Struct name or package path")) - FString Struct; - - UPROPERTY(meta=(Description="Name of the field to remove")) - FString FieldName; - - virtual FString GetDescription() const override - { - return TEXT("Remove a field from a UserDefinedStruct asset."); - } - - virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override - { - MCPFetcher F(Result); - UUserDefinedStruct* S = F.Walk(Struct).Cast(); - if (!S) return; - - // Find the field GUID by name - FGuid TargetGuid; - bool bFound = false; - for (const FStructVariableDescription& Var : FStructureEditorUtils::GetVarDesc(S)) - { - if (Var.FriendlyName.Equals(FieldName, ESearchCase::IgnoreCase) || - Var.VarName.ToString().Equals(FieldName, ESearchCase::IgnoreCase)) - { - TargetGuid = Var.VarGuid; - bFound = true; - break; - } - } - - if (!bFound) - { - Result.Appendf(TEXT("ERROR: Field '%s' not found in %s.\nAvailable fields:\n"), - *FieldName, *MCPUtils::FormatName(S)); - for (const FStructVariableDescription& Var : FStructureEditorUtils::GetVarDesc(S)) - { - Result.Appendf(TEXT(" %s\n"), *Var.FriendlyName); - } - return; - } - - if (!FStructureEditorUtils::RemoveVariable(S, TargetGuid)) - { - Result.Appendf(TEXT("ERROR: Failed to remove field '%s'."), *FieldName); - return; - } - - bool bSaved = MCPUtils::SaveGenericPackage(S); - Result.Appendf(TEXT("Removed field %s from %s.%s\n"), - *FieldName, *MCPUtils::FormatName(S), - bSaved ? TEXT("") : TEXT(" WARNING: save failed.")); - } -}; diff --git a/Plugins/UEWingman/Deprecated/UMCPHandler_SearchWithinMaterials.h b/Plugins/UEWingman/Deprecated/UMCPHandler_SearchWithinMaterials.h deleted file mode 100644 index a4171bae..00000000 --- a/Plugins/UEWingman/Deprecated/UMCPHandler_SearchWithinMaterials.h +++ /dev/null @@ -1,106 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "MCPHandler.h" -#include "MCPAssets.h" -#include "MCPUtils.h" -#include "Materials/Material.h" -#include "Materials/MaterialExpression.h" -#include "Materials/MaterialExpressionScalarParameter.h" -#include "Materials/MaterialExpressionVectorParameter.h" -#include "Materials/MaterialExpressionTextureSampleParameter2D.h" -#include "Materials/MaterialExpressionStaticSwitchParameter.h" -#include "UMCPHandler_SearchWithinMaterials.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS(meta=(Group="Unclassified")) -class UMCPHandler_SearchWithinMaterials : public UObject, public IMCPHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Search query string to match against material names, expression classes, and parameter names")) - FString Query; - - UPROPERTY(meta=(Optional, Description="Maximum number of results to return (default 50, max 200)")) - int32 MaxResults = 50; - - virtual FString GetDescription() const override - { - return TEXT("Search across all materials for matching material names, expression types, and parameter names."); - } - - virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override - { - FString DecodedQuery = MCPUtils::UrlDecode(Query); - MaxResults = FMath::Clamp(MaxResults, 1, 200); - - int32 Count = 0; - - MCPAssets AllMaterials; - AllMaterials.Load(); - - for (UMaterial* MaterialObj : AllMaterials.Objects()) - { - if (Count >= MaxResults) break; - if (!MaterialObj) continue; - - FString MatName = MCPUtils::FormatName(MaterialObj); - - // Check material name - bool bNameMatch = MatName.Contains(DecodedQuery, ESearchCase::IgnoreCase); - - if (bNameMatch) - { - Result.Appendf(TEXT("material %s\n"), *MatName); - Count++; - } - - // Search expressions - for (UMaterialExpression* Expr : MaterialObj->GetExpressions()) - { - if (!Expr || Count >= MaxResults) continue; - - FString ExprName = MCPUtils::FormatName(Expr); - FString ExprClass = Expr->GetClass()->GetName(); - FString ExprDesc = Expr->GetDescription(); - - // Check parameter name - FString ParamName; - if (auto* SP = Cast(Expr)) - ParamName = SP->ParameterName.ToString(); - else if (auto* VP = Cast(Expr)) - ParamName = VP->ParameterName.ToString(); - else if (auto* TP = Cast(Expr)) - ParamName = TP->ParameterName.ToString(); - else if (auto* SSP = Cast(Expr)) - ParamName = SSP->ParameterName.ToString(); - - bool bExprMatch = ExprDesc.Contains(DecodedQuery, ESearchCase::IgnoreCase) || - ExprClass.Contains(DecodedQuery, ESearchCase::IgnoreCase) || - (!ParamName.IsEmpty() && ParamName.Contains(DecodedQuery, ESearchCase::IgnoreCase)); - - if (!bExprMatch) continue; - - Result.Appendf(TEXT("expression %s in %s (%s)"), *ExprName, *MatName, *ExprClass); - if (!ParamName.IsEmpty()) - Result.Appendf(TEXT(" param=%s"), *ParamName); - Result.Append(TEXT("\n")); - Count++; - } - } - - if (Count == 0) - { - Result.Append(TEXT("No matches found.\n")); - } - else if (Count >= MaxResults) - { - Result.Appendf(TEXT("WARNING: Reached limit of %d results. Specify MaxResults to raise it.\n"), MaxResults); - } - } -}; diff --git a/Plugins/UEWingman/Deprecated/UMCPHandler_SetMaterialExpressionPosition.h b/Plugins/UEWingman/Deprecated/UMCPHandler_SetMaterialExpressionPosition.h deleted file mode 100644 index d1567b94..00000000 --- a/Plugins/UEWingman/Deprecated/UMCPHandler_SetMaterialExpressionPosition.h +++ /dev/null @@ -1,132 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "MCPHandler.h" -#include "MCPAssets.h" -#include "MCPFetcher.h" -#include "MCPUtils.h" -#include "Materials/Material.h" -#include "Materials/MaterialExpression.h" -#include "Materials/MaterialFunction.h" -#include "MaterialGraph/MaterialGraph.h" -#include "MaterialGraph/MaterialGraphNode.h" -#include "UMCPHandler_SetMaterialExpressionPosition.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS(meta=(Group="Unclassified")) -class UMCPHandler_SetMaterialExpressionPosition : public UObject, public IMCPHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Optional, Description="Material name or package path (specify this or materialFunction)")) - FString Material; - - UPROPERTY(meta=(Optional, Description="Material function name or package path (specify this or material)")) - FString MaterialFunction; - - UPROPERTY(meta=(Description="Expression name (use FormatName from DumpMaterial output)")) - FString Node; - - UPROPERTY(meta=(Description="New X position")) - int32 PosX = 0; - - UPROPERTY(meta=(Description="New Y position")) - int32 PosY = 0; - - UPROPERTY(meta=(Optional, Description="If true, preview the change without applying it")) - bool DryRun = false; - - virtual FString GetDescription() const override - { - return TEXT("Reposition a material expression node in the material graph editor."); - } - - virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override - { - if (Material.IsEmpty() && MaterialFunction.IsEmpty()) - { - MCPErrorCallback(Result).SetError(TEXT("Specify 'material' or 'materialFunction'.")); - return; - } - - // Load material or material function - UMaterial* MaterialObj = nullptr; - UMaterialFunction* MatFunc = nullptr; - - if (!MaterialFunction.IsEmpty()) - { - MCPAssets MFAssets; - if (!MFAssets.Exact(MaterialFunction).Errors(Result).ENone().ETwo().Load()) return; - MatFunc = MFAssets.Object(); - } - else - { - MCPAssets MatAssets; - if (!MatAssets.Exact(Material).Errors(Result).ENone().ETwo().Load()) return; - MaterialObj = MatAssets.Object(); - } - - if (MaterialObj) MCPUtils::EnsureMaterialGraph(MaterialObj); - UEdGraph* Graph = MaterialObj ? (UEdGraph*)MaterialObj->MaterialGraph : (MatFunc ? MatFunc->MaterialGraph : nullptr); - if (!Graph) - { - MCPErrorCallback(Result).SetError(TEXT("Asset has no material graph.")); - return; - } - - // Find node by name - UMaterialGraphNode* TargetMatNode = nullptr; - for (UEdGraphNode* GraphNode : Graph->Nodes) - { - if (!GraphNode) continue; - UMaterialGraphNode* MatNode = Cast(GraphNode); - if (!MatNode || !MatNode->MaterialExpression) continue; - if (MCPUtils::Identifies(Node, MatNode->MaterialExpression)) - { - TargetMatNode = MatNode; - break; - } - } - - if (!TargetMatNode) - { - MCPErrorCallback(Result).SetError(FString::Printf(TEXT("Expression '%s' not found."), *Node)); - return; - } - - if (DryRun) - { - Result.Appendf(TEXT("DryRun: would move %s to (%d, %d)\n"), - *MCPUtils::FormatName(TargetMatNode->MaterialExpression), PosX, PosY); - return; - } - - // Set position on the graph node - TargetMatNode->NodePosX = PosX; - TargetMatNode->NodePosY = PosY; - - // Also update the underlying expression position - if (TargetMatNode->MaterialExpression) - { - TargetMatNode->MaterialExpression->MaterialExpressionEditorX = PosX; - TargetMatNode->MaterialExpression->MaterialExpressionEditorY = PosY; - } - - UObject* Asset = MaterialObj ? (UObject*)MaterialObj : (UObject*)MatFunc; - Asset->PreEditChange(nullptr); - Asset->PostEditChange(); - - // Save - bool bSaved = MaterialObj ? MCPUtils::SaveMaterialPackage(MaterialObj) : MCPUtils::SaveGenericPackage(MatFunc); - - Result.Appendf(TEXT("Moved %s to (%d, %d)"), - *MCPUtils::FormatName(TargetMatNode->MaterialExpression), PosX, PosY); - if (!bSaved) Result.Append(TEXT(" (save failed)")); - Result.Append(TEXT("\n")); - } -}; diff --git a/Plugins/UEWingman/Deprecated/UMCPHandler_SetMaterialExpressionProperty.h b/Plugins/UEWingman/Deprecated/UMCPHandler_SetMaterialExpressionProperty.h deleted file mode 100644 index d5d18017..00000000 --- a/Plugins/UEWingman/Deprecated/UMCPHandler_SetMaterialExpressionProperty.h +++ /dev/null @@ -1,282 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "MCPHandler.h" -#include "MCPAssets.h" -#include "MCPFetcher.h" -#include "MCPUtils.h" -#include "Materials/Material.h" -#include "Materials/MaterialFunction.h" -#include "Materials/MaterialExpression.h" -#include "Materials/MaterialExpressionScalarParameter.h" -#include "Materials/MaterialExpressionVectorParameter.h" -#include "Materials/MaterialExpressionConstant.h" -#include "Materials/MaterialExpressionConstant3Vector.h" -#include "Materials/MaterialExpressionConstant4Vector.h" -#include "Materials/MaterialExpressionTextureSample.h" -#include "Materials/MaterialExpressionTextureCoordinate.h" -#include "Materials/MaterialExpressionComponentMask.h" -#include "Materials/MaterialExpressionCustom.h" -#include "MaterialGraph/MaterialGraph.h" -#include "MaterialGraph/MaterialGraphNode.h" -#include "UMCPHandler_SetMaterialExpressionProperty.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS(meta=(Group="Unclassified")) -class UMCPHandler_SetMaterialExpressionProperty : public UObject, public IMCPHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Optional, Description="Material name or package path (specify this or materialFunction)")) - FString Material; - - UPROPERTY(meta=(Optional, Description="Material function name or package path (specify this or material)")) - FString MaterialFunction; - - UPROPERTY(meta=(Description="Expression node name (from FormatName)")) - FString Node; - - virtual FString GetDescription() const override - { - return TEXT("Set the value or properties on a material expression node. " - "The 'value' field in the JSON payload provides the new value, whose format depends on the expression type."); - } - - virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override - { - if (Material.IsEmpty() && MaterialFunction.IsEmpty()) - { - Result.Append(TEXT("ERROR: Specify 'material' or 'materialFunction'\n")); - return; - } - - if (!Json->HasField(TEXT("value"))) - { - Result.Append(TEXT("ERROR: Missing required field: value\n")); - return; - } - - // Load material or material function - UMaterial* MaterialObj = nullptr; - UMaterialFunction* MatFunc = nullptr; - - if (!MaterialFunction.IsEmpty()) - { - MCPAssets MFAssets; - if (!MFAssets.Exact(MaterialFunction).Errors(Result).ENone().ETwo().Load()) return; - MatFunc = MFAssets.Object(); - } - else - { - MCPAssets MatAssets; - if (!MatAssets.Exact(Material).Errors(Result).ENone().ETwo().Load()) return; - MaterialObj = MatAssets.Object(); - } - - if (MaterialObj) MCPUtils::EnsureMaterialGraph(MaterialObj); - UEdGraph* Graph = MaterialObj ? (UEdGraph*)MaterialObj->MaterialGraph : (MatFunc ? MatFunc->MaterialGraph : nullptr); - if (!Graph) - { - Result.Appendf(TEXT("ERROR: No material graph found\n")); - return; - } - - // Find the node by name using Identifies - UMaterialGraphNode* TargetMatNode = nullptr; - for (UEdGraphNode* GraphNode : Graph->Nodes) - { - if (!GraphNode) continue; - if (!MCPUtils::Identifies(Node, GraphNode)) continue; - TargetMatNode = Cast(GraphNode); - if (TargetMatNode) break; - } - - if (!TargetMatNode) - { - Result.Appendf(TEXT("ERROR: Node '%s' not found in material graph\n"), *Node); - return; - } - - UMaterialExpression* Expr = TargetMatNode->MaterialExpression; - if (!Expr) - { - Result.Appendf(TEXT("ERROR: Node '%s' has no material expression\n"), *MCPUtils::FormatName(TargetMatNode)); - return; - } - - UObject* Asset = MaterialObj ? (UObject*)MaterialObj : (UObject*)MatFunc; - Asset->PreEditChange(nullptr); - - FString SetResult; - if (!ApplyValue(Expr, Json, Result, SetResult)) - { - Asset->PostEditChange(); - return; - } - - Asset->PostEditChange(); - Asset->MarkPackageDirty(); - - bool bSaved = MaterialObj ? MCPUtils::SaveMaterialPackage(MaterialObj) : MCPUtils::SaveGenericPackage(MatFunc); - - Result.Appendf(TEXT("%s = %s"), *MCPUtils::FormatName(Expr), *SetResult); - if (!bSaved) Result.Append(TEXT(" (save failed)")); - Result.Append(TEXT("\n")); - } - -private: - // Apply the value from JSON to the expression. Returns false on error (with message in Result). - // On success, fills SetResult with a human-readable summary of the new value. - bool ApplyValue(UMaterialExpression* Expr, const FJsonObject* Json, FStringBuilderBase& Result, FString& SetResult) - { - if (auto* E = Cast(Expr)) - { - double Value = Json->GetNumberField(TEXT("value")); - E->R = (float)Value; - SetResult = FString::Printf(TEXT("%g"), Value); - return true; - } - - if (auto* E = Cast(Expr)) - { - FLinearColor C; - if (!ParseColorValue(Json, C, false, Result)) return false; - E->Constant = C; - SetResult = FString::Printf(TEXT("(%.3f, %.3f, %.3f)"), C.R, C.G, C.B); - return true; - } - - if (auto* E = Cast(Expr)) - { - FLinearColor C; - if (!ParseColorValue(Json, C, true, Result)) return false; - E->Constant = C; - SetResult = FString::Printf(TEXT("(%.3f, %.3f, %.3f, %.3f)"), C.R, C.G, C.B, C.A); - return true; - } - - if (auto* E = Cast(Expr)) - { - double Value = Json->GetNumberField(TEXT("value")); - E->DefaultValue = (float)Value; - SetResult = FString::Printf(TEXT("%g"), Value); - ApplyParameterName(E, Json); - return true; - } - - if (auto* E = Cast(Expr)) - { - FLinearColor C; - if (!ParseColorValue(Json, C, true, Result)) return false; - E->DefaultValue = C; - SetResult = FString::Printf(TEXT("(%.3f, %.3f, %.3f, %.3f)"), C.R, C.G, C.B, C.A); - ApplyParameterName(E, Json); - return true; - } - - if (auto* E = Cast(Expr)) - { - const TSharedPtr* ValueObj = nullptr; - if (!Json->TryGetObjectField(TEXT("value"), ValueObj) || !ValueObj || !(*ValueObj).IsValid()) - { - Result.Append(TEXT("ERROR: TextureCoordinate requires value as {coordinateIndex, uTiling, vTiling}\n")); - return false; - } - double CoordIndex = 0, UTiling = 1, VTiling = 1; - (*ValueObj)->TryGetNumberField(TEXT("coordinateIndex"), CoordIndex); - (*ValueObj)->TryGetNumberField(TEXT("uTiling"), UTiling); - (*ValueObj)->TryGetNumberField(TEXT("vTiling"), VTiling); - E->CoordinateIndex = (int32)CoordIndex; - E->UTiling = (float)UTiling; - E->VTiling = (float)VTiling; - SetResult = FString::Printf(TEXT("index=%d uTiling=%g vTiling=%g"), (int32)CoordIndex, UTiling, VTiling); - return true; - } - - if (auto* E = Cast(Expr)) - { - FString Code; - if (Json->TryGetStringField(TEXT("code"), Code)) - { - E->Code = Code; - } - else if (Json->HasField(TEXT("value"))) - { - FString ValueStr = Json->GetStringField(TEXT("value")); - if (!ValueStr.IsEmpty()) E->Code = ValueStr; - } - SetResult = FString::Printf(TEXT("code: %d chars"), E->Code.Len()); - - FString OutputTypeStr; - if (Json->TryGetStringField(TEXT("outputType"), OutputTypeStr) && !OutputTypeStr.IsEmpty()) - { - ECustomMaterialOutputType OutType; - if (MCPUtils::StringToEnum(OutputTypeStr, OutType, MCPErrorCallback(Result))) - E->OutputType = OutType; - } - return true; - } - - if (auto* E = Cast(Expr)) - { - const TSharedPtr* ValueObj = nullptr; - if (!Json->TryGetObjectField(TEXT("value"), ValueObj) || !ValueObj || !(*ValueObj).IsValid()) - { - Result.Append(TEXT("ERROR: ComponentMask requires value as {r, g, b, a} (booleans)\n")); - return false; - } - bool bR = false, bG = false, bB = false, bA = false; - (*ValueObj)->TryGetBoolField(TEXT("r"), bR); - (*ValueObj)->TryGetBoolField(TEXT("g"), bG); - (*ValueObj)->TryGetBoolField(TEXT("b"), bB); - (*ValueObj)->TryGetBoolField(TEXT("a"), bA); - E->R = bR ? 1 : 0; - E->G = bG ? 1 : 0; - E->B = bB ? 1 : 0; - E->A = bA ? 1 : 0; - SetResult = FString::Printf(TEXT("R=%s G=%s B=%s A=%s"), - bR ? TEXT("true") : TEXT("false"), - bG ? TEXT("true") : TEXT("false"), - bB ? TEXT("true") : TEXT("false"), - bA ? TEXT("true") : TEXT("false")); - return true; - } - - Result.Appendf(TEXT("ERROR: Expression type '%s' does not support value setting. Supported: " - "Constant, Constant3Vector, Constant4Vector, ScalarParameter, VectorParameter, " - "TextureCoordinate, Custom, ComponentMask\n"), - *Expr->GetClass()->GetName()); - return false; - } - - // Parse {r, g, b[, a]} from the "value" JSON field. - bool ParseColorValue(const FJsonObject* Json, FLinearColor& OutColor, bool bHasAlpha, FStringBuilderBase& Result) - { - const TSharedPtr* ValueObj = nullptr; - if (!Json->TryGetObjectField(TEXT("value"), ValueObj) || !ValueObj || !(*ValueObj).IsValid()) - { - Result.Appendf(TEXT("ERROR: requires value as {r, g, b%s}\n"), bHasAlpha ? TEXT(", a") : TEXT("")); - return false; - } - double R = 0, G = 0, B = 0, A = 1; - (*ValueObj)->TryGetNumberField(TEXT("r"), R); - (*ValueObj)->TryGetNumberField(TEXT("g"), G); - (*ValueObj)->TryGetNumberField(TEXT("b"), B); - if (bHasAlpha) (*ValueObj)->TryGetNumberField(TEXT("a"), A); - OutColor = FLinearColor((float)R, (float)G, (float)B, (float)A); - return true; - } - - // If the JSON has a "parameterName" field, apply it to a parameter expression. - void ApplyParameterName(UMaterialExpressionParameter* Param, const FJsonObject* Json) - { - FString ParamName; - if (Json->TryGetStringField(TEXT("parameterName"), ParamName) && !ParamName.IsEmpty()) - Param->ParameterName = FName(*ParamName); - } -};