Files
UnrealEngine/Engine/Source/Editor/UnrealEd/Private/Subsystems/EditorAssetSubsystem.cpp
2025-05-18 13:04:45 +08:00

1794 lines
62 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Subsystems/EditorAssetSubsystem.h"
#include "AssetRegistry/IAssetRegistry.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "AssetToolsModule.h"
#include "AssetViewUtils.h"
#include "Editor.h"
#include "Editor/Transactor.h"
#include "EditorScriptingHelpers.h"
#include "FileHelpers.h"
#include "HAL/FileManager.h"
#include "Misc/EngineBuildSettings.h"
#include "Misc/PackageName.h"
#include "Misc/ScopedSlowTask.h"
#include "Modules/ModuleManager.h"
#include "ObjectTools.h"
#include "PackageTools.h"
#include "Templates/ValueOrError.h"
#include "UObject/MetaData.h"
DEFINE_LOG_CATEGORY_STATIC(LogEditorAssetSubsystem, Log, All);
namespace UE::Editor::Private
{
int32 AllowSavingAssetsDuringPIE = 0;
}
static FAutoConsoleVariableRef CVarAllowSavingAssetsDuringPIE(
TEXT("Editor.AllowSavingAssetsDuringPIE"),
UE::Editor::Private::AllowSavingAssetsDuringPIE,
TEXT("Allow assets to get saved during PIE through the EditorAssetSubsystem. Set to 0 (default) to disable and 1 to enable this option."),
ECVF_Default
);
namespace UE::EditorAssetUtils
{
struct ErrorTag{};
}
template<typename T>
using TError = TValueOrError<UE::EditorAssetUtils::ErrorTag, T>;
namespace UE::EditorAssetUtils
{
static bool EnsureAssetsLoaded()
{
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
if (IAssetRegistry& AssetRegistry = AssetRegistryModule.Get(); AssetRegistry.IsLoadingAssets())
{
if (AssetRegistry.IsSearchAsync() && AssetRegistry.IsSearchAllAssets())
{
AssetRegistry.WaitForCompletion();
}
else
{
AssetRegistry.SearchAllAssets(true /* bSynchronousSearch */);
}
}
return true;
}
static TValueOrError<FAssetData, FString> FindAssetDataFromAnyPath(const FString& AnyAssetPath)
{
FString FailureReason;
FString ObjectPath = EditorScriptingHelpers::ConvertAnyPathToSubObjectPath(AnyAssetPath, FailureReason);
if (ObjectPath.IsEmpty())
{
return MakeError(FailureReason);
}
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
FAssetData AssetData = AssetRegistryModule.Get().GetAssetByObjectPath(FSoftObjectPath(ObjectPath));
if (!AssetData.IsValid())
{
ObjectPath = EditorScriptingHelpers::ConvertAnyPathToObjectPath(AnyAssetPath, FailureReason);
if (ObjectPath.IsEmpty())
{
return MakeError(FailureReason);
}
AssetData = AssetRegistryModule.Get().GetAssetByObjectPath(FSoftObjectPath(ObjectPath));
if (!AssetData.IsValid())
{
return MakeError(FString::Printf(TEXT("The AssetData '%s' could not be found in the Asset Registry."), *ObjectPath));
}
}
return MakeValue(AssetData);
}
static TValueOrError<UObject*, FString> LoadAssetFromData(const FAssetData& AssetData)
{
if (!AssetData.IsValid())
{
return MakeError("Asset Data is not valid.");
}
UObject* FoundObject = AssetData.GetAsset();
if (!IsValid(FoundObject))
{
return MakeError(FString::Printf(TEXT("The asset '%s' exists but was not able to be loaded."), *AssetData.GetObjectPathString()));
}
else if (!FoundObject->IsAsset())
{
return MakeError(FString::Printf(TEXT("'%s' is not a valid asset."), *AssetData.GetObjectPathString()));
}
return MakeValue(FoundObject);
}
static TValueOrError<UObject*, FString> LoadAssetFromPath(const FString& AssetPath)
{
TValueOrError<FAssetData, FString> AssetDataResult = FindAssetDataFromAnyPath(AssetPath);
if (AssetDataResult.HasError())
{
return MakeError(AssetDataResult.StealError());
}
return LoadAssetFromData(AssetDataResult.GetValue());
}
static TError<FString> IsARegisteredAsset(UObject* Object, bool bAllowSkipBrowsableTestForExternalObject = false)
{
if (!IsValid(Object))
{
return MakeError(TEXT("The Asset is not valid."));
}
const bool bCanSkipIsBrowsable = bAllowSkipBrowsableTestForExternalObject && Object->IsPackageExternal();
if (!bCanSkipIsBrowsable && !ObjectTools::IsObjectBrowsable(Object))
{
return MakeError(FString::Printf(TEXT("The object '%s' is not an asset."), *Object->GetName()));
}
FSoftObjectPath ObjectPath = FSoftObjectPath(Object);
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
FAssetData AssetData = AssetRegistryModule.Get().GetAssetByObjectPath(ObjectPath);
if (!AssetData.IsValid())
{
return MakeError(FString::Printf(TEXT("The AssetData '%s' could not be found in the Asset Registry."), *Object->GetPathName()));
}
return MakeValue();
}
static TError<FString> EnumerateAssetsInDirectory(const FString& AnyPathDirectoryPath, bool bRecursive, TArray<FAssetData>& OutResult, FString& OutDirectoryPath)
{
OutResult.Reset();
OutDirectoryPath.Reset();
FString FailureReason;
OutDirectoryPath = EditorScriptingHelpers::ConvertAnyPathToLongPackagePath(AnyPathDirectoryPath, FailureReason);
if (OutDirectoryPath.IsEmpty())
{
return MakeError(FailureReason);
}
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
if (!AssetRegistryModule.Get().GetAssetsByPath(*OutDirectoryPath, OutResult, bRecursive))
{
return MakeError(FString::Printf(TEXT("Could not get assets from path '%s'"), *OutDirectoryPath));
}
return MakeValue();
}
enum class EPackageEnumerationFilter
{
NoFilter,
OnlyDirty
};
static TError<FString> EnumeratePackagesInDirectory(const FString& AnyDirectoryPath, EPackageEnumerationFilter EnumerationFilter, bool bRecursive, TArray<UPackage*>& OutResult)
{
FString ValidDirectoryPath;
TArray<FAssetData> Assets;
if (TError<FString> Result = EnumerateAssetsInDirectory(AnyDirectoryPath, bRecursive, Assets, ValidDirectoryPath); Result.HasError())
{
return Result;
}
if (EnumerationFilter == EPackageEnumerationFilter::OnlyDirty)
{
for (const FAssetData& AssetData : Assets)
{
// Can't be dirty if not loaded
if (AssetData.IsAssetLoaded())
{
if (UPackage* Package = AssetData.GetPackage(); Package && Package->IsDirty())
{
Package->FullyLoad();
OutResult.AddUnique(Package);
}
}
}
}
else
{
// load all assets
for (const FAssetData& AssetData : Assets)
{
if (UPackage* Package = AssetData.GetPackage())
{
Package->FullyLoad();
OutResult.AddUnique(Package);
}
}
}
return MakeValue();
}
static bool DeleteEmptyDirectoryFromDisk(const FString& LongPackagePath)
{
struct FEmptyDirectoryVisitor : public IPlatformFile::FDirectoryVisitor
{
bool bIsEmpty = true;
virtual bool Visit(const TCHAR*, bool bIsDirectory) override
{
if (!bIsDirectory)
{
bIsEmpty = false;
return false; // abort searching
}
return true; // continue searching
}
};
if (FString PathToDeleteOnDisk = UPackageTools::PackageNameToFilename(LongPackagePath); !PathToDeleteOnDisk.IsEmpty())
{
// Look for files on disk in case the directory contains things not tracked by the asset registry
FEmptyDirectoryVisitor EmptyDirectoryVisitor;
IFileManager::Get().IterateDirectoryRecursively(*PathToDeleteOnDisk, EmptyDirectoryVisitor);
if (EmptyDirectoryVisitor.bIsEmpty)
{
return IFileManager::Get().DeleteDirectory(*PathToDeleteOnDisk, false, true);
}
}
return false;
}
struct FDirectoryRenamePaths
{
FString SourceDirectoryPath;
FString SourceFilePath;
FString DestinationDirectoryPath;
FString DestinationFilePath;
};
static TValueOrError<FDirectoryRenamePaths, FString> GetDirectoryRenamePaths(const FString& SourceDirectoryPath, const FString& DestinationDirectoryPath)
{
FDirectoryRenamePaths Paths;
FString FailureReason;
Paths.SourceDirectoryPath = EditorScriptingHelpers::ConvertAnyPathToLongPackagePath(SourceDirectoryPath, FailureReason);
if (Paths.SourceDirectoryPath.IsEmpty())
{
return MakeError(FString::Printf(TEXT("Failed to convert the source path. %s"), *FailureReason));
}
Paths.SourceFilePath = FPaths::ConvertRelativePathToFull(UPackageTools::PackageNameToFilename(Paths.SourceDirectoryPath));
if (Paths.SourceFilePath.IsEmpty())
{
return MakeError(FString::Printf(TEXT("Failed to convert the source path '%s' to a full path."), *Paths.SourceDirectoryPath));
}
Paths.DestinationDirectoryPath = EditorScriptingHelpers::ConvertAnyPathToLongPackagePath(DestinationDirectoryPath, FailureReason);
if (Paths.DestinationDirectoryPath.IsEmpty())
{
return MakeError(FString::Printf(TEXT("Failed to convert the destination path. %s"), *FailureReason));
}
Paths.DestinationFilePath = FPaths::ConvertRelativePathToFull(UPackageTools::PackageNameToFilename(Paths.DestinationDirectoryPath));
if (Paths.DestinationFilePath.IsEmpty())
{
return MakeError(FString::Printf(TEXT("Failed to convert the destination path '%s' to a full path."), *Paths.DestinationDirectoryPath));
}
return MakeValue(Paths);
}
static TError<FString> SetupDirectoryRename(const FDirectoryRenamePaths& Paths)
{
// If the source directory doesn't exist on disk then it can't be operated on
if (!IFileManager::Get().DirectoryExists(*Paths.SourceFilePath))
{
return MakeError(FString::Printf(TEXT("The source directory '%s' does not exist on disk."), *Paths.SourceFilePath));
}
// Create the destination directory if it doesn't already exist
if (!IFileManager::Get().DirectoryExists(*Paths.DestinationFilePath))
{
const bool bTree = true;
if (!IFileManager::Get().MakeDirectory(*Paths.DestinationFilePath, bTree))
{
return MakeError(FString::Printf(TEXT("The destination directory '%s' could not be created."), *Paths.DestinationFilePath));
}
}
return MakeValue();
}
struct FDirectoryRenameAssetPaths
{
TArray<FAssetData> SourceDirectoryAssetDatas;
TArray<FString> DestinationDirectoryAssetPaths;
};
static TValueOrError<FDirectoryRenameAssetPaths, FString> GetDirectoryRenameAssetPaths(const FDirectoryRenamePaths& Paths)
{
FDirectoryRenameAssetPaths AssetPaths;
// Load all assets the directory contains
// Because we want to rename a directory, we can't rename any files that can't be deleted
FString OutPath;
if (TError<FString> Result = EnumerateAssetsInDirectory(Paths.SourceDirectoryPath, true, AssetPaths.SourceDirectoryAssetDatas, OutPath); Result.HasError())
{
return MakeError(Result.StealError());
}
for (const FAssetData& AssetData : AssetPaths.SourceDirectoryAssetDatas)
{
FString PackageName = AssetData.PackageName.ToString();
FString LongPackagePath = FPackageName::GetLongPackagePath(PackageName);
// Remove source from the object name
LongPackagePath.MidInline(Paths.SourceDirectoryPath.Len(), MAX_int32, EAllowShrinking::No);
// Create AssetPath /Game/MyFolder/MyAsset.MyAsset
FString NewAssetPackageName;
if (LongPackagePath.IsEmpty())
{
NewAssetPackageName = FString::Printf(TEXT("%s/%s.%s"), *Paths.DestinationDirectoryPath, *AssetData.AssetName.ToString(), *AssetData.AssetName.ToString());
}
else
{
NewAssetPackageName = FString::Printf(TEXT("%s%s/%s.%s"), *Paths.DestinationDirectoryPath, *LongPackagePath, *AssetData.AssetName.ToString(), *AssetData.AssetName.ToString());
}
FString FailureReason;
if (!EditorScriptingHelpers::IsAValidPathForCreateNewAsset(NewAssetPackageName, FailureReason))
{
return MakeError(FString::Printf(TEXT("Failed to validate the destination for asset '%s'. %s"), *AssetData.AssetName.ToString(), *FailureReason));
}
if (FPackageName::DoesPackageExist(NewAssetPackageName))
{
return MakeError(FString::Printf(TEXT("Failed to validate the destination for asset '%s'. There's already an asset at the destination."), *NewAssetPackageName));
}
// Keep AssetPath /Game/MyFolder
AssetPaths.DestinationDirectoryAssetPaths.Add(NewAssetPackageName);
}
return MakeValue(AssetPaths);
}
template<typename TLessThan>
static void SortAssets(
TArray<FAssetData>& Assets,
TLessThan&& Predicate,
EEditorAssetSortOrder SortOrder
)
{
switch (SortOrder)
{
case EEditorAssetSortOrder::Ascending: Assets.Sort(Predicate); break;
case EEditorAssetSortOrder::Descending:
Assets.Sort([&Predicate](const FAssetData& Left, const FAssetData& Right)
{
return Predicate(Right, Left);
});
break;
default: checkNoEntry(); break;
}
}
template<typename TType>
static bool Sort(
TArray<FAssetData>& Assets,
FName MetaDataTag,
TFunctionRef<bool(const FString& TagType, TType& Converted)> Converter,
EEditorAssetSortOrder SortOrder
)
{
TMap<FSoftObjectPath, TType> MetaData;
MetaData.Reserve(Assets.Num());
for (const FAssetData& AssetData : Assets)
{
const FAssetTagValueRef Value = AssetData.TagsAndValues.FindTag(MetaDataTag);
TType AssetTagValue;
if (Value.IsSet() && Converter(Value.GetValue(), AssetTagValue))
{
MetaData.Add(AssetData.GetSoftObjectPath(), AssetTagValue);
}
else
{
UE_LOG(LogEditorAssetSubsystem, Warning, TEXT("Not all assets have the tag '%s'"), *MetaDataTag.ToString());
return false;
}
}
SortAssets(Assets, [&MetaData](const FAssetData& Left, const FAssetData& Right)
{
return MetaData[Left.GetSoftObjectPath()] <= MetaData[Right.GetSoftObjectPath()];
}, SortOrder);
return true;
}
}
UEditorAssetSubsystem::UEditorAssetSubsystem()
{
}
void UEditorAssetSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
OnExtractAssetFromFile.AddUObject(this, &UEditorAssetSubsystem::CallOnExtractAssetFromFileDynamicArray);
}
void UEditorAssetSubsystem::Deinitialize()
{
OnExtractAssetFromFile.RemoveAll(this);
}
// Load operations
// A wrapper around
//unreal.AssetRegistryHelpers.get_asset(unreal.AssetRegistryHelpers.get_asset_registry().get_asset_by_object_path("/Game/NewDataTable.NewDataTable"))
UObject* UEditorAssetSubsystem::LoadAsset(const FString& AssetPath)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return nullptr;
}
TValueOrError<UObject*, FString> LoadedAssetResult = UE::EditorAssetUtils::LoadAssetFromPath(AssetPath);
if (LoadedAssetResult.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("LoadAsset failed: %s"), *LoadedAssetResult.GetError());
return nullptr;
}
return LoadedAssetResult.GetValue();
}
UClass* UEditorAssetSubsystem::LoadBlueprintClass(const FString& AssetPath)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return nullptr;
}
TValueOrError<UObject*, FString> LoadedAssetResult = UE::EditorAssetUtils::LoadAssetFromPath(AssetPath);
if (LoadedAssetResult.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("LoadBlueprintClass failed: %s"), *LoadedAssetResult.GetError());
return nullptr;
}
UBlueprint* Blueprint = Cast<UBlueprint>(LoadedAssetResult.GetValue());
if (!Blueprint)
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("LoadBlueprintClass failed: The asset is not a Blueprint."));
return nullptr;
}
return Blueprint->GeneratedClass.Get();
}
FString UEditorAssetSubsystem::GetPathNameForLoadedAsset(UObject* LoadedAsset)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return FString();
}
if (TError<FString> Result = UE::EditorAssetUtils::IsARegisteredAsset(LoadedAsset, true/*bAllowSkipBrowsableTestForExternalObject*/); Result.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("GetPathNameForLoadedAsset failed: %s"), *Result.GetError());
}
return LoadedAsset->GetPathName();
}
FAssetData UEditorAssetSubsystem::FindAssetData(const FString& AssetPath)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return FAssetData();
}
auto AssetDataResult = UE::EditorAssetUtils::FindAssetDataFromAnyPath(AssetPath);
if (AssetDataResult.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("FindAssetData failed: %s"), *AssetDataResult.GetError());
return FAssetData();
}
return AssetDataResult.GetValue();
}
bool UEditorAssetSubsystem::DoesAssetExist(const FString& AssetPath)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return false;
}
FString FailureReason;
FString ObjectPath = EditorScriptingHelpers::ConvertAnyPathToSubObjectPath(AssetPath, FailureReason);
if (ObjectPath.IsEmpty())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("DoesAssetExist failed: %s"), *FailureReason);
return false;
}
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
FAssetData AssetData = AssetRegistryModule.Get().GetAssetByObjectPath(FSoftObjectPath(ObjectPath));
if (!AssetData.IsValid())
{
ObjectPath = EditorScriptingHelpers::ConvertAnyPathToObjectPath(AssetPath, FailureReason);
if (ObjectPath.IsEmpty())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("DoesAssetExist failed: %s"), *FailureReason);
return false;
}
AssetData = AssetRegistryModule.Get().GetAssetByObjectPath(FSoftObjectPath(ObjectPath));
if (!AssetData.IsValid())
{
return false;
}
}
return true;
}
bool UEditorAssetSubsystem::DoAssetsExist(const TArray<FString>& AssetPaths)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return false;
}
FString FailureReason;
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
for (const FString& Path : AssetPaths)
{
FString ObjectPath = EditorScriptingHelpers::ConvertAnyPathToSubObjectPath(Path, FailureReason);
if (ObjectPath.IsEmpty())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("DoAssetsExist failed: %s"), *FailureReason);
return false;
}
FAssetData AssetData = AssetRegistryModule.Get().GetAssetByObjectPath(FSoftObjectPath(ObjectPath));
if (!AssetData.IsValid())
{
ObjectPath = EditorScriptingHelpers::ConvertAnyPathToObjectPath(Path, FailureReason);
if (ObjectPath.IsEmpty())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("DoAssetsExist failed: %s"), *FailureReason);
return false;
}
AssetData = AssetRegistryModule.Get().GetAssetByObjectPath(FSoftObjectPath(ObjectPath));
if (!AssetData.IsValid())
{
return false;
}
}
}
return true;
}
TArray<FString> UEditorAssetSubsystem::FindPackageReferencersForAsset(const FString& AnyAssetPath, bool bLoadAssetsToConfirm)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
TArray<FString> Result;
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return Result;
}
FString FailureReason;
FString AssetPath = EditorScriptingHelpers::ConvertAnyPathToSubObjectPath(AnyAssetPath, FailureReason);
if (AssetPath.IsEmpty())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("FindAssetPackageReferencers failed: %s"), *FailureReason);
return Result;
}
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
FAssetData AssetData = AssetRegistryModule.Get().GetAssetByObjectPath(FSoftObjectPath(AnyAssetPath));
if (!AssetData.IsValid())
{
FString ObjectPath = EditorScriptingHelpers::ConvertAnyPathToObjectPath(AnyAssetPath, FailureReason);
if (ObjectPath.IsEmpty())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("FindAssetPackageReferencers failed: %s"), *FailureReason);
return Result;
}
}
// Find the reference in packages. Load them to confirm the reference.
TArray<FName> PackageReferencers;
AssetRegistryModule.Get().GetReferencers(*FPackageName::ObjectPathToPackageName(AssetPath), PackageReferencers, UE::AssetRegistry::EDependencyCategory::Package);
if (bLoadAssetsToConfirm)
{
// Load the asset to confirm
TValueOrError<UObject*, FString> LoadedAssetResult = UE::EditorAssetUtils::LoadAssetFromPath(AssetPath);
if (LoadedAssetResult.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("FindAssetPackageReferencers failed: Could not load asset. %s"), *LoadedAssetResult.GetError());
return Result;
}
// Load the asset referencers to confirm
for (FName Referencer : PackageReferencers)
{
TValueOrError<UObject*, FString> ReferencerAssetResult = UE::EditorAssetUtils::LoadAssetFromPath(Referencer.ToString());
if (ReferencerAssetResult.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Warning, TEXT("FindAssetPackageReferencers: Not able to confirm reference: %s"), *ReferencerAssetResult.GetError());
// Add it to the list anyway
Result.AddUnique(Referencer.ToString());
}
}
// Check what we have in memory (but not in undo memory)
FReferencerInformationList MemoryReferences;
{
if (GEditor && GEditor->Trans)
{
GEditor->Trans->DisableObjectSerialization();
}
IsReferenced(LoadedAssetResult.GetValue(), GARBAGE_COLLECTION_KEEPFLAGS, EInternalObjectFlags_GarbageCollectionKeepFlags, true, &MemoryReferences);
if (GEditor && GEditor->Trans)
{
GEditor->Trans->EnableObjectSerialization();
}
}
// Check if any references are in the list. Only confirm if the package is referencing it. An inner object of the asset could reference it.
TArray<FName> ConfirmedReferencers;
ConfirmedReferencers.Reserve(PackageReferencers.Num());
for (const FReferencerInformation& RefInfo : MemoryReferences.InternalReferences)
{
FName PackageName = RefInfo.Referencer->GetPackage()->GetFName();
if (PackageReferencers.Contains(PackageName))
{
ConfirmedReferencers.AddUnique(PackageName);
}
}
for (const FReferencerInformation& RefInfo : MemoryReferences.ExternalReferences)
{
FName PackageName = RefInfo.Referencer->GetPackage()->GetFName();
if (PackageReferencers.Contains(PackageName))
{
ConfirmedReferencers.AddUnique(PackageName);
}
}
PackageReferencers.Empty();
PackageReferencers = MoveTemp(ConfirmedReferencers);
}
// Copy the list. Result may already have Map packages.
Result.Reserve(PackageReferencers.Num());
for (FName PackagePath : PackageReferencers)
{
Result.Add(PackagePath.ToString());
}
return Result;
}
bool UEditorAssetSubsystem::ConsolidateAssets(UObject* AssetToConsolidateTo, const TArray<UObject*>& AssetsToConsolidate)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return false;
}
if (AssetsToConsolidate.Num() == 0)
{
UE_LOG(LogEditorAssetSubsystem, Warning, TEXT("ConsolidateAssets: No assets to consolidate."));
return false;
}
if (TError<FString> Result = UE::EditorAssetUtils::IsARegisteredAsset(AssetToConsolidateTo); Result.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("ConsolidateAssets failed: Asset to consolidate to is not registered. %s"), *Result.GetError());
return false;
}
TArray<UObject*> ToConsolidationObjects;
ToConsolidationObjects.Reserve(AssetsToConsolidate.Num());
for (UObject* Object : AssetsToConsolidate)
{
if (TError<FString> Result = UE::EditorAssetUtils::IsARegisteredAsset(Object); Result.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("ConsolidateAssets failed: Asset '%s' to consolidate is not registered. %s"), *Object->GetName(), *Result.GetError());
return false;
}
if (AssetToConsolidateTo->GetClass() != Object->GetClass())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("ConsolidateAssets failed: The asset '%s' doesn't have the same class as the AssetToConsolidateTo."), *Object->GetName());
return false;
}
ToConsolidationObjects.Add(Object);
}
// Perform the object consolidation
const bool bShowDeleteConfirmation = false;
ObjectTools::FConsolidationResults ConsResults = ObjectTools::ConsolidateObjects(AssetToConsolidateTo, ToConsolidationObjects, bShowDeleteConfirmation);
// If the consolidation went off successfully with no failed objects
if (ConsResults.DirtiedPackages.Num() > 0 && ConsResults.FailedConsolidationObjs.Num() == 0)
{
const bool bOnlyIfIsDirty = false;
UEditorLoadingAndSavingUtils::SavePackages(ObjectPtrDecay(ConsResults.DirtiedPackages), bOnlyIfIsDirty);
}
// If the consolidation resulted in failed (partially consolidated) objects, do not save, and inform the user no save attempt was made
else if (ConsResults.FailedConsolidationObjs.Num() > 0)
{
UE_LOG(LogEditorAssetSubsystem, Warning, TEXT("ConsolidateAssets: Not all objects could be consolidated, no save has occurred"));
return false;
}
return true;
}
// Delete operations
bool UEditorAssetSubsystem::DeleteLoadedAsset(UObject* AssetToDelete)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return false;
}
if (TError<FString> Result = UE::EditorAssetUtils::IsARegisteredAsset(AssetToDelete, true/*bAllowSkipBrowsableTestForExternalObject*/); Result.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("DeleteLoadedAsset failed: Asset is not registered. %s"), *Result.GetError());
return false;
}
TArray<UObject*> AssetsToDelete;
AssetsToDelete.Add(AssetToDelete);
const bool bShowConfirmation = false;
return ObjectTools::ForceDeleteObjects(AssetsToDelete, bShowConfirmation) == AssetsToDelete.Num();
}
bool UEditorAssetSubsystem::DeleteLoadedAssets(const TArray<UObject*>& AssetsToDelete)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return false;
}
FString FailureReason;
for (UObject* Asset : AssetsToDelete)
{
if (TError<FString> Result = UE::EditorAssetUtils::IsARegisteredAsset(Asset, true/*bAllowSkipBrowsableTestForExternalObject*/); Result.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Warning, TEXT("DeleteLoadedAssets failed: An asset is not registered. %s"), *Result.GetError());
return false;
}
}
const bool bShowConfirmation = false;
return ObjectTools::ForceDeleteObjects(AssetsToDelete, bShowConfirmation) == AssetsToDelete.Num();
}
bool UEditorAssetSubsystem::DeleteAsset(const FString& AssetPathToDelete)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return false;
}
TValueOrError<UObject*, FString> AssetToDeleteResult = UE::EditorAssetUtils::LoadAssetFromPath(AssetPathToDelete);
if (AssetToDeleteResult.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("DeleteAsset failed: Could not find the source asset. %s"), *AssetToDeleteResult.GetError());
return false;
}
TArray<UObject*> AssetsToDelete;
AssetsToDelete.Add(AssetToDeleteResult.GetValue());
const bool bShowConfirmation = false;
return ObjectTools::ForceDeleteObjects(AssetsToDelete, bShowConfirmation) == AssetsToDelete.Num();
}
bool UEditorAssetSubsystem::DeleteDirectory(const FString& DirectoryPath)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return false;
}
FString FailureReason;
FString ValidDirectoryPath = EditorScriptingHelpers::ConvertAnyPathToLongPackagePath(DirectoryPath, FailureReason);
if (ValidDirectoryPath.IsEmpty())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("DeleteDirectory failed: Could not convert the path. %s"), *FailureReason);
return false;
}
FString FilePath = FPaths::ConvertRelativePathToFull(UPackageTools::PackageNameToFilename(ValidDirectoryPath + TEXT("/")));
if (FilePath.IsEmpty())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("DeleteDirectory failed: Could not convert the path '%s' to a full path."), *ValidDirectoryPath);
return false;
}
TArray<FAssetData> AssetDatas;
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
if (!AssetRegistryModule.Get().GetAssetsByPath(*ValidDirectoryPath, AssetDatas, true))
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("DeleteDirectory failed: Could not get asset data for the directory '%s'"), *ValidDirectoryPath);
return false;
}
// Load all assets
TArray<UObject*> LoadedAssets;
LoadedAssets.Reserve(AssetDatas.Num());
for (const FAssetData& AssetData : AssetDatas)
{
TValueOrError<UObject*, FString> LoadedObjectResult = UE::EditorAssetUtils::LoadAssetFromData(AssetData);
if (LoadedObjectResult.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("DeleteDirectory failed: Some assets couldn't be loaded. %s"), *LoadedObjectResult.GetError());
return false;
}
LoadedAssets.Add(LoadedObjectResult.GetValue());
}
const bool bShowConfirmation = false;
if (ObjectTools::ForceDeleteObjects(LoadedAssets, bShowConfirmation) != LoadedAssets.Num())
{
UE_LOG(LogEditorAssetSubsystem, Warning, TEXT("DeleteDirectory: Not all assets were deleted."));
return false;
}
// Remove the path from the Asset Registry
if (!AssetRegistryModule.Get().RemovePath(ValidDirectoryPath))
{
UE_LOG(LogEditorAssetSubsystem, Warning, TEXT("DeleteDirectory: Could not remove the directory from the Asset Registry."));
}
// Delete the directory from the drive
if (!UE::EditorAssetUtils::DeleteEmptyDirectoryFromDisk(ValidDirectoryPath))
{
UE_LOG(LogEditorAssetSubsystem, Warning, TEXT("DeleteDirectory: Could not remove the directory but its contained assets have been removed."));
return false;
}
return true;
}
// Duplicate operations
namespace UE::EditorAssetUtils
{
UObject* DuplicateAsset(UObject* SourceObject, const FString& DestinationAssetPath)
{
check(SourceObject);
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return nullptr;
}
FString FailureReason;
FString DestinationObjectPath = EditorScriptingHelpers::ConvertAnyPathToObjectPath(DestinationAssetPath, FailureReason);
if (DestinationObjectPath.IsEmpty())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("DuplicateAsset failed: The destination asset path '%s' is not valid. %s"), *DestinationAssetPath, *FailureReason);
return nullptr;
}
if (!EditorScriptingHelpers::IsAValidPathForCreateNewAsset(DestinationObjectPath, FailureReason))
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("DuplicateAsset failed: The destination path '%s' is not valid. %s"), *DestinationObjectPath, *FailureReason);
return nullptr;
}
// when IAssetTools::DuplicateAsset fails, it opens a modal, so check beforehand
if (FPackageName::DoesPackageExist(DestinationObjectPath))
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("DuplicateAsset failed: The asset at the path '%s' already exists."), *DestinationObjectPath);
return nullptr;
}
FString DestinationLongPackagePath = FPackageName::GetLongPackagePath(DestinationObjectPath);
FString DestinationObjectName = FPackageName::ObjectPathToObjectName(DestinationObjectPath);
// duplicate asset
FAssetToolsModule& Module = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools");
UObject* DuplicatedAsset = Module.Get().DuplicateAsset(DestinationObjectName, DestinationLongPackagePath, SourceObject);
return DuplicatedAsset;
}
}
UObject* UEditorAssetSubsystem::DuplicateLoadedAsset(UObject* SourceAsset, const FString& DestinationAssetPath)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
FString FailureReason;
if (TError<FString> Result = UE::EditorAssetUtils::IsARegisteredAsset(SourceAsset); Result.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("DuplicateAsset failed: Asset is not registered. %s"), *Result.GetError());
return nullptr;
}
return UE::EditorAssetUtils::DuplicateAsset(SourceAsset, DestinationAssetPath);
}
UObject* UEditorAssetSubsystem::DuplicateAsset(const FString& SourceAssetPath, const FString& DestinationAssetPath)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
FString FailureReason;
TValueOrError<UObject*, FString> SourceObjectResult = UE::EditorAssetUtils::LoadAssetFromPath(SourceAssetPath);
if (SourceObjectResult.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("DuplicateAsset failed: Could not find the source asset. %s"), *SourceObjectResult.GetError());
return nullptr;
}
return UE::EditorAssetUtils::DuplicateAsset(SourceObjectResult.GetValue(), DestinationAssetPath);
}
bool UEditorAssetSubsystem::DuplicateDirectory(const FString& SourceDirectoryPath, const FString& DestinationDirectoryPath)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return false;
}
TValueOrError<UE::EditorAssetUtils::FDirectoryRenamePaths, FString> PathsResult = UE::EditorAssetUtils::GetDirectoryRenamePaths(SourceDirectoryPath, DestinationDirectoryPath);
if (PathsResult.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("DuplicateDirectory failed: Could not duplicate directory. %s"), *PathsResult.GetError());
return false;
}
if (TError<FString> Result = UE::EditorAssetUtils::SetupDirectoryRename(PathsResult.GetValue()); Result.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("DuplicateDirectory failed: Could not duplicate directory. %s"), *Result.GetError());
return false;
}
TValueOrError<UE::EditorAssetUtils::FDirectoryRenameAssetPaths, FString> AssetPathsResult = UE::EditorAssetUtils::GetDirectoryRenameAssetPaths(PathsResult.GetValue());
if (AssetPathsResult.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("DuplicateDirectory failed: Could not duplicate directory. %s"), *AssetPathsResult.GetError());
return false;
}
// Prepare data
bool bHaveAFailedAsset = false;
UE::EditorAssetUtils::FDirectoryRenameAssetPaths& AssetPaths = AssetPathsResult.GetValue();
if (AssetPaths.SourceDirectoryAssetDatas.IsEmpty())
{
UE_LOG(LogEditorAssetSubsystem, Log, TEXT("DuplicateDirectory: No assets to duplicate."));
}
FAssetToolsModule& Module = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools");
for (int32 Index = 0; Index < AssetPaths.SourceDirectoryAssetDatas.Num(); ++Index)
{
UObject* DuplicatedAsset = Module.Get().DuplicateAsset(AssetPaths.SourceDirectoryAssetDatas[Index].AssetName.ToString(), FPackageName::GetLongPackagePath(AssetPaths.DestinationDirectoryAssetPaths[Index]), AssetPaths.SourceDirectoryAssetDatas[Index].GetAsset());
if (DuplicatedAsset == nullptr)
{
UE_LOG(LogEditorAssetSubsystem, Warning, TEXT("DuplicateDirectory failed: Could not duplicate object '%s'"), *AssetPaths.SourceDirectoryAssetDatas[Index].GetObjectPathString());
bHaveAFailedAsset = true;
}
}
// Make sure the Asset Registry knows about the new directory
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
AssetRegistryModule.Get().AddPath(PathsResult.GetValue().DestinationDirectoryPath);
return !bHaveAFailedAsset;
}
// Rename operations
namespace UE::EditorAssetUtils
{
bool RenameLoadedAsset(UObject* SourceObject, const FString& DestinationAssetPath)
{
check(SourceObject);
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return false;
}
FString FailureReason;
FString DestinationObjectPath = EditorScriptingHelpers::ConvertAnyPathToObjectPath(DestinationAssetPath, FailureReason);
if (DestinationObjectPath.IsEmpty())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("RenameAsset failed: The destination asset path '%s' is not valid. %s"), *DestinationAssetPath, *FailureReason);
return false;
}
if (!EditorScriptingHelpers::IsAValidPathForCreateNewAsset(DestinationObjectPath, FailureReason))
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("RenameAsset failed: The destination path '%s' is not valid. %s"), *DestinationObjectPath, *FailureReason);
return false;
}
// IAssetTools::Rename will do this check, but will suggest another location via a modal
if (FPackageName::DoesPackageExist(DestinationObjectPath))
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("RenameAsset failed: The destination path '%s' already exists."), *DestinationAssetPath);
return false;
}
FString DestinationLongPackagePath = FPackageName::GetLongPackagePath(DestinationObjectPath);
FString DestinationObjectName = FPackageName::ObjectPathToObjectName(DestinationObjectPath);
// rename asset
TArray<FAssetRenameData> AssetToRename;
AssetToRename.Add(FAssetRenameData(SourceObject, DestinationLongPackagePath, DestinationObjectName));
FAssetToolsModule& Module = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools");
return Module.Get().RenameAssets(AssetToRename);
}
}
bool UEditorAssetSubsystem::RenameLoadedAsset(UObject* SourceAsset, const FString& DestinationAssetPath)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
FString FailureReason;
if (TError<FString> Result = UE::EditorAssetUtils::IsARegisteredAsset(SourceAsset); Result.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("RenameAsset failed: Asset is not registered. %s"), *Result.GetError());
return false;
}
return UE::EditorAssetUtils::RenameLoadedAsset(SourceAsset, DestinationAssetPath);
}
bool UEditorAssetSubsystem::RenameAsset(const FString& SourceAssetPath, const FString& DestinationAssetPath)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
TValueOrError<UObject*, FString> SourceObjectResult = UE::EditorAssetUtils::LoadAssetFromPath(SourceAssetPath);
if (SourceObjectResult.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("RenameAsset failed: Could not find the source asset. %s"), *SourceObjectResult.GetError());
return false;
}
return UE::EditorAssetUtils::RenameLoadedAsset(SourceObjectResult.GetValue(), DestinationAssetPath);
}
bool UEditorAssetSubsystem::RenameDirectory(const FString& SourceDirectoryPath, const FString& DestinationDirectoryPath)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return false;
}
TValueOrError<UE::EditorAssetUtils::FDirectoryRenamePaths, FString> PathsResult = UE::EditorAssetUtils::GetDirectoryRenamePaths(SourceDirectoryPath, DestinationDirectoryPath);
if (PathsResult.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("RenameDirectory failed: Could not rename directory. %s"), *PathsResult.GetError());
return false;
}
if (TError<FString> Result = UE::EditorAssetUtils::SetupDirectoryRename(PathsResult.GetValue()); Result.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("RenameDirectory failed: Could not rename directory. %s"), *Result.GetError());
return false;
}
TValueOrError<UE::EditorAssetUtils::FDirectoryRenameAssetPaths, FString> AssetPathsResult = UE::EditorAssetUtils::GetDirectoryRenameAssetPaths(PathsResult.GetValue());
if (AssetPathsResult.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("RenameDirectory failed: Could not rename directory. %s"), *AssetPathsResult.GetError());
return false;
}
// Prepare data
if (AssetPathsResult.GetValue().SourceDirectoryAssetDatas.IsEmpty())
{
UE_LOG(LogEditorAssetSubsystem, Log, TEXT("RenameDirectory. No assets to rename."));
}
const int32 NumAssetsToRename = AssetPathsResult.GetValue().SourceDirectoryAssetDatas.Num();
if (NumAssetsToRename > 0)
{
TArray<FAssetRenameData> AssetsToRename;
AssetsToRename.Reserve(NumAssetsToRename);
FScopedSlowTask SlowTask((float)NumAssetsToRename, NSLOCTEXT("EditorUtilities", "RenameDirectorySlowTaskLabel", "Renaming directory and assets."));
for (int32 Index = 0; Index < AssetPathsResult.GetValue().SourceDirectoryAssetDatas.Num(); ++Index)
{
// Try to load the asset, since if it has not previously been loaded, the rename process will fail to resolve the object in AssetRenameManager
TValueOrError<UObject*, FString> LoadResult = UE::EditorAssetUtils::LoadAssetFromData(AssetPathsResult.GetValue().SourceDirectoryAssetDatas[Index]);
if (LoadResult.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("RenameDirectory failed: Some assets couldn't be renamed. %s"), *LoadResult.GetError());
return false;
}
AssetsToRename.Add(FAssetRenameData(AssetPathsResult.GetValue().SourceDirectoryAssetDatas[Index].GetSoftObjectPath(), FSoftObjectPath(AssetPathsResult.GetValue().DestinationDirectoryAssetPaths[Index])));
SlowTask.EnterProgressFrame(1.f);
}
// Rename the assets
FAssetToolsModule& Module = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools");
if (!Module.Get().RenameAssets(AssetsToRename))
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("RenameDirectory failed: Could not rename the assets."));
return false;
}
}
// Delete the old directory
if (!UE::EditorAssetUtils::DeleteEmptyDirectoryFromDisk(PathsResult.GetValue().SourceDirectoryPath))
{
UE_LOG(LogEditorAssetSubsystem, Warning, TEXT("RenameDirectory: Could not delete the original directory but the assets have been renamed."));
}
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
AssetRegistryModule.Get().RemovePath(PathsResult.GetValue().SourceDirectoryPath);
AssetRegistryModule.Get().AddPath(PathsResult.GetValue().DestinationDirectoryPath);
return true;
}
bool UEditorAssetSubsystem::SetDirtyFlag(UObject* Object, const bool bDirtyState)
{
if (!IsValid(Object) || !Object->IsAsset())
{
return false;
}
UPackage* Pkg = Object->GetOutermost();
if (!Pkg || Pkg == GetTransientPackage() || Pkg->ContainsMap())
{
return false;
}
bool bOldDirtyValue = Pkg->IsDirty();
if (bOldDirtyValue != bDirtyState)
{
//If we need to change the state, return true if the state has change to the one specified
Pkg->SetDirtyFlag(bDirtyState);
return bDirtyState == Pkg->IsDirty();
}
return true;
}
// Checkout operations
namespace UE::EditorAssetUtils
{
bool Checkout(const TArray<UPackage*>& Packages)
{
if (Packages.Num() > 0)
{
// Checkout without a prompt
TArray<UPackage*>* PackagesCheckedOut = nullptr;
const bool bErrorIfAlreadyCheckedOut = false;
ECommandResult::Type Result = FEditorFileUtils::CheckoutPackages(Packages, PackagesCheckedOut, bErrorIfAlreadyCheckedOut);
return Result == ECommandResult::Succeeded;
}
return true;
}
}
bool UEditorAssetSubsystem::CheckoutLoadedAsset(UObject* AssetToCheckout)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return false;
}
if (TError<FString> Result = UE::EditorAssetUtils::IsARegisteredAsset(AssetToCheckout, true/*bAllowSkipBrowsableTestForExternalObject*/); Result.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("CheckoutLoadedAsset failed: Asset is not registered. %s"), *Result.GetError());
return false;
}
TArray<UPackage*> Packages;
Packages.Add(AssetToCheckout->GetPackage()); // Fully load and check out is done in FEditorFileUtils::CheckoutPackages
return UE::EditorAssetUtils::Checkout(Packages);
}
bool UEditorAssetSubsystem::CheckoutLoadedAssets(const TArray<UObject*>& AssetsToCheckout)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return false;
}
TArray<UPackage*> Packages;
Packages.Reserve(AssetsToCheckout.Num());
for (UObject* AssetToCheckout: AssetsToCheckout)
{
if (TError<FString> Result = UE::EditorAssetUtils::IsARegisteredAsset(AssetToCheckout, true/*bAllowSkipBrowsableTestForExternalObject*/); Result.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Warning, TEXT("CheckoutLoadedAssets: An asset is not registered. %s"), *Result.GetError());
continue;
}
Packages.Add(AssetToCheckout->GetPackage());
}
return UE::EditorAssetUtils::Checkout(Packages);
}
bool UEditorAssetSubsystem::CheckoutAsset(const FString& AssetsToCheckout)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return false;
}
TValueOrError<UObject*, FString> LoadedAssetResult = UE::EditorAssetUtils::LoadAssetFromPath(AssetsToCheckout);
if (LoadedAssetResult.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("CheckoutAsset failed: Could not load asset: %s"), *LoadedAssetResult.GetError());
return false;
}
TArray<UPackage*> Packages;
Packages.Add(LoadedAssetResult.GetValue()->GetPackage()); // Fully loading and checking out is done in UEditorLoadingAndSavingUtils::SavePackages
return UE::EditorAssetUtils::Checkout(Packages);
}
bool UEditorAssetSubsystem::CheckoutDirectory(const FString& DirectoryPath, bool bRecursive)
{
using UE::EditorAssetUtils::EPackageEnumerationFilter;
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE())
{
return false;
}
TArray<UPackage*> Packages;
if (TError<FString> Result = UE::EditorAssetUtils::EnumeratePackagesInDirectory(DirectoryPath, EPackageEnumerationFilter::NoFilter, bRecursive, Packages); Result.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("CheckoutAssets failed: Could not get packages in directory. %s"), *Result.GetError());
return false;
}
return UE::EditorAssetUtils::Checkout(Packages);
}
// Save operations
bool UEditorAssetSubsystem::SaveLoadedAsset(UObject* AssetToSave, bool bOnlyIfIsDirty)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if ((UE::Editor::Private::AllowSavingAssetsDuringPIE == 0 && !EditorScriptingHelpers::CheckIfInEditorAndPIE()) || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return false;
}
if (TError<FString> Result = UE::EditorAssetUtils::IsARegisteredAsset(AssetToSave, true/*bAllowSkipBrowsableTestForExternalObject*/); Result.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("SaveLoadedAsset failed: Asset is not registered. %s"), *Result.GetError());
return false;
}
TArray<UPackage*> Packages;
Packages.Add(AssetToSave->GetPackage()); // Fully loading and checking out is done in UEditorLoadingAndSavingUtils::SavePackages
// Save without a prompt
return UEditorLoadingAndSavingUtils::SavePackages(Packages, bOnlyIfIsDirty);
}
bool UEditorAssetSubsystem::SaveLoadedAssets(const TArray<UObject*>& AssetsToSave, bool bOnlyIfIsDirty)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if ((UE::Editor::Private::AllowSavingAssetsDuringPIE == 0 && !EditorScriptingHelpers::CheckIfInEditorAndPIE()) || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return false;
}
FString FailureReason;
TArray<UPackage*> Packages;
Packages.Reserve(AssetsToSave.Num());
for (UObject* AssetToSave : AssetsToSave)
{
if (TError<FString> Result = UE::EditorAssetUtils::IsARegisteredAsset(AssetToSave, true/*bAllowSkipBrowsableTestForExternalObject*/); Result.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Warning, TEXT("SaveLoadedAsset: An asset is not registered. %s"), *Result.GetError());
}
else
{
Packages.Add(AssetToSave->GetPackage());
}
}
// Save without a prompt
return UEditorLoadingAndSavingUtils::SavePackages(Packages, bOnlyIfIsDirty);
}
bool UEditorAssetSubsystem::SaveAsset(const FString& AssetsToSave, bool bOnlyIfIsDirty)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if ((UE::Editor::Private::AllowSavingAssetsDuringPIE == 0 && !EditorScriptingHelpers::CheckIfInEditorAndPIE()) || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return false;
}
TValueOrError<UObject*, FString> LoadedAssetResult = UE::EditorAssetUtils::LoadAssetFromPath(AssetsToSave);
if (LoadedAssetResult.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("SaveAsset failed: Could not load asset: %s"), *LoadedAssetResult.GetError());
return false;
}
TArray<UPackage*> Packages;
Packages.Add(LoadedAssetResult.GetValue()->GetPackage()); // Fully load and check out is done in UEditorLoadingAndSavingUtils::SavePackages
// Save without a prompt
return UEditorLoadingAndSavingUtils::SavePackages(Packages, bOnlyIfIsDirty);
}
int32 UEditorAssetSubsystem::GetAssetFilenameLengthForCooking(const FString& AssetPath)
{
const FString PackagePath = FPackageName::ObjectPathToPackageName(AssetPath);
return AssetViewUtils::GetPackageLengthForCooking(PackagePath, FEngineBuildSettings::IsInternalBuild());
}
int32 UEditorAssetSubsystem::GetLoadedAssetFilenameLengthForCooking(const UObject* Asset)
{
if (Asset)
{
return AssetViewUtils::GetPackageLengthForCooking(Asset->GetPackage()->GetName(), FEngineBuildSettings::IsInternalBuild());
}
return 0;
}
bool UEditorAssetSubsystem::SaveDirectory(const FString& DirectoryPath, bool bOnlyIfIsDirty, bool bRecursive)
{
using UE::EditorAssetUtils::EPackageEnumerationFilter;
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if ((UE::Editor::Private::AllowSavingAssetsDuringPIE == 0 && !EditorScriptingHelpers::CheckIfInEditorAndPIE()) || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return false;
}
TArray<UPackage*> Packages;
const EPackageEnumerationFilter PackageFilter = bOnlyIfIsDirty ? EPackageEnumerationFilter::OnlyDirty : EPackageEnumerationFilter::NoFilter;
if (TError<FString> Result = UE::EditorAssetUtils::EnumeratePackagesInDirectory(DirectoryPath, PackageFilter, bRecursive, Packages); Result.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("SaveDirectory failed: Could not save. %s"), *Result.GetError());
return false;
}
// Save without a prompt
return UEditorLoadingAndSavingUtils::SavePackages(Packages, bOnlyIfIsDirty);
}
// Directory operations
bool UEditorAssetSubsystem::DoesDirectoryExist(const FString& DirectoryPath)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return false;
}
FString FailureReason;
FString DirectoryPackageName = EditorScriptingHelpers::ConvertAnyPathToLongPackagePath(DirectoryPath, FailureReason);
if (DirectoryPackageName.IsEmpty())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("DoesDirectoryExist failed: Could not convert path: %s"), *FailureReason);
return false;
}
// First check the Asset Registry's cache
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
if (AssetRegistryModule.Get().PathExists(DirectoryPackageName))
{
return true;
}
// Get the on-disk path
FString FilePath = FPaths::ConvertRelativePathToFull(UPackageTools::PackageNameToFilename(DirectoryPackageName + TEXT("/")));
if (FilePath.IsEmpty())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("DoesDirectoryExist failed: Could not convert path '%s' to a full path."), *DirectoryPackageName);
return false;
}
// If it's not cached, check if it's on disk
if (IFileManager::Get().DirectoryExists(*FilePath))
{
// Cache it in the Asset Registry if it exists
AssetRegistryModule.Get().AddPath(DirectoryPackageName);
return true;
}
return false;
}
bool UEditorAssetSubsystem::DoesDirectoryContainAssets(const FString& DirectoryPath, bool bRecursive)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return false;
}
FString FailureReason;
FString DirectoryPackageName = EditorScriptingHelpers::ConvertAnyPathToLongPackagePath(DirectoryPath, FailureReason);
if (DirectoryPackageName.IsEmpty())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("DoesDirectoryContainAssets failed: Could not convert path: %s"), *FailureReason);
return false;
}
FString FilePath = FPaths::ConvertRelativePathToFull(UPackageTools::PackageNameToFilename(DirectoryPackageName + TEXT("/")));
if (FilePath.IsEmpty())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("DoesDirectoryContainAssets failed: Could not convert path '%s' to a full path."), *DirectoryPackageName);
return false;
}
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
return AssetRegistryModule.Get().HasAssets(*DirectoryPackageName, bRecursive);
}
bool UEditorAssetSubsystem::MakeDirectory(const FString& DirectoryPath)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return false;
}
FString FailureReason;
FString DirectoryPackageName = EditorScriptingHelpers::ConvertAnyPathToLongPackagePath(DirectoryPath, FailureReason);
if (DirectoryPackageName.IsEmpty())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("MakeDirectory failed: Could not convert the path '%s'. %s"), *DirectoryPath, *FailureReason);
return false;
}
FString FilePath = FPaths::ConvertRelativePathToFull(FPackageName::LongPackageNameToFilename(DirectoryPackageName + TEXT("/")));
if (FilePath.IsEmpty())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("MakeDirectory failed: Could not convert the path '%s' to a full path."), *DirectoryPackageName);
return false;
}
// If the folder has not yet been created, create it before we try to add it to the AssetRegistry.
if (!IFileManager::Get().DirectoryExists(*FilePath))
{
const bool bTree = true;
if (IFileManager::Get().MakeDirectory(*FilePath, bTree))
{
// Add to the AssetRegistry cache
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
AssetRegistryModule.Get().AddPath(DirectoryPackageName);
return true;
}
}
return false;
}
// List operations
TArray<FString> UEditorAssetSubsystem::ListAssets(const FString& DirectoryPath, bool bRecursive, bool bIncludeFolder)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
TArray<FString> AssetPaths;
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return AssetPaths;
}
TArray<FAssetData> AssetDatas;
FString DirectoryPackageName;
// if there is a valid asset data (i.e. path belongs to a file)
if (TValueOrError<FAssetData, FString> AssetDataResult = UE::EditorAssetUtils::FindAssetDataFromAnyPath(DirectoryPath); AssetDataResult.HasValue() && AssetDataResult.GetValue().IsValid())
{
AssetDatas.Add(AssetDataResult.GetValue());
}
// path may belong to a directory
else if (TError<FString> Result = UE::EditorAssetUtils::EnumerateAssetsInDirectory(DirectoryPath, bRecursive, AssetDatas, DirectoryPackageName); Result.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("ListAssets failed: Could not enumerate assets in directory '%s'. %s"), *DirectoryPath, *Result.GetError());
return AssetPaths;
}
if (AssetDatas.Num() > 0)
{
AssetPaths.Reserve(AssetDatas.Num());
for (const FAssetData& AssetData : AssetDatas)
{
AssetPaths.Add(AssetData.GetObjectPathString());
}
}
if (bIncludeFolder && !DirectoryPackageName.IsEmpty())
{
TArray<FString> SubPaths;
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(FName("AssetRegistry"));
AssetRegistryModule.Get().GetSubPaths(DirectoryPackageName, SubPaths, bRecursive);
for (const FString& SubPath : SubPaths)
{
if (SubPath.Contains(DirectoryPath) && SubPath != DirectoryPath)
{
AssetPaths.Add(SubPath + TEXT('/'));
}
}
}
AssetPaths.Sort();
return AssetPaths;
}
TArray<FString> UEditorAssetSubsystem::ListAssetsByTagValue(FName TagName, const FString& TagValue)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
TArray<FString> Result;
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return Result;
}
if (TagName == NAME_None)
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("ListAssetsByTagValue failed: The empty tag '' is not valid."));
return Result;
}
TMultiMap<FName, FString> TagValues;
TagValues.Add(TagName, TagValue);
TArray<FAssetData> FoundAssets;
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
if (!AssetRegistryModule.Get().GetAssetsByTagValues(TagValues, FoundAssets))
{
UE_LOG(LogEditorAssetSubsystem, Warning, TEXT("ListAssetsByTagValue failed: Could not get assets with given tag values."));
return Result;
}
for (const FAssetData& AssetData : FoundAssets)
{
FString ObjectPathStr = AssetData.PackageName.ToString();
Result.Add(ObjectPathStr);
}
return Result;
}
TMap<FName, FString> UEditorAssetSubsystem::GetTagValues(const FString& AssetPath)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
TMap<FName, FString> Result;
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return Result;
}
TValueOrError<FAssetData, FString> AssetDataResult = UE::EditorAssetUtils::FindAssetDataFromAnyPath(AssetPath);
if (AssetDataResult.HasError())
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("GetTagValues failed: Could not get Asset Data. %s"), *AssetDataResult.GetError());
return Result;
}
for (TPair<FName, FAssetTagValueRef> Itt : AssetDataResult.GetValue().TagsAndValues)
{
Result.Add(Itt.Key, Itt.Value.AsString());
}
return Result;
}
TMap<FName, FString> UEditorAssetSubsystem::GetMetadataTagValues(UObject* Object)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
TMap<FName, FString> Result;
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE())
{
return Result;
}
#if WITH_EDITORONLY_DATA
if (Object)
{
TMap<FName, FString>* TagValues = FMetaData::GetMapForObject(Object);
if (TagValues != nullptr)
{
return *TagValues;
}
}
else
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("GetMetadataTagValues failed: Object is null."));
}
#endif // WITH_EDITORONLY_DATA
return Result;
}
FString UEditorAssetSubsystem::GetMetadataTag(UObject* Object, FName Tag)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE())
{
return FString();
}
#if WITH_EDITORONLY_DATA
if (!Object)
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("GetMetadataTag failed: Object is null."));
return FString();
}
return Object->GetPackage()->GetMetaData().GetValue(Object, Tag);
#else
return FString();
#endif // WITH_EDITORONLY_DATA
}
void UEditorAssetSubsystem::SetMetadataTag(UObject* Object, FName Tag, const FString& Value)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE())
{
return;
}
#if WITH_EDITORONLY_DATA
if (!Object)
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("SetMetadataTag failed: Object is null."));
return;
}
Object->Modify();
Object->GetPackage()->GetMetaData().SetValue(Object, Tag, *Value);
#endif // WITH_EDITORONLY_DATA
}
void UEditorAssetSubsystem::RemoveMetadataTag(UObject* Object, FName Tag)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE())
{
return;
}
#if WITH_EDITORONLY_DATA
if (!Object)
{
UE_LOG(LogEditorAssetSubsystem, Error, TEXT("RemoveMetadataTag failed: Object is null."));
return;
}
Object->Modify();
Object->GetPackage()->GetMetaData().RemoveValue(Object, Tag);
#endif // WITH_EDITORONLY_DATA
}
void UEditorAssetSubsystem::AddOnExtractAssetFromFile(FOnExtractAssetFromFileDynamic Delegate)
{
OnExtractAssetFromFileDynamicArray.Add(Delegate);
}
void UEditorAssetSubsystem::RemoveOnExtractAssetFromFile(FOnExtractAssetFromFileDynamic Delegate)
{
OnExtractAssetFromFileDynamicArray.Remove(Delegate);
}
void UEditorAssetSubsystem::CallOnExtractAssetFromFileDynamicArray(
const TArray<FString>& Files,
TArray<FAssetData>& OutAssetDataArray)
{
TArray<FAssetData> LocalArray;
for (FOnExtractAssetFromFileDynamic& Delegate : OnExtractAssetFromFileDynamicArray)
{
Delegate.ExecuteIfBound(Files, LocalArray);
OutAssetDataArray += LocalArray;
LocalArray.Reset();
}
}
TArray<FAssetData> UEditorAssetSubsystem::GetAllAssetsByMetaDataTags(
const TSet<FName>& RequiredTags,
const TSet<UClass*>& AllowedClasses
)
{
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
TArray<FAssetData> Result;
if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded())
{
return Result;
}
FARFilter Filter;
Filter.TagsAndValues.Reserve(RequiredTags.Num());
for (FName RequiredTag : RequiredTags)
{
Filter.TagsAndValues.Add(RequiredTag);
}
Filter.ClassPaths.Reserve(RequiredTags.Num());
for (UClass* AllowedClass : AllowedClasses)
{
Filter.ClassPaths.Add(FTopLevelAssetPath(AllowedClass));
}
IAssetRegistry::Get()->GetAssets(Filter, Result);
return Result;
}
bool UEditorAssetSubsystem::SortByMetaData(
TArray<FAssetData>& Assets,
FName MetaDataTag,
EEditorAssetMetaDataSortType MetaDataType,
EEditorAssetSortOrder SortOrder
)
{
using namespace UE::EditorAssetUtils;
switch (MetaDataType)
{
case EEditorAssetMetaDataSortType::String:
return Sort<FString>(Assets, MetaDataTag, [](const FString& String, FString& Result){ Result = String; return true; }, SortOrder);
case EEditorAssetMetaDataSortType::Numeric:
return Sort<double>(Assets, MetaDataTag, [](const FString& String, double& Result)
{
if (String.IsNumeric())
{
Result = FCString::Atod(*String);
return true;
}
return false;
}, SortOrder);
case EEditorAssetMetaDataSortType::DateTime:
return Sort<FDateTime>(Assets, MetaDataTag, [](const FString& String, FDateTime& Result){ return FDateTime::Parse(String, Result); }, SortOrder);
default:
checkNoEntry();
return false;
}
}