More work on blueprint component lists

This commit is contained in:
2026-03-19 20:44:04 -04:00
parent 2eb2be7af1
commit 9ad6515fb5
9 changed files with 150 additions and 104 deletions

View File

@@ -9,6 +9,9 @@
#include "GameFramework/Actor.h"
#include "Kismet2/BlueprintEditorUtils.h"
FWingActorComponent::FWingActorComponent(USCS_Node *Node) :
SCSNode(Node), Owner(Node->GetSCS()->GetBlueprint()->GeneratedClass) {}
FString FWingActorComponent::GetName() const
{
if (SCSNode) return WingUtils::FormatName(SCSNode);
@@ -48,56 +51,112 @@ bool FWingActorComponent::IsOwnedBy(const UBlueprint* BP) const
return BP && BP->GeneratedClass == Owner;
}
bool FWingActorComponent::SetParent(USCS_Node* ChildNode, const FWingActorComponent* Parent)
bool FWingActorComponent::CheckEditableComponent(UBlueprint *BP, const FWingActorComponent *Component)
{
// Validate before modifying anything
if (Parent)
if ((!Component) || (Component->IsEmpty()))
{
if (Parent->SCSNode)
{
// Check for cycles: walk up from parent to make sure we don't reach the child
USCS_Node* Ancestor = Parent->SCSNode;
while (Ancestor)
{
if (Ancestor == ChildNode)
{
UWingServer::Printf(TEXT("ERROR: Cannot reparent — would create a cycle.\n"));
return false;
}
Ancestor = Ancestor->GetSCS()->FindParentNode(Ancestor);
}
}
else
{
USceneComponent* NativeScene = Cast<USceneComponent>(Parent->NativeComponent);
if (!NativeScene)
{
UWingServer::Printf(TEXT("ERROR: Native parent '%s' is not a SceneComponent — cannot attach to it.\n"),
*Parent->GetName());
return false;
}
}
UWingServer::Printf(TEXT("Component does not exist.\n"));
return false;
}
// Clear any existing parent attachment
ChildNode->Modify();
ChildNode->bIsParentComponentNative = false;
ChildNode->ParentComponentOrVariableName = NAME_None;
ChildNode->ParentComponentOwnerClassName = NAME_None;
if (!Parent)
return true;
if (Parent->SCSNode)
if (Component->IsNative())
{
ChildNode->SetParent(Parent->SCSNode);
return true;
UWingServer::Printf(TEXT("Cannot edit native components.\n"));
return false;
}
if (!Component->IsOwnedBy(BP))
{
UWingServer::Printf(TEXT("Component %s is inherited, to edit it, you must edit BP %s\n"),
*WingUtils::FormatName(*Component), *WingUtils::FormatName(Component->Owner));
return false;
}
ChildNode->SetParent(Cast<USceneComponent>(Parent->NativeComponent));
return true;
}
bool FWingActorComponent::CheckValidParent(const FWingActorComponent *Parent)
{
if ((!Parent) || (Parent->IsEmpty()))
{
UWingServer::Printf(TEXT("Cannot create a component without a parent.\n"));
return false;
}
if (Parent->IsNative() && (!Parent->NativeComponent->GetClass()->IsChildOf(USceneComponent::StaticClass())))
{
UWingServer::Printf(TEXT("Native component %s is not a scene component, cannot be a parent."),
*WingUtils::FormatName(Parent->NativeComponent));
return false;
}
return true;
}
bool FWingActorComponent::CheckValidComponentClass(UClass *Class)
{
if (!Class->IsChildOf(UActorComponent::StaticClass()))
{
UWingServer::Printf(TEXT("Cannot create a component of class %s, which is not an actor component.\n"),
*WingUtils::FormatName(Class));
return false;
}
return true;
}
void FWingActorComponent::AddNodeInternal(UBlueprint *BP, USCS_Node *NewNode, const FWingActorComponent *Parent)
{
if (Parent->SCSNode && Parent->IsOwnedBy(BP))
{
Parent->SCSNode->AddChildNode(NewNode, true);
}
else if (Parent->SCSNode)
{
NewNode->SetParent(Parent->SCSNode);
BP->SimpleConstructionScript->AddNode(NewNode);
}
else
{
NewNode->SetParent(Cast<USceneComponent>(Parent->NativeComponent));
BP->SimpleConstructionScript->AddNode(NewNode);
}
}
USCS_Node *FWingActorComponent::AddComponent(UBlueprint *BP, UClass *Class, const FWingActorComponent* Parent, const FString &Name)
{
if (!CheckValidParent(Parent)) return nullptr;
if (!CheckValidComponentClass(Class)) return nullptr;
USCS_Node *NewNode = BP->SimpleConstructionScript->CreateNode(Class, FName(*Name));
if (NewNode == nullptr)
{
UWingServer::Printf(TEXT("Could not create new component %s of class %s, unknown reason.\n"),
*Name, *WingUtils::FormatName(Class));
return nullptr;
}
AddNodeInternal(BP, NewNode, Parent);
return NewNode;
}
bool FWingActorComponent::ReparentComponent(UBlueprint *BP, USCS_Node *SCSNode, const FWingActorComponent* Parent)
{
FWingActorComponent Component(SCSNode);
if (!CheckEditableComponent(BP, &Component)) return false;
if (!CheckValidParent(Parent)) return false;
if (Parent->SCSNode == SCSNode)
{
UWingServer::Printf(TEXT("You may not parent a component to itself.\n"));
return false;
}
if (Parent->SCSNode && Parent->SCSNode->IsChildOf(SCSNode))
{
UWingServer::Printf(TEXT("You may not parent a component to one of its own children.\n"));
return false;
}
BP->SimpleConstructionScript->RemoveNode(SCSNode);
AddNodeInternal(BP, SCSNode, Parent);
return true;
}
TArray<FWingActorComponent> FWingActorComponent::GetAll(UBlueprint* BP)
{
TArray<FWingActorComponent> Result;
@@ -123,9 +182,8 @@ TArray<FWingActorComponent> FWingActorComponent::GetAll(UBlueprint* BP)
{
UBlueprint* WalkBP = Hierarchy[i];
if (!WalkBP->SimpleConstructionScript) continue;
UClass* OwnerClass = WalkBP->GeneratedClass;
for (USCS_Node* Node : WalkBP->SimpleConstructionScript->GetAllNodes())
Result.Emplace(Node, OwnerClass);
Result.Emplace(Node);
}
return Result;

View File

@@ -115,20 +115,15 @@ WingFetcher& WingFetcher::Asset(const FString& PackagePath)
{
if (bError) return *this;
// Try to find the object in memory first (silent), then load if needed.
// LoadObject logs its own errors when the package doesn't exist, so
// we check DoesPackageExist first to avoid redundant log spam.
SetObj(FindObject<UObject>(nullptr, *PackagePath));
if (!Obj)
// Check if the package exists before calling LoadObject, because
// LoadObject logs its own errors when the package doesn't exist.
FString PackageName = FPackageName::ObjectPathToPackageName(PackagePath);
if (!FPackageName::DoesPackageExist(PackageName))
{
FString PackageName = FPackageName::ObjectPathToPackageName(PackagePath);
if (!FPackageName::DoesPackageExist(PackageName))
{
UWingServer::Printf(TEXT("ERROR: Asset '%s' does not exist.\n"), *PackagePath);
return SetError();
}
SetObj(LoadObject<UObject>(nullptr, *PackagePath));
UWingServer::Printf(TEXT("ERROR: Asset '%s' does not exist.\n"), *PackagePath);
return SetError();
}
SetObj(LoadObject<UObject>(nullptr, *PackagePath));
if (!Obj)
{
UWingServer::Printf(TEXT("ERROR: Could not load asset '%s'\n"), *PackagePath);

View File

@@ -7,6 +7,7 @@
#include "Materials/MaterialExpression.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "MaterialEditingLibrary.h"
#include "Subsystems/AssetEditorSubsystem.h"
void FWingNotifier::AddTouchedObject(UObject* Obj)
{