Files
UnrealEngineUWP/Engine/Source/Runtime/Engine/Private/Blueprint.cpp
jeanfrancois dube d57935dd36 [Backout] - CL24990850
[FYI] JeanFrancois.Dube
Original CL Desc
-----------------------------------------------------------------
World Partition Class Descriptors Registry:
- Don't rely on OnObjectLoaded event to prefetch existing class descriptors to avoid parsing the Asset Registry in the early process of initializing the editor.
- Instead, lazy prefetch existing class descriptors only when required.
- Optimized internal state validation when running in Debug.
Fix editor boot time regression.

#jira UE-182612
#rb patrick.enfedaque
[FYI] robert.millar
#preflight 64301ad3427eda5626b0e994

[CL 24998864 by jeanfrancois dube in ue5-main branch]
2023-04-11 17:25:21 -04:00

2327 lines
70 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Engine/Blueprint.h"
#include "Blueprint/BlueprintSupport.h"
#include "UObject/BlueprintsObjectVersion.h"
#include "EngineLogs.h"
#include "UObject/FrameworkObjectVersion.h"
#include "UObject/ObjectSaveContext.h"
#include "UObject/PrimaryAssetId.h"
#include "UObject/UE5ReleaseStreamObjectVersion.h"
#include "Serialization/PropertyLocalizationDataGathering.h"
#include "Components/TimelineComponent.h"
#include "Modules/ModuleManager.h"
#include "UObject/TextProperty.h"
#include "GameFramework/Actor.h"
#include "Engine/SimpleConstructionScript.h"
#include "Engine/SCS_Node.h"
#if WITH_EDITOR
#include "BlueprintCompilationManager.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Kismet2/ComponentEditorUtils.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "Kismet2/StructureEditorUtils.h"
#include "FindInBlueprintManager.h"
#include "CookerSettings.h"
#include "Editor.h"
#include "Logging/MessageLog.h"
#include "Settings/EditorLoadingSavingSettings.h"
#include "Engine/TimelineTemplate.h"
#include "Curves/CurveBase.h"
#include "Interfaces/ITargetPlatform.h"
#include "UObject/MetaData.h"
#include "Blueprint/BlueprintExtension.h"
#include "UObject/TextProperty.h"
#include "WorldPartition/WorldPartitionActorDescUtils.h"
#endif
#include "Engine/InheritableComponentHandler.h"
#if WITH_EDITORONLY_DATA
#include "Kismet2/KismetDebugUtilities.h"
#endif
DEFINE_LOG_CATEGORY(LogBlueprint);
//////////////////////////////////////////////////////////////////////////
// Static Helpers
#if WITH_EDITOR
/**
* Updates the blueprint's OwnedComponents, such that they reflect changes made
* natively since the blueprint was last saved (a change in AttachParents, etc.)
*
* @param Blueprint The blueprint whose components you wish to vet.
*/
void UBlueprint::ConformNativeComponents()
{
if (UClass* const BlueprintClass = GeneratedClass)
{
if (AActor* BlueprintCDO = Cast<AActor>(BlueprintClass->ClassDefaultObject))
{
TInlineComponentArray<UActorComponent*> OldNativeComponents;
// collect the native components that this blueprint was serialized out
// with (the native components it had last time it was saved)
BlueprintCDO->GetComponents(OldNativeComponents);
UClass* const NativeSuperClass = FBlueprintEditorUtils::FindFirstNativeClass(BlueprintClass);
AActor* NativeCDO = CastChecked<AActor>(NativeSuperClass->ClassDefaultObject);
// collect the more up to date native components (directly from the
// native super-class)
TInlineComponentArray<UActorComponent*> NewNativeComponents;
NativeCDO->GetComponents(NewNativeComponents);
// loop through all components that this blueprint thinks come from its
// native super-class (last time it was saved)
for (UActorComponent* Component : OldNativeComponents)
{
if (UActorComponent* NativeComponent = FComponentEditorUtils::FindMatchingComponent(Component, NewNativeComponents))
{
USceneComponent* SceneComponent = Cast<USceneComponent>(Component);
if (SceneComponent == nullptr)
{
// if this isn't a scene-component, then we don't care
// (we're looking to fixup scene-component parents)
continue;
}
USceneComponent* OldNativeParent = Cast<USceneComponent>(FComponentEditorUtils::FindMatchingComponent(SceneComponent->GetAttachParent(), NewNativeComponents));
USceneComponent* NativeSceneComponent = CastChecked<USceneComponent>(NativeComponent);
// if this native component has since been reparented, we need
// to make sure that this blueprint reflects that change
if (OldNativeParent != NativeSceneComponent->GetAttachParent())
{
USceneComponent* NewParent = nullptr;
if (NativeSceneComponent->GetAttachParent() != nullptr)
{
NewParent = CastChecked<USceneComponent>(FComponentEditorUtils::FindMatchingComponent(NativeSceneComponent->GetAttachParent(), OldNativeComponents));
}
SceneComponent->SetupAttachment(NewParent, SceneComponent->GetAttachSocketName());
}
}
else
{
// the component has been removed from the native class
// @TODO: I think we already handle removed native components elsewhere, so maybe we should error here?
// BlueprintCDO->RemoveOwnedComponent(Component);
//
// USimpleConstructionScript* BlueprintSCS = Blueprint->SimpleConstructionScript;
// USCS_Node* ComponentNode = BlueprintSCS->CreateNode(Component, Component->GetFName());
//
// BlueprintSCS->AddNode(ComponentNode);
}
}
}
}
}
#endif // WITH_EDITOR
//////////////////////////////////////////////////////////////////////////
// FBPVariableDescription
FBPVariableDescription::FBPVariableDescription()
: PropertyFlags(CPF_Edit)
, ReplicationCondition(ELifetimeCondition::COND_None)
{
}
int32 FBPVariableDescription::FindMetaDataEntryIndexForKey(const FName Key) const
{
for(int32 i=0; i<MetaDataArray.Num(); i++)
{
if(MetaDataArray[i].DataKey == Key)
{
return i;
}
}
return INDEX_NONE;
}
bool FBPVariableDescription::HasMetaData(const FName Key) const
{
return FindMetaDataEntryIndexForKey(Key) != INDEX_NONE;
}
/** Gets a metadata value on the variable; asserts if the value isn't present. Check for validiy using FindMetaDataEntryIndexForKey. */
const FString& FBPVariableDescription::GetMetaData(const FName Key) const
{
int32 EntryIndex = FindMetaDataEntryIndexForKey(Key);
check(EntryIndex != INDEX_NONE);
return MetaDataArray[EntryIndex].DataValue;
}
void FBPVariableDescription::SetMetaData(const FName Key, FString Value)
{
int32 EntryIndex = FindMetaDataEntryIndexForKey(Key);
if(EntryIndex != INDEX_NONE)
{
MetaDataArray[EntryIndex].DataValue = MoveTemp(Value);
}
else
{
MetaDataArray.Emplace( FBPVariableMetaDataEntry(Key, MoveTemp(Value)) );
}
}
void FBPVariableDescription::RemoveMetaData(const FName Key)
{
int32 EntryIndex = FindMetaDataEntryIndexForKey(Key);
if(EntryIndex != INDEX_NONE)
{
MetaDataArray.RemoveAtSwap(EntryIndex);
}
}
//////////////////////////////////////////////////////////////////////////
// UBlueprintCore
#if WITH_EDITORONLY_DATA
namespace
{
void GatherBlueprintForLocalization(const UObject* const Object, FPropertyLocalizationDataGatherer& PropertyLocalizationDataGatherer, const EPropertyLocalizationGathererTextFlags GatherTextFlags)
{
const UBlueprintCore* const BlueprintCore = CastChecked<UBlueprintCore>(Object);
// Blueprint assets never exist at runtime, so treat all of their properties as editor-only, but allow their script (which is available at runtime) to be gathered by a game
EPropertyLocalizationGathererTextFlags BlueprintGatherFlags = GatherTextFlags | EPropertyLocalizationGathererTextFlags::ForceEditorOnlyProperties;
#if WITH_EDITOR
if (const UBlueprint* const Blueprint = Cast<UBlueprint>(Object))
{
// Force non-data-only blueprints to set the HasScript flag, as they may not currently have bytecode due to a compilation error
bool bForceHasScript = !FBlueprintEditorUtils::IsDataOnlyBlueprint(Blueprint);
if (!bForceHasScript)
{
// Also do this for blueprints that derive from something containing text properties, as these may propagate default values from their parent class on load
if (UClass* BlueprintParentClass = Blueprint->ParentClass.Get())
{
TArray<UStruct*> TypesToCheck;
TypesToCheck.Add(BlueprintParentClass);
TSet<UStruct*> TypesChecked;
while (!bForceHasScript && TypesToCheck.Num() > 0)
{
UStruct* TypeToCheck = TypesToCheck.Pop(/*bAllowShrinking*/false);
TypesChecked.Add(TypeToCheck);
for (TFieldIterator<const FProperty> PropIt(TypeToCheck, EFieldIteratorFlags::IncludeSuper, EFieldIteratorFlags::ExcludeDeprecated, EFieldIteratorFlags::IncludeInterfaces); !bForceHasScript && PropIt; ++PropIt)
{
auto ProcessInnerProperty = [&bForceHasScript, &TypesToCheck, &TypesChecked](const FProperty* InProp) -> bool
{
if (const FTextProperty* TextProp = CastField<const FTextProperty>(InProp))
{
bForceHasScript = true;
return true;
}
if (const FStructProperty* StructProp = CastField<const FStructProperty>(InProp))
{
if (!TypesChecked.Contains(StructProp->Struct))
{
TypesToCheck.Add(StructProp->Struct);
}
return true;
}
return false;
};
if (!ProcessInnerProperty(*PropIt))
{
if (const FArrayProperty* ArrayProp = CastField<const FArrayProperty>(*PropIt))
{
ProcessInnerProperty(ArrayProp->Inner);
}
if (const FMapProperty* MapProp = CastField<const FMapProperty>(*PropIt))
{
ProcessInnerProperty(MapProp->KeyProp);
ProcessInnerProperty(MapProp->ValueProp);
}
if (const FSetProperty* SetProp = CastField<const FSetProperty>(*PropIt))
{
ProcessInnerProperty(SetProp->ElementProp);
}
}
}
}
}
}
if (bForceHasScript)
{
BlueprintGatherFlags |= EPropertyLocalizationGathererTextFlags::ForceHasScript;
}
}
#endif
PropertyLocalizationDataGatherer.GatherLocalizationDataFromObject(BlueprintCore, BlueprintGatherFlags);
}
}
#endif
UBlueprintCore::UBlueprintCore(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
#if WITH_EDITORONLY_DATA
{ static const FAutoRegisterLocalizationDataGatheringCallback AutomaticRegistrationOfLocalizationGatherer(UBlueprintCore::StaticClass(), &GatherBlueprintForLocalization); }
#endif
bLegacyNeedToPurgeSkelRefs = true;
}
void UBlueprintCore::Serialize(FArchive& Ar)
{
Super::Serialize(Ar);
#if WITH_EDITOR
Ar.UsingCustomVersion(FBlueprintsObjectVersion::GUID);
Ar.UsingCustomVersion(FFrameworkObjectVersion::GUID);
if (Ar.IsLoading() && Ar.CustomVer(FFrameworkObjectVersion::GUID) < FFrameworkObjectVersion::BlueprintGeneratedClassIsAlwaysAuthoritative)
{
// No longer in use.
bool bLegacyGeneratedClassIsAuthoritative;
Ar << bLegacyGeneratedClassIsAuthoritative;
}
#endif
if ((Ar.UEVer() < VER_UE4_BLUEPRINT_SKEL_CLASS_TRANSIENT_AGAIN)
&& (Ar.UEVer() != VER_UE4_BLUEPRINT_SKEL_TEMPORARY_TRANSIENT))
{
Ar << SkeletonGeneratedClass;
if( SkeletonGeneratedClass )
{
// If we serialized in a skeleton class, make sure it and all its children are updated to be transient
SkeletonGeneratedClass->SetFlags(RF_Transient);
TArray<UObject*> SubObjs;
GetObjectsWithOuter(SkeletonGeneratedClass, SubObjs, true);
for(auto SubObjIt = SubObjs.CreateIterator(); SubObjIt; ++SubObjIt)
{
(*SubObjIt)->SetFlags(RF_Transient);
}
}
// We only want to serialize in the GeneratedClass if the SkeletonClass didn't trigger a recompile
bool bSerializeGeneratedClass = true;
if (UBlueprint* BP = Cast<UBlueprint>(this))
{
bSerializeGeneratedClass = !Ar.IsLoading() || !BP->bHasBeenRegenerated;
}
if (bSerializeGeneratedClass)
{
Ar << GeneratedClass;
}
else if (Ar.IsLoading())
{
UClass* DummyClass = NULL;
Ar << DummyClass;
}
}
if( Ar.IsLoading() && !BlueprintGuid.IsValid() )
{
GenerateDeterministicGuid();
}
}
void UBlueprintCore::GenerateDeterministicGuid()
{
FString HashString = GetPathName();
HashString.Shrink();
ensure( HashString.Len() );
uint32 HashBuffer[ 5 ];
uint32 BufferLength = HashString.Len() * sizeof( HashString[0] );
FSHA1::HashBuffer(*HashString, BufferLength, reinterpret_cast< uint8* >( HashBuffer ));
BlueprintGuid.A = HashBuffer[ 1 ];
BlueprintGuid.B = HashBuffer[ 2 ];
BlueprintGuid.C = HashBuffer[ 3 ];
BlueprintGuid.D = HashBuffer[ 4 ];
}
UBlueprint::UBlueprint(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
#if WITH_EDITOR
, bRunConstructionScriptOnDrag(true)
, bRunConstructionScriptInSequencer(false)
, bGenerateConstClass(false)
#endif
#if WITH_EDITORONLY_DATA
, bDuplicatingReadOnly(false)
, bCachedDependenciesUpToDate(false)
// @todo: BP2CPP_remove
PRAGMA_DISABLE_DEPRECATION_WARNINGS
, bHasAnyNonReducibleFunction(EIsBPNonReducible::Unkown)
PRAGMA_ENABLE_DEPRECATION_WARNINGS
#endif
{
}
#if WITH_EDITORONLY_DATA
static TAutoConsoleVariable<bool> CVarBPDisableSearchDataUpdateOnSave(
TEXT("bp.DisableSearchDataUpdateOnSave"),
false,
TEXT("Don't update Blueprint search metadata on save (for QA/testing purposes only). On an editor relaunch, it should include the BP in the unindexed count after the first search."),
ECVF_Cheat);
static TAutoConsoleVariable<bool> CVarBPForceOldSearchDataFormatVersionOnSave(
TEXT("bp.ForceOldSearchDataFormatVersionOnSave"),
false,
TEXT("Force Blueprint search metadata to use an old format version on save (for QA/testing purposes only). On an editor relaunch, it should include the BP in the out-of-date count after the first search."),
ECVF_Cheat);
void UBlueprint::PreSave(const class ITargetPlatform* TargetPlatform)
{
PRAGMA_DISABLE_DEPRECATION_WARNINGS;
Super::PreSave(TargetPlatform);
PRAGMA_ENABLE_DEPRECATION_WARNINGS;
}
void UBlueprint::PreSave(FObjectPreSaveContext ObjectSaveContext)
{
Super::PreSave(ObjectSaveContext);
// Clear all upgrade notes, the user has saved and should not see them anymore
UpgradeNotesLog.Reset();
const ITargetPlatform* TargetPlatform = ObjectSaveContext.GetTargetPlatform();
if (!TargetPlatform || TargetPlatform->HasEditorOnlyData())
{
// This will force an immediate (synchronous) update of this Blueprint's index tag value.
EAddOrUpdateBlueprintSearchMetadataFlags Flags = EAddOrUpdateBlueprintSearchMetadataFlags::ForceRecache;
// For regression testing, we exclude the registry tag on save by clearing the cached value.
// Expected result: On an editor relaunch it should cause this BP to be reported as "unindexed," until the asset is loaded.
if (CVarBPDisableSearchDataUpdateOnSave.GetValueOnGameThread())
{
Flags |= EAddOrUpdateBlueprintSearchMetadataFlags::ClearCachedValue;
}
// For regression testing, we allow an old format version to be used as an override on save.
// Expected result: On an editor relaunch it should cause this BP to be reported as "out-of-date," until the asset is loaded.
EFiBVersion OverrideVersion = EFiBVersion::FIB_VER_NONE;
if (CVarBPForceOldSearchDataFormatVersionOnSave.GetValueOnGameThread())
{
OverrideVersion = EFiBVersion::FIB_VER_BASE;
}
// Cache the BP for use (immediate, since we're about to save)
FFindInBlueprintSearchManager::Get().AddOrUpdateBlueprintSearchMetadata(this, Flags, OverrideVersion);
}
}
void UBlueprint::GetPreloadDependencies(TArray<UObject*>& OutDeps)
{
Super::GetPreloadDependencies(OutDeps);
for (UClass* ClassIt = ParentClass; (ClassIt != NULL) && !(ClassIt->HasAnyClassFlags(CLASS_Native)); ClassIt = ClassIt->GetSuperClass())
{
if (ClassIt->ClassGeneratedBy)
{
OutDeps.Add(ClassIt->ClassGeneratedBy);
}
}
}
#endif // WITH_EDITORONLY_DATA
void UBlueprint::Serialize(FArchive& Ar)
{
Super::Serialize(Ar);
#if WITH_EDITORONLY_DATA
Ar.UsingCustomVersion(FUE5ReleaseStreamObjectVersion::GUID);
if(Ar.IsLoading() && Ar.UEVer() < VER_UE4_BLUEPRINT_VARS_NOT_READ_ONLY)
{
// Allow all blueprint defined vars to be read/write. undoes previous convention of making exposed variables read-only
for (int32 i = 0; i < NewVariables.Num(); ++i)
{
FBPVariableDescription& Variable = NewVariables[i];
Variable.PropertyFlags &= ~CPF_BlueprintReadOnly;
}
}
if (Ar.UEVer() < VER_UE4_K2NODE_REFERENCEGUIDS)
{
for (int32 Index = 0; Index < NewVariables.Num(); ++Index)
{
NewVariables[Index].VarGuid = FGuid::NewGuid();
}
}
// Preload our parent blueprints
if (Ar.IsLoading())
{
for (UClass* ClassIt = ParentClass; ClassIt && !ClassIt->HasAnyClassFlags(CLASS_Native); ClassIt = ClassIt->GetSuperClass())
{
// In some cases, a non-native parent class may not have an associated Blueprint asset - we consider that to be ok here since we're just preloading.
if (ClassIt->ClassGeneratedBy && ClassIt->ClassGeneratedBy->HasAnyFlags(RF_NeedLoad))
{
ClassIt->ClassGeneratedBy->GetLinker()->Preload(ClassIt->ClassGeneratedBy);
}
}
}
for (int32 i = 0; i < NewVariables.Num(); ++i)
{
FBPVariableDescription& Variable = NewVariables[i];
// Actor variables can't have default values (because Blueprint templates are library elements that can
// bridge multiple levels and different levels might not have the actor that the default is referencing).
if (Ar.UEVer() < VER_UE4_FIX_BLUEPRINT_VARIABLE_FLAGS)
{
const FEdGraphPinType& VarType = Variable.VarType;
bool bDisableEditOnTemplate = false;
if (VarType.PinSubCategoryObject.IsValid()) // ignore variables that don't have associated objects
{
const UClass* ClassObject = Cast<UClass>(VarType.PinSubCategoryObject.Get());
// if the object type is an actor...
if ((ClassObject != NULL) && ClassObject->IsChildOf(AActor::StaticClass()))
{
// hide the default value field
bDisableEditOnTemplate = true;
}
}
if (bDisableEditOnTemplate)
{
Variable.PropertyFlags |= CPF_DisableEditOnTemplate;
}
else
{
Variable.PropertyFlags &= ~CPF_DisableEditOnTemplate;
}
}
if (Ar.IsLoading())
{
// Validate metadata keys/values on load only
FBlueprintEditorUtils::FixupVariableDescription(this, Variable);
}
}
#endif // WITH_EDITORONLY_DATA
}
#if WITH_EDITOR
bool UBlueprint::RenameGeneratedClasses( const TCHAR* InName, UObject* NewOuter, ERenameFlags Flags )
{
const bool bRenameGeneratedClasses = !(Flags & REN_SkipGeneratedClasses );
if(bRenameGeneratedClasses)
{
const auto TryFreeCDOName = [](UClass* ForClass, UObject* ToOuter, ERenameFlags InFlags)
{
if(ForClass->ClassDefaultObject)
{
FName CDOName = ForClass->GetDefaultObjectName();
if(UObject* Obj = StaticFindObjectFast(UObject::StaticClass(), ToOuter, CDOName))
{
FName NewName = MakeUniqueObjectName(ToOuter, Obj->GetClass(), CDOName);
Obj->Rename(*(NewName.ToString()), ToOuter, InFlags|REN_ForceNoResetLoaders|REN_DontCreateRedirectors);
}
}
};
FName SkelClassName, GenClassName;
GetBlueprintClassNames(GenClassName, SkelClassName, FName(InName));
UPackage* NewTopLevelObjectOuter = NewOuter ? NewOuter->GetOutermost() : NULL;
if (GeneratedClass != NULL)
{
// check for collision of CDO name, move aside if necessary:
TryFreeCDOName(GeneratedClass, NewTopLevelObjectOuter, Flags);
bool bMovedOK = GeneratedClass->Rename(*GenClassName.ToString(), NewTopLevelObjectOuter, Flags);
if (!bMovedOK)
{
return false;
}
}
// Also move skeleton class, if different from generated class, to new package (again, to create redirector)
if (SkeletonGeneratedClass != NULL && SkeletonGeneratedClass != GeneratedClass)
{
TryFreeCDOName(SkeletonGeneratedClass, NewTopLevelObjectOuter, Flags);
bool bMovedOK = SkeletonGeneratedClass->Rename(*SkelClassName.ToString(), NewTopLevelObjectOuter, Flags);
if (!bMovedOK)
{
return false;
}
}
}
return true;
}
bool UBlueprint::Rename( const TCHAR* InName, UObject* NewOuter, ERenameFlags Flags )
{
const FName OldName = GetFName();
TArray<UObject*> LastEditedDocumentsObjects;
if (!(Flags & REN_Test))
{
LastEditedDocumentsObjects.Reserve(LastEditedDocuments.Num());
for (const FEditedDocumentInfo& LastEditedDocument : LastEditedDocuments)
{
LastEditedDocumentsObjects.Add(LastEditedDocument.EditedObjectPath.ResolveObject());
}
}
// Move generated class/CDO to the new package, to create redirectors
if ( !RenameGeneratedClasses(InName, NewOuter, Flags) )
{
return false;
}
if (Super::Rename( InName, NewOuter, Flags ))
{
if (!(Flags & REN_Test))
{
for (int32 i=0; i<LastEditedDocuments.Num(); i++)
{
if (LastEditedDocumentsObjects[i])
{
LastEditedDocuments[i].EditedObjectPath = FSoftObjectPath(LastEditedDocumentsObjects[i]);
}
}
}
return true;
}
return false;
}
void UBlueprint::PostDuplicate(bool bDuplicateForPIE)
{
Super::PostDuplicate(bDuplicateForPIE);
if( !bDuplicatingReadOnly )
{
FBlueprintEditorUtils::PostDuplicateBlueprint(this, bDuplicateForPIE);
}
if (GeneratedClass)
{
GeneratedClass->GetDefaultObject()->PostDuplicate(bDuplicateForPIE);
}
}
UClass* UBlueprint::RegenerateClass(UClass* ClassToRegenerate, UObject* PreviousCDO)
{
LoadModulesRequiredForCompilation();
// ensure that we have UProperties for any properties declared in the blueprint:
if(!GeneratedClass || !HasAnyFlags(RF_BeingRegenerated) || bIsRegeneratingOnLoad || bHasBeenRegenerated)
{
return GeneratedClass;
}
// tag ourself as bIsRegeneratingOnLoad so that any reentrance via ForceLoad calls doesn't recurse:
bIsRegeneratingOnLoad = true;
UPackage* Package = GetOutermost();
bool bIsPackageDirty = Package ? Package->IsDirty() : false;
UClass* GeneratedClassResolved = GeneratedClass;
UBlueprint::ForceLoadMetaData(this);
if (ensure(GeneratedClassResolved->ClassDefaultObject ))
{
UBlueprint::ForceLoadMembers(GeneratedClassResolved);
UBlueprint::ForceLoadMembers(GeneratedClassResolved->ClassDefaultObject);
}
UBlueprint::ForceLoadMembers(this);
PRAGMA_DISABLE_DEPRECATION_WARNINGS
for (auto It = Extensions.CreateIterator(); It; ++It)
{
if (!*It)
{
It.RemoveCurrent();
}
}
for (UBlueprintExtension* Extension : Extensions)
{
ForceLoad(Extension);
Extension->PreloadObjectsForCompilation(this);
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
FBlueprintEditorUtils::PreloadConstructionScript( this );
FBlueprintEditorUtils::LinkExternalDependencies( this );
FBlueprintEditorUtils::RefreshVariables(this);
// Preload Overridden Components
if (InheritableComponentHandler)
{
InheritableComponentHandler->PreloadAll();
}
FBlueprintCompilationManager::NotifyBlueprintLoaded( this );
FBlueprintEditorUtils::PreloadBlueprintSpecificData( this );
FBlueprintEditorUtils::UpdateOutOfDateAnimBlueprints(this);
// clear this now that we're not in a re-entrrant context - bHasBeenRegenerated will guard against 'real'
// double regeneration calls:
bIsRegeneratingOnLoad = false;
if( Package )
{
Package->SetDirtyFlag(bIsPackageDirty);
}
return GeneratedClassResolved;
}
void UBlueprint::RemoveChildRedirectors()
{
TArray<UObject*> ChildObjects;
GetObjectsWithOuter(this, ChildObjects);
for (UObject* ChildObject : ChildObjects)
{
if (ChildObject->IsA<UObjectRedirector>())
{
ChildObject->ClearFlags(RF_Public|RF_Standalone);
ChildObject->SetFlags(RF_Transient);
ChildObject->RemoveFromRoot();
}
}
}
void UBlueprint::RemoveGeneratedClasses()
{
FBlueprintEditorUtils::RemoveGeneratedClasses(this);
}
void UBlueprint::PostLoad()
{
Super::PostLoad();
// Can't use TGuardValue here as bIsRegeneratingOnLoad is a bitfield
struct FScopedRegeneratingOnLoad
{
UBlueprint& Blueprint;
bool bPreviousValue;
FScopedRegeneratingOnLoad(UBlueprint& InBlueprint)
: Blueprint(InBlueprint)
, bPreviousValue(InBlueprint.bIsRegeneratingOnLoad)
{
// if the blueprint's package is still in the midst of loading, then
// bIsRegeneratingOnLoad needs to be set to prevent UObject renames
// from resetting loaders
Blueprint.bIsRegeneratingOnLoad = true;
if (UPackage* Package = Blueprint.GetOutermost())
{
// checking (Package->LinkerLoad != nullptr) ensures this
// doesn't get set when duplicating blueprints (which also calls
// PostLoad), and checking RF_WasLoaded makes sure we only
// forcefully set bIsRegeneratingOnLoad for blueprints that need
// it (ones still actively loading)
Blueprint.bIsRegeneratingOnLoad = bPreviousValue || ((Package->GetLinker() != nullptr) && !Package->HasAnyFlags(RF_WasLoaded));
}
}
~FScopedRegeneratingOnLoad()
{
Blueprint.bIsRegeneratingOnLoad = bPreviousValue;
}
} GuardIsRegeneratingOnLoad(*this);
// Mark the blueprint as in error if there has been a major version bump
if (BlueprintSystemVersion < UBlueprint::GetCurrentBlueprintSystemVersion())
{
Status = BS_Error;
}
// Purge any NULL graphs
FBlueprintEditorUtils::PurgeNullGraphs(this);
#if WITH_EDITOR
// Restore breakpoints for this Blueprint
FKismetDebugUtilities::RestoreBreakpointsOnLoad(this);
# endif
Breakpoints_DEPRECATED.Empty();
// Make sure we have an SCS and ensure it's transactional
if( FBlueprintEditorUtils::SupportsConstructionScript(this) )
{
if(SimpleConstructionScript == nullptr)
{
check(nullptr != GeneratedClass);
SimpleConstructionScript = NewObject<USimpleConstructionScript>(GeneratedClass);
SimpleConstructionScript->SetFlags(RF_Transactional);
UBlueprintGeneratedClass* BPGClass = Cast<UBlueprintGeneratedClass>(*GeneratedClass);
if(BPGClass)
{
BPGClass->SimpleConstructionScript = SimpleConstructionScript;
}
}
else
{
if (!SimpleConstructionScript->HasAnyFlags(RF_Transactional))
{
SimpleConstructionScript->SetFlags(RF_Transactional);
}
}
}
// Make sure the CDO's scene root component is valid
FBlueprintEditorUtils::UpdateRootComponentReference(this);
// Make sure all the components are used by this blueprint
FBlueprintEditorUtils::UpdateComponentTemplates(this);
// Make sure that all of the parent function calls are valid
FBlueprintEditorUtils::ConformCallsToParentFunctions(this);
// Make sure that all of the events this BP implements are valid
FBlueprintEditorUtils::ConformImplementedEvents(this);
// Make sure that all of the interfaces this BP implements have all required graphs
FBlueprintEditorUtils::ConformImplementedInterfaces(this);
// Make sure that there are no function graphs that are marked as bAllowDeletion=false
// (possible if a blueprint was reparented prior to 4.11).
if (GetLinkerCustomVersion(FBlueprintsObjectVersion::GUID) < FBlueprintsObjectVersion::AllowDeletionConformed)
{
FBlueprintEditorUtils::ConformAllowDeletionFlag(this);
}
#if WITH_EDITORONLY_DATA
// Ensure all the pin watches we have point to something useful
FBlueprintEditorUtils::UpdateStalePinWatches(this);
#endif // WITH_EDITORONLY_DATA
FStructureEditorUtils::RemoveInvalidStructureMemberVariableFromBlueprint(this);
#if WITH_EDITOR
// Do not want to run this code without the editor present nor when running commandlets.
if(GEditor && GIsEditor && !IsRunningCommandlet())
{
// Gathers Find-in-Blueprint data, makes sure that it is fresh and ready, especially if the asset did not have any available.
FFindInBlueprintSearchManager::Get().AddOrUpdateBlueprintSearchMetadata(this);
}
#endif
}
#if WITH_EDITORONLY_DATA
void UBlueprint::DeclareConstructClasses(TArray<FTopLevelAssetPath>& OutConstructClasses, const UClass* SpecificSubclass)
{
Super::DeclareConstructClasses(OutConstructClasses, SpecificSubclass);
OutConstructClasses.Add(FTopLevelAssetPath(UObjectRedirector::StaticClass()));
}
#endif
void UBlueprint::DebuggingWorldRegistrationHelper(UObject* ObjectProvidingWorld, UObject* ValueToRegister)
{
if (ObjectProvidingWorld != NULL)
{
// Fix up the registration with the world
UWorld* ObjWorld = NULL;
UObject* ObjOuter = ObjectProvidingWorld->GetOuter();
while (ObjOuter != NULL)
{
ObjWorld = Cast<UWorld>(ObjOuter);
if (ObjWorld != NULL)
{
break;
}
ObjOuter = ObjOuter->GetOuter();
}
// if we can't find the world on the outer chain, fallback to the GetWorld method
if (ObjWorld == NULL)
{
ObjWorld = ObjectProvidingWorld->GetWorld();
}
if (ObjWorld != NULL)
{
if( !ObjWorld->HasAnyFlags(RF_BeginDestroyed))
{
ObjWorld->NotifyOfBlueprintDebuggingAssociation(this, ValueToRegister);
}
OnSetObjectBeingDebuggedDelegate.Broadcast(ValueToRegister);
}
}
}
UClass* UBlueprint::GetBlueprintClass() const
{
return UBlueprintGeneratedClass::StaticClass();
}
void UBlueprint::SetObjectBeingDebugged(UObject* NewObject)
{
// Unregister the old object (even if PendingKill)
if (UObject* OldObject = CurrentObjectBeingDebugged.Get(true))
{
if (OldObject == NewObject)
{
// Nothing changed
return;
}
DebuggingWorldRegistrationHelper(OldObject, nullptr);
}
// Note that we allow macro Blueprints to bypass this check
if ((NewObject != nullptr) && !GCompilingBlueprint && BlueprintType != BPTYPE_MacroLibrary)
{
// You can only debug instances of this!
if (!ensureMsgf(
NewObject->IsA(this->GeneratedClass),
TEXT("Type mismatch: Expected %s, Found %s"),
this->GeneratedClass ? *(this->GeneratedClass->GetName()) : TEXT("NULL"),
NewObject->GetClass() ? *(NewObject->GetClass()->GetName()) : TEXT("NULL")))
{
NewObject = nullptr;
}
}
// Update the current object being debugged
CurrentObjectBeingDebugged = NewObject;
// Register the new object
if (NewObject != nullptr)
{
ObjectPathToDebug = NewObject->GetPathName();
DebuggingWorldRegistrationHelper(NewObject, NewObject);
}
else
{
ObjectPathToDebug = FString();
}
}
void UBlueprint::UnregisterObjectBeingDebugged()
{
// This is implemented as a set to null and restore of ObjectPathToDebug, so subclasses have their overrides called properly
FString LastPath = ObjectPathToDebug;
SetObjectBeingDebugged(nullptr);
ObjectPathToDebug = LastPath;
}
void UBlueprint::SetWorldBeingDebugged(UWorld *NewWorld)
{
CurrentWorldBeingDebugged = NewWorld;
}
void UBlueprint::GetReparentingRules(TSet< const UClass* >& AllowedChildrenOfClasses, TSet< const UClass* >& DisallowedChildrenOfClasses) const
{
}
bool UBlueprint::CanAlwaysRecompileWhilePlayingInEditor() const
{
return false;
}
void UBlueprint::GetAssetRegistryTags(TArray<FAssetRegistryTag>& OutTags) const
{
// We use Generated instead of Skeleton because the CDO data is more accurate on Generated
UObject* BlueprintCDO = nullptr;
if (GeneratedClass)
{
BlueprintCDO = GeneratedClass->GetDefaultObject(/*bCreateIfNeeded*/false);
if (BlueprintCDO)
{
BlueprintCDO->GetAssetRegistryTags(OutTags);
}
}
Super::GetAssetRegistryTags(OutTags);
// Output native parent class and generated class as if they were AssetRegistrySearchable
FString GeneratedClassVal;
if (GeneratedClass)
{
GeneratedClassVal = FObjectPropertyBase::GetExportPath(GeneratedClass);
}
else
{
GeneratedClassVal = TEXT("None");
}
FString NativeParentClassName, ParentClassName;
if ( ParentClass )
{
ParentClassName = FObjectPropertyBase::GetExportPath(ParentClass);
// Walk up until we find a native class (ie 'while they are BP classes')
UClass* NativeParentClass = ParentClass;
while (Cast<UBlueprintGeneratedClass>(NativeParentClass) != nullptr) // can't use IsA on UClass
{
NativeParentClass = NativeParentClass->GetSuperClass();
}
NativeParentClassName = FObjectPropertyBase::GetExportPath(NativeParentClass);
}
else
{
NativeParentClassName = ParentClassName = TEXT("None");
}
OutTags.Add(FAssetRegistryTag(FBlueprintTags::BlueprintPathWithinPackage, GetPathName(GetOutermost()), FAssetRegistryTag::TT_Hidden));
OutTags.Add(FAssetRegistryTag(FBlueprintTags::GeneratedClassPath, GeneratedClassVal, FAssetRegistryTag::TT_Hidden));
OutTags.Add(FAssetRegistryTag(FBlueprintTags::ParentClassPath, ParentClassName, FAssetRegistryTag::TT_Alphabetical));
OutTags.Add(FAssetRegistryTag(FBlueprintTags::NativeParentClassPath, NativeParentClassName, FAssetRegistryTag::TT_Alphabetical));
// BlueprintGeneratedClass is not automatically traversed so we have to manually add NumReplicatedProperties
int32 NumReplicatedProperties = 0;
UBlueprintGeneratedClass* BlueprintClass = Cast<UBlueprintGeneratedClass>(SkeletonGeneratedClass);
if (BlueprintClass)
{
NumReplicatedProperties = BlueprintClass->NumReplicatedProperties;
}
OutTags.Add(FAssetRegistryTag(FBlueprintTags::NumReplicatedProperties, FString::FromInt(NumReplicatedProperties), FAssetRegistryTag::TT_Numerical));
OutTags.Add(FAssetRegistryTag(FBlueprintTags::BlueprintDescription, BlueprintDescription, FAssetRegistryTag::TT_Hidden));
OutTags.Add(FAssetRegistryTag(FBlueprintTags::BlueprintCategory, BlueprintCategory, FAssetRegistryTag::TT_Hidden));
OutTags.Add(FAssetRegistryTag(FBlueprintTags::BlueprintDisplayName, BlueprintDisplayName, FAssetRegistryTag::TT_Hidden));
uint32 ClassFlagsTagged = 0;
if (BlueprintClass)
{
ClassFlagsTagged = BlueprintClass->GetClassFlags();
}
else
{
ClassFlagsTagged = GetClass()->GetClassFlags();
}
OutTags.Add( FAssetRegistryTag(FBlueprintTags::ClassFlags, FString::FromInt(ClassFlagsTagged), FAssetRegistryTag::TT_Hidden) );
OutTags.Add( FAssetRegistryTag(FBlueprintTags::IsDataOnly,
FBlueprintEditorUtils::IsDataOnlyBlueprint(this) ? TEXT("True") : TEXT("False"),
FAssetRegistryTag::TT_Alphabetical ) );
// Only add the FiB tags in the editor, this now gets run for standalone uncooked games
if ( ParentClass && GIsEditor && !GetOutermost()->HasAnyPackageFlags(PKG_ForDiffing) && !IsRunningCookCommandlet())
{
FString Value;
const bool bRebuildSearchData = false;
FSearchData SearchData = FFindInBlueprintSearchManager::Get().QuerySingleBlueprint((UBlueprint*)this, bRebuildSearchData);
if (SearchData.IsValid())
{
Value = SearchData.Value;
}
OutTags.Add( FAssetRegistryTag(FBlueprintTags::FindInBlueprintsData, Value, FAssetRegistryTag::TT_Hidden) );
}
// Only show for strict blueprints (not animation or widget blueprints)
// Note: Can't be an Actor specific check on the gen class, as CB only queries the CDO for the majority type to determine which columns are shown in the view
if (ExactCast<UBlueprint>(this) != nullptr)
{
// Determine how many inherited native components exist
int32 NumNativeComponents = 0;
if (BlueprintCDO != nullptr)
{
TArray<UObject*> PotentialComponents;
BlueprintCDO->GetDefaultSubobjects(/*out*/ PotentialComponents);
for (UObject* TestSubObject : PotentialComponents)
{
if (Cast<UActorComponent>(TestSubObject) != nullptr)
{
++NumNativeComponents;
}
}
}
OutTags.Add(FAssetRegistryTag(FBlueprintTags::NumNativeComponents, FString::FromInt(NumNativeComponents), UObject::FAssetRegistryTag::TT_Numerical));
// Determine how many components are added via a SimpleConstructionScript (both newly introduced and inherited from parent BPs)
int32 NumAddedComponents = 0;
for (UBlueprintGeneratedClass* TestBPClass = BlueprintClass; TestBPClass != nullptr; TestBPClass = Cast<UBlueprintGeneratedClass>(TestBPClass->GetSuperClass()))
{
const UBlueprint* AssociatedBP = Cast<const UBlueprint>(TestBPClass->ClassGeneratedBy);
if (AssociatedBP && AssociatedBP->SimpleConstructionScript != nullptr)
{
NumAddedComponents += AssociatedBP->SimpleConstructionScript->GetAllNodes().Num();
}
}
OutTags.Add(FAssetRegistryTag(FBlueprintTags::NumBlueprintComponents, FString::FromInt(NumAddedComponents), UObject::FAssetRegistryTag::TT_Numerical));
}
}
#if WITH_EDITOR
void UBlueprint::GetExtendedAssetRegistryTagsForSave(const ITargetPlatform* TargetPlatform, TArray<FAssetRegistryTag>& OutTags) const
{
Super::GetExtendedAssetRegistryTagsForSave(TargetPlatform, OutTags);
if ( ParentClass && GIsEditor && !GetOutermost()->HasAnyPackageFlags(PKG_ForDiffing) && IsRunningCookCommandlet())
{
if (!TargetPlatform || TargetPlatform->HasEditorOnlyData())
{
FString Value;
const bool bRebuildSearchData = false;
FSearchData SearchData = FFindInBlueprintSearchManager::Get().QuerySingleBlueprint((UBlueprint*)this, bRebuildSearchData);
if (SearchData.IsValid())
{
Value = SearchData.Value;
}
OutTags.Add( FAssetRegistryTag(FBlueprintTags::FindInBlueprintsData, Value, FAssetRegistryTag::TT_Hidden) );
}
}
if (AActor* BlueprintCDO = GeneratedClass ? Cast<AActor>(GeneratedClass->ClassDefaultObject) : nullptr)
{
FWorldPartitionActorDescUtils::AppendAssetDataTagsFromActor(BlueprintCDO, OutTags);
}
}
#endif
void UBlueprint::PostLoadAssetRegistryTags(const FAssetData& InAssetData, TArray<FAssetRegistryTag>& OutTagsAndValuesToUpdate) const
{
Super::PostLoadAssetRegistryTags(InAssetData, OutTagsAndValuesToUpdate);
PostLoadBlueprintAssetRegistryTags(InAssetData, OutTagsAndValuesToUpdate);
}
void UBlueprint::PostLoadBlueprintAssetRegistryTags(const FAssetData& InAssetData, TArray<FAssetRegistryTag>& OutTagsAndValuesToUpdate)
{
auto FixTagValueShortClassName = [&InAssetData, &OutTagsAndValuesToUpdate](FName TagName, FAssetRegistryTag::ETagType TagType)
{
FString TagValue = InAssetData.GetTagValueRef<FString>(TagName);
if (!TagValue.IsEmpty() && TagValue != TEXT("None"))
{
if (UClass::TryFixShortClassNameExportPath(TagValue, ELogVerbosity::Warning, TEXT("UBlueprint::PostLoadAssetRegistryTags")))
{
OutTagsAndValuesToUpdate.Add(FAssetRegistryTag(TagName, TagValue, TagType));
}
}
};
FixTagValueShortClassName(FBlueprintTags::GeneratedClassPath, FAssetRegistryTag::TT_Hidden);
FixTagValueShortClassName(FBlueprintTags::ParentClassPath, FAssetRegistryTag::TT_Alphabetical);
FixTagValueShortClassName(FBlueprintTags::NativeParentClassPath, FAssetRegistryTag::TT_Alphabetical);
}
FPrimaryAssetId UBlueprint::GetPrimaryAssetId() const
{
// Forward to our Class, which will forward to CDO if needed
// We use Generated instead of Skeleton because the CDO data is more accurate on Generated
if (GeneratedClass && GeneratedClass->ClassDefaultObject)
{
return GeneratedClass->GetPrimaryAssetId();
}
return FPrimaryAssetId();
}
FString UBlueprint::GetFriendlyName() const
{
return GetName();
}
bool UBlueprint::AllowsDynamicBinding() const
{
return FBlueprintEditorUtils::IsActorBased(this);
}
bool UBlueprint::SupportsInputEvents() const
{
return ParentClass && ParentClass->IsChildOf(UObject::StaticClass());
}
struct FBlueprintInnerHelper
{
template<typename TOBJ, typename TARR>
static TOBJ* FindObjectByName(TARR& Array, const FName& TimelineName)
{
for(int32 i=0; i<Array.Num(); i++)
{
TOBJ* Obj = Array[i];
if((NULL != Obj) && (Obj->GetFName() == TimelineName))
{
return Obj;
}
}
return NULL;
}
};
UActorComponent* UBlueprint::FindTemplateByName(const FName& TemplateName) const
{
return FBlueprintInnerHelper::FindObjectByName<UActorComponent>(ComponentTemplates, TemplateName);
}
const UTimelineTemplate* UBlueprint::FindTimelineTemplateByVariableName(const FName& TimelineName) const
{
const FName TimelineTemplateName = *UTimelineTemplate::TimelineVariableNameToTemplateName(TimelineName);
const UTimelineTemplate* Timeline = FBlueprintInnerHelper::FindObjectByName<const UTimelineTemplate>(Timelines, TimelineTemplateName);
// >>> Backwards Compatibility: VER_UE4_EDITORONLY_BLUEPRINTS
if(Timeline)
{
ensure(Timeline->GetOuter() && Timeline->GetOuter()->IsA(UClass::StaticClass()));
}
else
{
Timeline = FBlueprintInnerHelper::FindObjectByName<const UTimelineTemplate>(Timelines, TimelineName);
if(Timeline)
{
ensure(Timeline->GetOuter() && Timeline->GetOuter() == this);
}
}
// <<< End Backwards Compatibility
return Timeline;
}
UTimelineTemplate* UBlueprint::FindTimelineTemplateByVariableName(const FName& TimelineName)
{
const FName TimelineTemplateName = *UTimelineTemplate::TimelineVariableNameToTemplateName(TimelineName);
UTimelineTemplate* Timeline = FBlueprintInnerHelper::FindObjectByName<UTimelineTemplate>(Timelines, TimelineTemplateName);
// >>> Backwards Compatibility: VER_UE4_EDITORONLY_BLUEPRINTS
if(Timeline)
{
ensure(Timeline->GetOuter() && Timeline->GetOuter()->IsA(UClass::StaticClass()));
}
else
{
Timeline = FBlueprintInnerHelper::FindObjectByName<UTimelineTemplate>(Timelines, TimelineName);
if(Timeline)
{
ensure(Timeline->GetOuter() && Timeline->GetOuter() == this);
}
}
// <<< End Backwards Compatibility
return Timeline;
}
bool UBlueprint::ForceLoad(UObject* Obj)
{
FLinkerLoad* Linker = Obj->GetLinker();
if (Linker && !Obj->HasAnyFlags(RF_LoadCompleted))
{
check(!GEventDrivenLoaderEnabled);
Obj->SetFlags(RF_NeedLoad);
Linker->Preload(Obj);
return true;
}
return false;
}
void UBlueprint::ForceLoadMembers(UObject* InObject)
{
if(const UBlueprint* Blueprint = Cast<UBlueprint>(InObject))
{
ForceLoadMembers(InObject, Blueprint);
return;
}
if(const UClass* Class = Cast<UClass>(InObject))
{
ForceLoadMembers(InObject, Cast<UBlueprint>(Class->ClassGeneratedBy));
return;
}
if(InObject->HasAnyFlags(RF_ClassDefaultObject))
{
if(const UClass* Class = InObject->GetClass())
{
ForceLoadMembers(InObject, Cast<UBlueprint>(Class->ClassGeneratedBy));
return;
}
}
ForceLoadMembers(InObject, nullptr);
}
void UBlueprint::ForceLoadMembers(UObject* InObject, const UBlueprint* InBlueprint)
{
check(InObject);
if(InObject && InBlueprint)
{
if(!InBlueprint->RequiresForceLoadMembers(InObject))
{
return;
}
}
// Collect a list of all things this element owns
TArray<UObject*> MemberReferences;
FReferenceFinder ComponentCollector(MemberReferences, InObject, false, true, true, true);
ComponentCollector.FindReferences(InObject);
// Iterate over the list, and preload everything so it is valid for refreshing
for (TArray<UObject*>::TIterator it(MemberReferences); it; ++it)
{
UObject* CurrentObject = *it;
if (ForceLoad(CurrentObject))
{
ForceLoadMembers(CurrentObject, InBlueprint);
}
}
}
void UBlueprint::ForceLoadMetaData(UObject* InObject)
{
checkSlow(InObject);
UPackage* Package = InObject->GetOutermost();
checkSlow(Package);
UMetaData* MetaData = Package->GetMetaData();
checkSlow(MetaData);
ForceLoad(MetaData);
}
bool UBlueprint::ValidateGeneratedClass(const UClass* InClass)
{
const UBlueprintGeneratedClass* GeneratedClass = Cast<const UBlueprintGeneratedClass>(InClass);
if (!ensure(GeneratedClass))
{
return false;
}
const UBlueprint* Blueprint = GetBlueprintFromClass(GeneratedClass);
if (!ensure(Blueprint))
{
return false;
}
for (auto CompIt = Blueprint->ComponentTemplates.CreateConstIterator(); CompIt; ++CompIt)
{
const UActorComponent* Component = (*CompIt);
if (!ensure(Component && (Component->GetOuter() == GeneratedClass)))
{
return false;
}
}
for (auto CompIt = GeneratedClass->ComponentTemplates.CreateConstIterator(); CompIt; ++CompIt)
{
const UActorComponent* Component = (*CompIt);
if (!ensure(Component && (Component->GetOuter() == GeneratedClass)))
{
return false;
}
}
for (auto CompIt = Blueprint->Timelines.CreateConstIterator(); CompIt; ++CompIt)
{
const UTimelineTemplate* Template = (*CompIt);
if (!ensure(Template && (Template->GetOuter() == GeneratedClass)))
{
return false;
}
}
for (auto CompIt = GeneratedClass->Timelines.CreateConstIterator(); CompIt; ++CompIt)
{
const UTimelineTemplate* Template = (*CompIt);
if (!ensure(Template && (Template->GetOuter() == GeneratedClass)))
{
return false;
}
}
if (const USimpleConstructionScript* SimpleConstructionScript = Blueprint->SimpleConstructionScript)
{
if (!ensure(SimpleConstructionScript->GetOuter() == GeneratedClass))
{
return false;
}
}
if (const USimpleConstructionScript* SimpleConstructionScript = GeneratedClass->SimpleConstructionScript)
{
if (!ensure(SimpleConstructionScript->GetOuter() == GeneratedClass))
{
return false;
}
}
if (const UInheritableComponentHandler* InheritableComponentHandler = Blueprint->InheritableComponentHandler)
{
if (!ensure(InheritableComponentHandler->GetOuter() == GeneratedClass))
{
return false;
}
}
if (const UInheritableComponentHandler* InheritableComponentHandler = GeneratedClass->InheritableComponentHandler)
{
if (!ensure(InheritableComponentHandler->GetOuter() == GeneratedClass))
{
return false;
}
}
return true;
}
void UBlueprint::BeginCacheForCookedPlatformData(const ITargetPlatform *TargetPlatform)
{
Super::BeginCacheForCookedPlatformData(TargetPlatform);
// Reset, in case data was previously cooked for another platform.
ClearAllCachedCookedPlatformData();
if (GeneratedClass && GeneratedClass->IsChildOf<AActor>())
{
int32 NumCookedComponents = 0;
const double StartTime = FPlatformTime::Seconds();
// Don't cook component data if the template won't be valid in the target context.
auto ShouldCookBlueprintComponentTemplate = [TargetPlatform](const UActorComponent* InComponentTemplate) -> bool
{
if (InComponentTemplate)
{
return InComponentTemplate->NeedsLoadForTargetPlatform(TargetPlatform)
&& (!TargetPlatform->IsClientOnly() || InComponentTemplate->NeedsLoadForClient())
&& (!TargetPlatform->IsServerOnly() || InComponentTemplate->NeedsLoadForServer());
}
return false;
};
auto ShouldCookBlueprintComponentTemplateData = [](UBlueprintGeneratedClass* InBPGClass) -> bool
{
// Check to see if we should cook component data for the given class type.
bool bResult = false;
switch (GetDefault<UCookerSettings>()->BlueprintComponentDataCookingMethod)
{
case EBlueprintComponentDataCookingMethod::EnabledBlueprintsOnly:
if (AActor* CDO = Cast<AActor>(InBPGClass->GetDefaultObject(false)))
{
bResult = CDO->ShouldCookOptimizedBPComponentData();
}
break;
case EBlueprintComponentDataCookingMethod::AllBlueprints:
bResult = true;
break;
case EBlueprintComponentDataCookingMethod::Disabled:
default:
break;
}
return bResult;
};
// Only cook component data if the setting is enabled and this is an Actor-based Blueprint class.
if (UBlueprintGeneratedClass* BPGClass = CastChecked<UBlueprintGeneratedClass>(*GeneratedClass))
{
if (ShouldCookBlueprintComponentTemplateData(BPGClass))
{
// Cook all overridden SCS component node templates inherited from the parent class hierarchy.
if (UInheritableComponentHandler* TargetInheritableComponentHandler = BPGClass->GetInheritableComponentHandler())
{
for (auto RecordIt = TargetInheritableComponentHandler->CreateRecordIterator(); RecordIt; ++RecordIt)
{
// Only generate cooked data if the target platform supports the template class type. Cooked data may already have been generated if the component was inherited from a nativized parent class.
if (!RecordIt->CookedComponentInstancingData.bHasValidCookedData && ShouldCookBlueprintComponentTemplate(RecordIt->ComponentTemplate))
{
// Note: This will currently block until finished.
// @TODO - Make this an async task so we can potentially cook instancing data for multiple components in parallel.
FBlueprintEditorUtils::BuildComponentInstancingData(RecordIt->ComponentTemplate, RecordIt->CookedComponentInstancingData);
++NumCookedComponents;
}
}
}
// Cook all SCS component templates that are owned by this class.
if (BPGClass->SimpleConstructionScript)
{
for (auto Node : BPGClass->SimpleConstructionScript->GetAllNodes())
{
// Only generate cooked data if the target platform supports the template class type.
if (ShouldCookBlueprintComponentTemplate(Node->ComponentTemplate))
{
// Note: This will currently block until finished.
// @TODO - Make this an async task so we can potentially cook instancing data for multiple components in parallel.
FBlueprintEditorUtils::BuildComponentInstancingData(Node->ComponentTemplate, Node->CookedComponentInstancingData);
++NumCookedComponents;
}
}
}
// Cook all UCS/AddComponent node templates that are owned by this class.
for (UActorComponent* ComponentTemplate : BPGClass->ComponentTemplates)
{
// Only generate cooked data if the target platform supports the template class type.
if (ShouldCookBlueprintComponentTemplate(ComponentTemplate))
{
FBlueprintCookedComponentInstancingData& CookedComponentInstancingData = BPGClass->CookedComponentInstancingData.FindOrAdd(ComponentTemplate->GetFName());
// Note: This will currently block until finished.
// @TODO - Make this an async task so we can potentially cook instancing data for multiple components in parallel.
FBlueprintEditorUtils::BuildComponentInstancingData(ComponentTemplate, CookedComponentInstancingData);
++NumCookedComponents;
}
}
// Flag that the BP class has cooked data to support fast path component instancing.
BPGClass->bHasCookedComponentInstancingData = true;
}
}
if (NumCookedComponents > 0)
{
UE_LOG(LogBlueprint, Log, TEXT("%s: Cooked %d component(s) in %.02g ms"), *GetName(), NumCookedComponents, (FPlatformTime::Seconds() - StartTime) * 1000.0);
}
}
}
bool UBlueprint::IsCachedCookedPlatformDataLoaded(const ITargetPlatform* TargetPlatform)
{
// @TODO - Check async tasks for completion. For now just return TRUE since all tasks will currently block until finished.
return true;
}
void UBlueprint::ClearAllCachedCookedPlatformData()
{
Super::ClearAllCachedCookedPlatformData();
if (UBlueprintGeneratedClass* BPGClass = Cast<UBlueprintGeneratedClass>(*GeneratedClass))
{
// Clear cooked data for overridden SCS component node templates inherited from the parent class hierarchy.
if (UInheritableComponentHandler* TargetInheritableComponentHandler = BPGClass->GetInheritableComponentHandler())
{
for (auto RecordIt = TargetInheritableComponentHandler->CreateRecordIterator(); RecordIt; ++RecordIt)
{
RecordIt->CookedComponentInstancingData = FBlueprintCookedComponentInstancingData();
}
}
// Clear cooked data for SCS component node templates.
if (BPGClass->SimpleConstructionScript)
{
for (auto Node : BPGClass->SimpleConstructionScript->GetAllNodes())
{
Node->CookedComponentInstancingData = FBlueprintCookedComponentInstancingData();
}
}
// Clear cooked data for UCS/AddComponent node templates.
BPGClass->CookedComponentInstancingData.Empty();
}
}
void UBlueprint::BeginDestroy()
{
Super::BeginDestroy();
FBlueprintEditorUtils::RemoveAllLocalBookmarks(this);
// For each cached dependency, remove ourselves from its cached dependent set.
for (const TWeakObjectPtr<UBlueprint>& DependencyReference : CachedDependencies)
{
if (UBlueprint* Dependency = DependencyReference.Get())
{
Dependency->CachedDependents.Remove(MakeWeakObjectPtr(this));
}
}
}
#endif // WITH_EDITOR
#if WITH_EDITORONLY_DATA
bool UBlueprint::ShouldCookPropertyGuids() const
{
switch (ShouldCookPropertyGuidsValue)
{
case EShouldCookBlueprintPropertyGuids::No:
return false;
case EShouldCookBlueprintPropertyGuids::Yes:
return true;
case EShouldCookBlueprintPropertyGuids::Inherit:
if (const UBlueprint* ParentBlueprint = UBlueprint::GetBlueprintFromClass(ParentClass))
{
return ParentBlueprint->ShouldCookPropertyGuids();
}
break;
}
return false;
}
UBlueprint* UBlueprint::GetBlueprintFromClass(const UClass* InClass)
{
UBlueprint* BP = NULL;
if (InClass != NULL)
{
BP = Cast<UBlueprint>(InClass->ClassGeneratedBy);
}
return BP;
}
bool UBlueprint::GetBlueprintHierarchyFromClass(const UClass* InClass, TArray<UBlueprint*>& OutBlueprintParents)
{
OutBlueprintParents.Reset();
bool bNoErrors = true;
const UClass* CurrentClass = InClass;
while (UBlueprint* BP = UBlueprint::GetBlueprintFromClass(CurrentClass))
{
OutBlueprintParents.Add(BP);
bNoErrors &= (BP->Status != BS_Error);
// If valid, use stored ParentClass rather than the actual UClass::GetSuperClass(); handles the case when the class has not been recompiled yet after a reparent operation.
if (BP->ParentClass)
{
CurrentClass = BP->ParentClass;
}
else
{
check(CurrentClass);
CurrentClass = CurrentClass->GetSuperClass();
}
}
return bNoErrors;
}
#endif
bool UBlueprint::GetBlueprintHierarchyFromClass(const UClass* InClass, TArray<UBlueprintGeneratedClass*>& OutBlueprintParents)
{
OutBlueprintParents.Reset();
bool bNoErrors = true;
UBlueprintGeneratedClass* CurrentClass = Cast<UBlueprintGeneratedClass>(const_cast<UClass*>(InClass));
while (CurrentClass)
{
OutBlueprintParents.Add(CurrentClass);
#if WITH_EDITORONLY_DATA
UBlueprint* BP = UBlueprint::GetBlueprintFromClass(CurrentClass);
if (BP)
{
bNoErrors &= (BP->Status != BS_Error);
}
// If valid, use stored ParentClass rather than the actual UClass::GetSuperClass(); handles the case when the class has not been recompiled yet after a reparent operation.
if (BP && BP->ParentClass)
{
CurrentClass = Cast<UBlueprintGeneratedClass>(BP->ParentClass);
}
else
#endif // #if WITH_EDITORONLY_DATA
{
check(CurrentClass);
CurrentClass = Cast<UBlueprintGeneratedClass>(CurrentClass->GetSuperClass());
}
}
return bNoErrors;
}
bool UBlueprint::ForEachBlueprintGeneratedClassInHierarchy(const UClass* InClass, TFunctionRef<bool(UBlueprintGeneratedClass*)> InFunc)
{
bool bNoErrors = true;
UBlueprintGeneratedClass* CurrentClass = Cast<UBlueprintGeneratedClass>(const_cast<UClass*>(InClass));
while (CurrentClass)
{
if (!InFunc(CurrentClass))
{
return bNoErrors;
}
#if WITH_EDITORONLY_DATA
UBlueprint* BP = UBlueprint::GetBlueprintFromClass(CurrentClass);
if (BP)
{
bNoErrors &= (BP->Status != BS_Error);
}
// If valid, use stored ParentClass rather than the actual UClass::GetSuperClass(); handles the case when the class has not been recompiled yet after a reparent operation.
if (BP && BP->ParentClass)
{
CurrentClass = Cast<UBlueprintGeneratedClass>(BP->ParentClass);
}
else
#endif // #if WITH_EDITORONLY_DATA
{
check(CurrentClass);
CurrentClass = Cast<UBlueprintGeneratedClass>(CurrentClass->GetSuperClass());
}
}
return bNoErrors;
}
bool UBlueprint::GetBlueprintHierarchyFromClass(const UClass* InClass, TArray<IBlueprintPropertyGuidProvider*>& OutBlueprintParents)
{
OutBlueprintParents.Reset();
bool bNoErrors = true;
UBlueprintGeneratedClass* CurrentClass = Cast<UBlueprintGeneratedClass>(const_cast<UClass*>(InClass));
while (CurrentClass)
{
IBlueprintPropertyGuidProvider* GuidProviderToAdd = CurrentClass;
#if WITH_EDITORONLY_DATA
UBlueprint* BP = UBlueprint::GetBlueprintFromClass(CurrentClass);
if (BP)
{
GuidProviderToAdd = BP;
bNoErrors &= (BP->Status != BS_Error);
}
// If valid, use stored ParentClass rather than the actual UClass::GetSuperClass(); handles the case when the class has not been recompiled yet after a reparent operation.
if (BP && BP->ParentClass)
{
CurrentClass = Cast<UBlueprintGeneratedClass>(BP->ParentClass);
}
else
#endif // #if WITH_EDITORONLY_DATA
{
check(CurrentClass);
CurrentClass = Cast<UBlueprintGeneratedClass>(CurrentClass->GetSuperClass());
}
OutBlueprintParents.Add(GuidProviderToAdd);
}
return bNoErrors;
}
#if WITH_EDITOR
bool UBlueprint::IsBlueprintHierarchyErrorFree(const UClass* InClass)
{
const UClass* CurrentClass = InClass;
while (UBlueprint* BP = UBlueprint::GetBlueprintFromClass(CurrentClass))
{
if(BP->Status == BS_Error)
{
return false;
}
// If valid, use stored ParentClass rather than the actual UClass::GetSuperClass(); handles the case when the class has not been recompiled yet after a reparent operation.
if(const UClass* ParentClass = BP->ParentClass)
{
CurrentClass = ParentClass;
}
else
{
check(CurrentClass);
CurrentClass = CurrentClass->GetSuperClass();
}
}
return true;
}
#endif
void UBlueprint::GetActorClassDefaultComponents(const TSubclassOf<AActor>& InActorClass, const TSubclassOf<UActorComponent>& InComponentClass, TArray<const UActorComponent*>& OutComponents)
{
OutComponents.Reset();
ForEachComponentOfActorClassDefault(InActorClass, InComponentClass, [&](const UActorComponent* TemplateComponent)
{
OutComponents.Add(TemplateComponent);
return true;
});
}
const UActorComponent* UBlueprint::GetActorClassDefaultComponentByName(const TSubclassOf<AActor>& InActorClass, const TSubclassOf<UActorComponent>& InComponentClass, FName InComponentName)
{
const UActorComponent* Result = nullptr;
ForEachComponentOfActorClassDefault(InActorClass, InComponentClass, [&Result, InComponentName](const UActorComponent* TemplateComponent)
{
// Try to strip suffix used to identify template component instances
FString StrippedName = TemplateComponent->GetName();
if (StrippedName.RemoveFromEnd(UActorComponent::ComponentTemplateNameSuffix))
{
if (StrippedName == InComponentName.ToString())
{
Result = TemplateComponent;
return false;
}
}
else if (TemplateComponent->GetFName() == InComponentName)
{
Result = TemplateComponent;
return false;
}
return true;
});
return Result;
}
const UActorComponent* UBlueprint::GetActorClassDefaultComponent(const TSubclassOf<AActor>& InActorClass, const TSubclassOf<UActorComponent>& InComponentClass)
{
const UActorComponent* Result = nullptr;
ForEachComponentOfActorClassDefault(InActorClass, InComponentClass, [&Result](const UActorComponent* TemplateComponent)
{
Result = TemplateComponent;
return false;
});
return Result;
}
void UBlueprint::ForEachComponentOfActorClassDefault(const TSubclassOf<AActor>& ActorClass, const TSubclassOf<UActorComponent>& InComponentClass, TFunctionRef<bool(const UActorComponent*)> InFunc)
{
if (!ActorClass.Get())
{
return;
}
auto FilterFunc = [&](const UActorComponent* TemplateComponent)
{
if (!InComponentClass.Get() || TemplateComponent->IsA(InComponentClass))
{
return InFunc(TemplateComponent);
}
return true;
};
// Process native components
const AActor* CDO = ActorClass->GetDefaultObject<AActor>();
for (const UActorComponent* Component : CDO->GetComponents())
{
if (!FilterFunc(Component))
{
return;
}
}
// Process blueprint components
if (UBlueprintGeneratedClass* ActorBlueprintGeneratedClass = Cast<UBlueprintGeneratedClass>(ActorClass))
{
ForEachBlueprintGeneratedClassInHierarchy(ActorClass, [&](UBlueprintGeneratedClass* CurrentBPGC)
{
if (const USimpleConstructionScript* const ConstructionScript = CurrentBPGC->SimpleConstructionScript)
{
// Gets all BP added components
for (const USCS_Node* const Node : ConstructionScript->GetAllNodes())
{
if (!FilterFunc(Node->GetActualComponentTemplate(ActorBlueprintGeneratedClass)))
{
return false;
}
}
}
return true;
});
}
}
FName UBlueprint::FindBlueprintPropertyNameFromGuid(const FGuid& PropertyGuid) const
{
#if WITH_EDITORONLY_DATA
for (const FBPVariableDescription& BPVarDesc : NewVariables)
{
if (BPVarDesc.VarGuid == PropertyGuid)
{
return BPVarDesc.VarName;
}
}
#endif
return NAME_None;
}
FGuid UBlueprint::FindBlueprintPropertyGuidFromName(const FName PropertyName) const
{
#if WITH_EDITORONLY_DATA
for (const FBPVariableDescription& BPVarDesc : NewVariables)
{
if (BPVarDesc.VarName == PropertyName)
{
return BPVarDesc.VarGuid;
}
}
#endif
return FGuid();
}
ETimelineSigType UBlueprint::GetTimelineSignatureForFunctionByName(const FName& FunctionName, const FName& ObjectPropertyName)
{
check(SkeletonGeneratedClass != NULL);
UClass* UseClass = SkeletonGeneratedClass;
// If an object property was specified, find the class of that property instead
if(ObjectPropertyName != NAME_None)
{
FObjectPropertyBase* ObjProperty = FindFProperty<FObjectPropertyBase>(SkeletonGeneratedClass, ObjectPropertyName);
if(ObjProperty == NULL)
{
UE_LOG(LogBlueprint, Log, TEXT("GetTimelineSignatureForFunction: Object Property '%s' not found."), *ObjectPropertyName.ToString());
return ETS_InvalidSignature;
}
UseClass = ObjProperty->PropertyClass;
}
UFunction* Function = FindUField<UFunction>(UseClass, FunctionName);
if(Function == NULL)
{
UE_LOG(LogBlueprint, Log, TEXT("GetTimelineSignatureForFunction: Function '%s' not found in class '%s'."), *FunctionName.ToString(), *UseClass->GetName());
return ETS_InvalidSignature;
}
return UTimelineComponent::GetTimelineSignatureForFunction(Function);
//UE_LOG(LogBlueprint, Log, TEXT("GetTimelineSignatureForFunction: No SkeletonGeneratedClass in Blueprint '%s'."), *GetName());
//return ETS_InvalidSignature;
}
FString UBlueprint::GetDesc(void)
{
FString BPType;
switch (BlueprintType)
{
case BPTYPE_MacroLibrary:
BPType = TEXT("macros for");
break;
case BPTYPE_Const:
BPType = TEXT("const extends");
break;
case BPTYPE_Interface:
// Always extends interface, so no extraneous information needed
BPType = TEXT("");
break;
default:
BPType = TEXT("extends");
break;
}
const FString ResultString = FString::Printf(TEXT("%s %s"), *BPType, *ParentClass->GetName());
return ResultString;
}
bool UBlueprint::NeedsLoadForClient() const
{
return false;
}
bool UBlueprint::NeedsLoadForServer() const
{
return false;
}
bool UBlueprint::NeedsLoadForEditorGame() const
{
return true;
}
void UBlueprint::TagSubobjects(EObjectFlags NewFlags)
{
Super::TagSubobjects(NewFlags);
if (GeneratedClass && !GeneratedClass->HasAnyFlags(GARBAGE_COLLECTION_KEEPFLAGS))
{
GeneratedClass->SetFlags(NewFlags);
GeneratedClass->TagSubobjects(NewFlags);
}
if (SkeletonGeneratedClass && SkeletonGeneratedClass != GeneratedClass && !SkeletonGeneratedClass->HasAnyFlags(GARBAGE_COLLECTION_KEEPFLAGS))
{
SkeletonGeneratedClass->SetFlags(NewFlags);
SkeletonGeneratedClass->TagSubobjects(NewFlags);
}
}
void UBlueprint::GetAllGraphs(TArray<UEdGraph*>& Graphs) const
{
#if WITH_EDITORONLY_DATA
for (int32 i = 0; i < FunctionGraphs.Num(); ++i)
{
UEdGraph* Graph = FunctionGraphs[i];
if(Graph)
{
Graphs.Add(Graph);
Graph->GetAllChildrenGraphs(Graphs);
}
}
for (int32 i = 0; i < MacroGraphs.Num(); ++i)
{
UEdGraph* Graph = MacroGraphs[i];
if(Graph)
{
Graphs.Add(Graph);
Graph->GetAllChildrenGraphs(Graphs);
}
}
for (int32 i = 0; i < UbergraphPages.Num(); ++i)
{
UEdGraph* Graph = UbergraphPages[i];
if(Graph)
{
Graphs.Add(Graph);
Graph->GetAllChildrenGraphs(Graphs);
}
}
for (int32 i = 0; i < DelegateSignatureGraphs.Num(); ++i)
{
UEdGraph* Graph = DelegateSignatureGraphs[i];
if(Graph)
{
Graphs.Add(Graph);
Graph->GetAllChildrenGraphs(Graphs);
}
}
for (int32 BPIdx=0; BPIdx<ImplementedInterfaces.Num(); BPIdx++)
{
const FBPInterfaceDescription& InterfaceDesc = ImplementedInterfaces[BPIdx];
for (int32 GraphIdx = 0; GraphIdx < InterfaceDesc.Graphs.Num(); GraphIdx++)
{
UEdGraph* Graph = InterfaceDesc.Graphs[GraphIdx];
if(Graph)
{
Graphs.Add(Graph);
Graph->GetAllChildrenGraphs(Graphs);
}
}
}
#endif // WITH_EDITORONLY_DATA
}
#if WITH_EDITOR
bool UBlueprint::ChangeOwnerOfTemplates()
{
struct FUniqueNewNameHelper
{
private:
FString NewName;
bool isValid;
public:
FUniqueNewNameHelper(const FString& Name, UObject* Outer) : isValid(false)
{
const uint32 Hash = FCrc::StrCrc32<TCHAR>(*Name);
NewName = FString::Printf(TEXT("%s__%08X"), *Name, Hash);
isValid = IsUniqueObjectName(FName(*NewName), Outer);
if (!isValid)
{
check(Outer);
UE_LOG(LogBlueprint, Warning, TEXT("ChangeOwnerOfTemplates: Cannot generate a deterministic new name. Old name: %s New outer: %s"), *Name, *Outer->GetName());
}
}
const TCHAR* Get() const
{
return isValid ? *NewName : NULL;
}
};
UBlueprintGeneratedClass* BPGClass = Cast<UBlueprintGeneratedClass>(*GeneratedClass);
bool bIsStillStale = false;
if (BPGClass)
{
// >>> Backwards Compatibility: VER_UE4_EDITORONLY_BLUEPRINTS
bool bMigratedOwner = false;
TSet<class UCurveBase*> Curves;
for( auto CompIt = ComponentTemplates.CreateIterator(); CompIt; ++CompIt )
{
UActorComponent* Component = (*CompIt);
if (Component)
{
if (Component->GetOuter() == this)
{
const bool bRenamed = Component->Rename(*Component->GetName(), BPGClass, REN_ForceNoResetLoaders | REN_DoNotDirty);
ensure(bRenamed);
bIsStillStale |= !bRenamed;
bMigratedOwner = true;
}
if (auto TimelineComponent = Cast<UTimelineComponent>(Component))
{
TimelineComponent->GetAllCurves(Curves);
}
}
}
for( auto CompIt = Timelines.CreateIterator(); CompIt; ++CompIt )
{
UTimelineTemplate* Template = (*CompIt);
if (Template)
{
if(Template->GetOuter() == this)
{
const FName OldTemplateName = Template->GetFName();
ensure(!OldTemplateName.ToString().EndsWith(UTimelineTemplate::TemplatePostfix));
const bool bRenamed = Template->Rename(*UTimelineTemplate::TimelineVariableNameToTemplateName(Template->GetFName()), BPGClass, REN_ForceNoResetLoaders|REN_DoNotDirty);
ensure(bRenamed);
bIsStillStale |= !bRenamed;
ensure(OldTemplateName == Template->GetVariableName());
bMigratedOwner = true;
}
Template->GetAllCurves(Curves);
}
}
for (auto Curve : Curves)
{
if (Curve && (Curve->GetOuter() == this))
{
const bool bRenamed = Curve->Rename(FUniqueNewNameHelper(Curve->GetName(), BPGClass).Get(), BPGClass, REN_ForceNoResetLoaders | REN_DoNotDirty);
ensure(bRenamed);
bIsStillStale |= !bRenamed;
}
}
if(USimpleConstructionScript* SCS = SimpleConstructionScript)
{
if(SCS->GetOuter() == this)
{
const bool bRenamed = SCS->Rename(FUniqueNewNameHelper(SCS->GetName(), BPGClass).Get(), BPGClass, REN_ForceNoResetLoaders | REN_DoNotDirty);
ensure(bRenamed);
bIsStillStale |= !bRenamed;
bMigratedOwner = true;
}
for (USCS_Node* SCSNode : SCS->GetAllNodes())
{
UActorComponent* Component = SCSNode ? ToRawPtr(SCSNode->ComponentTemplate) : NULL;
if (Component && Component->GetOuter() == this)
{
const bool bRenamed = Component->Rename(FUniqueNewNameHelper(Component->GetName(), BPGClass).Get(), BPGClass, REN_ForceNoResetLoaders | REN_DoNotDirty);
ensure(bRenamed);
bIsStillStale |= !bRenamed;
bMigratedOwner = true;
}
}
}
if (bMigratedOwner)
{
if( !HasAnyFlags( RF_Transient ))
{
// alert the user that blueprints gave been migrated and require re-saving to enable them to locate and fix them without nagging them.
FMessageLog("BlueprintLog").Warning( FText::Format( NSLOCTEXT( "Blueprint", "MigrationWarning", "Blueprint {0} has been migrated and requires re-saving to avoid import errors" ), FText::FromString( *GetName() )));
if( GetDefault<UEditorLoadingSavingSettings>()->bDirtyMigratedBlueprints )
{
UPackage* BPPackage = GetOutermost();
if( BPPackage )
{
BPPackage->SetDirtyFlag( true );
}
}
}
BPGClass->ComponentTemplates = ComponentTemplates;
BPGClass->Timelines = Timelines;
BPGClass->SimpleConstructionScript = SimpleConstructionScript;
}
// <<< End Backwards Compatibility
}
else
{
UE_LOG(LogBlueprint, Log, TEXT("ChangeOwnerOfTemplates: No BlueprintGeneratedClass in %s"), *GetName());
}
return !bIsStillStale;
}
#if WITH_EDITOR
bool UBlueprint::Modify(bool bAlwaysMarkDirty)
{
bCachedDependenciesUpToDate = false;
return Super::Modify(bAlwaysMarkDirty);
}
#endif
void UBlueprint::GatherDependencies(TSet<TWeakObjectPtr<UBlueprint>>& InDependencies) const
{
}
void UBlueprint::ReplaceDeprecatedNodes()
{
TArray<UEdGraph*> Graphs;
GetAllGraphs(Graphs);
for ( auto It = Graphs.CreateIterator(); It; ++It )
{
UEdGraph* const Graph = *It;
const UEdGraphSchema* Schema = Graph->GetSchema();
Schema->BackwardCompatibilityNodeConversion(Graph, true);
}
}
void UBlueprint::ClearEditorReferences()
{
FKismetEditorUtilities::OnBlueprintUnloaded.Broadcast(this);
if (UBlueprintGeneratedClass* BPGC = Cast<UBlueprintGeneratedClass>(GeneratedClass))
{
FKismetEditorUtilities::OnBlueprintGeneratedClassUnloaded.Broadcast(BPGC);
}
}
UInheritableComponentHandler* UBlueprint::GetInheritableComponentHandler(bool bCreateIfNecessary)
{
static const FBoolConfigValueHelper EnableInheritableComponents(TEXT("Kismet"), TEXT("bEnableInheritableComponents"), GEngineIni);
if (!EnableInheritableComponents)
{
return nullptr;
}
if (!InheritableComponentHandler && bCreateIfNecessary)
{
UBlueprintGeneratedClass* BPGC = CastChecked<UBlueprintGeneratedClass>(GeneratedClass);
ensure(!BPGC->InheritableComponentHandler);
InheritableComponentHandler = BPGC->GetInheritableComponentHandler(true);
}
return InheritableComponentHandler;
}
EDataValidationResult UBlueprint::IsDataValid(TArray<FText>& ValidationErrors)
{
EDataValidationResult IsValid = GeneratedClass ? GeneratedClass->GetDefaultObject()->IsDataValid(ValidationErrors) : EDataValidationResult::Invalid;
IsValid = (IsValid == EDataValidationResult::NotValidated) ? EDataValidationResult::Valid : IsValid;
if (SimpleConstructionScript)
{
EDataValidationResult IsSCSValid = SimpleConstructionScript->IsDataValid(ValidationErrors);
IsValid = CombineDataValidationResults(IsValid, IsSCSValid);
}
if (InheritableComponentHandler)
{
EDataValidationResult IsICHValid = InheritableComponentHandler->IsDataValid(ValidationErrors);
IsValid = CombineDataValidationResults(IsValid, IsICHValid);
}
for (UActorComponent* Component : ComponentTemplates)
{
if (Component)
{
EDataValidationResult IsComponentValid = Component->IsDataValid(ValidationErrors);
IsValid = CombineDataValidationResults(IsValid, IsComponentValid);
}
}
for (UTimelineTemplate* Timeline : Timelines)
{
if (Timeline)
{
EDataValidationResult IsTimelineValid = Timeline->IsDataValid(ValidationErrors);
IsValid = CombineDataValidationResults(IsValid, IsTimelineValid);
}
}
return IsValid;
}
bool UBlueprint::FindDiffs(const UBlueprint* OtherBlueprint, FDiffResults& Results) const
{
return false;
}
FName UBlueprint::GetFunctionNameFromClassByGuid(const UClass* InClass, const FGuid FunctionGuid)
{
return FBlueprintEditorUtils::GetFunctionNameFromClassByGuid(InClass, FunctionGuid);
}
bool UBlueprint::GetFunctionGuidFromClassByFieldName(const UClass* InClass, const FName FunctionName, FGuid& FunctionGuid)
{
return FBlueprintEditorUtils::GetFunctionGuidFromClassByFieldName(InClass, FunctionName, FunctionGuid);
}
UEdGraph* UBlueprint::GetLastEditedUberGraph() const
{
for ( int32 LastEditedIndex = LastEditedDocuments.Num() - 1; LastEditedIndex >= 0; LastEditedIndex-- )
{
if ( UObject* Obj = LastEditedDocuments[LastEditedIndex].EditedObjectPath.ResolveObject() )
{
if ( UEdGraph* Graph = Cast<UEdGraph>(Obj) )
{
for ( int32 GraphIndex = 0; GraphIndex < UbergraphPages.Num(); GraphIndex++ )
{
if ( Graph == UbergraphPages[GraphIndex] )
{
return UbergraphPages[GraphIndex];
}
}
}
}
}
if ( UbergraphPages.Num() > 0 )
{
return UbergraphPages[0];
}
return nullptr;
}
#if WITH_EDITOR
UClass* UBlueprint::GetBlueprintParentClassFromAssetTags(const FAssetData& BlueprintAsset)
{
UClass* ParentClass = nullptr;
FString ParentClassName;
if(!BlueprintAsset.GetTagValue(FBlueprintTags::NativeParentClassPath, ParentClassName))
{
BlueprintAsset.GetTagValue(FBlueprintTags::ParentClassPath, ParentClassName);
}
if(!ParentClassName.IsEmpty())
{
UObject* Outer = nullptr;
ResolveName(Outer, ParentClassName, false, false);
ParentClass = FindObject<UClass>(Outer, *ParentClassName);
}
return ParentClass;
}
#endif
PRAGMA_DISABLE_DEPRECATION_WARNINGS
TArrayView<const TObjectPtr<UBlueprintExtension>> UBlueprint::GetExtensions() const
{
return Extensions;
}
int32 UBlueprint::AddExtension(const TObjectPtr<UBlueprintExtension>& InExtension)
{
int32 Index = Extensions.Add(InExtension);
OnExtensionAdded.Broadcast(InExtension);
return Index;
}
int32 UBlueprint::RemoveExtension(const TObjectPtr<UBlueprintExtension>& InExtension)
{
int32 NumRemoved = Extensions.RemoveSingleSwap(InExtension);
if (NumRemoved > 0)
{
OnExtensionRemoved.Broadcast(InExtension);
}
return NumRemoved;
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
#endif //WITH_EDITOR
#if WITH_EDITORONLY_DATA
void UBlueprint::LoadModulesRequiredForCompilation()
{
static const FName KismetCompilerModuleName("KismetCompiler");
static const FName MovieSceneToolsModuleName("MovieSceneTools");
FModuleManager::Get().LoadModule(KismetCompilerModuleName);
FModuleManager::Get().LoadModule(MovieSceneToolsModuleName);
}
#endif //WITH_EDITORONLY_DATA