More blueprint handler work
This commit is contained in:
@@ -27,13 +27,13 @@ public:
|
|||||||
FString Blueprint;
|
FString Blueprint;
|
||||||
|
|
||||||
UPROPERTY(meta=(Description="Component class name (e.g. StaticMeshComponent, SceneComponent)"))
|
UPROPERTY(meta=(Description="Component class name (e.g. StaticMeshComponent, SceneComponent)"))
|
||||||
FString ComponentClass;
|
FString Class;
|
||||||
|
|
||||||
UPROPERTY(meta=(Description="Component name for the new component"))
|
UPROPERTY(meta=(Description="Component name for the new component"))
|
||||||
FString Component;
|
FString Component;
|
||||||
|
|
||||||
UPROPERTY(meta=(Optional, Description="Name of the parent component to attach to"))
|
UPROPERTY(meta=(Optional, Description="Name of the parent component to attach to"))
|
||||||
FString ParentComponent;
|
FString Parent;
|
||||||
|
|
||||||
virtual FString GetDescription() const override
|
virtual FString GetDescription() const override
|
||||||
{
|
{
|
||||||
@@ -61,22 +61,22 @@ public:
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Resolve the component class by name
|
// Resolve the component class by name
|
||||||
UClass* ComponentClassObj = UWingTypes::TextToOneObjectType(ComponentClass);
|
UClass* ComponentClassObj = UWingTypes::TextToOneObjectType(Class);
|
||||||
if (!ComponentClassObj) return;
|
if (!ComponentClassObj) return;
|
||||||
if (!ComponentClassObj->IsChildOf(UActorComponent::StaticClass()))
|
if (!ComponentClassObj->IsChildOf(UActorComponent::StaticClass()))
|
||||||
{
|
{
|
||||||
UWingServer::Printf(TEXT("ERROR: '%s' is not a subclass of UActorComponent\n"), *ComponentClass);
|
UWingServer::Printf(TEXT("ERROR: '%s' is not a subclass of UActorComponent\n"), *Class);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If parent component specified, find its SCS node
|
// If parent component specified, find its SCS node
|
||||||
USCS_Node* ParentSCSNode = nullptr;
|
USCS_Node* ParentSCSNode = nullptr;
|
||||||
if (!ParentComponent.IsEmpty())
|
if (!Parent.IsEmpty())
|
||||||
{
|
{
|
||||||
for (USCS_Node* Node : Existing)
|
for (USCS_Node* Node : Existing)
|
||||||
{
|
{
|
||||||
if (Node && Node->ComponentTemplate &&
|
if (Node && Node->ComponentTemplate &&
|
||||||
WingUtils::Identifies(ParentComponent, Node->ComponentTemplate))
|
WingUtils::Identifies(Parent, Node->ComponentTemplate))
|
||||||
{
|
{
|
||||||
ParentSCSNode = Node;
|
ParentSCSNode = Node;
|
||||||
break;
|
break;
|
||||||
@@ -86,7 +86,7 @@ public:
|
|||||||
if (!ParentSCSNode)
|
if (!ParentSCSNode)
|
||||||
{
|
{
|
||||||
UWingServer::Printf(TEXT("ERROR: Parent component '%s' not found in Blueprint '%s'\n"),
|
UWingServer::Printf(TEXT("ERROR: Parent component '%s' not found in Blueprint '%s'\n"),
|
||||||
*ParentComponent, *WingUtils::FormatName(BP));
|
*Parent, *WingUtils::FormatName(BP));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,87 +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 "Kismet2/BlueprintEditorUtils.h"
|
|
||||||
#include "Blueprint_RefreshAllNodes.generated.h"
|
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
UCLASS()
|
|
||||||
class UWing_Blueprint_RefreshAllNodes : public UObject, public IWingHandler
|
|
||||||
{
|
|
||||||
GENERATED_BODY()
|
|
||||||
|
|
||||||
public:
|
|
||||||
UPROPERTY(meta=(Description="Blueprint package path"))
|
|
||||||
FString Blueprint;
|
|
||||||
|
|
||||||
virtual FString GetDescription() const override
|
|
||||||
{
|
|
||||||
return TEXT("Refresh all nodes in a Blueprint, removing orphaned pins. "
|
|
||||||
"Reports compiler warnings and errors.");
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void Handle() override
|
|
||||||
{
|
|
||||||
// Load Blueprint
|
|
||||||
WingFetcher F;
|
|
||||||
UBlueprint* BP = F.Asset(Blueprint).Cast<UBlueprint>();
|
|
||||||
if (!BP) return;
|
|
||||||
|
|
||||||
int32 GraphCount = WingUtils::AllGraphs(BP).Num();
|
|
||||||
int32 NodeCount = WingUtils::AllNodes(BP).Num();
|
|
||||||
|
|
||||||
// Refresh all nodes
|
|
||||||
FBlueprintEditorUtils::RefreshAllNodes(BP);
|
|
||||||
|
|
||||||
// Remove orphaned pins from all nodes
|
|
||||||
int32 OrphanedPinsRemoved = 0;
|
|
||||||
for (UEdGraphNode* Node : WingUtils::AllNodes(BP))
|
|
||||||
{
|
|
||||||
for (int32 i = Node->Pins.Num() - 1; i >= 0; --i)
|
|
||||||
{
|
|
||||||
UEdGraphPin* Pin = Node->Pins[i];
|
|
||||||
if (Pin && Pin->bOrphanedPin)
|
|
||||||
{
|
|
||||||
Pin->BreakAllPinLinks();
|
|
||||||
Node->Pins.RemoveAt(i);
|
|
||||||
OrphanedPinsRemoved++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Summary
|
|
||||||
UWingServer::Printf(TEXT("Refreshed %s: %d graphs, %d nodes"), *WingUtils::FormatName(BP), GraphCount, NodeCount);
|
|
||||||
if (OrphanedPinsRemoved > 0)
|
|
||||||
{
|
|
||||||
UWingServer::Printf(TEXT(", %d orphaned pins removed"), OrphanedPinsRemoved);
|
|
||||||
}
|
|
||||||
UWingServer::Print(TEXT("\n"));
|
|
||||||
|
|
||||||
// Collect compiler warnings and errors
|
|
||||||
if (BP->Status == BS_Error)
|
|
||||||
{
|
|
||||||
UWingServer::Print(TEXT("ERROR: Blueprint has compiler errors after refresh\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (UEdGraphNode* Node : WingUtils::AllNodes(BP))
|
|
||||||
{
|
|
||||||
if (!Node->bHasCompilerMessage) continue;
|
|
||||||
const TCHAR* Prefix = (Node->ErrorType == EMessageSeverity::Error) ? TEXT("ERROR") : TEXT("WARNING");
|
|
||||||
UWingServer::Printf(TEXT("%s: [%s] %s: %s\n"),
|
|
||||||
Prefix, *WingUtils::FormatName(Node->GetGraph()),
|
|
||||||
*WingUtils::FormatName(Node), *Node->ErrorMsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -3,11 +3,11 @@
|
|||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "WingHandler.h"
|
#include "WingHandler.h"
|
||||||
#include "WingFetcher.h"
|
#include "WingFetcher.h"
|
||||||
|
#include "WingTypes.h"
|
||||||
#include "WingUtils.h"
|
#include "WingUtils.h"
|
||||||
#include "WingServer.h"
|
#include "WingServer.h"
|
||||||
#include "Engine/Blueprint.h"
|
#include "Engine/Blueprint.h"
|
||||||
#include "Kismet2/BlueprintEditorUtils.h"
|
#include "Kismet2/BlueprintEditorUtils.h"
|
||||||
#include "UObject/UObjectIterator.h"
|
|
||||||
#include "Blueprint_AddInterface.generated.h"
|
#include "Blueprint_AddInterface.generated.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -25,12 +25,11 @@ public:
|
|||||||
FString Blueprint;
|
FString Blueprint;
|
||||||
|
|
||||||
UPROPERTY(meta=(Description="Native UInterface class name or Blueprint Interface package path"))
|
UPROPERTY(meta=(Description="Native UInterface class name or Blueprint Interface package path"))
|
||||||
FString InterfaceName;
|
FString Interface;
|
||||||
|
|
||||||
virtual FString GetDescription() const override
|
virtual FString GetDescription() const override
|
||||||
{
|
{
|
||||||
return TEXT("Add a Blueprint Interface implementation to a Blueprint. "
|
return TEXT("Add an interface to a blueprint");
|
||||||
"Creates stub function graphs for each interface function.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void Handle() override
|
virtual void Handle() override
|
||||||
@@ -40,7 +39,7 @@ public:
|
|||||||
if (!BP) return;
|
if (!BP) return;
|
||||||
|
|
||||||
// Resolve the interface class
|
// Resolve the interface class
|
||||||
UClass* InterfaceClass = FindInterfaceClass(InterfaceName);
|
UClass* InterfaceClass = UWingTypes::TextToOneInterfaceType(Interface);
|
||||||
if (!InterfaceClass) return;
|
if (!InterfaceClass) return;
|
||||||
|
|
||||||
// Check for duplicates
|
// Check for duplicates
|
||||||
@@ -65,41 +64,14 @@ public:
|
|||||||
|
|
||||||
// Collect stub function graph names from the newly added interface entry
|
// Collect stub function graph names from the newly added interface entry
|
||||||
UWingServer::Printf(TEXT("Added interface %s\n"), *WingUtils::FormatName(InterfaceClass));
|
UWingServer::Printf(TEXT("Added interface %s\n"), *WingUtils::FormatName(InterfaceClass));
|
||||||
UWingServer::Printf(TEXT("Function stubs:\n"));
|
|
||||||
for (const FBPInterfaceDescription& IfaceDesc : BP->ImplementedInterfaces)
|
for (const FBPInterfaceDescription& IfaceDesc : BP->ImplementedInterfaces)
|
||||||
{
|
{
|
||||||
if (IfaceDesc.Interface != InterfaceClass) continue;
|
if (IfaceDesc.Interface != InterfaceClass) continue;
|
||||||
for (const UEdGraph* Graph : IfaceDesc.Graphs)
|
for (const UEdGraph* Graph : IfaceDesc.Graphs)
|
||||||
{
|
{
|
||||||
if (Graph)
|
UWingServer::Printf(TEXT("New Graph: %s\n"), *WingUtils::FormatName(Graph));
|
||||||
UWingServer::Printf(TEXT(" %s\n"), *WingUtils::FormatName(Graph));
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
// Resolve an interface name to a UClass. Tries loaded UInterface classes
|
|
||||||
// first (for native interfaces), then falls back to loading a Blueprint
|
|
||||||
// Interface asset by package path.
|
|
||||||
static UClass* FindInterfaceClass(const FString& Name)
|
|
||||||
{
|
|
||||||
// Strategy 1: Search loaded UInterface classes by name
|
|
||||||
for (TObjectIterator<UClass> It; It; ++It)
|
|
||||||
{
|
|
||||||
if (!It->IsChildOf(UInterface::StaticClass())) continue;
|
|
||||||
if (WingUtils::Identifies(Name, *It))
|
|
||||||
return *It;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Strategy 2: Try loading as a Blueprint Interface asset by package path
|
|
||||||
WingFetcher F;
|
|
||||||
UBlueprint* IfaceBP = F.Asset(Name).Cast<UBlueprint>();
|
|
||||||
if (IfaceBP && IfaceBP->GeneratedClass && IfaceBP->GeneratedClass->IsChildOf(UInterface::StaticClass()))
|
|
||||||
return IfaceBP->GeneratedClass;
|
|
||||||
|
|
||||||
UWingServer::Printf(TEXT("ERROR: Interface '%s' not found. Provide a native UInterface class name or Blueprint Interface package path.\n"),
|
|
||||||
*Name);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "WingFetcher.h"
|
#include "WingFetcher.h"
|
||||||
#include "WingUtils.h"
|
#include "WingUtils.h"
|
||||||
#include "Engine/Blueprint.h"
|
#include "Engine/Blueprint.h"
|
||||||
|
#include "EdGraph/EdGraphNode.h"
|
||||||
#include "Kismet2/KismetEditorUtilities.h"
|
#include "Kismet2/KismetEditorUtilities.h"
|
||||||
#include "Blueprint_Compile.generated.h"
|
#include "Blueprint_Compile.generated.h"
|
||||||
|
|
||||||
@@ -20,35 +21,38 @@ class UWing_Blueprint_Compile : public UObject, public IWingHandler
|
|||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UPROPERTY(meta=(Optional, Description="Path of the blueprint."))
|
UPROPERTY(meta=(Description="Blueprint to compile"))
|
||||||
FString Blueprint;
|
FString Blueprint;
|
||||||
|
|
||||||
virtual FString GetDescription() const override
|
virtual FString GetDescription() const override
|
||||||
{
|
{
|
||||||
return TEXT("Compile a blueprint. ");
|
return TEXT("Compile a blueprint. "
|
||||||
|
"Reports compiler warnings and errors.");
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void Handle() override
|
virtual void Handle() override
|
||||||
{
|
{
|
||||||
WingFetcher F;
|
WingFetcher F;
|
||||||
UBlueprint *BP = F.Walk(Blueprint).Cast<UBlueprint>();
|
UBlueprint* BP = F.Asset(Blueprint).Cast<UBlueprint>();
|
||||||
|
if (!BP) return;
|
||||||
|
|
||||||
|
// Compile
|
||||||
EBlueprintCompileOptions CompileOpts =
|
EBlueprintCompileOptions CompileOpts =
|
||||||
EBlueprintCompileOptions::SkipSave |
|
EBlueprintCompileOptions::SkipSave |
|
||||||
EBlueprintCompileOptions::SkipGarbageCollection |
|
EBlueprintCompileOptions::SkipGarbageCollection |
|
||||||
EBlueprintCompileOptions::SkipFiBSearchMetaUpdate;
|
EBlueprintCompileOptions::SkipFiBSearchMetaUpdate;
|
||||||
|
|
||||||
FKismetEditorUtilities::CompileBlueprint(BP, CompileOpts, nullptr);
|
FKismetEditorUtilities::CompileBlueprint(BP, CompileOpts, nullptr);
|
||||||
|
|
||||||
// Collect compiler messages from nodes
|
// Report compiler messages
|
||||||
for (UEdGraphNode* Node : WingUtils::AllNodes(BP))
|
for (UEdGraphNode* Node : WingUtils::AllNodes(BP))
|
||||||
{
|
{
|
||||||
if (!Node->bHasCompilerMessage) continue;
|
if (!Node->bHasCompilerMessage) continue;
|
||||||
UWingServer::Printf(TEXT("%s %s: %s\n"),
|
const TCHAR* Prefix = (Node->ErrorType == EMessageSeverity::Error) ? TEXT("ERROR") : TEXT("WARNING");
|
||||||
*WingUtils::FormatName(Node->GetGraph()),
|
UWingServer::Printf(TEXT("%s: [%s] %s: %s\n"),
|
||||||
*WingUtils::FormatName(Node),
|
Prefix, *WingUtils::FormatName(Node->GetGraph()),
|
||||||
*Node->ErrorMsg);
|
*WingUtils::FormatName(Node), *Node->ErrorMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
UWingServer::Printf(TEXT("Compilation Done.\n"));
|
UWingServer::Printf(TEXT("Compilation Done.\n"));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -61,8 +61,7 @@ public:
|
|||||||
// Interfaces
|
// Interfaces
|
||||||
for (const FBPInterfaceDescription& I : BP->ImplementedInterfaces)
|
for (const FBPInterfaceDescription& I : BP->ImplementedInterfaces)
|
||||||
{
|
{
|
||||||
if (I.Interface)
|
if (I.Interface) UWingServer::Printf(TEXT("Interface: %s\n"), *WingUtils::FormatName(I));
|
||||||
UWingServer::Printf(TEXT("Interface: %s\n"), *WingUtils::FormatName(I.Interface));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public:
|
|||||||
FString Blueprint;
|
FString Blueprint;
|
||||||
|
|
||||||
UPROPERTY(meta=(Description="Interface name to remove"))
|
UPROPERTY(meta=(Description="Interface name to remove"))
|
||||||
FString InterfaceName;
|
FString Interface;
|
||||||
|
|
||||||
UPROPERTY(meta=(Optional, Description="If true, keep the function graphs as regular functions"))
|
UPROPERTY(meta=(Optional, Description="If true, keep the function graphs as regular functions"))
|
||||||
bool PreserveFunctions = false;
|
bool PreserveFunctions = false;
|
||||||
@@ -42,27 +42,10 @@ public:
|
|||||||
if (!BP) return;
|
if (!BP) return;
|
||||||
|
|
||||||
// Find the interface by name
|
// Find the interface by name
|
||||||
UClass* FoundInterface = nullptr;
|
FBPInterfaceDescription *IFaceDesc =
|
||||||
for (const FBPInterfaceDescription& IfaceDesc : BP->ImplementedInterfaces)
|
WingUtils::FindExactlyOneNamed(Interface, BP->ImplementedInterfaces);
|
||||||
{
|
if (!IFaceDesc) return;
|
||||||
if (!IfaceDesc.Interface) continue;
|
UClass* FoundInterface = IFaceDesc->Interface;
|
||||||
if (WingUtils::Identifies(InterfaceName, IfaceDesc.Interface))
|
|
||||||
{
|
|
||||||
FoundInterface = IfaceDesc.Interface;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!FoundInterface)
|
|
||||||
{
|
|
||||||
UWingServer::Printf(TEXT("ERROR: Interface '%s' not found. Implemented interfaces:\n"), *InterfaceName);
|
|
||||||
for (const FBPInterfaceDescription& IfaceDesc : BP->ImplementedInterfaces)
|
|
||||||
{
|
|
||||||
if (IfaceDesc.Interface)
|
|
||||||
UWingServer::Printf(TEXT(" %s\n"), *WingUtils::FormatName(IfaceDesc.Interface));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
FTopLevelAssetPath InterfacePath = FoundInterface->GetClassPathName();
|
FTopLevelAssetPath InterfacePath = FoundInterface->GetClassPathName();
|
||||||
FBlueprintEditorUtils::RemoveInterface(BP, InterfacePath, PreserveFunctions);
|
FBlueprintEditorUtils::RemoveInterface(BP, InterfacePath, PreserveFunctions);
|
||||||
@@ -40,8 +40,6 @@ public:
|
|||||||
UBlueprint* BP = F.Asset(Blueprint).Cast<UBlueprint>();
|
UBlueprint* BP = F.Asset(Blueprint).Cast<UBlueprint>();
|
||||||
if (!BP) return;
|
if (!BP) return;
|
||||||
|
|
||||||
FString OldParentName = BP->ParentClass ? WingUtils::FormatName(BP->ParentClass) : TEXT("None");
|
|
||||||
|
|
||||||
// Find the new parent class by short type name
|
// Find the new parent class by short type name
|
||||||
UClass* NewParentClassObj = UWingTypes::TextToOneObjectType(Parent);
|
UClass* NewParentClassObj = UWingTypes::TextToOneObjectType(Parent);
|
||||||
if (!NewParentClassObj) return;
|
if (!NewParentClassObj) return;
|
||||||
@@ -51,7 +49,7 @@ public:
|
|||||||
FBlueprintEditorUtils::RefreshAllNodes(BP);
|
FBlueprintEditorUtils::RefreshAllNodes(BP);
|
||||||
FKismetEditorUtilities::CompileBlueprint(BP);
|
FKismetEditorUtilities::CompileBlueprint(BP);
|
||||||
|
|
||||||
UWingServer::Printf(TEXT("Reparented %s: %s -> %s\n"),
|
UWingServer::Printf(TEXT("Reparented %s -> %s\n"),
|
||||||
*WingUtils::FormatName(BP), *OldParentName, *WingUtils::FormatName(NewParentClassObj));
|
*WingUtils::FormatName(BP), *WingUtils::FormatName(NewParentClassObj));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -210,6 +210,11 @@ FString WingUtils::FormatName(const FUserPinInfo &Pin)
|
|||||||
return SanitizeName(Pin.PinName);
|
return SanitizeName(Pin.PinName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FString WingUtils::FormatName(const FBPInterfaceDescription &IFace)
|
||||||
|
{
|
||||||
|
return FormatName(IFace.Interface);
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Formatting other things
|
// Formatting other things
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ class USCS_Node;
|
|||||||
struct FMemberReference;
|
struct FMemberReference;
|
||||||
struct FBPVariableDescription;
|
struct FBPVariableDescription;
|
||||||
struct FUserPinInfo;
|
struct FUserPinInfo;
|
||||||
|
struct FBPInterfaceDescription;
|
||||||
|
|
||||||
// Stateless utility functions used by MCP handlers and the MCP server.
|
// Stateless utility functions used by MCP handlers and the MCP server.
|
||||||
// This is effectively a namespace — all methods are static.
|
// This is effectively a namespace — all methods are static.
|
||||||
class WingUtils
|
class WingUtils
|
||||||
@@ -73,6 +75,7 @@ public:
|
|||||||
static FString FormatName(const UEnum *Enum);
|
static FString FormatName(const UEnum *Enum);
|
||||||
static FString FormatName(const FProperty *Prop);
|
static FString FormatName(const FProperty *Prop);
|
||||||
static FString FormatName(const FUserPinInfo &Pin);
|
static FString FormatName(const FUserPinInfo &Pin);
|
||||||
|
static FString FormatName(const FBPInterfaceDescription &IFace);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
|||||||
Reference in New Issue
Block a user