Added WingActorComponent

This commit is contained in:
2026-03-19 13:32:05 -04:00
parent 3688a36682
commit 2e4606c9e4
6 changed files with 136 additions and 75 deletions

View File

@@ -73,29 +73,15 @@ public:
USCS_Node* ParentSCSNode = nullptr; USCS_Node* ParentSCSNode = nullptr;
if (!Parent.IsEmpty()) if (!Parent.IsEmpty())
{ {
for (USCS_Node* Node : Existing) ParentSCSNode = WingUtils::FindExactlyOneNamed(Parent, Existing);
{ if (!ParentSCSNode) return;
if (Node && Node->ComponentTemplate &&
WingUtils::Identifies(Parent, Node->ComponentTemplate))
{
ParentSCSNode = Node;
break;
}
}
if (!ParentSCSNode)
{
UWingServer::Printf(TEXT("ERROR: Parent component '%s' not found in Blueprint '%s'\n"),
*Parent, *WingUtils::FormatName(BP));
return;
}
} }
// Create the SCS node // Create the SCS node
USCS_Node* NewNode = SCS->CreateNode(ComponentClassObj, FName(*Component)); USCS_Node* NewNode = SCS->CreateNode(ComponentClassObj, FName(*Component));
if (!NewNode) if (!NewNode)
{ {
UWingServer::Printf(TEXT("ERROR: Failed to create SCS node for component '%s' with class '%s'\n"), UWingServer::Printf(TEXT("ERROR: Failed to create SCS node for component '%s' with class '%s'\n"),
*Component, *WingUtils::FormatName(ComponentClassObj)); *Component, *WingUtils::FormatName(ComponentClassObj));
return; return;
} }

View File

