Code to make Blueprint_Reparent safer.

This commit is contained in:
2026-03-22 15:44:32 -04:00
parent f94727d857
commit f38630ea08
6 changed files with 64 additions and 3 deletions

Binary file not shown.

BIN
Content/Testing/WB_Test.uasset LFS Normal file

Binary file not shown.

View File

@@ -44,6 +44,14 @@ public:
UClass* NewParentClassObj = UWingTypes::TextToOneObjectType(Parent); UClass* NewParentClassObj = UWingTypes::TextToOneObjectType(Parent);
if (!NewParentClassObj) return; if (!NewParentClassObj) return;
// Validate reparent
if (!WingUtils::CanReparentBlueprint(BP->GeneratedClass, NewParentClassObj))
{
UWingServer::Printf(TEXT("Error: Cannot reparent %s to %s — incompatible class hierarchy.\n"),
*WingUtils::FormatName(BP), *WingUtils::FormatName(NewParentClassObj));
return;
}
// Perform reparent // Perform reparent
BP->ParentClass = NewParentClassObj; BP->ParentClass = NewParentClassObj;
FBlueprintEditorUtils::RefreshAllNodes(BP); FBlueprintEditorUtils::RefreshAllNodes(BP);

View File

@@ -22,6 +22,11 @@
#include "Misc/Paths.h" #include "Misc/Paths.h"
#include "Misc/PackageName.h" #include "Misc/PackageName.h"
// Reparent validation
#include "Engine/LevelScriptActor.h"
#include "Animation/AnimInstance.h"
#include "Blueprint/UserWidget.h"
// Animation Blueprint support // Animation Blueprint support
#include "AnimStateNode.h" #include "AnimStateNode.h"
#include "AnimStateTransitionNode.h" #include "AnimStateTransitionNode.h"
@@ -397,6 +402,47 @@ bool WingUtils::CheckCanRename(UEdGraphNode* Node, const FString &Name)
return true; return true;
} }
// ============================================================
// Reparent validation
// ============================================================
bool WingUtils::CanReparentBlueprint(UClass* CurrentGenerated, UClass* Proposed)
{
if (!CurrentGenerated || !Proposed) return false;
UClass* CurrentParent = CurrentGenerated->GetSuperClass();
if (!CurrentParent) return false;
// Don't allow parenting to itself or one of its children
if (Proposed->IsChildOf(CurrentGenerated)) return false;
// Don't allow parenting to an interface
if (Proposed->IsChildOf(UInterface::StaticClass())) return false;
// LevelScriptActor blueprints stay in the LevelScriptActor hierarchy
if (CurrentParent->IsChildOf(ALevelScriptActor::StaticClass()))
return Proposed->IsChildOf(ALevelScriptActor::StaticClass());
// Actor blueprints stay in the Actor hierarchy, but not LevelScriptActor
if (CurrentParent->IsChildOf(AActor::StaticClass()))
return Proposed->IsChildOf(AActor::StaticClass()) &&
!Proposed->IsChildOf(ALevelScriptActor::StaticClass());
// Component blueprints stay in the ActorComponent hierarchy
if (CurrentParent->IsChildOf(UActorComponent::StaticClass()))
return Proposed->IsChildOf(UActorComponent::StaticClass());
// Anim blueprints stay in the AnimInstance hierarchy
if (CurrentParent->IsChildOf(UAnimInstance::StaticClass()))
return Proposed->IsChildOf(UAnimInstance::StaticClass());
// Widget blueprints stay in the UserWidget hierarchy
if (CurrentParent->IsChildOf(UUserWidget::StaticClass()))
return Proposed->IsChildOf(UUserWidget::StaticClass());
return false;
}
// ============================================================ // ============================================================
// Blueprint helpers // Blueprint helpers
// ============================================================ // ============================================================

View File

@@ -255,6 +255,9 @@ public:
static FString GetHandlerGroup(UClass* HandlerClass); static FString GetHandlerGroup(UClass* HandlerClass);
static void PrintHandlerHelp(UClass* HandlerClass); static void PrintHandlerHelp(UClass* HandlerClass);
// ----- Reparent validation -----
static bool CanReparentBlueprint(UClass* CurrentGenerated, UClass* Proposed);
// ----- Common Error Reporting ----- // ----- Common Error Reporting -----
static bool CheckExactlyOneNamed(int Count, const TCHAR *Kind, const FString &Name); static bool CheckExactlyOneNamed(int Count, const TCHAR *Kind, const FString &Name);
static bool CheckExactlyNoneNamed(int Count, const TCHAR *Kind, const FString &Name); static bool CheckExactlyNoneNamed(int Count, const TCHAR *Kind, const FString &Name);

View File

@@ -32,7 +32,8 @@ public class UEWingman : ModuleRules
"RHI", "RHI",
"Slate", "Slate",
"SlateCore", "SlateCore",
"ToolMenus" "ToolMenus",
"UMG"
}); });
} }
} }