Files
UnrealEngine/Engine/Source/Developer/CollectionManager/Private/CollectionContainer.h
2025-05-18 13:04:45 +08:00

281 lines
17 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Collection.h"
#include "CollectionManagerTypes.h"
#include "ICollectionContainer.h"
#include "Templates/PimplPtr.h"
namespace DirectoryWatcher
{
class FFileCache;
}
enum class ECollectionCacheFlags;
class FCollectionContainerCache;
class FCollectionManager;
UE_INTERNAL class FCollectionRecursiveRwLock
{
struct FThreadLockDepths
{
FThreadLockDepths(void* TlsSlotValue);
void* GetTlsSlotValue();
#if PLATFORM_64BITS
static_assert(sizeof(UPTRINT) == sizeof(uint64), "Expected pointer size to be 64 bits");
using HalfUPtrInt = uint32;
#else
static_assert(sizeof(UPTRINT) == sizeof(uint32), "Expected pointer size to be 32 bits");
using HalfUPtrInt = uint16;
#endif
HalfUPtrInt ThreadReadDepth = 0;
HalfUPtrInt ThreadWriteDepth = 0;
};
public:
FCollectionRecursiveRwLock();
~FCollectionRecursiveRwLock();
void ReadLock();
void WriteLock();
void ReadUnlock();
void WriteUnlock();
// Promote the lock from read to write, possibly being interrupted by another writer in between.
// Returns false if the lock cannot be promoted, which can happen if the thread already holds a read
// lock then reenters and tries to promote to a write lock.
[[nodiscard]] bool PromoteInterruptible();
private:
FRWLock RwLock;
uint32 TlsSlot;
};
// Objects wrapping locks to read, write, or begin-reading-then-write (for cache updates) internal state.
// Used as internal function parameters to show what lock type must be held to perform the operation and prevent
// recursive lock acquisition
// Functions taking Lock/Lock_Read need to be able to read data but not update caches
class FCollectionScopeLock;
class FCollectionScopeLock_Read;
// Functions taking Lock_RW may need to promote the lock to a write state to update caches
class FCollectionScopeLock_RW;
// Functions taking Lock_Write have exclusive access and can update collections as well as update caches
class FCollectionScopeLock_Write;
class FCollectionContainer final : public ICollectionContainer
{
public:
FCollectionContainer(FCollectionManager& InCollectionManager, const TSharedRef<ICollectionSource>& InCollectionSource);
~FCollectionContainer();
virtual const TSharedRef<ICollectionSource>& GetCollectionSource() const override
{
return CollectionSource;
}
virtual bool IsReadOnly(ECollectionShareType::Type ShareType) const override;
virtual void SetReadOnly(ECollectionShareType::Type ShareType, bool bReadOnly) override;
virtual bool IsHidden() const override;
virtual void SetHidden(bool bHidden) override;
virtual bool HasCollections() const override;
virtual void GetCollections(TArray<FCollectionNameType>& OutCollections) const override;
virtual void GetCollections(FName CollectionName, TArray<FCollectionNameType>& OutCollections) const override;
virtual void GetCollectionNames(ECollectionShareType::Type ShareType, TArray<FName>& CollectionNames) const override;
virtual void GetRootCollections(TArray<FCollectionNameType>& OutCollections) const override;
virtual void GetRootCollectionNames(ECollectionShareType::Type ShareType, TArray<FName>& CollectionNames) const override;
virtual void GetChildCollections(FName CollectionName, ECollectionShareType::Type ShareType, TArray<FCollectionNameType>& OutCollections) const override;
virtual void GetChildCollectionNames(FName CollectionName, ECollectionShareType::Type ShareType, ECollectionShareType::Type ChildShareType, TArray<FName>& CollectionNames) const override;
virtual TOptional<FCollectionNameType> GetParentCollection(FName CollectionName, ECollectionShareType::Type ShareType) const override;
virtual bool CollectionExists(FName CollectionName, ECollectionShareType::Type ShareType) const override;
virtual bool GetAssetsInCollection(FName CollectionName, ECollectionShareType::Type ShareType, TArray<FSoftObjectPath>& AssetPaths, ECollectionRecursionFlags::Flags RecursionMode = ECollectionRecursionFlags::Self) const override;
virtual bool GetObjectsInCollection(FName CollectionName, ECollectionShareType::Type ShareType, TArray<FSoftObjectPath>& ObjectPaths, ECollectionRecursionFlags::Flags RecursionMode = ECollectionRecursionFlags::Self) const override;
virtual bool GetClassesInCollection(FName CollectionName, ECollectionShareType::Type ShareType, TArray<FTopLevelAssetPath>& ClassPaths, ECollectionRecursionFlags::Flags RecursionMode = ECollectionRecursionFlags::Self) const override;
virtual void GetCollectionsContainingObject(const FSoftObjectPath& ObjectPath, ECollectionShareType::Type ShareType, TArray<FName>& OutCollectionNames, ECollectionRecursionFlags::Flags RecursionMode = ECollectionRecursionFlags::Self) const override;
virtual void GetCollectionsContainingObject(const FSoftObjectPath& ObjectPath, TArray<FCollectionNameType>& OutCollections, ECollectionRecursionFlags::Flags RecursionMode = ECollectionRecursionFlags::Self) const override;
virtual void GetCollectionsContainingObjects(const TArray<FSoftObjectPath>& ObjectPaths, TMap<FCollectionNameType, TArray<FSoftObjectPath>>& OutCollectionsAndMatchedObjects, ECollectionRecursionFlags::Flags RecursionMode = ECollectionRecursionFlags::Self) const override;
virtual FString GetCollectionsStringForObject(const FSoftObjectPath& ObjectPath, ECollectionShareType::Type ShareType, ECollectionRecursionFlags::Flags RecursionMode = ECollectionRecursionFlags::Self, bool bFullPaths = true) const override;
virtual FString MakeCollectionPath(FName CollectionName, ECollectionShareType::Type ShareType) const override;
virtual void CreateUniqueCollectionName(FName BaseName, ECollectionShareType::Type ShareType, FName& OutCollectionName) const override;
virtual bool IsValidCollectionName(const FString& CollectionName, ECollectionShareType::Type ShareType, FText* OutError = nullptr) const override;
virtual bool CreateCollection(FName CollectionName, ECollectionShareType::Type ShareType, ECollectionStorageMode::Type StorageMode, FText* OutError = nullptr) override;
virtual bool RenameCollection(FName CurrentCollectionName, ECollectionShareType::Type CurrentShareType, FName NewCollectionName, ECollectionShareType::Type NewShareType, FText* OutError = nullptr) override;
virtual bool ReparentCollection(FName CollectionName, ECollectionShareType::Type ShareType, FName ParentCollectionName, ECollectionShareType::Type ParentShareType, FText* OutError = nullptr) override;
virtual bool DestroyCollection(FName CollectionName, ECollectionShareType::Type ShareType, FText* OutError = nullptr) override;
virtual bool AddToCollection(FName CollectionName, ECollectionShareType::Type ShareType, const FSoftObjectPath& ObjectPath, FText* OutError = nullptr) override;
virtual bool AddToCollection(FName CollectionName, ECollectionShareType::Type ShareType, TConstArrayView<FSoftObjectPath> ObjectPaths, int32* OutNumAdded = nullptr, FText* OutError = nullptr) override;
virtual bool RemoveFromCollection(FName CollectionName, ECollectionShareType::Type ShareType, const FSoftObjectPath& ObjectPath, FText* OutError = nullptr) override;
virtual bool RemoveFromCollection(FName CollectionName, ECollectionShareType::Type ShareType, TConstArrayView<FSoftObjectPath> ObjectPaths, int32* OutNumRemoved = nullptr, FText* OutError = nullptr) override;
virtual bool SetDynamicQueryText(FName CollectionName, ECollectionShareType::Type ShareType, const FString& InQueryText, FText* OutError = nullptr) override;
virtual bool GetDynamicQueryText(FName CollectionName, ECollectionShareType::Type ShareType, FString& OutQueryText, FText* OutError = nullptr) const override;
virtual bool TestDynamicQuery(FName CollectionName, ECollectionShareType::Type ShareType, const ITextFilterExpressionContext& InContext, bool& OutResult, FText* OutError = nullptr) const override;
virtual bool EmptyCollection(FName CollectionName, ECollectionShareType::Type ShareType, FText* OutError = nullptr) override;
virtual bool SaveCollection(FName CollectionName, ECollectionShareType::Type ShareType, FText* OutError = nullptr) override;
virtual bool UpdateCollection(FName CollectionName, ECollectionShareType::Type ShareType, FText* OutError = nullptr) override;
virtual bool GetCollectionStatusInfo(FName CollectionName, ECollectionShareType::Type ShareType, FCollectionStatusInfo& OutStatusInfo, FText* OutError = nullptr) const override;
virtual bool HasCollectionColors(TArray<FLinearColor>* OutColors = nullptr) const override;
virtual bool GetCollectionColor(FName CollectionName, ECollectionShareType::Type ShareType, TOptional<FLinearColor>& OutColor, FText* OutError = nullptr) const override;
virtual bool SetCollectionColor(FName CollectionName, ECollectionShareType::Type ShareType, const TOptional<FLinearColor>& NewColor, FText* OutError = nullptr) override;
virtual bool GetCollectionStorageMode(FName CollectionName, ECollectionShareType::Type ShareType, ECollectionStorageMode::Type& OutStorageMode, FText* OutError = nullptr) const override;
virtual bool IsObjectInCollection(const FSoftObjectPath& ObjectPath, FName CollectionName, ECollectionShareType::Type ShareType, ECollectionRecursionFlags::Flags RecursionMode = ECollectionRecursionFlags::Self, FText* OutError = nullptr) const override;
virtual bool IsValidParentCollection(FName CollectionName, ECollectionShareType::Type ShareType, FName ParentCollectionName, ECollectionShareType::Type ParentShareType, FText* OutError) const override;
/*
* Returns false if the collection container's internal lock cannot be promoted preventing the cache from updating,
* which can happen if the thread already holds a read lock then reenters and tries to promote to a write lock.
*/
bool UpdateCaches(ECollectionCacheFlags ToUpdate);
void HandleFixupRedirectors(ICollectionRedirectorFollower& InRedirectorFollower);
bool HandleRedirectorsDeleted(TConstArrayView<FSoftObjectPath> ObjectPaths, FText* OutError);
void HandleObjectRenamed(const FSoftObjectPath& OldObjectPath, const FSoftObjectPath& NewObjectPath);
void HandleObjectsDeleted(TConstArrayView<FSoftObjectPath> ObjectPaths);
/** Event for when the collection container's hidden state changes */
DECLARE_DERIVED_EVENT(FCollectionContainer, ICollectionContainer::FIsHiddenChangedEvent, FIsHiddenChangedEvent);
virtual FIsHiddenChangedEvent& OnIsHiddenChanged() override { return IsHiddenChangedEvent; }
/** Event for when collections are created */
DECLARE_DERIVED_EVENT(FCollectionContainer, ICollectionContainer::FCollectionCreatedEvent, FCollectionCreatedEvent);
virtual FCollectionCreatedEvent& OnCollectionCreated() override { return CollectionCreatedEvent; }
/** Event for when collections are destroyed */
DECLARE_DERIVED_EVENT(FCollectionContainer, ICollectionContainer::FCollectionDestroyedEvent, FCollectionDestroyedEvent);
virtual FCollectionDestroyedEvent& OnCollectionDestroyed() override { return CollectionDestroyedEvent; }
/** Event for when assets are added to a collection */
virtual FOnAssetsAddedToCollection& OnAssetsAddedToCollection() override { return AssetsAddedToCollectionDelegate; }
/** Event for when assets are removed from a collection */
virtual FOnAssetsRemovedFromCollection& OnAssetsRemovedFromCollection() override { return AssetsRemovedFromCollectionDelegate; }
/** Event for when collections are renamed */
DECLARE_DERIVED_EVENT(FCollectionContainer, ICollectionContainer::FCollectionRenamedEvent, FCollectionRenamedEvent);
virtual FCollectionRenamedEvent& OnCollectionRenamed() override { return CollectionRenamedEvent; }
/** Event for when collections are re-parented */
DECLARE_DERIVED_EVENT(FCollectionContainer, ICollectionContainer::FCollectionReparentedEvent, FCollectionReparentedEvent);
virtual FCollectionReparentedEvent& OnCollectionReparented() override { return CollectionReparentedEvent; }
/** Event for when collections is updated, or otherwise changed and we can't tell exactly how (eg, after updating from source control and merging) */
DECLARE_DERIVED_EVENT(FCollectionContainer, ICollectionContainer::FCollectionUpdatedEvent, FCollectionUpdatedEvent);
virtual FCollectionUpdatedEvent& OnCollectionUpdated() override { return CollectionUpdatedEvent; }
void OnRemovedFromCollectionManager();
/** Tick this collection container so it can process any file cache events */
void TickFileCache();
private:
/** Loads all collection files from disk. Must only be called from constructor as it does not lock for the full duration. */
void LoadCollections();
/** Returns true if the specified share type requires source control */
bool ShouldUseSCC(ECollectionShareType::Type ShareType) const;
/** Given a collection name and share type, work out the full filename for the collection to use on disk */
FString GetCollectionFilename(const FName& InCollectionName, const ECollectionShareType::Type InCollectionShareType) const;
/** Returns the read-only bit mask for the specified share type. */
static uint8 GetReadOnlyMask(ECollectionShareType::Type ShareType);
bool IsReadOnly(FCollectionScopeLock& InGuard, ECollectionShareType::Type ShareType) const;
/** Returns whether the collection container is in a valid state for writing */
bool ValidateWritable(FCollectionScopeLock& InGuard, ECollectionShareType::Type ShareType, FText* OutError) const;
/** Adds a collection to the lookup maps */
bool AddCollection(FCollectionScopeLock_Write& InGuard, const TSharedRef<FCollection>& CollectionRef, ECollectionShareType::Type ShareType);
/** Removes a collection from the lookup maps */
bool RemoveCollection(FCollectionScopeLock_Write& InGuard, const TSharedRef<FCollection>& CollectionRef, ECollectionShareType::Type ShareType);
/** Removes an object from any collections that contain it */
void RemoveObjectFromCollections(FCollectionScopeLock_Write& InGuard, const FSoftObjectPath& ObjectPath, TArray<FCollectionNameType>& OutUpdatedCollections);
/** Replaces an object with another in any collections that contain it */
void ReplaceObjectInCollections(
FCollectionScopeLock_Write& InGuard, const FSoftObjectPath& OldObjectPath, const FSoftObjectPath& NewObjectPath,
TArray<FCollectionNameType>& OutUpdatedCollections);
/** Internal common functionality for saving a collection
* bForceCommitToRevisionControl - If the collection's storage mode will save it to source control, then
* bForceCommitToRevisionControl will ensure that it is committed after save. If this is false, then the collection
* will be left as a modified file which can be advantageous for slow source control servers.
*/
bool InternalSaveCollection(FCollectionScopeLock_Write&, const TSharedRef<FCollection>& CollectionRef, FText* OutError, bool bForceCommitToRevisionControl);
/*
* Internal version of IsValidParentCollection to avoid taking lock recursively.
* Cache must be updated for recursion before calling.
*/
bool IsValidParentCollection_Locked(FCollectionScopeLock& InGuard, FName CollectionName, ECollectionShareType::Type ShareType, FName ParentCollectionName, ECollectionShareType::Type ParentShareType, FText* OutError) const;
/**
* Check if the given collection exists.
* Using the public API function risks acquiring the lock recursively.
*/
bool CollectionExists_Locked(FCollectionScopeLock& InGuard, FName CollectionName, ECollectionShareType::Type ShareType) const;
private:
/** The extension used for collection files */
static FStringView CollectionExtension;
/** Required for updating caches as well as write operations to collections */
mutable FCollectionRecursiveRwLock Lock;
/** The collection managed that is managing this collection container.
* Null if this collection container has been removed from the collection manager.
*/
FCollectionManager* CollectionManager;
/** The folders that contain collections */
TSharedRef<ICollectionSource> CollectionSource;
/** Bit representation of the read-only state of each share type */
static_assert(sizeof(uint8) * 8 >= ECollectionShareType::CST_All, "ReadOnlyFlags is not large enough for all share types.");
uint8 ReadOnlyFlags;
/** True if the collection container is hidden in the Editor's UI */
bool bIsHidden;
/** Array of file cache instances that are watching for the collection files changing on disk */
TSharedPtr<DirectoryWatcher::FFileCache> CollectionFileCaches[ECollectionShareType::CST_All];
/** A map of collection names to FCollection objects */
TMap<FCollectionNameType, TSharedRef<FCollection>> AvailableCollections;
/** Cache of collection hierarchy, identity, etc */
TPimplPtr<FCollectionContainerCache> CollectionCache;
/** Event for when the collection container's hidden state changes */
FIsHiddenChangedEvent IsHiddenChangedEvent;
/** Event for when assets are added to a collection */
FOnAssetsAddedToCollection AssetsAddedToCollectionDelegate;
/** Event for when assets are removed from a collection */
FOnAssetsRemovedFromCollection AssetsRemovedFromCollectionDelegate;
/** Event for when collections are renamed */
FCollectionRenamedEvent CollectionRenamedEvent;
/** Event for when collections are re-parented */
FCollectionReparentedEvent CollectionReparentedEvent;
/** Event for when collections are updated, or otherwise changed and we can't tell exactly how (eg, after updating from source control and merging) */
FCollectionUpdatedEvent CollectionUpdatedEvent;
/** Event for when collections are created */
FCollectionCreatedEvent CollectionCreatedEvent;
/** Event for when collections are destroyed */
FCollectionDestroyedEvent CollectionDestroyedEvent;
};