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