Files
UnrealEngineUWP/Engine/Source/Developer/CollectionManager/Private/CollectionManager.cpp
bob tellez b0bbbcd9bf [Backout] - CL33005160
[FYI] robert.millar
Original CL Desc
-----------------------------------------------------------------
Making collection manager thread safe to allow future work to run content browser search in parallel.
Deprecate GetLastError and replace with FText* output parameters on fallible methods.
Use hierarchy of guard types to document access requirements for internal methods.
Separate cache updates from cache access. Cache updates must be performed explicitly so that read->write lock update and possible preemption is clear.

#rb logan.buchy

[CL 33030091 by bob tellez in ue5-main branch]
2024-04-17 02:10:31 -04:00

2111 lines
72 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "CollectionManager.h"
#include "HAL/FileManager.h"
#include "Misc/Paths.h"
#include "Containers/Ticker.h"
#include "CollectionManagerLog.h"
#include "CollectionManagerModule.h"
#include "FileCache.h"
#include "Misc/FileHelper.h"
#include "Misc/ScopeRWLock.h"
#include "Async/ParallelFor.h"
#include "Misc/CommandLine.h"
#include "SourceControlPreferences.h"
#define LOCTEXT_NAMESPACE "CollectionManager"
FCollectionManagerCache::FCollectionManagerCache(FAvailableCollectionsMap& InAvailableCollections)
: AvailableCollections(InAvailableCollections)
{
bIsCachedCollectionNamesFromGuidsDirty = true;
bIsCachedObjectsDirty = true;
bIsCachedHierarchyDirty = true;
bIsCachedColorsDirty = true;
}
void FCollectionManagerCache::HandleCollectionAdded()
{
bIsCachedCollectionNamesFromGuidsDirty = true;
}
void FCollectionManagerCache::HandleCollectionRemoved()
{
bIsCachedCollectionNamesFromGuidsDirty = true;
bIsCachedObjectsDirty = true;
bIsCachedHierarchyDirty = true;
bIsCachedColorsDirty = true;
}
void FCollectionManagerCache::HandleCollectionChanged()
{
bIsCachedObjectsDirty = true;
bIsCachedHierarchyDirty = true;
bIsCachedColorsDirty = true;
}
const FGuidToCollectionNamesMap& FCollectionManagerCache::GetCachedCollectionNamesFromGuids() const
{
if (bIsCachedCollectionNamesFromGuidsDirty)
{
CachedCollectionNamesFromGuids_Internal.Reset();
bIsCachedCollectionNamesFromGuidsDirty = false;
const double CacheStartTime = FPlatformTime::Seconds();
for (const auto& AvailableCollection : AvailableCollections)
{
const FCollectionNameType& CollectionKey = AvailableCollection.Key;
const TSharedRef<FCollection>& Collection = AvailableCollection.Value;
CachedCollectionNamesFromGuids_Internal.Add(Collection->GetCollectionGuid(), CollectionKey);
}
UE_LOG(LogCollectionManager, Verbose, TEXT("Rebuilt the GUID cache for %d collections in %0.6f seconds"), AvailableCollections.Num(), FPlatformTime::Seconds() - CacheStartTime);
}
return CachedCollectionNamesFromGuids_Internal;
}
const FCollectionObjectsMap& FCollectionManagerCache::GetCachedObjects() const
{
if (bIsCachedObjectsDirty)
{
CachedObjects_Internal.Reset();
bIsCachedObjectsDirty = false;
const double CacheStartTime = FPlatformTime::Seconds();
for (const auto& AvailableCollection : AvailableCollections)
{
const FCollectionNameType& CollectionKey = AvailableCollection.Key;
const TSharedRef<FCollection>& Collection = AvailableCollection.Value;
const TSet<FSoftObjectPath>& ObjectsInCollection = Collection->GetObjectSet();
if (ObjectsInCollection.Num() > 0)
{
auto RebuildCachedObjectsWorker = [&](const FCollectionNameType& InCollectionKey, ECollectionRecursionFlags::Flag InReason) -> ERecursiveWorkerFlowControl
{
// The worker reason will tell us why this collection is being processed (eg, because it is a parent of the collection we told it to DoWork on),
// however, the reason this object exists in that parent collection is because a child collection contains it, and this is the reason we need
// to put into the FObjectCollectionInfo, since that's what we'll test against later when we do the "do my children contain this object"? test
// That's why we flip the reason logic here...
ECollectionRecursionFlags::Flag ReasonObjectInCollection = InReason;
switch (InReason)
{
case ECollectionRecursionFlags::Parents:
ReasonObjectInCollection = ECollectionRecursionFlags::Children;
break;
case ECollectionRecursionFlags::Children:
ReasonObjectInCollection = ECollectionRecursionFlags::Parents;
break;
default:
break;
}
for (const FSoftObjectPath& ObjectPath : ObjectsInCollection)
{
TArray<FObjectCollectionInfo>& ObjectCollectionInfos = CachedObjects_Internal.FindOrAdd(ObjectPath);
FObjectCollectionInfo* ObjectInfoPtr = ObjectCollectionInfos.FindByPredicate([&](const FObjectCollectionInfo& InCollectionInfo) { return InCollectionInfo.CollectionKey == InCollectionKey; });
if (ObjectInfoPtr)
{
ObjectInfoPtr->Reason |= ReasonObjectInCollection;
}
else
{
ObjectCollectionInfos.Add(FObjectCollectionInfo(InCollectionKey, ReasonObjectInCollection));
}
}
return ERecursiveWorkerFlowControl::Continue;
};
// Recursively process all collections so that they know they contain these objects (and why!)
RecursionHelper_DoWork(CollectionKey, ECollectionRecursionFlags::All, RebuildCachedObjectsWorker);
}
}
UE_LOG(LogCollectionManager, Verbose, TEXT("Rebuilt the object cache for %d collections in %0.6f seconds (found %d objects)"), AvailableCollections.Num(), FPlatformTime::Seconds() - CacheStartTime, CachedObjects_Internal.Num());
}
return CachedObjects_Internal;
}
const FCollectionHierarchyMap& FCollectionManagerCache::GetCachedHierarchy() const
{
if (bIsCachedHierarchyDirty)
{
CachedHierarchy_Internal.Reset();
bIsCachedHierarchyDirty = false;
const FGuidToCollectionNamesMap& CachedCollectionNamesFromGuids = GetCachedCollectionNamesFromGuids();
const double CacheStartTime = FPlatformTime::Seconds();
for (const auto& AvailableCollection : AvailableCollections)
{
const FCollectionNameType& CollectionKey = AvailableCollection.Key;
const TSharedRef<FCollection>& Collection = AvailableCollection.Value;
// Make sure this is a known parent GUID before adding it to the map
const FGuid& ParentCollectionGuid = Collection->GetParentCollectionGuid();
if (CachedCollectionNamesFromGuids.Contains(ParentCollectionGuid))
{
auto& CollectionChildren = CachedHierarchy_Internal.FindOrAdd(ParentCollectionGuid);
CollectionChildren.AddUnique(Collection->GetCollectionGuid());
}
}
UE_LOG(LogCollectionManager, Verbose, TEXT("Rebuilt the hierarchy cache for %d collections in %0.6f seconds"), AvailableCollections.Num(), FPlatformTime::Seconds() - CacheStartTime);
}
return CachedHierarchy_Internal;
}
const FCollectionColorArray& FCollectionManagerCache::GetCachedColors() const
{
if (bIsCachedColorsDirty)
{
CachedColors_Internal.Reset();
bIsCachedColorsDirty = false;
const double CacheStartTime = FPlatformTime::Seconds();
for (const auto& AvailableCollection : AvailableCollections)
{
const TSharedRef<FCollection>& Collection = AvailableCollection.Value;
if (const TOptional<FLinearColor> CollectionColor = Collection->GetCollectionColor())
{
// Only add if not already present (ignores near matches too)
const bool bExists = CachedColors_Internal.ContainsByPredicate([CurrentColor = CollectionColor.GetValue()](const FLinearColor& Color) { return CurrentColor.Equals(Color); });
if (!bExists)
{
CachedColors_Internal.Add(CollectionColor.GetValue());
}
}
}
UE_LOG(LogCollectionManager, Verbose, TEXT("Rebuilt the color cache for %d collections in %0.6f seconds"), AvailableCollections.Num(), FPlatformTime::Seconds() - CacheStartTime);
}
return CachedColors_Internal;
}
void FCollectionManagerCache::RecursionHelper_DoWork(const FCollectionNameType& InCollectionKey, const ECollectionRecursionFlags::Flags InRecursionMode, FRecursiveWorkerFunc InWorkerFunc) const
{
if ((InRecursionMode & ECollectionRecursionFlags::Self) && InWorkerFunc(InCollectionKey, ECollectionRecursionFlags::Self) == ERecursiveWorkerFlowControl::Stop)
{
return;
}
if ((InRecursionMode & ECollectionRecursionFlags::Parents) && RecursionHelper_DoWorkOnParents(InCollectionKey, InWorkerFunc) == ERecursiveWorkerFlowControl::Stop)
{
return;
}
if ((InRecursionMode & ECollectionRecursionFlags::Children) && RecursionHelper_DoWorkOnChildren(InCollectionKey, InWorkerFunc) == ERecursiveWorkerFlowControl::Stop)
{
return;
}
}
FCollectionManagerCache::ERecursiveWorkerFlowControl FCollectionManagerCache::RecursionHelper_DoWorkOnParents(const FCollectionNameType& InCollectionKey, FRecursiveWorkerFunc InWorkerFunc) const
{
const TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(InCollectionKey);
if (CollectionRefPtr)
{
const FGuidToCollectionNamesMap& CachedCollectionNamesFromGuids = GetCachedCollectionNamesFromGuids();
const FCollectionNameType* const ParentCollectionKeyPtr = CachedCollectionNamesFromGuids.Find((*CollectionRefPtr)->GetParentCollectionGuid());
if (ParentCollectionKeyPtr)
{
if (InWorkerFunc(*ParentCollectionKeyPtr, ECollectionRecursionFlags::Parents) == ERecursiveWorkerFlowControl::Stop || RecursionHelper_DoWorkOnParents(*ParentCollectionKeyPtr, InWorkerFunc) == ERecursiveWorkerFlowControl::Stop)
{
return ERecursiveWorkerFlowControl::Stop;
}
}
}
return ERecursiveWorkerFlowControl::Continue;
}
FCollectionManagerCache::ERecursiveWorkerFlowControl FCollectionManagerCache::RecursionHelper_DoWorkOnChildren(const FCollectionNameType& InCollectionKey, FRecursiveWorkerFunc InWorkerFunc) const
{
const TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(InCollectionKey);
if (CollectionRefPtr)
{
const FCollectionHierarchyMap& CachedHierarchy = GetCachedHierarchy();
const TArray<FGuid>* const ChildCollectionGuids = CachedHierarchy.Find((*CollectionRefPtr)->GetCollectionGuid());
if (ChildCollectionGuids)
{
for (const FGuid& ChildCollectionGuid : *ChildCollectionGuids)
{
const FGuidToCollectionNamesMap& CachedCollectionNamesFromGuids = GetCachedCollectionNamesFromGuids();
const FCollectionNameType* const ChildCollectionKeyPtr = CachedCollectionNamesFromGuids.Find(ChildCollectionGuid);
if (ChildCollectionKeyPtr)
{
if (InWorkerFunc(*ChildCollectionKeyPtr, ECollectionRecursionFlags::Children) == ERecursiveWorkerFlowControl::Stop || RecursionHelper_DoWorkOnChildren(*ChildCollectionKeyPtr, InWorkerFunc) == ERecursiveWorkerFlowControl::Stop)
{
return ERecursiveWorkerFlowControl::Stop;
}
}
}
}
}
return ERecursiveWorkerFlowControl::Continue;
}
FCollectionManager::FCollectionManager()
: CollectionCache(AvailableCollections)
{
LastError = LOCTEXT("Error_Unknown", "None");
CollectionFolders[ECollectionShareType::CST_Local] = FPaths::ProjectSavedDir() / TEXT("Collections");
CollectionFolders[ECollectionShareType::CST_Private] = FPaths::GameUserDeveloperDir() / TEXT("Collections");
CollectionFolders[ECollectionShareType::CST_Shared] = FPaths::ProjectContentDir() / TEXT("Collections");
CollectionExtension = TEXT("collection");
bNoFixupRedirectors = FParse::Param(FCommandLine::Get(), TEXT("NoFixupRedirectorsInCollections"));
LoadCollections();
// Watch for changes that may happen outside of the collection manager
for (int32 CacheIdx = 0; CacheIdx < ECollectionShareType::CST_All; ++CacheIdx)
{
const FString& CollectionFolder = CollectionFolders[CacheIdx];
if (CollectionFolder.IsEmpty())
{
continue;
}
// Make sure the folder we want to watch exists on disk
if (!IFileManager::Get().MakeDirectory(*CollectionFolder, true))
{
continue;
}
DirectoryWatcher::FFileCacheConfig FileCacheConfig(FPaths::ConvertRelativePathToFull(CollectionFolder), FString());
FileCacheConfig.DetectMoves(false);
FileCacheConfig.RequireFileHashes(false);
CollectionFileCaches[CacheIdx] = MakeShareable(new DirectoryWatcher::FFileCache(FileCacheConfig));
}
TickFileCacheDelegateHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateRaw(this, &FCollectionManager::TickFileCache), 1.0f);
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
FCollectionManager::~FCollectionManager()
{
FTSTicker::GetCoreTicker().RemoveTicker(TickFileCacheDelegateHandle);
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
bool FCollectionManager::HasCollections() const
{
return AvailableCollections.Num() > 0;
}
void FCollectionManager::GetCollections(TArray<FCollectionNameType>& OutCollections) const
{
OutCollections.Reserve(AvailableCollections.Num());
for (const auto& AvailableCollection : AvailableCollections)
{
const FCollectionNameType& CollectionKey = AvailableCollection.Key;
OutCollections.Add(CollectionKey);
}
}
void FCollectionManager::GetCollections(FName CollectionName, TArray<FCollectionNameType>& OutCollections) const
{
for (int32 CacheIdx = 0; CacheIdx < ECollectionShareType::CST_All; ++CacheIdx)
{
if (AvailableCollections.Contains(FCollectionNameType(CollectionName, ECollectionShareType::Type(CacheIdx))))
{
OutCollections.Add(FCollectionNameType(CollectionName, ECollectionShareType::Type(CacheIdx)));
}
}
}
void FCollectionManager::GetCollectionNames(ECollectionShareType::Type ShareType, TArray<FName>& CollectionNames) const
{
for (const auto& AvailableCollection : AvailableCollections)
{
const FCollectionNameType& CollectionKey = AvailableCollection.Key;
if (ShareType == ECollectionShareType::CST_All || ShareType == CollectionKey.Type)
{
CollectionNames.AddUnique(CollectionKey.Name);
}
}
}
void FCollectionManager::GetRootCollections(TArray<FCollectionNameType>& OutCollections) const
{
const FGuidToCollectionNamesMap& CachedCollectionNamesFromGuids = CollectionCache.GetCachedCollectionNamesFromGuids();
OutCollections.Reserve(AvailableCollections.Num());
for (const auto& AvailableCollection : AvailableCollections)
{
const FCollectionNameType& CollectionKey = AvailableCollection.Key;
const TSharedRef<FCollection>& Collection = AvailableCollection.Value;
// A root collection either has no parent GUID, or a parent GUID that cannot currently be found - the check below handles both
if (!CachedCollectionNamesFromGuids.Contains(Collection->GetParentCollectionGuid()))
{
OutCollections.Add(CollectionKey);
}
}
}
void FCollectionManager::GetRootCollectionNames(ECollectionShareType::Type ShareType, TArray<FName>& CollectionNames) const
{
const FGuidToCollectionNamesMap& CachedCollectionNamesFromGuids = CollectionCache.GetCachedCollectionNamesFromGuids();
for (const auto& AvailableCollection : AvailableCollections)
{
const FCollectionNameType& CollectionKey = AvailableCollection.Key;
const TSharedRef<FCollection>& Collection = AvailableCollection.Value;
if (ShareType == ECollectionShareType::CST_All || ShareType == CollectionKey.Type)
{
// A root collection either has no parent GUID, or a parent GUID that cannot currently be found - the check below handles both
if (!CachedCollectionNamesFromGuids.Contains(Collection->GetParentCollectionGuid()))
{
CollectionNames.AddUnique(CollectionKey.Name);
}
}
}
}
void FCollectionManager::GetChildCollections(FName CollectionName, ECollectionShareType::Type ShareType, TArray<FCollectionNameType>& OutCollections) const
{
const FGuidToCollectionNamesMap& CachedCollectionNamesFromGuids = CollectionCache.GetCachedCollectionNamesFromGuids();
const FCollectionHierarchyMap& CachedHierarchy = CollectionCache.GetCachedHierarchy();
auto GetChildCollectionsInternal = [&](const FCollectionNameType& InCollectionKey)
{
const TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(InCollectionKey);
if (CollectionRefPtr)
{
const auto* ChildCollectionGuids = CachedHierarchy.Find((*CollectionRefPtr)->GetCollectionGuid());
if (ChildCollectionGuids)
{
for (const FGuid& ChildCollectionGuid : *ChildCollectionGuids)
{
const FCollectionNameType* const ChildCollectionKeyPtr = CachedCollectionNamesFromGuids.Find(ChildCollectionGuid);
if (ChildCollectionKeyPtr)
{
OutCollections.Add(*ChildCollectionKeyPtr);
}
}
}
}
};
if (ShareType == ECollectionShareType::CST_All)
{
// Asked for all share types, find children in the specified collection name in any cache
for (int32 CacheIdx = 0; CacheIdx < ECollectionShareType::CST_All; ++CacheIdx)
{
GetChildCollectionsInternal(FCollectionNameType(CollectionName, ECollectionShareType::Type(CacheIdx)));
}
}
else
{
GetChildCollectionsInternal(FCollectionNameType(CollectionName, ShareType));
}
}
void FCollectionManager::GetChildCollectionNames(FName CollectionName, ECollectionShareType::Type ShareType, ECollectionShareType::Type ChildShareType, TArray<FName>& CollectionNames) const
{
const FGuidToCollectionNamesMap& CachedCollectionNamesFromGuids = CollectionCache.GetCachedCollectionNamesFromGuids();
const FCollectionHierarchyMap& CachedHierarchy = CollectionCache.GetCachedHierarchy();
auto GetChildCollectionsInternal = [&](const FCollectionNameType& InCollectionKey)
{
const TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(InCollectionKey);
if (CollectionRefPtr)
{
const auto* ChildCollectionGuids = CachedHierarchy.Find((*CollectionRefPtr)->GetCollectionGuid());
if (ChildCollectionGuids)
{
for (const FGuid& ChildCollectionGuid : *ChildCollectionGuids)
{
const FCollectionNameType* const ChildCollectionKeyPtr = CachedCollectionNamesFromGuids.Find(ChildCollectionGuid);
if (ChildCollectionKeyPtr && (ChildShareType == ECollectionShareType::CST_All || ChildShareType == ChildCollectionKeyPtr->Type))
{
CollectionNames.AddUnique(ChildCollectionKeyPtr->Name);
}
}
}
}
};
if (ShareType == ECollectionShareType::CST_All)
{
// Asked for all share types, find children in the specified collection name in any cache
for (int32 CacheIdx = 0; CacheIdx < ECollectionShareType::CST_All; ++CacheIdx)
{
GetChildCollectionsInternal(FCollectionNameType(CollectionName, ECollectionShareType::Type(CacheIdx)));
}
}
else
{
GetChildCollectionsInternal(FCollectionNameType(CollectionName, ShareType));
}
}
TOptional<FCollectionNameType> FCollectionManager::GetParentCollection(FName CollectionName, ECollectionShareType::Type ShareType) const
{
const TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(FCollectionNameType(CollectionName, ShareType));
if (CollectionRefPtr)
{
const FGuidToCollectionNamesMap& CachedCollectionNamesFromGuids = CollectionCache.GetCachedCollectionNamesFromGuids();
const FCollectionNameType* const ParentCollectionKeyPtr = CachedCollectionNamesFromGuids.Find((*CollectionRefPtr)->GetParentCollectionGuid());
if (ParentCollectionKeyPtr)
{
return *ParentCollectionKeyPtr;
}
}
return TOptional<FCollectionNameType>();
}
bool FCollectionManager::CollectionExists(FName CollectionName, ECollectionShareType::Type ShareType) const
{
if (ShareType == ECollectionShareType::CST_All)
{
// Asked to check all share types...
for (int32 CacheIdx = 0; CacheIdx < ECollectionShareType::CST_All; ++CacheIdx)
{
if (AvailableCollections.Contains(FCollectionNameType(CollectionName, ECollectionShareType::Type(CacheIdx))))
{
// Collection exists in at least one cache
return true;
}
}
// Collection not found in any cache
return false;
}
else
{
return AvailableCollections.Contains(FCollectionNameType(CollectionName, ShareType));
}
}
bool FCollectionManager::GetAssetsInCollection(FName CollectionName, ECollectionShareType::Type ShareType, TArray<FSoftObjectPath>& AssetsPaths, ECollectionRecursionFlags::Flags RecursionMode) const
{
bool bFoundAssets = false;
auto GetAssetsInCollectionWorker = [&](const FCollectionNameType& InCollectionKey, ECollectionRecursionFlags::Flag InReason) -> FCollectionManagerCache::ERecursiveWorkerFlowControl
{
const TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(InCollectionKey);
if (CollectionRefPtr)
{
(*CollectionRefPtr)->GetAssetsInCollection(AssetsPaths);
bFoundAssets = true;
}
return FCollectionManagerCache::ERecursiveWorkerFlowControl::Continue;
};
if (ShareType == ECollectionShareType::CST_All)
{
// Asked for all share types, find assets in the specified collection name in any cache
for (int32 CacheIdx = 0; CacheIdx < ECollectionShareType::CST_All; ++CacheIdx)
{
CollectionCache.RecursionHelper_DoWork(FCollectionNameType(CollectionName, ECollectionShareType::Type(CacheIdx)), RecursionMode, GetAssetsInCollectionWorker);
}
}
else
{
CollectionCache.RecursionHelper_DoWork(FCollectionNameType(CollectionName, ShareType), RecursionMode, GetAssetsInCollectionWorker);
}
return bFoundAssets;
}
bool FCollectionManager::GetClassesInCollection(FName CollectionName, ECollectionShareType::Type ShareType, TArray<FTopLevelAssetPath>& ClassPaths, ECollectionRecursionFlags::Flags RecursionMode) const
{
bool bFoundClasses = false;
auto GetClassesInCollectionWorker = [&](const FCollectionNameType& InCollectionKey, ECollectionRecursionFlags::Flag InReason) -> FCollectionManagerCache::ERecursiveWorkerFlowControl
{
const TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(InCollectionKey);
if (CollectionRefPtr)
{
(*CollectionRefPtr)->GetClassesInCollection(ClassPaths);
bFoundClasses = true;
}
return FCollectionManagerCache::ERecursiveWorkerFlowControl::Continue;
};
if (ShareType == ECollectionShareType::CST_All)
{
// Asked for all share types, find classes in the specified collection name in any cache
for (int32 CacheIdx = 0; CacheIdx < ECollectionShareType::CST_All; ++CacheIdx)
{
CollectionCache.RecursionHelper_DoWork(FCollectionNameType(CollectionName, ECollectionShareType::Type(CacheIdx)), RecursionMode, GetClassesInCollectionWorker);
}
}
else
{
CollectionCache.RecursionHelper_DoWork(FCollectionNameType(CollectionName, ShareType), RecursionMode, GetClassesInCollectionWorker);
}
return bFoundClasses;
}
bool FCollectionManager::GetObjectsInCollection(FName CollectionName, ECollectionShareType::Type ShareType, TArray<FSoftObjectPath>& ObjectPaths, ECollectionRecursionFlags::Flags RecursionMode) const
{
bool bFoundObjects = false;
auto GetObjectsInCollectionWorker = [&](const FCollectionNameType& InCollectionKey, ECollectionRecursionFlags::Flag InReason) -> FCollectionManagerCache::ERecursiveWorkerFlowControl
{
const TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(InCollectionKey);
if (CollectionRefPtr)
{
(*CollectionRefPtr)->GetObjectsInCollection(ObjectPaths);
bFoundObjects = true;
}
return FCollectionManagerCache::ERecursiveWorkerFlowControl::Continue;
};
if (ShareType == ECollectionShareType::CST_All)
{
// Asked for all share types, find classes in the specified collection name in any cache
for (int32 CacheIdx = 0; CacheIdx < ECollectionShareType::CST_All; ++CacheIdx)
{
CollectionCache.RecursionHelper_DoWork(FCollectionNameType(CollectionName, ECollectionShareType::Type(CacheIdx)), RecursionMode, GetObjectsInCollectionWorker);
}
}
else
{
CollectionCache.RecursionHelper_DoWork(FCollectionNameType(CollectionName, ShareType), RecursionMode, GetObjectsInCollectionWorker);
}
return bFoundObjects;
}
void FCollectionManager::GetCollectionsContainingObject(const FSoftObjectPath& ObjectPath, ECollectionShareType::Type ShareType, TArray<FName>& OutCollectionNames, ECollectionRecursionFlags::Flags RecursionMode) const
{
const FCollectionObjectsMap& CachedObjects = CollectionCache.GetCachedObjects();
const auto* ObjectCollectionInfosPtr = CachedObjects.Find(ObjectPath);
if (ObjectCollectionInfosPtr)
{
for (const FObjectCollectionInfo& ObjectCollectionInfo : *ObjectCollectionInfosPtr)
{
if ((ShareType == ECollectionShareType::CST_All || ShareType == ObjectCollectionInfo.CollectionKey.Type) && (RecursionMode & ObjectCollectionInfo.Reason) != 0)
{
OutCollectionNames.Add(ObjectCollectionInfo.CollectionKey.Name);
}
}
}
}
void FCollectionManager::GetCollectionsContainingObject(const FSoftObjectPath& ObjectPath, TArray<FCollectionNameType>& OutCollections, ECollectionRecursionFlags::Flags RecursionMode) const
{
const FCollectionObjectsMap& CachedObjects = CollectionCache.GetCachedObjects();
const auto* ObjectCollectionInfosPtr = CachedObjects.Find(ObjectPath);
if (ObjectCollectionInfosPtr)
{
OutCollections.Reserve(OutCollections.Num() + ObjectCollectionInfosPtr->Num());
for (const FObjectCollectionInfo& ObjectCollectionInfo : *ObjectCollectionInfosPtr)
{
if ((RecursionMode & ObjectCollectionInfo.Reason) != 0)
{
OutCollections.Add(ObjectCollectionInfo.CollectionKey);
}
}
}
}
void FCollectionManager::GetCollectionsContainingObjects(const TArray<FSoftObjectPath>& ObjectPaths, TMap<FCollectionNameType, TArray<FSoftObjectPath>>& OutCollectionsAndMatchedObjects, ECollectionRecursionFlags::Flags RecursionMode) const
{
const FCollectionObjectsMap& CachedObjects = CollectionCache.GetCachedObjects();
for (const FSoftObjectPath& ObjectPath : ObjectPaths)
{
const auto* ObjectCollectionInfosPtr = CachedObjects.Find(ObjectPath);
if (ObjectCollectionInfosPtr)
{
for (const FObjectCollectionInfo& ObjectCollectionInfo : *ObjectCollectionInfosPtr)
{
if ((RecursionMode & ObjectCollectionInfo.Reason) != 0)
{
TArray<FSoftObjectPath>& MatchedObjects = OutCollectionsAndMatchedObjects.FindOrAdd(ObjectCollectionInfo.CollectionKey);
MatchedObjects.Add(ObjectPath);
}
}
}
}
}
FString FCollectionManager::GetCollectionsStringForObject(const FSoftObjectPath& ObjectPath, ECollectionShareType::Type ShareType, ECollectionRecursionFlags::Flags RecursionMode, bool bFullPaths) const
{
const FCollectionObjectsMap& CachedObjects = CollectionCache.GetCachedObjects();
const auto* ObjectCollectionInfosPtr = CachedObjects.Find(ObjectPath);
if (ObjectCollectionInfosPtr)
{
TArray<FString> CollectionNameStrings;
TArray<FString> CollectionPathStrings;
auto GetCollectionsStringForObjectWorker = [&](const FCollectionNameType& InCollectionKey, ECollectionRecursionFlags::Flag InReason) -> FCollectionManagerCache::ERecursiveWorkerFlowControl
{
CollectionPathStrings.Insert(InCollectionKey.Name.ToString(), 0);
return FCollectionManagerCache::ERecursiveWorkerFlowControl::Continue;
};
for (const FObjectCollectionInfo& ObjectCollectionInfo : *ObjectCollectionInfosPtr)
{
if ((ShareType == ECollectionShareType::CST_All || ShareType == ObjectCollectionInfo.CollectionKey.Type) && (RecursionMode & ObjectCollectionInfo.Reason) != 0)
{
if (bFullPaths)
{
CollectionPathStrings.Reset();
CollectionCache.RecursionHelper_DoWork(ObjectCollectionInfo.CollectionKey, ECollectionRecursionFlags::SelfAndParents, GetCollectionsStringForObjectWorker);
CollectionNameStrings.Add(FString::Join(CollectionPathStrings, TEXT("/")));
}
else
{
CollectionNameStrings.Add(ObjectCollectionInfo.CollectionKey.Name.ToString());
}
}
}
if (CollectionNameStrings.Num() > 0)
{
CollectionNameStrings.Sort();
return FString::Join(CollectionNameStrings, TEXT(", "));
}
}
return FString();
}
void FCollectionManager::CreateUniqueCollectionName(const FName& BaseName, ECollectionShareType::Type ShareType, FName& OutCollectionName) const
{
int32 IntSuffix = 1;
bool CollectionAlreadyExists = false;
do
{
if (IntSuffix <= 1)
{
OutCollectionName = BaseName;
}
else
{
OutCollectionName = *FString::Printf(TEXT("%s%d"), *BaseName.ToString(), IntSuffix);
}
CollectionAlreadyExists = CollectionExists(OutCollectionName, ShareType);
++IntSuffix;
}
while (CollectionAlreadyExists);
}
bool FCollectionManager::IsValidCollectionName(const FString& CollectionName, ECollectionShareType::Type ShareType) const
{
// Make sure we are not creating an FName that is too large
if (CollectionName.Len() >= NAME_SIZE)
{
LastError = FText::Format(LOCTEXT("Error_CollectionNameTooLong", "This collection name is too long ({0} characters), the maximum is {1}. Please choose a shorter name. Collection name: {2}"),
FText::AsNumber(CollectionName.Len()), FText::AsNumber(NAME_SIZE), FText::FromString(CollectionName));
return false;
}
const FName CollectionNameFinal = *CollectionName;
// Make sure the we actually have a new name set
if (CollectionNameFinal.IsNone())
{
LastError = LOCTEXT("Error_CollectionNameEmptyOrNone", "This collection name cannot be empty or 'None'.");
return false;
}
// Make sure the new name only contains valid characters
if (!CollectionNameFinal.IsValidXName(INVALID_OBJECTNAME_CHARACTERS INVALID_LONGPACKAGE_CHARACTERS, &LastError))
{
return false;
}
// Make sure we're not duplicating an existing collection name
if (CollectionExists(CollectionNameFinal, ShareType))
{
LastError = FText::Format(LOCTEXT("Error_CollectionAlreadyExists", "A collection already exists with the name '{0}'."), FText::FromName(CollectionNameFinal));
return false;
}
return true;
}
bool FCollectionManager::CreateCollection(FName CollectionName, ECollectionShareType::Type ShareType, ECollectionStorageMode::Type StorageMode)
{
if (!ensure(ShareType < ECollectionShareType::CST_All))
{
// Bad share type
LastError = LOCTEXT("Error_Internal", "There was an internal error.");
return false;
}
// Try to add the collection
const bool bUseSCC = ShouldUseSCC(ShareType);
const FString CollectionFilename = GetCollectionFilename(CollectionName, ShareType);
// Validate collection name as file name
bool bFilenameValid = FFileHelper::IsFilenameValidForSaving(CollectionName.ToString(), LastError);
if (!bFilenameValid)
{
return false;
}
TSharedRef<FCollection> NewCollection = MakeShareable(new FCollection(CollectionFilename, bUseSCC, StorageMode));
if (!AddCollection(NewCollection, ShareType))
{
// Failed to add the collection, it already exists
LastError = LOCTEXT("Error_AlreadyExists", "The collection already exists.");
return false;
}
constexpr bool bForceCommitToRevisionControl = true;
if (InternalSaveCollection(NewCollection, LastError, bForceCommitToRevisionControl))
{
CollectionFileCaches[ShareType]->IgnoreNewFile(NewCollection->GetSourceFilename());
// Collection saved!
CollectionCreatedEvent.Broadcast(FCollectionNameType(CollectionName, ShareType));
return true;
}
else
{
// Collection failed to save, remove it from the cache
RemoveCollection(NewCollection, ShareType);
return false;
}
}
bool FCollectionManager::RenameCollection(FName CurrentCollectionName, ECollectionShareType::Type CurrentShareType, FName NewCollectionName, ECollectionShareType::Type NewShareType)
{
if (!ensure(CurrentShareType < ECollectionShareType::CST_All) || !ensure(NewShareType < ECollectionShareType::CST_All))
{
// Bad share type
LastError = LOCTEXT("Error_Internal", "There was an internal error.");
return false;
}
const FCollectionNameType OriginalCollectionKey(CurrentCollectionName, CurrentShareType);
const FCollectionNameType NewCollectionKey(NewCollectionName, NewShareType);
TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(OriginalCollectionKey);
if (!CollectionRefPtr)
{
// The collection doesn't exist
LastError = LOCTEXT("Error_DoesntExist", "The collection doesn't exist.");
return false;
}
// Add the new collection
TSharedPtr<FCollection> NewCollection;
{
const bool bUseSCC = ShouldUseSCC(NewShareType);
const FString NewCollectionFilename = GetCollectionFilename(NewCollectionName, NewShareType);
// Create an exact copy of the collection using its new path - this will preserve its GUID and avoid losing hierarchy data
NewCollection = (*CollectionRefPtr)->Clone(NewCollectionFilename, bUseSCC, ECollectionCloneMode::Exact);
if (!AddCollection(NewCollection.ToSharedRef(), NewShareType))
{
// Failed to add the collection, it already exists
LastError = LOCTEXT("Error_AlreadyExists", "The collection already exists.");
return false;
}
bool bForceCommitToRevisionControl = true;
if (!InternalSaveCollection(NewCollection.ToSharedRef(), LastError, bForceCommitToRevisionControl))
{
// Collection failed to save, remove it from the cache
RemoveCollection(NewCollection.ToSharedRef(), NewShareType);
return false;
}
}
// Remove the old collection
{
if ((*CollectionRefPtr)->DeleteSourceFile(LastError))
{
CollectionFileCaches[CurrentShareType]->IgnoreDeletedFile((*CollectionRefPtr)->GetSourceFilename());
RemoveCollection(*CollectionRefPtr, CurrentShareType);
}
else
{
// Failed to remove the old collection, so remove the collection we created.
NewCollection->DeleteSourceFile(LastError);
RemoveCollection(NewCollection.ToSharedRef(), NewShareType);
return false;
}
}
CollectionFileCaches[NewShareType]->IgnoreNewFile(NewCollection->GetSourceFilename());
CollectionCache.HandleCollectionChanged();
// Success
CollectionRenamedEvent.Broadcast(OriginalCollectionKey, NewCollectionKey);
return true;
}
bool FCollectionManager::ReparentCollection(FName CollectionName, ECollectionShareType::Type ShareType, FName ParentCollectionName, ECollectionShareType::Type ParentShareType)
{
if (!ensure(ShareType < ECollectionShareType::CST_All) || (!ParentCollectionName.IsNone() && !ensure(ParentShareType < ECollectionShareType::CST_All)))
{
// Bad share type
LastError = LOCTEXT("Error_Internal", "There was an internal error.");
return false;
}
const FCollectionNameType CollectionKey(CollectionName, ShareType);
TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(CollectionKey);
if (!CollectionRefPtr)
{
// The collection doesn't exist
LastError = LOCTEXT("Error_DoesntExist", "The collection doesn't exist.");
return false;
}
const FGuid OldParentGuid = (*CollectionRefPtr)->GetParentCollectionGuid();
FGuid NewParentGuid;
TOptional<FCollectionNameType> OldParentCollectionKey;
TOptional<FCollectionNameType> NewParentCollectionKey;
if (!ParentCollectionName.IsNone())
{
// Find and set the new parent GUID
NewParentCollectionKey = FCollectionNameType(ParentCollectionName, ParentShareType);
TSharedRef<FCollection>* const ParentCollectionRefPtr = AvailableCollections.Find(NewParentCollectionKey.GetValue());
if (!ParentCollectionRefPtr)
{
// The collection doesn't exist
LastError = LOCTEXT("Error_DoesntExist", "The collection doesn't exist.");
return false;
}
// Does the parent collection need saving in order to have a stable GUID?
if ((*ParentCollectionRefPtr)->GetCollectionVersion() < ECollectionVersion::AddedCollectionGuid)
{
bool bForceCommitToRevisionControl = false;
// Try and re-save the parent collection now
if (InternalSaveCollection(*ParentCollectionRefPtr, LastError, bForceCommitToRevisionControl))
{
CollectionFileCaches[ParentShareType]->IgnoreFileModification((*ParentCollectionRefPtr)->GetSourceFilename());
}
else
{
return false;
}
}
if (!IsValidParentCollection(CollectionName, ShareType, ParentCollectionName, ParentShareType))
{
// IsValidParentCollection fills in LastError itself
return false;
}
NewParentGuid = (*ParentCollectionRefPtr)->GetCollectionGuid();
}
// Anything changed?
if (OldParentGuid == NewParentGuid)
{
return true;
}
(*CollectionRefPtr)->SetParentCollectionGuid(NewParentGuid);
// Try and save with the new parent GUID
bool bForceCommitToRevisionControl = false;
if (InternalSaveCollection(*CollectionRefPtr, LastError, bForceCommitToRevisionControl))
{
CollectionFileCaches[ShareType]->IgnoreFileModification((*CollectionRefPtr)->GetSourceFilename());
}
else
{
// Failed to save... rollback the collection to use its old parent GUID
(*CollectionRefPtr)->SetParentCollectionGuid(OldParentGuid);
return false;
}
CollectionCache.HandleCollectionChanged();
// Find the old parent so we can notify about the change
{
const FGuidToCollectionNamesMap& CachedCollectionNamesFromGuids = CollectionCache.GetCachedCollectionNamesFromGuids();
const FCollectionNameType* const OldParentCollectionKeyPtr = CachedCollectionNamesFromGuids.Find(OldParentGuid);
if (OldParentCollectionKeyPtr)
{
OldParentCollectionKey = *OldParentCollectionKeyPtr;
}
}
// Success
CollectionReparentedEvent.Broadcast(CollectionKey, OldParentCollectionKey, NewParentCollectionKey);
return true;
}
bool FCollectionManager::DestroyCollection(FName CollectionName, ECollectionShareType::Type ShareType)
{
if (!ensure(ShareType < ECollectionShareType::CST_All))
{
// Bad share type
LastError = LOCTEXT("Error_Internal", "There was an internal error.");
return false;
}
const FCollectionNameType CollectionKey(CollectionName, ShareType);
TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(CollectionKey);
if (!CollectionRefPtr)
{
// The collection doesn't exist
LastError = LOCTEXT("Error_DoesntExist", "The collection doesn't exist.");
return false;
}
if ((*CollectionRefPtr)->DeleteSourceFile(LastError))
{
CollectionFileCaches[ShareType]->IgnoreDeletedFile((*CollectionRefPtr)->GetSourceFilename());
RemoveCollection(*CollectionRefPtr, ShareType);
CollectionDestroyedEvent.Broadcast(CollectionKey);
return true;
}
else
{
// Failed to delete the source file
return false;
}
}
bool FCollectionManager::AddToCollection(FName CollectionName, ECollectionShareType::Type ShareType, const FSoftObjectPath& ObjectPath)
{
TArray<FSoftObjectPath> Paths;
Paths.Add(ObjectPath);
return AddToCollection(CollectionName, ShareType, Paths);
}
bool FCollectionManager::AddToCollection(FName CollectionName, ECollectionShareType::Type ShareType, TConstArrayView<FSoftObjectPath> ObjectPaths, int32* OutNumAdded)
{
if (OutNumAdded)
{
*OutNumAdded = 0;
}
if (!ensure(ShareType < ECollectionShareType::CST_All))
{
// Bad share type
LastError = LOCTEXT("Error_Internal", "There was an internal error.");
return false;
}
const FCollectionNameType CollectionKey(CollectionName, ShareType);
TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(CollectionKey);
if (!CollectionRefPtr)
{
// Collection doesn't exist
LastError = LOCTEXT("Error_DoesntExist", "The collection doesn't exist.");
return false;
}
if ((*CollectionRefPtr)->GetStorageMode() != ECollectionStorageMode::Static)
{
LastError = LOCTEXT("Error_AddNeedsStaticCollection", "Objects can only be added to static collections.");
return false;
}
int32 NumAdded = 0;
for (const FSoftObjectPath& ObjectPath : ObjectPaths)
{
if ((*CollectionRefPtr)->AddObjectToCollection(ObjectPath))
{
NumAdded++;
}
}
if (NumAdded > 0)
{
constexpr bool bForceCommitToRevisionControl = false;
if (InternalSaveCollection(*CollectionRefPtr, LastError, bForceCommitToRevisionControl))
{
CollectionFileCaches[ShareType]->IgnoreFileModification((*CollectionRefPtr)->GetSourceFilename());
// Added and saved
if (OutNumAdded)
{
*OutNumAdded = NumAdded;
}
CollectionCache.HandleCollectionChanged();
AssetsAddedToCollectionDelegate.Broadcast(CollectionKey, ObjectPaths);
PRAGMA_DISABLE_DEPRECATION_WARNINGS
if (AssetsAddedEvent.IsBound()) // Avoid conversion if nothing is bound
{
AssetsAddedEvent.Broadcast(CollectionKey, UE::SoftObjectPath::Private::ConvertSoftObjectPaths(ObjectPaths));
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
return true;
}
else
{
// Added but not saved, revert the add
for (const FSoftObjectPath& ObjectPath : ObjectPaths)
{
(*CollectionRefPtr)->RemoveObjectFromCollection(ObjectPath);
}
return false;
}
}
else
{
// Failed to add, all of the objects were already in the collection
LastError = LOCTEXT("Error_AlreadyInCollection", "All of the assets were already in the collection.");
return false;
}
}
bool FCollectionManager::RemoveFromCollection(FName CollectionName, ECollectionShareType::Type ShareType, const FSoftObjectPath& ObjectPath)
{
TArray<FSoftObjectPath> Paths;
Paths.Add(ObjectPath);
return RemoveFromCollection(CollectionName, ShareType, Paths);
}
bool FCollectionManager::RemoveFromCollection(FName CollectionName, ECollectionShareType::Type ShareType, TConstArrayView<FSoftObjectPath> ObjectPaths, int32* OutNumRemoved)
{
if (OutNumRemoved)
{
*OutNumRemoved = 0;
}
if (!ensure(ShareType < ECollectionShareType::CST_All))
{
// Bad share type
LastError = LOCTEXT("Error_Internal", "There was an internal error.");
return false;
}
const FCollectionNameType CollectionKey(CollectionName, ShareType);
TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(CollectionKey);
if (!CollectionRefPtr)
{
// Collection not found
LastError = LOCTEXT("Error_DoesntExist", "The collection doesn't exist.");
return false;
}
if ((*CollectionRefPtr)->GetStorageMode() != ECollectionStorageMode::Static)
{
LastError = LOCTEXT("Error_RemoveNeedsStaticCollection", "Objects can only be removed from static collections.");
return false;
}
TArray<FSoftObjectPath> RemovedAssets;
for (const FSoftObjectPath& ObjectPath : ObjectPaths)
{
if ((*CollectionRefPtr)->RemoveObjectFromCollection(ObjectPath))
{
RemovedAssets.Add(ObjectPath);
}
}
if (RemovedAssets.Num() == 0)
{
// Failed to remove, none of the objects were in the collection
LastError = LOCTEXT("Error_NotInCollection", "None of the assets were in the collection.");
return false;
}
constexpr bool bForceCommitToRevisionControl = false;
if (InternalSaveCollection(*CollectionRefPtr, LastError, bForceCommitToRevisionControl))
{
CollectionFileCaches[ShareType]->IgnoreFileModification((*CollectionRefPtr)->GetSourceFilename());
// Removed and saved
if (OutNumRemoved)
{
*OutNumRemoved = RemovedAssets.Num();
}
CollectionCache.HandleCollectionChanged();
AssetsRemovedFromCollectionDelegate.Broadcast(CollectionKey, ObjectPaths);
PRAGMA_DISABLE_DEPRECATION_WARNINGS
if (AssetsRemovedEvent.IsBound())
{
AssetsRemovedEvent.Broadcast(CollectionKey, UE::SoftObjectPath::Private::ConvertSoftObjectPaths(ObjectPaths));
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
return true;
}
else
{
// Removed but not saved, revert the remove
for (const FSoftObjectPath& RemovedAssetName : RemovedAssets)
{
(*CollectionRefPtr)->AddObjectToCollection(RemovedAssetName);
}
return false;
}
}
bool FCollectionManager::SetDynamicQueryText(FName CollectionName, ECollectionShareType::Type ShareType, const FString& InQueryText)
{
if (!ensure(ShareType < ECollectionShareType::CST_All))
{
// Bad share type
LastError = LOCTEXT("Error_Internal", "There was an internal error.");
return false;
}
const FCollectionNameType CollectionKey(CollectionName, ShareType);
TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(CollectionKey);
if (!CollectionRefPtr)
{
// Collection doesn't exist
LastError = LOCTEXT("Error_DoesntExist", "The collection doesn't exist.");
return false;
}
if ((*CollectionRefPtr)->GetStorageMode() != ECollectionStorageMode::Dynamic)
{
LastError = LOCTEXT("Error_SetNeedsDynamicCollection", "Search queries can only be set on dynamic collections.");
return false;
}
(*CollectionRefPtr)->SetDynamicQueryText(InQueryText);
constexpr bool bForceCommitToRevisionControl = true;
if (InternalSaveCollection(*CollectionRefPtr, LastError, bForceCommitToRevisionControl))
{
CollectionFileCaches[ShareType]->IgnoreFileModification((*CollectionRefPtr)->GetSourceFilename());
CollectionCache.HandleCollectionChanged();
CollectionUpdatedEvent.Broadcast(CollectionKey);
return true;
}
return false;
}
bool FCollectionManager::GetDynamicQueryText(FName CollectionName, ECollectionShareType::Type ShareType, FString& OutQueryText) const
{
if (!ensure(ShareType < ECollectionShareType::CST_All))
{
// Bad share type
LastError = LOCTEXT("Error_Internal", "There was an internal error.");
return false;
}
const FCollectionNameType CollectionKey(CollectionName, ShareType);
const TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(CollectionKey);
if (!CollectionRefPtr)
{
// Collection doesn't exist
LastError = LOCTEXT("Error_DoesntExist", "The collection doesn't exist.");
return false;
}
if ((*CollectionRefPtr)->GetStorageMode() != ECollectionStorageMode::Dynamic)
{
LastError = LOCTEXT("Error_GetNeedsDynamicCollection", "Search queries can only be got from dynamic collections.");
return false;
}
OutQueryText = (*CollectionRefPtr)->GetDynamicQueryText();
return true;
}
bool FCollectionManager::TestDynamicQuery(FName CollectionName, ECollectionShareType::Type ShareType, const ITextFilterExpressionContext& InContext, bool& OutResult) const
{
if (!ensure(ShareType < ECollectionShareType::CST_All))
{
// Bad share type
LastError = LOCTEXT("Error_Internal", "There was an internal error.");
return false;
}
const FCollectionNameType CollectionKey(CollectionName, ShareType);
const TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(CollectionKey);
if (!CollectionRefPtr)
{
// Collection doesn't exist
LastError = LOCTEXT("Error_DoesntExist", "The collection doesn't exist.");
return false;
}
if ((*CollectionRefPtr)->GetStorageMode() != ECollectionStorageMode::Dynamic)
{
LastError = LOCTEXT("Error_TestNeedsDynamicCollection", "Search queries can only be tested on dynamic collections.");
return false;
}
OutResult = (*CollectionRefPtr)->TestDynamicQuery(InContext);
return true;
}
bool FCollectionManager::EmptyCollection(FName CollectionName, ECollectionShareType::Type ShareType)
{
if (!ensure(ShareType < ECollectionShareType::CST_All))
{
// Bad share type
LastError = LOCTEXT("Error_Internal", "There was an internal error.");
return false;
}
const FCollectionNameType CollectionKey(CollectionName, ShareType);
const TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(CollectionKey);
if (!CollectionRefPtr)
{
// Collection doesn't exist
LastError = LOCTEXT("Error_DoesntExist", "The collection doesn't exist.");
return false;
}
if ((*CollectionRefPtr)->IsEmpty())
{
// Already empty - nothing to do
return true;
}
(*CollectionRefPtr)->Empty();
constexpr bool bForceCommitToRevisionControl = true;
if (InternalSaveCollection(*CollectionRefPtr, LastError, bForceCommitToRevisionControl))
{
CollectionFileCaches[ShareType]->IgnoreFileModification((*CollectionRefPtr)->GetSourceFilename());
CollectionCache.HandleCollectionChanged();
CollectionUpdatedEvent.Broadcast(CollectionKey);
return true;
}
return false;
}
bool FCollectionManager::SaveCollection(FName CollectionName, ECollectionShareType::Type ShareType)
{
if (!ensure(ShareType < ECollectionShareType::CST_All))
{
// Bad share type
LastError = LOCTEXT("Error_Internal", "There was an internal error.");
return false;
}
const FCollectionNameType CollectionKey(CollectionName, ShareType);
const TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(CollectionKey);
if (CollectionRefPtr)
{
FCollectionStatusInfo StatusInfo = (*CollectionRefPtr)->GetStatusInfo();
const bool bNeedsSave = StatusInfo.bIsDirty || (StatusInfo.SCCState.IsValid() && StatusInfo.SCCState->IsModified());
if (!bNeedsSave)
{
// No changes - nothing to save
return true;
}
constexpr bool bForceCommitToRevisionControl = true;
if (InternalSaveCollection(*CollectionRefPtr, LastError, bForceCommitToRevisionControl))
{
CollectionFileCaches[ShareType]->IgnoreFileModification((*CollectionRefPtr)->GetSourceFilename());
CollectionCache.HandleCollectionChanged();
CollectionUpdatedEvent.Broadcast(CollectionKey);
return true;
}
}
else
{
LastError = LOCTEXT("Error_DoesntExist", "The collection doesn't exist.");
}
return false;
}
bool FCollectionManager::UpdateCollection(FName CollectionName, ECollectionShareType::Type ShareType)
{
if (!ensure(ShareType < ECollectionShareType::CST_All))
{
// Bad share type
LastError = LOCTEXT("Error_Internal", "There was an internal error.");
return false;
}
const FCollectionNameType CollectionKey(CollectionName, ShareType);
const TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(CollectionKey);
if (CollectionRefPtr)
{
if ((*CollectionRefPtr)->Update(LastError))
{
CollectionFileCaches[ShareType]->IgnoreFileModification((*CollectionRefPtr)->GetSourceFilename());
CollectionCache.HandleCollectionChanged();
CollectionUpdatedEvent.Broadcast(CollectionKey);
return true;
}
}
else
{
LastError = LOCTEXT("Error_DoesntExist", "The collection doesn't exist.");
}
return false;
}
bool FCollectionManager::GetCollectionStatusInfo(FName CollectionName, ECollectionShareType::Type ShareType, FCollectionStatusInfo& OutStatusInfo) const
{
TRACE_CPUPROFILER_EVENT_SCOPE(FCollectionManager::GetCollectionStatusInfo);
if (!ensure(ShareType < ECollectionShareType::CST_All))
{
// Bad share type
LastError = LOCTEXT("Error_Internal", "There was an internal error.");
return true;
}
const FCollectionNameType CollectionKey(CollectionName, ShareType);
const TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(CollectionKey);
if (CollectionRefPtr)
{
OutStatusInfo = (*CollectionRefPtr)->GetStatusInfo();
return true;
}
else
{
LastError = LOCTEXT("Error_DoesntExist", "The collection doesn't exist.");
}
return false;
}
bool FCollectionManager::HasCollectionColors(TArray<FLinearColor>* OutColors) const
{
const FCollectionColorArray& CollectionColors = CollectionCache.GetCachedColors();
if (OutColors)
{
*OutColors = CollectionColors;
}
return CollectionColors.Num() > 0;
}
bool FCollectionManager::GetCollectionColor(FName CollectionName, ECollectionShareType::Type ShareType, TOptional<FLinearColor>& OutColor) const
{
if (!ensure(ShareType < ECollectionShareType::CST_All))
{
// Bad share type
LastError = LOCTEXT("Error_Internal", "There was an internal error.");
return true;
}
const FCollectionNameType CollectionKey(CollectionName, ShareType);
const TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(CollectionKey);
if (CollectionRefPtr)
{
OutColor = (*CollectionRefPtr)->GetCollectionColor();
return true;
}
else
{
LastError = LOCTEXT("Error_DoesntExist", "The collection doesn't exist.");
}
return false;
}
bool FCollectionManager::SetCollectionColor(FName CollectionName, ECollectionShareType::Type ShareType, const TOptional<FLinearColor>& NewColor)
{
if (!ensure(ShareType < ECollectionShareType::CST_All))
{
// Bad share type
LastError = LOCTEXT("Error_Internal", "There was an internal error.");
return true;
}
const FCollectionNameType CollectionKey(CollectionName, ShareType);
const TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(CollectionKey);
if (CollectionRefPtr)
{
(*CollectionRefPtr)->SetCollectionColor(NewColor);
constexpr bool bForceCommitToRevisionControl = false;
if (InternalSaveCollection(*CollectionRefPtr, LastError, bForceCommitToRevisionControl))
{
CollectionFileCaches[ShareType]->IgnoreFileModification((*CollectionRefPtr)->GetSourceFilename());
CollectionCache.HandleCollectionChanged();
CollectionUpdatedEvent.Broadcast(CollectionKey);
return true;
}
}
else
{
LastError = LOCTEXT("Error_DoesntExist", "The collection doesn't exist.");
}
return false;
}
bool FCollectionManager::GetCollectionStorageMode(FName CollectionName, ECollectionShareType::Type ShareType, ECollectionStorageMode::Type& OutStorageMode) const
{
if (!ensure(ShareType < ECollectionShareType::CST_All))
{
// Bad share type
LastError = LOCTEXT("Error_Internal", "There was an internal error.");
return true;
}
const FCollectionNameType CollectionKey(CollectionName, ShareType);
const TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(CollectionKey);
if (CollectionRefPtr)
{
OutStorageMode = (*CollectionRefPtr)->GetStorageMode();
return true;
}
else
{
LastError = LOCTEXT("Error_DoesntExist", "The collection doesn't exist.");
}
return false;
}
bool FCollectionManager::IsObjectInCollection(const FSoftObjectPath& ObjectPath, FName CollectionName, ECollectionShareType::Type ShareType, ECollectionRecursionFlags::Flags RecursionMode) const
{
if (!ensure(ShareType < ECollectionShareType::CST_All))
{
// Bad share type
LastError = LOCTEXT("Error_Internal", "There was an internal error.");
return true;
}
bool bFoundObject = false;
auto IsObjectInCollectionWorker = [&](const FCollectionNameType& InCollectionKey, ECollectionRecursionFlags::Flag InReason) -> FCollectionManagerCache::ERecursiveWorkerFlowControl
{
const TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(InCollectionKey);
if (CollectionRefPtr)
{
bFoundObject = (*CollectionRefPtr)->IsObjectInCollection(ObjectPath);
}
return (bFoundObject) ? FCollectionManagerCache::ERecursiveWorkerFlowControl::Stop : FCollectionManagerCache::ERecursiveWorkerFlowControl::Continue;
};
CollectionCache.RecursionHelper_DoWork(FCollectionNameType(CollectionName, ShareType), RecursionMode, IsObjectInCollectionWorker);
return bFoundObject;
}
bool FCollectionManager::IsValidParentCollection(FName CollectionName, ECollectionShareType::Type ShareType, FName ParentCollectionName, ECollectionShareType::Type ParentShareType) const
{
if (!ensure(ShareType < ECollectionShareType::CST_All) || (!ParentCollectionName.IsNone() && !ensure(ParentShareType < ECollectionShareType::CST_All)))
{
// Bad share type
LastError = LOCTEXT("Error_Internal", "There was an internal error.");
return true;
}
if (ParentCollectionName.IsNone())
{
// Clearing the parent is always valid
return true;
}
bool bValidParent = true;
auto IsValidParentCollectionWorker = [&](const FCollectionNameType& InCollectionKey, ECollectionRecursionFlags::Flag InReason) -> FCollectionManagerCache::ERecursiveWorkerFlowControl
{
const bool bMatchesCollectionBeingReparented = (CollectionName == InCollectionKey.Name && ShareType == InCollectionKey.Type);
if (bMatchesCollectionBeingReparented)
{
bValidParent = false;
LastError = (InReason == ECollectionRecursionFlags::Self)
? LOCTEXT("InvalidParent_CannotParentToSelf", "A collection cannot be parented to itself")
: LOCTEXT("InvalidParent_CannotParentToChildren", "A collection cannot be parented to its children");
return FCollectionManagerCache::ERecursiveWorkerFlowControl::Stop;
}
const bool bIsValidChildType = ECollectionShareType::IsValidChildType(InCollectionKey.Type, ShareType);
if (!bIsValidChildType)
{
bValidParent = false;
LastError = FText::Format(LOCTEXT("InvalidParent_InvalidChildType", "A {0} collection cannot contain a {1} collection"), ECollectionShareType::ToText(InCollectionKey.Type), ECollectionShareType::ToText(ShareType));
return FCollectionManagerCache::ERecursiveWorkerFlowControl::Stop;
}
const TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(InCollectionKey);
if (CollectionRefPtr)
{
const ECollectionStorageMode::Type StorageMode = (*CollectionRefPtr)->GetStorageMode();
if (StorageMode == ECollectionStorageMode::Dynamic)
{
bValidParent = false;
LastError = LOCTEXT("InvalidParent_InvalidParentStorageType", "A dynamic collection cannot contain child collections");
return FCollectionManagerCache::ERecursiveWorkerFlowControl::Stop;
}
}
return FCollectionManagerCache::ERecursiveWorkerFlowControl::Continue;
};
CollectionCache.RecursionHelper_DoWork(FCollectionNameType(ParentCollectionName, ParentShareType), ECollectionRecursionFlags::SelfAndParents, IsValidParentCollectionWorker);
return bValidParent;
}
void FCollectionManager::HandleFixupRedirectors(ICollectionRedirectorFollower& InRedirectorFollower)
{
if (bNoFixupRedirectors)
{
return;
}
const double LoadStartTime = FPlatformTime::Seconds();
TArray<TPair<FSoftObjectPath, FSoftObjectPath>> ObjectsToRename;
// Build up the list of redirected object into rename pairs
{
const FCollectionObjectsMap& CachedObjects = CollectionCache.GetCachedObjects();
for (const auto& CachedObjectInfo : CachedObjects)
{
FSoftObjectPath NewObjectPath;
if (InRedirectorFollower.FixupObject(CachedObjectInfo.Key, NewObjectPath))
{
ObjectsToRename.Emplace(CachedObjectInfo.Key, NewObjectPath);
}
}
}
TArray<FCollectionNameType> UpdatedCollections;
TArray<FSoftObjectPath> AddedObjects;
AddedObjects.Reserve(ObjectsToRename.Num());
TArray<FSoftObjectPath> RemovedObjects;
RemovedObjects.Reserve(ObjectsToRename.Num());
// Handle the rename for each redirected object
for (const TPair<FSoftObjectPath, FSoftObjectPath>& ObjectToRename : ObjectsToRename)
{
AddedObjects.Add(ObjectToRename.Value);
RemovedObjects.Add(ObjectToRename.Key);
ReplaceObjectInCollections(ObjectToRename.Key, ObjectToRename.Value, UpdatedCollections);
}
if (UpdatedCollections.Num() > 0)
{
CollectionCache.HandleCollectionChanged();
// Notify every collection that changed
for (const FCollectionNameType& UpdatedCollection : UpdatedCollections)
{
AssetsRemovedFromCollectionDelegate.Broadcast(UpdatedCollection, RemovedObjects);
AssetsAddedToCollectionDelegate.Broadcast(UpdatedCollection, AddedObjects);
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
if (AssetsRemovedEvent.IsBound() || AssetsAddedEvent.IsBound())
{
TArray<FName> RemovedObjectPathNames = UE::SoftObjectPath::Private::ConvertSoftObjectPaths(RemovedObjects);
TArray<FName> AddedObjectPathNames = UE::SoftObjectPath::Private::ConvertSoftObjectPaths(AddedObjects);
for (const FCollectionNameType& UpdatedCollection : UpdatedCollections)
{
AssetsRemovedEvent.Broadcast(UpdatedCollection, RemovedObjectPathNames);
AssetsAddedEvent.Broadcast(UpdatedCollection, AddedObjectPathNames);
}
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
}
UE_LOG(LogCollectionManager, Log, TEXT( "Fixed up redirectors for %d collections in %0.6f seconds (updated %d objects)" ), AvailableCollections.Num(), FPlatformTime::Seconds() - LoadStartTime, ObjectsToRename.Num());
for (const auto& ObjectToRename : ObjectsToRename)
{
UE_LOG(LogCollectionManager, Verbose, TEXT( "\tRedirected '%s' to '%s'" ), *ObjectToRename.Key.ToString(), *ObjectToRename.Value.ToString());
}
}
bool FCollectionManager::HandleRedirectorsDeleted(TConstArrayView<FSoftObjectPath> ObjectPaths)
{
bool bSavedAllCollections = true;
FTextBuilder AllErrors;
TArray<FCollectionNameType> UpdatedCollections;
TSet<FCollectionNameType> CollectionsToSave;
for (const FSoftObjectPath& ObjectPath : ObjectPaths)
{
// We don't have a cache for on-disk objects, so we have to do this the slower way and query each collection in turn
for (const auto& AvailableCollection : AvailableCollections)
{
const FCollectionNameType& CollectionKey = AvailableCollection.Key;
const TSharedRef<FCollection>& Collection = AvailableCollection.Value;
if (Collection->IsRedirectorInCollection(ObjectPath))
{
CollectionsToSave.Add(CollectionKey);
}
}
}
for (const FCollectionNameType& CollectionKey : CollectionsToSave)
{
if (TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(CollectionKey))
{
const TSharedRef<FCollection>& Collection = *CollectionRefPtr;
FText SaveError;
constexpr bool bForceCommitToRevisionControl = true;
if (InternalSaveCollection(Collection, SaveError, bForceCommitToRevisionControl))
{
CollectionFileCaches[CollectionKey.Type]->IgnoreFileModification(Collection->GetSourceFilename());
UpdatedCollections.Add(CollectionKey);
}
else
{
AllErrors.AppendLine(SaveError);
bSavedAllCollections = false;
}
}
}
// Notify every collection that changed
for (const FCollectionNameType& UpdatedCollection : UpdatedCollections)
{
AssetsRemovedFromCollectionDelegate.Broadcast(UpdatedCollection, ObjectPaths);
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
if( AssetsRemovedEvent.IsBound())
{
TArray<FName> RemovedObjectPathNames = UE::SoftObjectPath::Private::ConvertSoftObjectPaths(ObjectPaths);
for (const FCollectionNameType& UpdatedCollection : UpdatedCollections)
{
AssetsRemovedEvent.Broadcast(UpdatedCollection, RemovedObjectPathNames);
}
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
if (!bSavedAllCollections)
{
LastError = AllErrors.ToText();
}
return bSavedAllCollections;
}
bool FCollectionManager::HandleRedirectorDeleted(const FSoftObjectPath& ObjectPath)
{
return HandleRedirectorsDeleted(MakeArrayView(&ObjectPath, 1));
}
void FCollectionManager::HandleObjectRenamed(const FSoftObjectPath& OldObjectPath, const FSoftObjectPath& NewObjectPath)
{
TArray<FCollectionNameType> UpdatedCollections;
ReplaceObjectInCollections(OldObjectPath, NewObjectPath, UpdatedCollections);
TArray<FSoftObjectPath> AddedObjects;
AddedObjects.Add(NewObjectPath);
TArray<FSoftObjectPath> RemovedObjects;
RemovedObjects.Add(OldObjectPath);
if (UpdatedCollections.Num() > 0)
{
CollectionCache.HandleCollectionChanged();
// Notify every collection that changed
for (const FCollectionNameType& UpdatedCollection : UpdatedCollections)
{
AssetsRemovedFromCollectionDelegate.Broadcast(UpdatedCollection, RemovedObjects);
AssetsAddedToCollectionDelegate.Broadcast(UpdatedCollection, AddedObjects);
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
if( AssetsAddedEvent.IsBound() || AssetsRemovedEvent.IsBound())
{
TArray<FName> RemovedObjectPathNames = UE::SoftObjectPath::Private::ConvertSoftObjectPaths(RemovedObjects);
TArray<FName> AddedObjectPathNames = UE::SoftObjectPath::Private::ConvertSoftObjectPaths(AddedObjects);
for (const FCollectionNameType& UpdatedCollection : UpdatedCollections)
{
AssetsRemovedEvent.Broadcast(UpdatedCollection, RemovedObjectPathNames);
AssetsAddedEvent.Broadcast(UpdatedCollection, AddedObjectPathNames);
}
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
}
}
void FCollectionManager::HandleObjectsDeleted(TConstArrayView<FSoftObjectPath> ObjectPaths)
{
TArray<FCollectionNameType> UpdatedCollections;
for (const FSoftObjectPath& ObjectPath : ObjectPaths)
{
RemoveObjectFromCollections(ObjectPath, UpdatedCollections);
}
if (UpdatedCollections.Num() > 0)
{
CollectionCache.HandleCollectionChanged();
// Notify every collection that changed
for (const FCollectionNameType& UpdatedCollection : UpdatedCollections)
{
AssetsRemovedFromCollectionDelegate.Broadcast(UpdatedCollection, ObjectPaths);
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
if (AssetsRemovedEvent.IsBound())
{
TArray<FName> RemovedObjectPathNames = UE::SoftObjectPath::Private::ConvertSoftObjectPaths(ObjectPaths);
for (const FCollectionNameType& UpdatedCollection : UpdatedCollections)
{
AssetsRemovedEvent.Broadcast(UpdatedCollection, RemovedObjectPathNames);
}
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
}
}
void FCollectionManager::HandleObjectDeleted(const FSoftObjectPath& ObjectPath)
{
HandleObjectsDeleted(MakeArrayView(&ObjectPath, 1));
}
bool FCollectionManager::TickFileCache(float InDeltaTime)
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_FCollectionManager_TickFileCache);
enum class ECollectionFileAction : uint8
{
None,
AddCollection,
MergeCollection,
RemoveCollection,
};
bool bDidChangeCollection = false;
// Process changes that have happened outside of the collection manager
for (int32 CacheIdx = 0; CacheIdx < ECollectionShareType::CST_All; ++CacheIdx)
{
const ECollectionShareType::Type ShareType = ECollectionShareType::Type(CacheIdx);
auto& FileCache = CollectionFileCaches[CacheIdx];
if (FileCache.IsValid())
{
FileCache->Tick();
const auto FileCacheChanges = FileCache->GetOutstandingChanges();
for (const auto& FileCacheChange : FileCacheChanges)
{
const FString CollectionFilename = FileCacheChange.Filename.Get();
if (FPaths::GetExtension(CollectionFilename) != CollectionExtension)
{
continue;
}
const FName CollectionName = *FPaths::GetBaseFilename(CollectionFilename);
ECollectionFileAction CollectionFileAction = ECollectionFileAction::None;
switch (FileCacheChange.Action)
{
case DirectoryWatcher::EFileAction::Added:
case DirectoryWatcher::EFileAction::Modified:
// File was added or modified, but does this collection already exist?
CollectionFileAction = (AvailableCollections.Contains(FCollectionNameType(CollectionName, ShareType)))
? ECollectionFileAction::MergeCollection
: ECollectionFileAction::AddCollection;
break;
case DirectoryWatcher::EFileAction::Removed:
// File was removed, but does this collection actually exist?
CollectionFileAction = (AvailableCollections.Contains(FCollectionNameType(CollectionName, ShareType)))
? ECollectionFileAction::RemoveCollection
: ECollectionFileAction::None;
break;
default:
break;
}
switch (CollectionFileAction)
{
case ECollectionFileAction::AddCollection:
{
const bool bUseSCC = ShouldUseSCC(ShareType);
FText LoadErrorText;
TSharedRef<FCollection> NewCollection = MakeShareable(new FCollection(GetCollectionFilename(CollectionName, ShareType), bUseSCC, ECollectionStorageMode::Static));
if (NewCollection->Load(LoadErrorText))
{
if (AddCollection(NewCollection, ShareType))
{
bDidChangeCollection = true;
CollectionCreatedEvent.Broadcast(FCollectionNameType(CollectionName, ShareType));
}
}
else
{
UE_LOG(LogCollectionManager, Warning, TEXT("%s"), *LoadErrorText.ToString());
}
}
break;
case ECollectionFileAction::MergeCollection:
{
TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(FCollectionNameType(CollectionName, ShareType));
check(CollectionRefPtr); // We tested AvailableCollections.Contains(...) above, so this shouldn't fail
FText LoadErrorText;
FCollection TempCollection(GetCollectionFilename(CollectionName, ShareType), /*bUseSCC*/false, ECollectionStorageMode::Static);
if (TempCollection.Load(LoadErrorText))
{
if ((*CollectionRefPtr)->Merge(TempCollection))
{
bDidChangeCollection = true;
CollectionUpdatedEvent.Broadcast(FCollectionNameType(CollectionName, ShareType));
}
}
else
{
UE_LOG(LogCollectionManager, Warning, TEXT("%s"), *LoadErrorText.ToString());
}
}
break;
case ECollectionFileAction::RemoveCollection:
{
TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(FCollectionNameType(CollectionName, ShareType));
check(CollectionRefPtr); // We tested AvailableCollections.Contains(...) above, so this shouldn't fail
RemoveCollection(*CollectionRefPtr, ShareType);
CollectionDestroyedEvent.Broadcast(FCollectionNameType(CollectionName, ShareType));
}
break;
default:
break;
}
}
}
}
if (bDidChangeCollection)
{
CollectionCache.HandleCollectionChanged();
}
return true; // Tick again
}
void FCollectionManager::LoadCollections()
{
TRACE_CPUPROFILER_EVENT_SCOPE(FCollectionManager::LoadCollections)
const double LoadStartTime = FPlatformTime::Seconds();
const int32 PrevNumCollections = AvailableCollections.Num();
LLM_SCOPE_BYNAME(TEXT("CollectionManager"));
FRWLock CollectionLock;
ParallelFor(
TEXT("LoadCollections.PF"),
ECollectionShareType::CST_All,1,
[this, &CollectionLock](int32 CacheIdx)
{
const ECollectionShareType::Type ShareType = ECollectionShareType::Type(CacheIdx);
const bool bUseSCC = ShouldUseSCC(ShareType);
const FString& CollectionFolder = CollectionFolders[CacheIdx];
const FString WildCard = FString::Printf(TEXT("%s/*.%s"), *CollectionFolder, *CollectionExtension);
TArray<FString> Filenames;
IFileManager::Get().FindFiles(Filenames, *WildCard, true, false);
ParallelFor(
TEXT("LoadCollections.PF"),
Filenames.Num(),1,
[this, &CollectionLock, &Filenames, &CollectionFolder, bUseSCC, ShareType](int32 FilenameIdx)
{
const FString& BaseFilename = Filenames[FilenameIdx];
const FString Filename = CollectionFolder / BaseFilename;
FText LoadErrorText;
TSharedRef<FCollection> NewCollection = MakeShareable(new FCollection(Filename, bUseSCC, ECollectionStorageMode::Static));
if (NewCollection->Load(LoadErrorText))
{
FRWScopeLock Lock(CollectionLock, SLT_Write);
AddCollection(NewCollection, ShareType);
}
else
{
UE_LOG(LogCollectionManager, Warning, TEXT("%s"), *LoadErrorText.ToString());
}
},
EParallelForFlags::Unbalanced
);
},
EParallelForFlags::Unbalanced
);
// AddCollection is assumed to be adding an empty collection, so also notify that collection cache that the collection has "changed" since loaded collections may not always be empty
CollectionCache.HandleCollectionChanged();
UE_LOG(LogCollectionManager, Log, TEXT( "Loaded %d collections in %0.6f seconds" ), AvailableCollections.Num() - PrevNumCollections, FPlatformTime::Seconds() - LoadStartTime);
}
bool FCollectionManager::ShouldUseSCC(ECollectionShareType::Type ShareType) const
{
return ShareType != ECollectionShareType::CST_Local && ShareType != ECollectionShareType::CST_System;
}
FString FCollectionManager::GetCollectionFilename(const FName& InCollectionName, const ECollectionShareType::Type InCollectionShareType) const
{
FString CollectionFilename = CollectionFolders[InCollectionShareType] / InCollectionName.ToString() + TEXT(".") + CollectionExtension;
FPaths::NormalizeFilename(CollectionFilename);
return CollectionFilename;
}
bool FCollectionManager::AddCollection(const TSharedRef<FCollection>& CollectionRef, ECollectionShareType::Type ShareType)
{
if (!ensure(ShareType < ECollectionShareType::CST_All))
{
// Bad share type
return false;
}
const FCollectionNameType CollectionKey(CollectionRef->GetCollectionName(), ShareType);
if (AvailableCollections.Contains(CollectionKey))
{
UE_LOG(LogCollectionManager, Warning, TEXT("Failed to add collection '%s' because it already exists."), *CollectionRef->GetCollectionName().ToString());
return false;
}
AvailableCollections.Add(CollectionKey, CollectionRef);
CollectionCache.HandleCollectionAdded();
return true;
}
bool FCollectionManager::RemoveCollection(const TSharedRef<FCollection>& CollectionRef, ECollectionShareType::Type ShareType)
{
if (!ensure(ShareType < ECollectionShareType::CST_All))
{
// Bad share type
return false;
}
const FCollectionNameType CollectionKey(CollectionRef->GetCollectionName(), ShareType);
if (AvailableCollections.Remove(CollectionKey) > 0)
{
CollectionCache.HandleCollectionRemoved();
return true;
}
return false;
}
void FCollectionManager::RemoveObjectFromCollections(const FSoftObjectPath& ObjectPath, TArray<FCollectionNameType>& OutUpdatedCollections)
{
const FCollectionObjectsMap& CachedObjects = CollectionCache.GetCachedObjects();
const auto* ObjectCollectionInfosPtr = CachedObjects.Find(ObjectPath);
if (!ObjectCollectionInfosPtr)
{
return;
}
// Remove this object reference from all collections that use it
for (const FObjectCollectionInfo& ObjectCollectionInfo : *ObjectCollectionInfosPtr)
{
if ((ObjectCollectionInfo.Reason & ECollectionRecursionFlags::Self) != 0)
{
// The object is contained directly within this collection (rather than coming from a parent or child collection), so remove the object reference
const TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(ObjectCollectionInfo.CollectionKey);
if (CollectionRefPtr)
{
OutUpdatedCollections.AddUnique(ObjectCollectionInfo.CollectionKey);
(*CollectionRefPtr)->RemoveObjectFromCollection(ObjectPath);
}
}
}
}
void FCollectionManager::ReplaceObjectInCollections(const FSoftObjectPath& OldObjectPath, const FSoftObjectPath& NewObjectPath, TArray<FCollectionNameType>& OutUpdatedCollections)
{
const FCollectionObjectsMap& CachedObjects = CollectionCache.GetCachedObjects();
const auto* OldObjectCollectionInfosPtr = CachedObjects.Find(OldObjectPath);
if (!OldObjectCollectionInfosPtr)
{
return;
}
// Replace this object reference in all collections that use it
for (const FObjectCollectionInfo& OldObjectCollectionInfo : *OldObjectCollectionInfosPtr)
{
if ((OldObjectCollectionInfo.Reason & ECollectionRecursionFlags::Self) != 0)
{
// The old object is contained directly within this collection (rather than coming from a parent or child collection), so update the object reference
const TSharedRef<FCollection>* const CollectionRefPtr = AvailableCollections.Find(OldObjectCollectionInfo.CollectionKey);
if (CollectionRefPtr)
{
OutUpdatedCollections.AddUnique(OldObjectCollectionInfo.CollectionKey);
(*CollectionRefPtr)->RemoveObjectFromCollection(OldObjectPath);
(*CollectionRefPtr)->AddObjectToCollection(NewObjectPath);
}
}
}
}
bool FCollectionManager::InternalSaveCollection(const TSharedRef<FCollection>& CollectionRef, FText& OutError, bool bForceCommitToRevisionControl)
{
TArray<FText> AdditionalChangelistText;
// Give game specific editors a chance to add lines
AddToCollectionCheckinDescriptionEvent.Broadcast(CollectionRef->GetCollectionName(), AdditionalChangelistText);
// Give settings a chance to add lines
TArray<FString> SettingsLines;
const USourceControlPreferences* Settings = GetDefault<USourceControlPreferences>();
if (const FString* SpecificMatch = Settings->SpecificCollectionChangelistTags.Find(CollectionRef->GetCollectionName()))
{
// Parse input buffer into an array of lines
SpecificMatch->ParseIntoArrayLines(SettingsLines, /*bCullEmpty=*/ false);
}
SettingsLines.Append(Settings->CollectionChangelistTags);
for (const FString& OneSettingLine : SettingsLines)
{
AdditionalChangelistText.Add(FText::FromString(*OneSettingLine));
}
// Save the collection
return CollectionRef->Save(AdditionalChangelistText, OutError, bForceCommitToRevisionControl);
}
#undef LOCTEXT_NAMESPACE