// Copyright Epic Games, Inc. All Rights Reserved. #include "Subsystems/EditorAssetSubsystem.h" #include "AssetRegistry/IAssetRegistry.h" #include "AssetRegistry/AssetRegistryModule.h" #include "AssetToolsModule.h" #include "Editor.h" #include "EditorScriptingHelpers.h" #include "Experimental/Async/LazyEvent.h" #include "FileHelpers.h" #include "HAL/FileManager.h" #include "Misc/PackageName.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::EditorAssetUtils { struct ErrorTag{}; } template using TError = TValueOrError; namespace UE::EditorAssetUtils { bool EnsureAssetsLoaded() { FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked("AssetRegistry"); if (IAssetRegistry& AssetRegistry = AssetRegistryModule.Get(); AssetRegistry.IsLoadingAssets()) { // Event is used like a condition variable here UE::FLazyEvent AssetRegistryLoadEvent{EEventMode::ManualReset}; FDelegateHandle DelegateHandle = AssetRegistry.OnFilesLoaded().AddLambda([&AssetRegistryLoadEvent] { AssetRegistryLoadEvent.Trigger(); }); // open some message here AssetRegistryLoadEvent.Wait(); AssetRegistry.OnFilesLoaded().Remove(DelegateHandle); } return true; } TValueOrError FindAssetDataFromAnyPath(const FString& AnyAssetPath) { FString FailureReason; FString ObjectPath = EditorScriptingHelpers::ConvertAnyPathToObjectPath(AnyAssetPath, FailureReason); if (ObjectPath.IsEmpty()) { return MakeError(FailureReason); } FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked("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."), *ObjectPath)); } return MakeValue(AssetData); } TValueOrError 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.ObjectPath.ToString())); } else if (!FoundObject->IsAsset()) { return MakeError(FString::Printf(TEXT("'%s' is not a valid asset."), *AssetData.ObjectPath.ToString())); } return MakeValue(FoundObject); } TValueOrError LoadAssetFromPath(const FString& AssetPath) { TValueOrError AssetDataResult = FindAssetDataFromAnyPath(AssetPath); if (AssetDataResult.HasError()) { return MakeError(AssetDataResult.StealError()); } return LoadAssetFromData(AssetDataResult.GetValue()); } TError IsARegisteredAsset(UObject* Object) { if (!IsValid(Object)) { return MakeError(TEXT("The Asset is not valid.")); } if (Cast(Object)) { return MakeError(FString::Printf(TEXT("The object is of the base class type '%s'"), *Object->GetClass()->GetName())); } if (!ObjectTools::IsObjectBrowsable(Object)) { return MakeError(FString::Printf(TEXT("The object '%s' is not an asset."), *Object->GetName())); } UPackage* Package = Object->GetPackage(); FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked("AssetRegistry"); FAssetData AssetData = AssetRegistryModule.Get().GetAssetByObjectPath(Package->GetFName()); if (!AssetData.IsValid()) { return MakeError(FString::Printf(TEXT("The AssetData '%s' could not be found in the Asset Registry."), *Package->GetName())); } return MakeValue(); } TError EnumerateAssetsInDirectory(const FString& AnyPathDirectoryPath, bool bRecursive, TArray& OutResult, FString& OutDirectoryPath) { OutResult.Reset(); OutDirectoryPath.Reset(); FString FailureReason; OutDirectoryPath = EditorScriptingHelpers::ConvertAnyPathToLongPackagePath(AnyPathDirectoryPath, FailureReason); if (OutDirectoryPath.IsEmpty()) { return MakeError(FailureReason); } FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked("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 }; TError EnumeratePackagesInDirectory(const FString& AnyDirectoryPath, EPackageEnumerationFilter EnumerationFilter, bool bRecursive, TArray& OutResult) { FString ValidDirectoryPath; TArray Assets; if (TError 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(); } 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; }; TValueOrError 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); } TError 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 SourceDirectoryAssetDatas; TArray DestinationDirectoryAssetPaths; }; TValueOrError 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 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, false); // 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(FPackageName::GetLongPackagePath(NewAssetPackageName)); } return MakeValue(AssetPaths); } } UEditorAssetSubsystem::UEditorAssetSubsystem() { } // 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 UnattendedScriptGuard(GIsRunningUnattendedScript, true); if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded()) { return nullptr; } TValueOrError 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 UnattendedScriptGuard(GIsRunningUnattendedScript, true); if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded()) { return nullptr; } TValueOrError LoadedAssetResult = UE::EditorAssetUtils::LoadAssetFromPath(AssetPath); if (LoadedAssetResult.HasError()) { UE_LOG(LogEditorAssetSubsystem, Error, TEXT("LoadBlueprintClass failed: %s"), *LoadedAssetResult.GetError()); return nullptr; } UBlueprint* Blueprint = Cast(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 UnattendedScriptGuard(GIsRunningUnattendedScript, true); if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded()) { return FString(); } if (TError Result = UE::EditorAssetUtils::IsARegisteredAsset(LoadedAsset); Result.HasError()) { UE_LOG(LogEditorAssetSubsystem, Error, TEXT("GetPathNameForLoadedAsset failed: %s"), *Result.GetError()); } return LoadedAsset->GetPathName(); } FAssetData UEditorAssetSubsystem::FindAssetData(const FString& AssetPath) { TGuardValue 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 UnattendedScriptGuard(GIsRunningUnattendedScript, true); if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded()) { return false; } FString FailureReason; FString ObjectPath = EditorScriptingHelpers::ConvertAnyPathToObjectPath(AssetPath, FailureReason); if (ObjectPath.IsEmpty()) { UE_LOG(LogEditorAssetSubsystem, Error, TEXT("DoesAssetExist failed: %s"), *FailureReason); return false; } FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked("AssetRegistry"); FAssetData AssetData = AssetRegistryModule.Get().GetAssetByObjectPath(*ObjectPath); if (!AssetData.IsValid()) { return false; } return true; } bool UEditorAssetSubsystem::DoAssetsExist(const TArray& AssetPaths) { TGuardValue UnattendedScriptGuard(GIsRunningUnattendedScript, true); if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded()) { return false; } FString FailureReason; FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked("AssetRegistry"); for (const FString& Path : AssetPaths) { FString ObjectPath = EditorScriptingHelpers::ConvertAnyPathToObjectPath(Path, FailureReason); if (ObjectPath.IsEmpty()) { UE_LOG(LogEditorAssetSubsystem, Error, TEXT("DoAssetsExist failed: %s"), *FailureReason); return false; } FAssetData AssetData = AssetRegistryModule.Get().GetAssetByObjectPath(*ObjectPath); if (!AssetData.IsValid()) { return false; } } return true; } TArray UEditorAssetSubsystem::FindPackageReferencersForAsset(const FString& AnyAssetPath, bool bLoadAssetsToConfirm) { TGuardValue UnattendedScriptGuard(GIsRunningUnattendedScript, true); TArray Result; if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded()) { return Result; } FString FailureReason; FString AssetPath = EditorScriptingHelpers::ConvertAnyPathToObjectPath(AnyAssetPath, FailureReason); if (AssetPath.IsEmpty()) { UE_LOG(LogEditorAssetSubsystem, Error, TEXT("FindAssetPackageReferencers failed: %s"), *FailureReason); return Result; } // Find the reference in packages. Load them to confirm the reference. TArray PackageReferencers; { FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); AssetRegistryModule.Get().GetReferencers(*FPackageName::ObjectPathToPackageName(AssetPath), PackageReferencers, UE::AssetRegistry::EDependencyCategory::Package); } if (bLoadAssetsToConfirm) { // Load the asset to confirm TValueOrError 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 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 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& AssetsToConsolidate) { TGuardValue 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 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 ToConsolidationObjects; ToConsolidationObjects.Reserve(AssetsToConsolidate.Num()); for (UObject* Object : AssetsToConsolidate) { if (TError 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(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 UnattendedScriptGuard(GIsRunningUnattendedScript, true); if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded()) { return false; } if (TError Result = UE::EditorAssetUtils::IsARegisteredAsset(AssetToDelete); Result.HasError()) { UE_LOG(LogEditorAssetSubsystem, Error, TEXT("DeleteLoadedAsset failed: Asset is not registered. %s"), *Result.GetError()); return false; } TArray AssetsToDelete; AssetsToDelete.Add(AssetToDelete); const bool bShowConfirmation = false; return ObjectTools::ForceDeleteObjects(AssetsToDelete, bShowConfirmation) == AssetsToDelete.Num(); } bool UEditorAssetSubsystem::DeleteLoadedAssets(const TArray& AssetsToDelete) { TGuardValue UnattendedScriptGuard(GIsRunningUnattendedScript, true); if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded()) { return false; } FString FailureReason; for (UObject* Asset : AssetsToDelete) { if (TError Result = UE::EditorAssetUtils::IsARegisteredAsset(Asset); 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 UnattendedScriptGuard(GIsRunningUnattendedScript, true); if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded()) { return false; } TValueOrError 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 AssetsToDelete; AssetsToDelete.Add(AssetToDeleteResult.GetValue()); const bool bShowConfirmation = false; return ObjectTools::ForceDeleteObjects(AssetsToDelete, bShowConfirmation) == AssetsToDelete.Num(); } bool UEditorAssetSubsystem::DeleteDirectory(const FString& DirectoryPath) { TGuardValue 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 AssetDatas; FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked("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 LoadedAssets; LoadedAssets.Reserve(AssetDatas.Num()); for (const FAssetData& AssetData : AssetDatas) { TValueOrError 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("AssetTools"); UObject* DuplicatedAsset = Module.Get().DuplicateAsset(DestinationObjectName, DestinationLongPackagePath, SourceObject); return DuplicatedAsset; } } UObject* UEditorAssetSubsystem::DuplicateLoadedAsset(UObject* SourceAsset, const FString& DestinationAssetPath) { TGuardValue UnattendedScriptGuard(GIsRunningUnattendedScript, true); FString FailureReason; if (TError 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 UnattendedScriptGuard(GIsRunningUnattendedScript, true); FString FailureReason; TValueOrError 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 UnattendedScriptGuard(GIsRunningUnattendedScript, true); if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded()) { return false; } TValueOrError 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 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 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("AssetTools"); for (int32 Index = 0; Index < AssetPaths.SourceDirectoryAssetDatas.Num(); ++Index) { UObject* DuplicatedAsset = Module.Get().DuplicateAsset(AssetPaths.SourceDirectoryAssetDatas[Index].AssetName.ToString(), 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].ObjectPath.ToString()); bHaveAFailedAsset = true; } } // Make sure the Asset Registry knows about the new directory FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked("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 AssetToRename; AssetToRename.Add(FAssetRenameData(SourceObject, DestinationLongPackagePath, DestinationObjectName)); FAssetToolsModule& Module = FModuleManager::GetModuleChecked("AssetTools"); return Module.Get().RenameAssets(AssetToRename); } } bool UEditorAssetSubsystem::RenameLoadedAsset(UObject* SourceAsset, const FString& DestinationAssetPath) { TGuardValue UnattendedScriptGuard(GIsRunningUnattendedScript, true); FString FailureReason; if (TError 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 UnattendedScriptGuard(GIsRunningUnattendedScript, true); TValueOrError 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 UnattendedScriptGuard(GIsRunningUnattendedScript, true); if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded()) { return false; } TValueOrError 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 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 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.")); } TArray AssetsToRename; AssetsToRename.Reserve(AssetPathsResult.GetValue().SourceDirectoryAssetDatas.Num()); for (int32 Index = 0; Index < AssetPathsResult.GetValue().SourceDirectoryAssetDatas.Num(); ++Index) { AssetsToRename.Add(FAssetRenameData(AssetPathsResult.GetValue().SourceDirectoryAssetDatas[Index].ObjectPath.ToString(), AssetPathsResult.GetValue().DestinationDirectoryAssetPaths[Index])); } // Rename the assets FAssetToolsModule& Module = FModuleManager::GetModuleChecked("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("AssetRegistry"); AssetRegistryModule.Get().RemovePath(PathsResult.GetValue().SourceDirectoryPath); AssetRegistryModule.Get().AddPath(PathsResult.GetValue().DestinationDirectoryPath); return true; } // Checkout operations namespace UE::EditorAssetUtils { bool Checkout(const TArray& Packages) { if (Packages.Num() > 0) { // Checkout without a prompt TArray* 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 UnattendedScriptGuard(GIsRunningUnattendedScript, true); if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded()) { return false; } if (TError Result = UE::EditorAssetUtils::IsARegisteredAsset(AssetToCheckout); Result.HasError()) { UE_LOG(LogEditorAssetSubsystem, Error, TEXT("CheckoutLoadedAsset failed: Asset is not registered. %s"), *Result.GetError()); return false; } TArray 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& AssetsToCheckout) { TGuardValue UnattendedScriptGuard(GIsRunningUnattendedScript, true); if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded()) { return false; } TArray Packages; Packages.Reserve(AssetsToCheckout.Num()); for (UObject* AssetToCheckout: AssetsToCheckout) { if (TError Result = UE::EditorAssetUtils::IsARegisteredAsset(AssetToCheckout); 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 UnattendedScriptGuard(GIsRunningUnattendedScript, true); if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded()) { return false; } TValueOrError 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 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 UnattendedScriptGuard(GIsRunningUnattendedScript, true); if (!EditorScriptingHelpers::CheckIfInEditorAndPIE()) { return false; } TArray Packages; if (TError 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 UnattendedScriptGuard(GIsRunningUnattendedScript, true); if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded()) { return false; } if (TError Result = UE::EditorAssetUtils::IsARegisteredAsset(AssetToSave); Result.HasError()) { UE_LOG(LogEditorAssetSubsystem, Error, TEXT("SaveLoadedAsset failed: Asset is not registered. %s"), *Result.GetError()); return false; } TArray 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& AssetsToSave, bool bOnlyIfIsDirty) { TGuardValue UnattendedScriptGuard(GIsRunningUnattendedScript, true); if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded()) { return false; } FString FailureReason; TArray Packages; Packages.Reserve(AssetsToSave.Num()); for (UObject* AssetToSave : AssetsToSave) { if (TError Result = UE::EditorAssetUtils::IsARegisteredAsset(AssetToSave); 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 UnattendedScriptGuard(GIsRunningUnattendedScript, true); if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded()) { return false; } TValueOrError 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 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); } bool UEditorAssetSubsystem::SaveDirectory(const FString& DirectoryPath, bool bOnlyIfIsDirty, bool bRecursive) { using UE::EditorAssetUtils::EPackageEnumerationFilter; TGuardValue UnattendedScriptGuard(GIsRunningUnattendedScript, true); if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded()) { return false; } TArray Packages; const EPackageEnumerationFilter PackageFilter = bOnlyIfIsDirty ? EPackageEnumerationFilter::OnlyDirty : EPackageEnumerationFilter::NoFilter; if (TError 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 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("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 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("AssetRegistry"); return AssetRegistryModule.Get().HasAssets(*DirectoryPackageName, bRecursive); } bool UEditorAssetSubsystem::MakeDirectory(const FString& DirectoryPath) { TGuardValue 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"), *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("AssetRegistry"); AssetRegistryModule.Get().AddPath(DirectoryPackageName); return true; } } return false; } // List operations TArray UEditorAssetSubsystem::ListAssets(const FString& DirectoryPath, bool bRecursive, bool bIncludeFolder) { TGuardValue UnattendedScriptGuard(GIsRunningUnattendedScript, true); TArray AssetPaths; if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded()) { return AssetPaths; } TArray AssetDatas; FString DirectoryPackageName; // if there is a valid asset data (i.e. path belongs to a file) if (TValueOrError AssetDataResult = UE::EditorAssetUtils::FindAssetDataFromAnyPath(DirectoryPath); AssetDataResult.HasValue() && AssetDataResult.GetValue().IsValid()) { AssetDatas.Add(AssetDataResult.GetValue()); } // path may belong to a directory else if (TError 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) { AssetDatas.Reserve(AssetDatas.Num()); for (const FAssetData& AssetData : AssetDatas) { AssetPaths.Add(AssetData.ObjectPath.ToString()); } } if (bIncludeFolder && !DirectoryPackageName.IsEmpty()) { TArray SubPaths; FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(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 UEditorAssetSubsystem::ListAssetsByTagValue(FName TagName, const FString& TagValue) { TGuardValue UnattendedScriptGuard(GIsRunningUnattendedScript, true); TArray 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 TagValues; TagValues.Add(TagName, TagValue); TArray FoundAssets; FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked("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 UEditorAssetSubsystem::GetTagValues(const FString& AssetPath) { TGuardValue UnattendedScriptGuard(GIsRunningUnattendedScript, true); TMap Result; if (!EditorScriptingHelpers::CheckIfInEditorAndPIE() || !UE::EditorAssetUtils::EnsureAssetsLoaded()) { return Result; } TValueOrError 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 Itt : AssetDataResult.GetValue().TagsAndValues) { Result.Add(Itt.Key, Itt.Value.AsString()); } return Result; } TMap UEditorAssetSubsystem::GetMetadataTagValues(UObject* Object) { TGuardValue UnattendedScriptGuard(GIsRunningUnattendedScript, true); TMap Result; if (!EditorScriptingHelpers::CheckIfInEditorAndPIE()) { return Result; } #if WITH_EDITORONLY_DATA if (Object) { TMap* TagValues = UMetaData::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 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); #endif // WITH_EDITORONLY_DATA return FString(); } void UEditorAssetSubsystem::SetMetadataTag(UObject* Object, FName Tag, const FString& Value) { TGuardValue 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 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 }