@@ -9,10 +9,7 @@
#include "Engine/Blueprint.h" #include "Engine/Blueprint.h"
#include "Animation/AnimBlueprint.h" #include "Animation/AnimBlueprint.h"
#include "Animation/Skeleton.h" #include "Animation/Skeleton.h"
#include "Engine/SimpleConstructionScript.h" #include "WingActorComponent.h"
#include "Engine/SCS_Node.h"
#include "Components/SceneComponent.h"
#include "GameFramework/Actor.h"
#include "WingFunctionArgs.h" #include "WingFunctionArgs.h"
#include "Kismet2/BlueprintEditorUtils.h" #include "Kismet2/BlueprintEditorUtils.h"
#include "AnimationGraph.h" #include "AnimationGraph.h"
@@ -82,34 +79,21 @@ public:
} }
// Components // Components
bool bHasAny = false; TArray<FWingActorComponent> Components = FWingActorComponent::GetAll(BP);
if (UClass* GenClass = BP->GeneratedClass) if (!Components.IsEmpty())
{ {
// Native components first. UWingServer::Print(TEXT("\nComponents:\n"));
if (AActor* CDO = Cast<AActor>(GenClass->GetDefaultObject())) for (const FWingActorComponent& Comp : Components)
{ {
TArray<UActorComponent*> NativeComponents; UWingServer::Printf(TEXT(" %s %s"), *Comp.GetClassName(), *Comp.GetName());
CDO->GetComponents(NativeComponents); FString ParentName = Comp.GetParentName();
for (UActorComponent* Comp : NativeComponents) if (!ParentName.IsEmpty())
{ UWingServer::Printf(TEXT(" [parent: %s]"), *ParentName);
if (!bHasAny) { UWingServer::Print(TEXT("\nComponents:\n")); bHasAny = true; } if (Comp.IsNative())
PrintComponent(Comp, TEXT("native")); UWingServer::Print(TEXT(" [native]"));
} else if (!Comp.IsOwnedBy(BP))
} UWingServer::Print(TEXT(" [inherited]"));
UWingServer::Print(TEXT("\n"));
// Blueprint SCS components (this blueprint and parents)
TArray<UBlueprint*> Hierarchy;
UBlueprint::GetBlueprintHierarchyFromClass(GenClass, Hierarchy);
for (int32 i = Hierarchy.Num() - 1; i >= 0; --i)
{
UBlueprint* WalkBP = Hierarchy[i];
if (!WalkBP->SimpleConstructionScript) continue;
const TCHAR* Annotation = (WalkBP == BP) ? nullptr : TEXT("inherited");
for (USCS_Node* Node : WalkBP->SimpleConstructionScript->GetAllNodes())
{
if (!bHasAny) { UWingServer::Print(TEXT("\nComponents:\n")); bHasAny = true; }
PrintComponent(Node, Annotation);
}
} }
} }
@@ -203,34 +187,6 @@ private:
UWingServer::Printf(TEXT(" %s(%s)\n"), *WingUtils::FormatName(Graph), *Args); UWingServer::Printf(TEXT(" %s(%s)\n"), *WingUtils::FormatName(Graph), *Args);
} }
void PrintComponent(USCS_Node* Node, const TCHAR* Annotation = nullptr)
{
FString ClassName = Node->ComponentClass
? WingUtils::FormatName(Node->ComponentClass)
: TEXT("None");
UWingServer::Printf(TEXT(" %s %s"),
*ClassName,
*WingUtils::FormatName(Node));
if (Node->ParentComponentOrVariableName != NAME_None)
UWingServer::Printf(TEXT(" [parent: %s]"), *Node->ParentComponentOrVariableName.ToString());
if (Annotation)
UWingServer::Printf(TEXT(" [%s]"), Annotation);
UWingServer::Print(TEXT("\n"));
}
void PrintComponent(UActorComponent* Comp, const TCHAR* Annotation = nullptr)
{
UWingServer::Printf(TEXT(" %s %s"),
*WingUtils::FormatName(Comp->GetClass()),
*Comp->GetName());
if (USceneComponent* Scene = Cast<USceneComponent>(Comp))
if (USceneComponent* Parent = Scene->GetAttachParent())
UWingServer::Printf(TEXT(" [parent: %s]"), *Parent->GetName());
if (Annotation)
UWingServer::Printf(TEXT(" [%s]"), Annotation);
UWingServer::Print(TEXT("\n"));
}
void PrintGraph(UEdGraph* Graph, const TCHAR* Type, UClass* Interface = nullptr) void PrintGraph(UEdGraph* Graph, const TCHAR* Type, UClass* Interface = nullptr)
{ {
TWeakObjectPtr<UK2Node_EditablePinBase> EntryNode; TWeakObjectPtr<UK2Node_EditablePinBase> EntryNode;

View File

@@ -0,0 +1,81 @@
#include "WingActorComponent.h"
#include "WingUtils.h"
#include "Engine/Blueprint.h"
#include "Engine/SCS_Node.h"
#include "Engine/SimpleConstructionScript.h"
#include "Components/ActorComponent.h"
#include "Components/SceneComponent.h"
#include "GameFramework/Actor.h"
#include "Kismet2/BlueprintEditorUtils.h"
FString FWingActorComponent::GetName() const
{
if (SCSNode) return WingUtils::FormatName(SCSNode);
if (NativeComponent) return WingUtils::FormatName(NativeComponent);
return FString();
}
FString FWingActorComponent::GetClassName() const
{
if (SCSNode)
return SCSNode->ComponentClass ? WingUtils::FormatName(SCSNode->ComponentClass) : TEXT("None");
if (NativeComponent)
return WingUtils::FormatName(NativeComponent->GetClass());
return FString();
}
FString FWingActorComponent::GetParentName() const
{
if (SCSNode)
{
if (SCSNode->ParentComponentOrVariableName != NAME_None)
return WingUtils::SanitizeName(SCSNode->ParentComponentOrVariableName);
return FString();
}
if (NativeComponent)
{
if (USceneComponent* Scene = Cast<USceneComponent>(NativeComponent))
if (USceneComponent* Parent = Scene->GetAttachParent())
return WingUtils::FormatName(Parent);
return FString();
}
return FString();
}
bool FWingActorComponent::IsOwnedBy(const UBlueprint* BP) const
{
return BP && BP->GeneratedClass == Owner;
}
TArray<FWingActorComponent> FWingActorComponent::GetAll(UBlueprint* BP)
{
TArray<FWingActorComponent> Result;
if (!BP) return Result;
UClass* GenClass = BP->GeneratedClass;
if (!GenClass) return Result;
// Native components from CDO
if (AActor* CDO = Cast<AActor>(GenClass->GetDefaultObject()))
{
UClass* NativeClass = FBlueprintEditorUtils::FindFirstNativeClass(GenClass);
TArray<UActorComponent*> NativeComponents;
CDO->GetComponents(NativeComponents);
for (UActorComponent* Comp : NativeComponents)
Result.Emplace(Comp, NativeClass);
}
// SCS nodes, walking hierarchy from oldest ancestor to current BP
TArray<UBlueprint*> Hierarchy;
UBlueprint::GetBlueprintHierarchyFromClass(GenClass, Hierarchy);
for (int32 i = Hierarchy.Num() - 1; i >= 0; --i)
{
UBlueprint* WalkBP = Hierarchy[i];
if (!WalkBP->SimpleConstructionScript) continue;
UClass* OwnerClass = WalkBP->GeneratedClass;
for (USCS_Node* Node : WalkBP->SimpleConstructionScript->GetAllNodes())
Result.Emplace(Node, OwnerClass);
}
return Result;
}

View File

@@ -1,4 +1,5 @@
#include "WingUtils.h" #include "WingUtils.h"
#include "WingActorComponent.h"
#include "WingJson.h" #include "WingJson.h"
#include "WingTypes.h" #include "WingTypes.h"
#include "WingServer.h" #include "WingServer.h"
@@ -215,6 +216,11 @@ FString WingUtils::FormatName(const FBPInterfaceDescription &IFace)
return FormatName(IFace.Interface); return FormatName(IFace.Interface);
} }
FString WingUtils::FormatName(const FWingActorComponent &Comp)
{
return Comp.GetName();
}
// ============================================================ // ============================================================
// Formatting other things // Formatting other things
// ============================================================ // ============================================================

