// 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& InCollectionSource); ~FCollectionContainer(); virtual const TSharedRef& 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& OutCollections) const override; virtual void GetCollections(FName CollectionName, TArray& OutCollections) const override; virtual void GetCollectionNames(ECollectionShareType::Type ShareType, TArray& CollectionNames) const override; virtual void GetRootCollections(TArray& OutCollections) const override; virtual void GetRootCollectionNames(ECollectionShareType::Type ShareType, TArray& CollectionNames) const override; virtual void GetChildCollections(FName CollectionName, ECollectionShareType::Type ShareType, TArray& OutCollections) const override; virtual void GetChildCollectionNames(FName CollectionName, ECollectionShareType::Type ShareType, ECollectionShareType::Type ChildShareType, TArray& CollectionNames) const override; virtual TOptional 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& AssetPaths, ECollectionRecursionFlags::Flags RecursionMode = ECollectionRecursionFlags::Self) const override; virtual bool GetObjectsInCollection(FName CollectionName, ECollectionShareType::Type ShareType, TArray& ObjectPaths, ECollectionRecursionFlags::Flags RecursionMode = ECollectionRecursionFlags::Self) const override; virtual bool GetClassesInCollection(FName CollectionName, ECollectionShareType::Type ShareType, TArray& ClassPaths, ECollectionRecursionFlags::Flags RecursionMode = ECollectionRecursionFlags::Self) const override; virtual void GetCollectionsContainingObject(const FSoftObjectPath& ObjectPath, ECollectionShareType::Type ShareType, TArray& OutCollectionNames, ECollectionRecursionFlags::Flags RecursionMode = ECollectionRecursionFlags::Self) const override; virtual void GetCollectionsContainingObject(const FSoftObjectPath& ObjectPath, TArray& OutCollections, ECollectionRecursionFlags::Flags RecursionMode = ECollectionRecursionFlags::Self) const override; virtual void GetCollectionsContainingObjects(const TArray& ObjectPaths, TMap>& 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 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 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* OutColors = nullptr) const override; virtual bool GetCollectionColor(FName CollectionName, ECollectionShareType::Type ShareType, TOptional& OutColor, FText* OutError = nullptr) const override; virtual bool SetCollectionColor(FName CollectionName, ECollectionShareType::Type ShareType, const TOptional& 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 ObjectPaths, FText* OutError); void HandleObjectRenamed(const FSoftObjectPath& OldObjectPath, const FSoftObjectPath& NewObjectPath); void HandleObjectsDeleted(TConstArrayView 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& CollectionRef, ECollectionShareType::Type ShareType); /** Removes a collection from the lookup maps */ bool RemoveCollection(FCollectionScopeLock_Write& InGuard, const TSharedRef& CollectionRef, ECollectionShareType::Type ShareType); /** Removes an object from any collections that contain it */ void RemoveObjectFromCollections(FCollectionScopeLock_Write& InGuard, const FSoftObjectPath& ObjectPath, TArray& OutUpdatedCollections); /** Replaces an object with another in any collections that contain it */ void ReplaceObjectInCollections( FCollectionScopeLock_Write& InGuard, const FSoftObjectPath& OldObjectPath, const FSoftObjectPath& NewObjectPath, TArray& 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& 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 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 CollectionFileCaches[ECollectionShareType::CST_All]; /** A map of collection names to FCollection objects */ TMap> AvailableCollections; /** Cache of collection hierarchy, identity, etc */ TPimplPtr 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; };