View File

@@ -0,0 +1,30 @@
#pragma once
#include "CoreMinimal.h"
class UBlueprint;
class USCS_Node;
class UActorComponent;
struct FWingActorComponent
{
USCS_Node* SCSNode = nullptr;
UActorComponent* NativeComponent = nullptr;
UClass* Owner = nullptr;
FWingActorComponent() = default;
FWingActorComponent(USCS_Node* InSCSNode, UClass* InOwner) : SCSNode(InSCSNode), Owner(InOwner) {}
FWingActorComponent(UActorComponent* InNative, UClass* InOwner) : NativeComponent(InNative), Owner(InOwner) {}
explicit operator bool() const { return SCSNode || NativeComponent; }
FString GetName() const;
FString GetClassName() const;
FString GetParentName() const;
bool IsNative() const { return NativeComponent != nullptr; }
bool IsOwnedBy(const UBlueprint* BP) const;
// Collect all components: native from CDO first, then SCS nodes
// walking the hierarchy from oldest ancestor to current blueprint.
static TArray<FWingActorComponent> GetAll(UBlueprint* BP);
};

View File

@@ -32,6 +32,7 @@ struct FMemberReference;
struct FBPVariableDescription; struct FBPVariableDescription;
struct FUserPinInfo; struct FUserPinInfo;
struct FBPInterfaceDescription; struct FBPInterfaceDescription;
struct FWingActorComponent;
// 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.
@@ -76,6 +77,7 @@ public:
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); static FString FormatName(const FBPInterfaceDescription &IFace);
static FString FormatName(const FWingActorComponent &Comp);
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
// //