// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "UObject/GCObject.h" #include "Containers/StaticArray.h" #include "MassEntityTypes.h" #include "MassProcessingTypes.h" #include "MassEntityQuery.h" #if UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_5 #include "StructUtils/InstancedStruct.h" #include "StructUtils/StructUtilsTypes.h" #endif #include "MassObserverManager.h" #include "Containers/MpscQueue.h" #include "MassRequirementAccessDetector.h" #include "Templates/FunctionFwd.h" #include "MassEntityManagerStorage.h" #include "MassArchetypeGroup.h" #if WITH_MASSENTITY_DEBUG #include "Misc/EnumClassFlags.h" #endif #define UE_API MASSENTITY_API struct FInstancedStruct; struct FMassEntityQuery; struct FMassExecutionContext; struct FMassArchetypeData; struct FMassCommandBuffer; struct FMassArchetypeEntityCollection; class FOutputDevice; struct FMassDebugger; enum class EMassFragmentAccess : uint8; enum class EForkProcessRole : uint8; namespace UE::Mass { struct FEntityBuilder; struct FTypeManager; namespace Private { struct FEntityStorageInitializer; } namespace ObserverManager { struct FObserverLock; struct FCreationContext; } } // use REQUESTED_MASS_CONCURRENT_RESERVE=0 in your project's Build.cs file to disable concurrent storage // NOTE that it will always be enabled in WITH_EDITOR since editor code requires it. // @see WITH_MASS_CONCURRENT_RESERVE #ifndef REQUESTED_MASS_CONCURRENT_RESERVE #define REQUESTED_MASS_CONCURRENT_RESERVE 1 #endif #define WITH_MASS_CONCURRENT_RESERVE (REQUESTED_MASS_CONCURRENT_RESERVE || WITH_EDITOR) namespace UE::Mass { #if WITH_MASS_CONCURRENT_RESERVE using FStorageType = IEntityStorageInterface; #else using FStorageType = FSingleThreadedEntityStorage; #endif //WITH_MASS_CONCURRENT_RESERVE } /** * The type responsible for hosting Entities managing Archetypes. * Entities are stored as FEntityData entries in a chunked array. * Each valid entity is assigned to an Archetype that stored fragments associated with a given entity at the moment. * * FMassEntityManager supplies API for entity creation (that can result in archetype creation) and entity manipulation. * Even though synchronized manipulation methods are available in most cases the entity operations are performed via a command * buffer. The default command buffer can be obtained with a Defer() call. @see FMassCommandBuffer for more details. * * FMassEntityManager are meant to be stored with a TSharedPtr or TSharedRef. Some of Mass API pass around * FMassEntityManager& but programmers can always use AsShared() call to obtain a shared ref for a given manager instance * (as supplied by deriving from TSharedFromThis). * IMPORTANT: if you create your own FMassEntityManager instance remember to call Initialize() before using it. */ struct FMassEntityManager : public TSharedFromThis, public FGCObject { friend FMassEntityQuery; friend FMassDebugger; DECLARE_MULTICAST_DELEGATE_OneParam(FOnNewArchetypeDelegate, const FMassArchetypeHandle&); private: // Index 0 is reserved so we can treat that index as an invalid entity handle constexpr static int32 NumReservedEntities = 1; public: struct FScopedProcessing { explicit FScopedProcessing(std::atomic& InProcessingScopeCount) : ScopedProcessingCount(InProcessingScopeCount) { ++ScopedProcessingCount; } ~FScopedProcessing() { --ScopedProcessingCount; } private: std::atomic& ScopedProcessingCount; }; using FStructInitializationCallback = TFunctionRef; const UE_API static FMassEntityHandle InvalidEntity; UE_API explicit FMassEntityManager(UObject* InOwner = nullptr); FMassEntityManager(const FMassEntityManager& Other) = delete; UE_API virtual ~FMassEntityManager(); // FGCObject interface UE_API virtual void AddReferencedObjects(FReferenceCollector& Collector) override; virtual FString GetReferencerName() const override { return TEXT("FMassEntityManager"); } // End of FGCObject interface UE_API void GetResourceSizeEx(FResourceSizeEx& CumulativeResourceSize); // Default to use single threaded implementation UE_API void Initialize(); UE_API void Initialize(const FMassEntityManagerStorageInitParams& InitializationParams); UE_API void PostInitialize(); UE_API void Deinitialize(); /** * A special, relaxed but slower version of CreateArchetype functions that allows FragmentAngTagsList to contain * both fragments and tags. */ UE_API FMassArchetypeHandle CreateArchetype(TConstArrayView FragmentsAndTagsList, const FMassArchetypeCreationParams& CreationParams = FMassArchetypeCreationParams()); /** * A special, relaxed but slower version of CreateArchetype functions that allows FragmentAngTagsList to contain * both fragments and tags. This version takes an original archetype and copies it layout, then appends any fragments and tags from the * provided list if they're not already in the original archetype. * * @param SourceArchetype The archetype where the composition will be copied from. * @param FragmentsAndTagsList The list of fragments and tags to add to the copied composition. */ UE_API FMassArchetypeHandle CreateArchetype(FMassArchetypeHandle SourceArchetype, TConstArrayView FragmentsAndTagsList); /** * A special, relaxed but slower version of CreateArchetype functions that allows FragmentAngTagsList to contain * both fragments and tags. This version takes an original archetype and copies it layout, then appends any fragments and tags from the * provided list if they're not already in the original archetype. * * @param SourceArchetype The archetype where the composition will be copied from. * @param FragmentsAndTagsList The list of fragments and tags to add to the copied composition. * @param CreationParams Additional arguments used to create the new archetype. */ UE_API FMassArchetypeHandle CreateArchetype(FMassArchetypeHandle SourceArchetype, TConstArrayView FragmentsAndTagsList, const FMassArchetypeCreationParams& CreationParams); /** * CreateArchetype from a composition descriptor and initial values * * @param Composition of fragment, tag and chunk fragment types * @param CreationParams Parameters used during archetype construction * @return a handle of a new archetype */ UE_API FMassArchetypeHandle CreateArchetype(const FMassArchetypeCompositionDescriptor& Composition, const FMassArchetypeCreationParams& CreationParams = FMassArchetypeCreationParams()); /** * Creates an archetype like SourceArchetype + InFragments. * @param SourceArchetype the archetype used to initially populate the list of fragments of the archetype being created. * @param InFragments list of unique fragments to add to fragments fetched from SourceArchetype. Note that * adding an empty list is not supported and doing so will result in failing a `check` * @return a handle of a new archetype * @note it's caller's responsibility to ensure that NewFragmentList is not empty and contains only fragment * types that SourceArchetype doesn't already have. If the caller cannot guarantee it use of AddFragment functions * family is recommended. */ UE_API FMassArchetypeHandle CreateArchetype(const TSharedPtr& SourceArchetype, const FMassFragmentBitSet& InFragments); /** * Creates an archetype like SourceArchetype + InFragments. * @param SourceArchetype the archetype used to initially populate the list of fragments of the archetype being created. * @param InFragments list of unique fragments to add to fragments fetched from SourceArchetype. Note that * adding an empty list is not supported and doing so will result in failing a `check` * @param CreationParams Parameters used during archetype construction * @return a handle of a new archetype * @note it's caller's responsibility to ensure that NewFragmentList is not empty and contains only fragment * types that SourceArchetype doesn't already have. If the caller cannot guarantee it use of AddFragment functions * family is recommended. */ UE_API FMassArchetypeHandle CreateArchetype(const TSharedPtr& SourceArchetype, const FMassFragmentBitSet& InFragments, const FMassArchetypeCreationParams& CreationParams); /** * A helper function to be used when creating entities with shared fragments provided, or when adding shared fragments * to existing entities * @param ArchetypeHandle that's the assumed target archetype. But we'll be making sure its composition matches SharedFragmentsBitSet * @param SharedFragmentBitSet indicates which shared fragments we want the target archetype to have. If ArchetypeHandle * doesn't have these a new archetype will be created. */ UE_API FMassArchetypeHandle GetOrCreateSuitableArchetype(const FMassArchetypeHandle& ArchetypeHandle , const FMassSharedFragmentBitSet& SharedFragmentBitSet , const FMassConstSharedFragmentBitSet& ConstSharedFragmentBitSet , const FMassArchetypeCreationParams& CreationParams = FMassArchetypeCreationParams()); /** Fetches the archetype for a given EntityHandle. If EntityHandle is not valid it will still return a handle, just with an invalid archetype */ UE_API FMassArchetypeHandle GetArchetypeForEntity(FMassEntityHandle EntityHandle) const; /** * Fetches the archetype for a given EntityHandle. Note that it's callers responsibility the given EntityHandle handle is valid. * If you can't ensure that call GetArchetypeForEntity. */ UE_API FMassArchetypeHandle GetArchetypeForEntityUnsafe(FMassEntityHandle EntityHandle) const; /** * Searches through all known archetypes and matches them to the provided requirements. All archetypes that pass the requirement check are returned. * @param Requirements The set of fragments and tags that need to be on available in the request form on an archetype before it's added. * @param OutValidArchetypes Archetypes that pass the requirements test are added here. */ UE_API void GetMatchingArchetypes(const FMassFragmentRequirements& Requirements, TArray& OutValidArchetypes) const; /** Method to iterate on all the fragment types of an archetype */ static UE_API void ForEachArchetypeFragmentType(const FMassArchetypeHandle& ArchetypeHandle, TFunction< void(const UScriptStruct* /*FragmentType*/)> Function); /** * Go through all archetypes and compact entities * @param TimeAllowed to do entity compaction, once it reach that time it will stop and return */ UE_API void DoEntityCompaction(const double TimeAllowed); /** * Creates fully built entity ready to be used by the subsystem * @param ArchetypeHandle you want this entity to be * @param SharedFragmentValues to be associated with the entity * @return FMassEntityHandle id of the newly created entity */ UE_API FMassEntityHandle CreateEntity(const FMassArchetypeHandle& ArchetypeHandle, const FMassArchetypeSharedFragmentValues& SharedFragmentValues = {}); /** * Creates fully built entity ready to be used by the subsystem * @param FragmentInstanceList is the fragments to create the entity from and initialize values * @param SharedFragmentValues to be associated with the entity * @return FMassEntityHandle id of the newly created entity */ UE_API FMassEntityHandle CreateEntity(TConstArrayView FragmentInstanceList, const FMassArchetypeSharedFragmentValues& SharedFragmentValues = {}, const FMassArchetypeCreationParams& CreationParams = FMassArchetypeCreationParams()); using FEntityCreationContext = UE::Mass::ObserverManager::FCreationContext; /** * The main use-case for this function is to create a blank FEntityCreationContext and hold on to it while creating * a bunch of entities (with multiple calls to BatchCreate* and/or BatchBuild*) and modifying them (with mutating batched API) * while not causing multiple Observers to trigger. All the observers will be triggered at one go, once the FEntityCreationContext * instance gets destroyed. * * !Important note: the "Creation Context" is a specialized wrapper for an "Observers Lock" (@see GetOrMakeObserversLock). * As long as the creation context is alive all the operations will be assumed to affect the newly created entities. * The consequence of that is operations performed on already existing entities won't be tracked, as long * as the creation context is alive. * Note that you can hold a FMassObserverManager::FObserverLock instance while the creation lock gets destroyed, the * observers lock is a lower-level concept than the creation context. * * @return the existing (if valid) or a newly created creation context */ UE_API TSharedRef GetOrMakeCreationContext(); /** * Fetches the observers lock (as hosted by FMassObserverManager). If one is not currently active, * one will be created. While the lock is active all the observers notifications are suspended, and * will be sent out when FMassObserverManager::FObserverLock instance gets destroyed. * Locking observers needs to be used when entities are being configured with multiple operations, * and we want observers to be triggered only once all the operations are executed. * * Note that while the observers are locked we're unable to send "Remove" notifications, so once * the lock is released and the observers get notified, the data being removed won't be available anymore * (which is a difference in behavior as compared to removal notifications while the observers are not locked). */ TSharedRef GetOrMakeObserversLock(); /** * A version of CreateEntity that's creating a number of entities (Count) in one go * @param ArchetypeHandle you want this entity to be * @param SharedFragmentValues to be associated with the entities * @param ReservedEntities a list of reserved entities that have not yet been assigned to an archetype. * @return a creation context that will notify all the interested observers about newly created fragments once the context is released */ UE_API TSharedRef BatchCreateReservedEntities(const FMassArchetypeHandle& ArchetypeHandle, const FMassArchetypeSharedFragmentValues& SharedFragmentValues, TConstArrayView ReservedEntities); FORCEINLINE TSharedRef BatchCreateReservedEntities(const FMassArchetypeHandle& ArchetypeHandle, TConstArrayView OutEntities) { return BatchCreateReservedEntities(ArchetypeHandle, FMassArchetypeSharedFragmentValues(), OutEntities); } /** * A version of CreateEntity that's creating a number of entities (Count) in one go * @param ArchetypeHandle you want this entity to be * @param SharedFragmentValues to be associated with the entities * @param Count number of entities to create * @param InOutEntities the newly created entities are appended to given array, i.e. the pre-existing content of OutEntities won't be affected by the call * @return a creation context that will notify all the interested observers about newly created fragments once the context is released */ UE_API TSharedRef BatchCreateEntities(const FMassArchetypeHandle& ArchetypeHandle, const FMassArchetypeSharedFragmentValues& SharedFragmentValues, const int32 Count, TArray& InOutEntities); FORCEINLINE TSharedRef BatchCreateEntities(const FMassArchetypeHandle& ArchetypeHandle, const int32 Count, TArray& InOutEntities) { return BatchCreateEntities(ArchetypeHandle, FMassArchetypeSharedFragmentValues(), Count, InOutEntities); } /** * Destroys a fully built entity, use ReleaseReservedEntity if entity was not yet built. * @param EntityHandle identifying the entity to destroy */ UE_API void DestroyEntity(FMassEntityHandle EntityHandle); /** * Reserves an entity in the subsystem, the entity is still not ready to be used by the subsystem, need to call BuildEntity() * @return FMassEntityHandle id of the reserved entity */ UE_API FMassEntityHandle ReserveEntity(); /** * Builds an entity for it to be ready to be used by the subsystem * @param EntityHandle identifying the entity to build, which was retrieved with ReserveEntity() method * @param ArchetypeHandle you want this entity to be * @param SharedFragmentValues to be associated with the entity */ UE_API void BuildEntity(FMassEntityHandle EntityHandle, const FMassArchetypeHandle& ArchetypeHandle, const FMassArchetypeSharedFragmentValues& SharedFragmentValues = {}); /** * Builds an entity for it to be ready to be used by the subsystem * @param EntityHandle identifying the entity to build, which was retrieved with ReserveEntity() method * @param FragmentInstanceList is the fragments to create the entity from and initialize values * @param SharedFragmentValues to be associated with the entity */ UE_API void BuildEntity(FMassEntityHandle EntityHandle, TConstArrayView FragmentInstanceList, const FMassArchetypeSharedFragmentValues& SharedFragmentValues = {}); /* * Releases a previously reserved entity handle that was not yet built, otherwise call DestroyEntity * @param EntityHandle to release */ UE_API void ReleaseReservedEntity(FMassEntityHandle EntityHandle); /** * Destroys all the entities in the provided array of entities. The function will also gracefully handle entities * that have been reserved but not created yet. * @note the function doesn't handle duplicates in InEntities. * @param InEntities to destroy */ UE_API void BatchDestroyEntities(TConstArrayView InEntities); /** * Destroys all the entities provided via the Collection. The function will also gracefully handle entities * that have been reserved but not created yet. * @param Collection to destroy */ UE_API void BatchDestroyEntityChunks(const FMassArchetypeEntityCollection& Collection); UE_API void BatchDestroyEntityChunks(TConstArrayView Collections); /** * Assigns all entities indicated by Collections to a given archetype group. * Note that depending on their individual composition each entity can end up in a different archetype. * @paramm GroupHandle indicates the target group. Passing an invalid group handle will get logged as warning and ignored. */ UE_API void BatchGroupEntities(const UE::Mass::FArchetypeGroupHandle GroupHandle, TConstArrayView Collections); /** * Assigns all entities indicated by InEntities to a given archetype group. * Note that depending on their individual composition each entity can end up in a different archetype. * @paramm GroupHandle indicates the target group. Passing an invalid group handle will get logged as warning and ignored. */ UE_API void BatchGroupEntities(const UE::Mass::FArchetypeGroupHandle GroupHandle, TConstArrayView InEntities); /** * Fetches FArchetypeGroupType instance (copy) associated with the given GroupName. A new group type * is created if GroupName has not been used in the past. */ UE_API UE::Mass::FArchetypeGroupType FinOrAddArchetypeGroupType(const FName GroupName); UE_API const UE::Mass::FArchetypeGroups& GetGroupsForArchetype(const FMassArchetypeHandle& ArchetypeHandle) const; UE_API void AddFragmentToEntity(FMassEntityHandle EntityHandle, const UScriptStruct* FragmentType); UE_API void AddFragmentToEntity(FMassEntityHandle EntityHandle, const UScriptStruct* FragmentType, const FStructInitializationCallback& Initializer); /** * Ensures that only unique fragments are added. * @note It's caller's responsibility to ensure EntityHandle's and FragmentList's validity. */ UE_API void AddFragmentListToEntity(FMassEntityHandle EntityHandle, TConstArrayView FragmentList); UE_API void AddFragmentInstanceListToEntity(FMassEntityHandle EntityHandle, TConstArrayView FragmentInstanceList); UE_API void RemoveFragmentFromEntity(FMassEntityHandle EntityHandle, const UScriptStruct* FragmentType); UE_API void RemoveFragmentListFromEntity(FMassEntityHandle EntityHandle, TConstArrayView FragmentList); UE_API void AddTagToEntity(FMassEntityHandle EntityHandle, const UScriptStruct* TagType); UE_API void RemoveTagFromEntity(FMassEntityHandle EntityHandle, const UScriptStruct* TagType); UE_API void SwapTagsForEntity(FMassEntityHandle EntityHandle, const UScriptStruct* FromFragmentType, const UScriptStruct* ToFragmentType); /** * Adds a new const shared fragment to the given entity. Note that it only works if the given entity doesn't have * a shared fragment of the given type. The function will give a soft "pass" if the entity has the shared fragment * of the same value. Setting shared fragment value (i.e. changing) is not supported and the function will log * a warning if that's attempted. * @return whether the entity has the Fragment value assigned to it, regardless of its original state (i.e. the function will * return true also if the entity already had the same values associated with it) */ UE_API bool AddConstSharedFragmentToEntity(const FMassEntityHandle EntityHandle, const FConstSharedStruct& InConstSharedFragment); /** * Removes a const shared fragment of the given type from the entity. * Will do nothing if entity did not have the shared fragment. * @return True if fragment removed from entity, false otherwise. */ UE_API bool RemoveConstSharedFragmentFromEntity(const FMassEntityHandle EntityHandle, const UScriptStruct& ConstSharedFragmentType); /** * Adds a new shared fragment to the given entity. Note that it only works if the given entity doesn't have * a shared fragment of the given type. The function will give a soft "pass" if the entity has the shared fragment * of the same value. Setting shared fragment value (i.e. changing) is not supported and the function will log * a warning if that's attempted. * @return whether the entity has the Fragment value assigned to it, regardless of its original state (i.e. the function will * return true also if the entity already had the same values associated with it) */ UE_API bool AddSharedFragmentToEntity(const FMassEntityHandle EntityHandle, const FSharedStruct& InSharedFragment); /** * Removes a shared fragment of the given type from the entity. * Will do nothing if entity did not have the shared fragment. * @return True if fragment removed from entity, false otherwise. */ UE_API bool RemoveSharedFragmentFromEntity(const FMassEntityHandle EntityHandle, const UScriptStruct& SharedFragmentType); /** * Removes EntityHandle from any-and-all groups of given type - i.e. the entity will be moved to an archetype * not in any of the groups of the given type. */ UE_API void RemoveEntityFromGroupType(FMassEntityHandle EntityHandle, UE::Mass::FArchetypeGroupType GroupType); /** * @return the group handle of the specific group of type GroupType that the entity belongs to */ UE_API UE::Mass::FArchetypeGroupHandle GetGroupForEntity(FMassEntityHandle EntityHandle, UE::Mass::FArchetypeGroupType GroupType) const; /** * Reserves Count number of entities and appends them to InOutEntities * @return a view into InOutEntities containing only the freshly reserved entities */ UE_API TConstArrayView BatchReserveEntities(const int32 Count, TArray& InOutEntities); /** * Reserves number of entities corresponding to number of entries in the provided array view InOutEntities. * As a result InOutEntities gets filled with handles of reserved entities * @return the number of entities reserved */ UE_API int32 BatchReserveEntities(TArrayView InOutEntities); UE_API TSharedRef BatchBuildEntities(const FMassArchetypeEntityCollectionWithPayload& EncodedEntitiesWithPayload, const FMassFragmentBitSet& FragmentsAffected , const FMassArchetypeSharedFragmentValues& SharedFragmentValues = {}, const FMassArchetypeCreationParams& CreationParams = FMassArchetypeCreationParams()); UE_API TSharedRef BatchBuildEntities(const FMassArchetypeEntityCollectionWithPayload& EncodedEntitiesWithPayload, const FMassArchetypeCompositionDescriptor& Composition , const FMassArchetypeSharedFragmentValues& SharedFragmentValues = {}, const FMassArchetypeCreationParams& CreationParams = FMassArchetypeCreationParams()); UE_API void BatchChangeTagsForEntities(TConstArrayView EntityCollections, const FMassTagBitSet& TagsToAdd, const FMassTagBitSet& TagsToRemove); UE_API void BatchChangeFragmentCompositionForEntities(TConstArrayView EntityCollections, const FMassFragmentBitSet& FragmentsToAdd, const FMassFragmentBitSet& FragmentsToRemove); UE_API void BatchAddFragmentInstancesForEntities(TConstArrayView EntityCollections, const FMassFragmentBitSet& FragmentsAffected); /** * Adds a new const and non-const shared fragments to all entities provided via EntityCollections */ UE_API void BatchAddSharedFragmentsForEntities(TConstArrayView EntityCollections, const FMassArchetypeSharedFragmentValues& AddedFragmentValues); /** * Adds elements indicated by InOutDescriptor to the entity indicated by EntityHandle. The function also figures out which elements * in InOutDescriptor are missing from the current composition of the given entity and then returns the resulting * delta via InOutDescriptor. * If InOutDescriptor indicates shared fragments to be added the caller is required to provide matching values for the indicated * shared fragment types, via AddedSharedFragmentValues. */ UE_API void AddCompositionToEntity_GetDelta(FMassEntityHandle EntityHandle, FMassArchetypeCompositionDescriptor& InOutDescriptor, const FMassArchetypeSharedFragmentValues* AddedSharedFragmentValues = nullptr); UE_API void RemoveCompositionFromEntity(FMassEntityHandle EntityHandle, const FMassArchetypeCompositionDescriptor& InDescriptor); UE_API const FMassArchetypeCompositionDescriptor& GetArchetypeComposition(const FMassArchetypeHandle& ArchetypeHandle) const; /** * Moves an entity over to a new archetype by copying over fragments common to both archetypes * @param EntityHandle idicates the entity to move * @param NewArchetypeHandle the handle to the new archetype * @param SharedFragmentValuesOverride if provided will override all given entity's shared fragment values */ UE_API void MoveEntityToAnotherArchetype(FMassEntityHandle EntityHandle, FMassArchetypeHandle NewArchetypeHandle, const FMassArchetypeSharedFragmentValues* SharedFragmentValuesOverride = nullptr); /** * Copies values from FragmentInstanceList over to target entity's fragment. Caller is responsible for ensuring that * the given entity does have given fragments. Failing this assumption will cause a check-fail. * @param EntityHandle idicates the target entity */ UE_API void SetEntityFragmentValues(FMassEntityHandle EntityHandle, TArrayView FragmentInstanceList); /** Copies values from FragmentInstanceList over to fragments of given entities collection. The caller is responsible * for ensuring that the given entity archetype (FMassArchetypeEntityCollection .Archetype) does have given fragments. * Failing this assumption will cause a check-fail. */ UE_API void BatchSetEntityFragmentValues(const FMassArchetypeEntityCollection& SparseEntities, TArrayView FragmentInstanceList); UE_API void BatchSetEntityFragmentValues(TConstArrayView EntityCollections, TArrayView FragmentInstanceList); /** * @return whether the given handle represents a valid and built entity * (i.e., the handle is valid and the entity represent has been constructed already) */ bool IsEntityActive(FMassEntityHandle EntityHandle) const { return IsEntityValid(EntityHandle) && IsEntityBuilt(EntityHandle); } /** * @return whether the given entity handle is valid, i.e. it points * to a valid spot in the entity storage and the handle's serial number is up to date */ UE_API bool IsEntityValid(FMassEntityHandle EntityHandle) const; /** whether the entity handle represents an entity that has been fully built (expecting a valid EntityHandle) */ UE_API bool IsEntityBuilt(FMassEntityHandle EntityHandle) const; /** * @return whether the given EntityHandle is valid and the entity it represents is in `Reserved` state * (i.e. it will also fail if the entity has already been `Created`) */ UE_API bool IsEntityReserved(FMassEntityHandle EntityHandle) const; /** Asserts that IsEntityValid */ UE_API void CheckIfEntityIsValid(FMassEntityHandle EntityHandle) const; /** Asserts that IsEntityBuilt */ UE_API void CheckIfEntityIsActive(FMassEntityHandle EntityHandle) const; template FragmentType& GetFragmentDataChecked(FMassEntityHandle EntityHandle) const { static_assert(UE::Mass::CFragment , "Given struct doesn't represent a valid fragment type. Make sure to inherit from FMassFragment or one of its child-types."); return *((FragmentType*)InternalGetFragmentDataChecked(EntityHandle, FragmentType::StaticStruct())); } template FragmentType* GetFragmentDataPtr(FMassEntityHandle EntityHandle) const { static_assert(UE::Mass::CFragment , "Given struct doesn't represent a valid fragment type. Make sure to inherit from FMassFragment or one of its child-types."); return (FragmentType*)InternalGetFragmentDataPtr(EntityHandle, FragmentType::StaticStruct()); } FStructView GetFragmentDataStruct(FMassEntityHandle EntityHandle, const UScriptStruct* FragmentType) const { checkf(UE::Mass::IsA(FragmentType) , TEXT("GetFragmentDataStruct called with an invalid fragment type '%s'"), *GetPathNameSafe(FragmentType)); return FStructView(FragmentType, static_cast(InternalGetFragmentDataPtr(EntityHandle, FragmentType))); } template ConstSharedFragmentType* GetConstSharedFragmentDataPtr(FMassEntityHandle EntityHandle) const { static_assert(UE::Mass::CConstSharedFragment, "Given struct doesn't represent a valid const shared fragment type. Make sure to inherit from FMassConstSharedFragment or one of its child-types."); const FConstSharedStruct* ConstSharedStruct = InternalGetConstSharedFragmentPtr(EntityHandle, ConstSharedFragmentType::StaticStruct()); return (ConstSharedFragmentType*)(ConstSharedStruct ? ConstSharedStruct->GetMemory() : nullptr); } template ConstSharedFragmentType& GetConstSharedFragmentDataChecked(FMassEntityHandle EntityHandle) const { ConstSharedFragmentType* TypePtr = GetConstSharedFragmentDataPtr(EntityHandle); check(TypePtr); return *TypePtr; } FConstStructView GetConstSharedFragmentDataStruct(FMassEntityHandle EntityHandle, const UScriptStruct* ConstSharedFragmentType) const { checkf(UE::Mass::IsA(ConstSharedFragmentType) , TEXT("GetConstSharedFragmentDataStruct called with an invalid fragment type '%s'"), *GetPathNameSafe(ConstSharedFragmentType)); const FConstSharedStruct* ConstSharedStruct = InternalGetConstSharedFragmentPtr(EntityHandle, ConstSharedFragmentType); return ConstSharedStruct ? FConstStructView(*ConstSharedStruct) : FConstStructView(); } template TConstArrayView GetSharedFragmentsOfType() { static_assert(UE::Mass::CSharedFragment , "Given struct doesn't represent a valid shared fragment type. Make sure to inherit from FMassSharedFragment or one of its child-types."); TArray* InstancesOfType = SharedFragmentsContainer.Find(SharedFragmentType::StaticStruct()); return InstancesOfType ? *InstancesOfType : TConstArrayView(); } template SharedFragmentType* GetSharedFragmentDataPtr(FMassEntityHandle EntityHandle) const { static_assert(UE::Mass::CSharedFragment , "Given struct doesn't represent a valid shared fragment type. Make sure to inherit from FMassSharedFragment or one of its child-types."); const FSharedStruct* FragmentPtr = InternalGetSharedFragmentPtr(EntityHandle, SharedFragmentType::StaticStruct()); return (SharedFragmentType*)(FragmentPtr ? FragmentPtr->GetMemory() : nullptr); } template SharedFragmentType& GetSharedFragmentDataChecked(FMassEntityHandle EntityHandle) const { SharedFragmentType* TypePtr = GetSharedFragmentDataPtr(EntityHandle); check(TypePtr); return *TypePtr; } FConstStructView GetSharedFragmentDataStruct(FMassEntityHandle EntityHandle, const UScriptStruct* SharedFragmentType) const { checkf(UE::Mass::IsA(SharedFragmentType) , TEXT("GetSharedFragmentDataStruct called with an invalid fragment type '%s'"), *GetPathNameSafe(SharedFragmentType)); const FSharedStruct* FragmentPtr = InternalGetSharedFragmentPtr(EntityHandle, SharedFragmentType); return FragmentPtr ? FConstStructView(*FragmentPtr) : FConstStructView(); } template FConstStructView GetElementDataStruct(FMassEntityHandle EntityHandle, TNotNull FragmentType) const { if constexpr (std::is_same_v) { return GetFragmentDataStruct(EntityHandle, FragmentType); } else if constexpr (std::is_same_v) { return GetSharedFragmentDataStruct(EntityHandle, FragmentType); } else if constexpr (std::is_same_v) { return GetConstSharedFragmentDataStruct(EntityHandle, FragmentType); } else { static_assert(UE::Mass::TAlwaysFalse, "Unsupported element type passed to GetElementDataStruct"); return {}; } } uint32 GetArchetypeDataVersion() const { return ArchetypeDataVersion; } /** * Creates and initializes a FMassExecutionContext instance. */ UE_API FMassExecutionContext CreateExecutionContext(const float DeltaSeconds); FScopedProcessing NewProcessingScope() { return FScopedProcessing(ProcessingScopeCount); } /** * Indicates whether there are processors out there performing operations on this instance of MassEntityManager. * Used to ensure that mutating operations (like entity destruction) are not performed while processors are running, * which rely on the assumption that the data layout doesn't change during calculations. */ bool IsProcessing() const { return ProcessingScopeCount > 0; } FMassCommandBuffer& Defer() const { return *DeferredCommandBuffers[OpenedCommandBufferIndex].Get(); } /** * @param InCommandBuffer if not set then the default command buffer will be flushed. If set and there's already * a command buffer being flushed (be it the main one or a previously requested one) then this command buffer * will be queued itself. */ UE_API void FlushCommands(const TSharedPtr& InCommandBuffer); UE_API void FlushCommands(); /** * Depending on the current state of Manager's command buffer the function will either move all the commands out of * InOutCommandBuffer into the main command buffer or append it to the list of command buffers waiting to be flushed. * @note as a consequence of the call InOutCommandBuffer can get its contents emptied due some of the underlying code using Move semantics */ UE_API void AppendCommands(const TSharedPtr& InOutCommandBuffer); template UE_DEPRECATED(5.5, "This method will no longer be exposed. Use GetOrCreateConstSharedFragment instead.") const FConstSharedStruct& GetOrCreateConstSharedFragmentByHash(const uint32 Hash, const T& Fragment) { static_assert(UE::Mass::CConstSharedFragment, "Given struct doesn't represent a valid const shared fragment type. Make sure to inherit from FMassConstSharedFragment or one of its child-types."); return ConstSharedFragmentsContainer.FindOrAdd(Hash, T::StaticStruct(), reinterpret_cast(&Fragment)); } private: template const FSharedStruct& GetOrCreateSharedFragmentByHash(const uint32 Hash, const T& Fragment) { static_assert(UE::Mass::CSharedFragment, "Given struct doesn't represent a valid shared fragment type. Make sure to inherit from FMassSharedFragment or one of its child-types."); return SharedFragmentsContainer.FindOrAdd(Hash, T::StaticStruct(), reinterpret_cast(&Fragment)); } const FConstSharedStruct& GetOrCreateConstSharedFragmentByHash(const uint32 Hash, const UScriptStruct* InScriptStruct, const uint8* InStructMemory) { return ConstSharedFragmentsContainer.FindOrAdd(Hash, InScriptStruct, InStructMemory); } const FSharedStruct& GetOrCreateSharedFragmentByHash(const uint32 Hash, const UScriptStruct* InScriptStruct, const uint8* InStructMemory) { return SharedFragmentsContainer.FindOrAdd(Hash, InScriptStruct, InStructMemory); } template const FConstSharedStruct& GetOrCreateConstSharedFragmentByHash(const uint32 Hash, TArgs&&... InArgs) { static_assert(UE::Mass::CConstSharedFragment, "Given struct doesn't represent a valid const shared fragment type. Make sure to inherit from FMassConstSharedFragment or one of its child-types."); return ConstSharedFragmentsContainer.FindOrAdd(Hash, Forward(InArgs)...); } public: #if WITH_EDITOR || WITH_MASSENTITY_DEBUG template void ForEachArchetype(int32 BeginRange, int32 EndRange, const CallableT& Callable) const { if (EndRange > AllArchetypes.Num()) { EndRange = AllArchetypes.Num(); } for (int32 Cursor = BeginRange; Cursor < EndRange; ++Cursor) { FMassArchetypeHandle Handle(AllArchetypes[Cursor]); Callable(*this, Handle); } } #endif template UE_DEPRECATED(5.5, "This method will no longer be exposed. Use GetOrCreateSharedFragment instead.") const FSharedStruct& GetOrCreateSharedFragmentByHash(const uint32 Hash, TArgs&&... InArgs) { static_assert(UE::Mass::CSharedFragment, "Given struct doesn't represent a valid shared fragment type. Make sure to inherit from FMassSharedFragment or one of its child-types."); return SharedFragmentsContainer.FindOrAdd(Hash, Forward(InArgs)...); } /** * Returns or creates a shared struct associated to a given shared fragment set of values * identified internally by a CRC. * Use this overload when an instance of the desired const shared fragment type is available and * that can be used directly to compute a CRC (i.e., UE::StructUtils::GetStructCrc32) * e.g., * USTRUCT() * struct FIntConstSharedFragment : public FMassConstSharedFragment * { * GENERATED_BODY() * * UPROPERTY() * int32 Value = 0; * }; * * FIntConstSharedFragment Fragment; * Fragment.Value = 123; * const FConstSharedStruct SharedStruct = EntityManager.GetOrCreateConstSharedFragment(Fragment); * * @params Fragment Instance of the desired fragment type * @return FConstSharedStruct to the matching, or newly created shared fragment */ template const FConstSharedStruct& GetOrCreateConstSharedFragment(const T& Fragment) { static_assert(UE::Mass::CConstSharedFragment, "Given struct doesn't represent a valid const shared fragment type. Make sure to inherit from FMassConstSharedFragment or one of its child-types."); const uint32 Hash = UE::StructUtils::GetStructCrc32(FConstStructView::Make(Fragment)); PRAGMA_DISABLE_DEPRECATION_WARNINGS return GetOrCreateConstSharedFragmentByHash(Hash, Fragment); PRAGMA_ENABLE_DEPRECATION_WARNINGS } /** * Returns or creates a shared struct associated to a given shared fragment set of values * identified internally by a CRC. * Use this overload when an instance of the desired shared fragment type is available and * that can be used directly to compute a CRC (i.e., UE::StructUtils::GetStructCrc32) * e.g., * USTRUCT() * struct FIntSharedFragment : public FMassSharedFragment * { * GENERATED_BODY() * * UPROPERTY() * int32 Value = 0; * }; * * FIntSharedFragment Fragment; * Fragment.Value = 123; * const FSharedStruct SharedStruct = EntityManager.GetOrCreateSharedFragment(Fragment); * * @params Fragment Instance of the desired fragment type * @return FSharedStruct to the matching, or newly created shared fragment */ template const FSharedStruct& GetOrCreateSharedFragment(const T& Fragment) { static_assert(UE::Mass::CSharedFragment, "Given struct doesn't represent a valid shared fragment type. Make sure to inherit from FMassSharedFragment or one of its child-types."); const uint32 Hash = UE::StructUtils::GetStructCrc32(FConstStructView::Make(Fragment)); return GetOrCreateSharedFragmentByHash(Hash, Fragment); } /** * Returns or creates a shared struct associated to a given shared fragment set of values * identified internally by a CRC. * Use this overload when values can be provided as constructor arguments for the desired const shared fragment type and * that can be used directly to compute a CRC (i.e., UE::StructUtils::GetStructCrc32) * e.g., * USTRUCT() * struct FIntConstSharedFragment : public FMassConstSharedFragment * { * GENERATED_BODY() * * FIntConstSharedFragment(const int32 InValue) : Value(InValue) {} * * UPROPERTY() * int32 Value = 0; * }; * * const FConstSharedStruct SharedStruct = EntityManager.GetOrCreateConstSharedFragment(123); * * @params InArgs List of arguments provided to the constructor of the desired fragment type * @return FConstSharedStruct to the matching, or newly created shared fragment */ template const FConstSharedStruct& GetOrCreateConstSharedFragment(TArgs&&... InArgs) { static_assert(UE::Mass::CConstSharedFragment, "Given struct doesn't represent a valid const shared fragment type. Make sure to inherit from FMassConstSharedFragment or one of its child-types."); T Struct(Forward(InArgs)...); const uint32 Hash = UE::StructUtils::GetStructCrc32(FConstStructView::Make(Struct)); PRAGMA_DISABLE_DEPRECATION_WARNINGS return GetOrCreateConstSharedFragmentByHash(Hash, MoveTemp(Struct)); PRAGMA_ENABLE_DEPRECATION_WARNINGS } /** * Returns or creates a shared struct associated to a given shared fragment set of values * identified internally by a CRC. * Use this overload when values can be provided as constructor arguments for the desired shared fragment type and * that can be used directly to compute a CRC (i.e., UE::StructUtils::GetStructCrc32) * e.g., * USTRUCT() * struct FIntSharedFragment : public FMassSharedFragment * { * GENERATED_BODY() * * FIntSharedFragment(const int32 InValue) : Value(InValue) {} * * UPROPERTY() * int32 Value = 0; * }; * * const FSharedStruct SharedStruct = EntityManager.GetOrCreateSharedFragment(123); * * @params InArgs List of arguments provided to the constructor of the desired fragment type * @return FSharedStruct to the matching, or newly created shared fragment */ template const FSharedStruct& GetOrCreateSharedFragment(TArgs&&... InArgs) { static_assert(UE::Mass::CSharedFragment, "Given struct doesn't represent a valid shared fragment type. Make sure to inherit from FMassSharedFragment or one of its child-types."); T Struct(Forward(InArgs)...); const uint32 Hash = UE::StructUtils::GetStructCrc32(FConstStructView::Make(Struct)); return GetOrCreateSharedFragmentByHash(Hash, MoveTemp(Struct)); } /** * Returns or creates a shared struct associated to a given shared fragment set of values * identified internally by a CRC. * Use this overload when the reflection data and the memory of an instance of the desired const shared fragment type * is available and that can be used directly to compute a CRC (i.e., UE::StructUtils::GetStructCrc32) * e.g., * FSharedStruct SharedStruct = EntityManager.GetOrCreateConstSharedFragment(*StructView.GetScriptStruct(), StructView.GetMemory()); * * @params InScriptStruct Reflection data structure associated to the desired fragment type * @params InStructMemory Actual data of the desired fragment type * @return FConstSharedStruct to the matching, or newly created shared fragment */ const FConstSharedStruct& GetOrCreateConstSharedFragment(const UScriptStruct& InScriptStruct, const uint8* InStructMemory) { checkf(InScriptStruct.IsChildOf(TBaseStructure::Get()), TEXT("Given struct doesn't represent a valid const shared fragment type. Make sure to inherit from FMassConstSharedFragment or one of its child-types.")); const uint32 Hash = UE::StructUtils::GetStructCrc32(InScriptStruct, InStructMemory); return GetOrCreateConstSharedFragmentByHash(Hash, &InScriptStruct, InStructMemory); } /** * Returns or creates a shared struct associated to a given shared fragment set of values * identified internally by a CRC. * Use this overload when the reflection data and the memory of an instance of the desired shared fragment type * is available and that can be used directly to compute a CRC (i.e., UE::StructUtils::GetStructCrc32) * e.g., * FSharedStruct SharedStruct = EntityManager.GetOrCreateSharedFragment(*StructView.GetScriptStruct(), StructView.GetMemory()); * * @params InScriptStruct Reflection data structure associated to the desired fragment type * @params InStructMemory Actual data of the desired fragment type * @return FSharedStruct to the matching, or newly created shared fragment */ const FSharedStruct& GetOrCreateSharedFragment(const UScriptStruct& InScriptStruct, const uint8* InStructMemory) { checkf(InScriptStruct.IsChildOf(TBaseStructure::Get()), TEXT("Given struct doesn't represent a valid shared fragment type. Make sure to inherit from FMassSharedFragment or one of its child-types.")); const uint32 Hash = UE::StructUtils::GetStructCrc32(InScriptStruct, InStructMemory); return GetOrCreateSharedFragmentByHash(Hash, &InScriptStruct, InStructMemory); } /** * Returns or creates a shared struct associated to a given shared fragment set of values * identified internally by a CRC. * Use this overload when a different struct should be used to compute a CRC (i.e., UE::StructUtils::GetStructCrc32) * and values can be provided as constructor arguments for the desired const shared fragment type * e.g., * * USTRUCT() * struct FIntConstSharedFragmentParams * { * GENERATED_BODY() * * FIntConstSharedFragmentParams(const int32 InValue) : Value(InValue) {} * * UPROPERTY() * int32 Value = 0; * }; * * USTRUCT() * struct FIntConstSharedFragment : public FMassConstSharedFragment * { * GENERATED_BODY() * * FIntConstSharedFragment(const FIntConstSharedFragmentParams& InParams) : Value(InParams.Value) {} * * int32 Value = 0; * }; * * FIntConstSharedFragmentParams Params(123); * const FConstSharedStruct SharedStruct = EntityManager.GetOrCreateConstSharedFragment(FConstStructView::Make(Params), Params); * * @params HashingHelperStruct Struct view passed to UE::StructUtils::GetStructCrc32 to compute the CRC * @params InArgs List of arguments provided to the constructor of the desired fragment type * @return FConstSharedStruct to the matching, or newly created shared fragment */ template const FConstSharedStruct& GetOrCreateConstSharedFragment(const FConstStructView HashingHelperStruct, TArgs&&... InArgs) { static_assert(UE::Mass::CConstSharedFragment, "Given struct doesn't represent a valid const shared fragment type. Make sure to inherit from FMassConstSharedFragment or one of its child-types."); T Fragment(Forward(InArgs)...); const uint32 Hash = UE::StructUtils::GetStructCrc32(HashingHelperStruct); PRAGMA_DISABLE_DEPRECATION_WARNINGS return GetOrCreateConstSharedFragmentByHash(Hash, MoveTemp(Fragment)); PRAGMA_ENABLE_DEPRECATION_WARNINGS } /** * Returns or creates a shared struct associated to a given shared fragment set of values * identified internally by a CRC. * Use this overload when a different struct should be used to compute a CRC (i.e., UE::StructUtils::GetStructCrc32) * and values can be provided as constructor arguments for the desired shared fragment type * e.g., * * USTRUCT() * struct FIntSharedFragmentParams * { * GENERATED_BODY() * * FInSharedFragmentParams(const int32 InValue) : Value(InValue) {} * * UPROPERTY() * int32 Value = 0; * }; * * USTRUCT() * struct FIntSharedFragment : public FMassSharedFragment * { * GENERATED_BODY() * * FIntSharedFragment(const FIntConstSharedFragmentParams& InParams) : Value(InParams.Value) {} * * int32 Value = 0; * }; * * FIntSharedFragmentParams Params(123); * const FSharedStruct SharedStruct = EntityManager.GetOrCreateSharedFragment(FConstStructView::Make(Params), Params); * * @params HashingHelperStruct Struct view passed to UE::StructUtils::GetStructCrc32 to compute the CRC * @params InArgs List of arguments provided to the constructor of the desired fragment type * @return FSharedStruct to the matching, or newly created shared fragment */ template const FSharedStruct& GetOrCreateSharedFragment(const FConstStructView HashingHelperStruct, TArgs&&... InArgs) { static_assert(UE::Mass::CSharedFragment, "Given struct doesn't represent a valid shared fragment type. Make sure to inherit from FMassSharedFragment or one of its child-types."); T Fragment(Forward(InArgs)...); const uint32 Hash = UE::StructUtils::GetStructCrc32(HashingHelperStruct); return GetOrCreateSharedFragmentByHash(Hash, MoveTemp(Fragment)); } template void ForEachSharedFragment(TFunctionRef< void(T& /*SharedFragment*/) > ExecuteFunction); template void ForEachSharedFragmentConditional(TFunctionRef< bool(T& /*SharedFragment*/) > ConditionFunction, TFunctionRef< void(T& /*SharedFragment*/) > ExecuteFunction); template void ForEachConstSharedFragment(TFunctionRef< void(const T& /*ConstSharedFragment*/) > ExecuteFunction); template void ForEachConstSharedFragmentConditional(TFunctionRef< bool(const T& /*ConstSharedFragment*/) > ConditionFunction, TFunctionRef< void(const T& /*ConstSharedFragment*/) > ExecuteFunction); [[nodiscard]] UE_API UE::Mass::FEntityBuilder MakeEntityBuilder(); const UE::Mass::FTypeManager& GetTypeManager() const; UE::Mass::FTypeManager& GetTypeManager(); FMassObserverManager& GetObserverManager() { return ObserverManager; } FOnNewArchetypeDelegate& GetOnNewArchetypeEvent() { return OnNewArchetypeEvent; } /** * Fetches the world associated with the Owner. * @note that it's ok for a given EntityManager to not have an owner or the owner not being part of a UWorld, depending on the use case */ UWorld* GetWorld() const { return Owner.IsValid() ? Owner->GetWorld() : nullptr; } UObject* GetOwner() const { return Owner.Get(); } bool IsDuringEntityCreation() const; UE_API void SetDebugName(const FString& NewDebugGame); #if WITH_MASSENTITY_DEBUG UE_API void DebugPrintArchetypes(FOutputDevice& Ar, const bool bIncludeEmpty = true) const; UE_API void DebugGetArchetypesStringDetails(FOutputDevice& Ar, const bool bIncludeEmpty = true) const; UE_API void DebugGetArchetypeFragmentTypes(const FMassArchetypeHandle& Archetype, TArray& InOutFragmentList) const; UE_API int32 DebugGetArchetypeEntitiesCount(const FMassArchetypeHandle& Archetype) const; UE_API int32 DebugGetArchetypeEntitiesCountPerChunk(const FMassArchetypeHandle& Archetype) const; UE_API int32 DebugGetEntityCount() const; UE_API int32 DebugGetArchetypesCount() const; UE_API void DebugRemoveAllEntities(); UE_API void DebugForceArchetypeDataVersionBump(); UE_API void DebugGetArchetypeStrings(const FMassArchetypeHandle& Archetype, TArray& OutFragmentNames, TArray& OutTagNames); UE_API FMassEntityHandle DebugGetEntityIndexHandle(const int32 EntityIndex) const; UE_API const FString& DebugGetName() const; enum class EDebugFeatures { None, TraceProcessors = 1 << 0, // Used to track information about processors such as their name. All = TraceProcessors }; UE_API void DebugEnableDebugFeature(EDebugFeatures Features); UE_API void DebugDisableDebugFeature(EDebugFeatures Features); UE_API bool DebugHasAllDebugFeatures(EDebugFeatures Features) const; UE_API FMassRequirementAccessDetector& GetRequirementAccessDetector(); // For use by the friend MassDebugger UE_API UE::Mass::FStorageType& DebugGetEntityStorageInterface(); // For use by the friend MassDebugger UE_API const UE::Mass::FStorageType& DebugGetEntityStorageInterface() const; UE_API bool DebugHasCommandsToFlush() const; #endif // WITH_MASSENTITY_DEBUG protected: /** Called on the child process upon process's forking */ UE_API void OnPostFork(EForkProcessRole Role); UE_API void GetMatchingArchetypes(const FMassFragmentRequirements& Requirements, TArray& OutValidArchetypes, const uint32 FromArchetypeDataVersion) const; /** * A "similar" archetype is an archetype exactly the same as SourceArchetype except for one composition aspect * like Fragments or "Tags" */ UE_API FMassArchetypeHandle InternalCreateSimilarArchetype(const TSharedPtr& SourceArchetype, const FMassTagBitSet& OverrideTags); UE_API FMassArchetypeHandle InternalCreateSimilarArchetype(const TSharedPtr& SourceArchetype, const FMassFragmentBitSet& OverrideFragments); UE_API FMassArchetypeHandle InternalCreateSimilarArchetype(const TSharedPtr& SourceArchetype, const UE::Mass::FArchetypeGroups& GroupsOverride); UE_API FMassArchetypeHandle InternalCreateSimilarArchetype(const FMassArchetypeData& SourceArchetypeRef, FMassArchetypeCompositionDescriptor&& NewComposition, const UE::Mass::FArchetypeGroups& GroupsOverride); UE_API void InternalAppendFragmentsAndTagsToArchetypeCompositionDescriptor(FMassArchetypeCompositionDescriptor& InOutComposition, TConstArrayView FragmentsAndTagsList) const; private: void InternalBuildEntity(FMassEntityHandle EntityHandle, const FMassArchetypeHandle& ArchetypeHandle, const FMassArchetypeSharedFragmentValues& SharedFragmentValues); void InternalReleaseEntity(FMassEntityHandle EntityHandle); /** * Adds fragments in FragmentList to the entity indicated by EntityHandle. Only the unique fragments will be added. * @return Bitset for the added fragments (might be empty or a subset of `InFragments` depending on the current archetype fragments) */ FMassFragmentBitSet InternalAddFragmentListToEntityChecked(FMassEntityHandle EntityHandle, const FMassFragmentBitSet& InFragments); /** * Similar to InternalAddFragmentListToEntity but expects NewFragmentList not overlapping with current entity's * fragment list. It's callers responsibility to ensure that's true. Failing this will cause a `check` fail. */ void InternalAddFragmentListToEntity(FMassEntityHandle EntityHandle, const FMassFragmentBitSet& InFragments); /** Note that it's the caller's responsibility to ensure `FragmentType` is a kind of FMassFragment */ UE_API void* InternalGetFragmentDataChecked(FMassEntityHandle EntityHandle, const UScriptStruct* FragmentType) const; /** Note that it's the caller's responsibility to ensure `FragmentType` is a kind of FMassFragment */ UE_API void* InternalGetFragmentDataPtr(FMassEntityHandle EntityHandle, const UScriptStruct* FragmentType) const; /** Note that it's the caller's responsibility to ensure `ConstSharedFragmentType` is a kind of FMassSharedFragment */ UE_API const FConstSharedStruct* InternalGetConstSharedFragmentPtr(FMassEntityHandle EntityHandle, const UScriptStruct* ConstSharedFragmentType) const; /** Note that it's the caller's responsibility to ensure `SharedFragmentType` is a kind of FMassSharedFragment */ UE_API const FSharedStruct* InternalGetSharedFragmentPtr(FMassEntityHandle EntityHandle, const UScriptStruct* SharedFragmentType) const; TSharedRef InternalBatchCreateReservedEntities(const FMassArchetypeHandle& ArchetypeHandle, const FMassArchetypeSharedFragmentValues& SharedFragmentValues, TConstArrayView ReservedEntities); UE::Mass::FStorageType& GetEntityStorageInterface() const { #if WITH_MASS_CONCURRENT_RESERVE using namespace UE::Mass; struct StorageSelector { UE::Mass::IEntityStorageInterface* operator()(FEmptyVariantState&) const { checkf(false, TEXT("Attempt to use EntityStorageInterface without initialization")); return nullptr; } UE::Mass::IEntityStorageInterface* operator()(FSingleThreadedEntityStorage& Storage) const { return &Storage; } UE::Mass::IEntityStorageInterface* operator()(FConcurrentEntityStorage& Storage) const { return &Storage; } }; UE::Mass::IEntityStorageInterface* Interface = Visit(StorageSelector{}, EntityStorage); return *Interface; #else return EntityStorage.Get(); #endif } bool DebugDoCollectionsOverlapCreationContext(TConstArrayView EntityCollections) const; private: friend struct UE::Mass::Private::FEntityStorageInitializer; using FEntityStorageContainerType = TVariant< FEmptyVariantState, UE::Mass::FSingleThreadedEntityStorage, UE::Mass::FConcurrentEntityStorage>; mutable FEntityStorageContainerType EntityStorage; #if WITH_MASSENTITY_DEBUG UE::Mass::FStorageType* DebugEntityStoragePtr = nullptr; #endif // WITH_MASSENTITY_DEBUG std::atomic ProcessingScopeCount = 0; // the "version" number increased every time an archetype gets added uint32 ArchetypeDataVersion = 0; // Map of hash of sorted fragment list to archetypes with that hash TMap>> FragmentHashToArchetypeMap; // Map to list of archetypes that contain the specified fragment type TMap>> FragmentTypeToArchetypeMap; // Contains all archetypes ever created. The array always growing and a given archetypes remains at a given index // throughout its lifetime, and the index is never reused for another archetype. TArray> AllArchetypes; /** * This is a struct wrapping shared fragment management to ensure consistency between how * shared and const shared fragment are added and fetched, across all the functions that do that */ template struct TSharedFragmentsContainer { TArray* Find(const UScriptStruct* Type) { return TypeToInstanceMap.Find(Type); } TSharedStructType& FindOrAdd(const uint32 Hash, const UScriptStruct* Type, const uint8* Data) { int32& Index = HashToInstanceIndexMap.FindOrAddByHash(Hash, Hash, INDEX_NONE); if (Index == INDEX_NONE) { Index = Add(TSharedStructType::Make(Type, Data)); } return Instances[Index]; } template TSharedStructType& FindOrAdd(const uint32 Hash, TArgs&&... InArgs) { int32& Index = HashToInstanceIndexMap.FindOrAddByHash(Hash, Hash, INDEX_NONE); if (Index == INDEX_NONE) { Index = Add(TSharedStructType::template Make(Forward(InArgs)...)); } return Instances[Index]; } TSharedStructType& operator[](const int32 Index) { return Instances[Index]; } TArrayView GetAllInstances() { return Instances; } private: int32 Add(TSharedStructType&& SharedStruct) { TArray& InstancesOfType = TypeToInstanceMap.FindOrAdd(SharedStruct.GetScriptStruct(), {}); const int32 Index = Instances.Add(Forward(SharedStruct)); // note that even though we're copying the input F[Const]SharedStruct instance it's perfectly fine since // F[Const]SharedStruct does guarantee there's not going to be data duplication (via a member shared pointer to hosted data) InstancesOfType.Add(Instances[Index]); return Index; } TArray Instances; // Hash/Index in array pair TMap HashToInstanceIndexMap; // Maps specific struct type to a collection of FSharedStruct instances of that type TMap> TypeToInstanceMap; }; TSharedFragmentsContainer ConstSharedFragmentsContainer; TSharedFragmentsContainer SharedFragmentsContainer; FMassObserverManager ObserverManager; TSharedRef TypeManager; TMap GroupNameToTypeIndex; // @todo we'll probably have some "GroupTypeInformation" here in the future TArray GroupTypes; #if WITH_MASSENTITY_DEBUG FMassRequirementAccessDetector RequirementAccessDetector; FString DebugName; EDebugFeatures EnabledDebugFeatures = EDebugFeatures::All; #endif // WITH_MASSENTITY_DEBUG TWeakObjectPtr Owner; FOnNewArchetypeDelegate OnNewArchetypeEvent; FDelegateHandle OnPostForkHandle; /** * This index will be enough to control which buffer is available for pushing commands since flashing is taking place * in the game thread and pushing commands to the buffer fetched by Defer() is only supported also on the game thread * (due to checking the cached thread ID). * The whole CL aims to support non-mass code trying to push commands while the flushing is going on (as triggered * by MassObservers reacting to the commands being flushed currently). */ static constexpr int32 NumCommandBuffers = 2; TStaticArray, NumCommandBuffers> DeferredCommandBuffers; uint8 OpenedCommandBufferIndex = 0; std::atomic bCommandBufferFlushingInProgress = false; bool bFirstCommandFlush = true; enum class EInitializationState : uint8 { Uninitialized, Initialized, Deinitialized }; EInitializationState InitializationState = EInitializationState::Uninitialized; //----------------------------------------------------------------------------- // DEPRECATED //----------------------------------------------------------------------------- protected: UE_DEPRECATED(5.6, "This flavor of InternalCreateSimilarArchetype is deprecated due to the introduction of archetype grouping. Use InternalCreateSimilarArchetype with a FArchetypeGroups parameter instead") UE_API FMassArchetypeHandle InternalCreateSimilarArchetype(const FMassArchetypeData& SourceArchetypeRef, FMassArchetypeCompositionDescriptor&& NewComposition); public: UE_DEPRECATED(5.6, "SetEntityFragmentsValues is deprecated. Use SetEntityFragmentValues instead (note the slight change in name).") UE_API void SetEntityFragmentsValues(FMassEntityHandle EntityHandle, TArrayView FragmentInstanceList); UE_DEPRECATED(5.6, "Static BatchSetEntityFragmentsValues is deprecated. Use EntityManager's member function BatchSetEntityFragmentValues (note the slight change in name).") static UE_API void BatchSetEntityFragmentsValues(const FMassArchetypeEntityCollection& SparseEntities, TArrayView FragmentInstanceList); UE_DEPRECATED(5.6, "Static BatchSetEntityFragmentsValues is deprecated. Use EntityManager's member function BatchSetEntityFragmentValues (note the slight change in name).") static UE_API void BatchSetEntityFragmentsValues(TConstArrayView EntityCollections, TArrayView FragmentInstanceList); template UE_DEPRECATED(5.6, "Using ForEachSharedFragment for Const Shared Fragments has been deprecated. Use ForEachConstSharedFragment instead.") void ForEachSharedFragment(TFunctionRef< void(T& /*SharedFragment*/) > ExecuteFunction) { } template UE_DEPRECATED(5.6, "Using ForEachSharedFragmentConditional for Const Shared Fragments has been deprecated. Use ForEachConstSharedFragmentConditional instead.") void ForEachSharedFragmentConditional(TFunctionRef< bool(T& /*SharedFragment*/) > ConditionFunction, TFunctionRef< void(T& /*SharedFragment*/) > ExecuteFunction) { } }; //----------------------------------------------------------------------------- // INLINE //----------------------------------------------------------------------------- #if WITH_MASSENTITY_DEBUG ENUM_CLASS_FLAGS(FMassEntityManager::EDebugFeatures); #endif template void FMassEntityManager::ForEachSharedFragment(TFunctionRef< void(T& /*SharedFragment*/) > ExecuteFunction) { if (TArray* InstancesOfType = SharedFragmentsContainer.Find(T::StaticStruct())) { for (const FSharedStruct& SharedStruct : *InstancesOfType) { ExecuteFunction(SharedStruct.Get()); } } } template void FMassEntityManager::ForEachSharedFragmentConditional(TFunctionRef< bool(T& /*SharedFragment*/) > ConditionFunction, TFunctionRef< void(T& /*SharedFragment*/) > ExecuteFunction) { if (TArray* InstancesOfType = SharedFragmentsContainer.Find(T::StaticStruct())) { for (const FSharedStruct& SharedStruct : *InstancesOfType) { T& StructInstanceRef = SharedStruct.Get(); if (ConditionFunction(StructInstanceRef)) { ExecuteFunction(StructInstanceRef); } } } } template void FMassEntityManager::ForEachConstSharedFragment(TFunctionRef< void(const T& /*ConstSharedFragment*/) > ExecuteFunction) { if (TArray* InstancesOfType = ConstSharedFragmentsContainer.Find(T::StaticStruct())) { for (const FConstSharedStruct& SharedStruct : *InstancesOfType) { ExecuteFunction(SharedStruct.Get()); } } } template void FMassEntityManager::ForEachConstSharedFragmentConditional(TFunctionRef< bool(const T& /*ConstSharedFragment*/) > ConditionFunction, TFunctionRef< void(const T& /*ConstSharedFragment*/) > ExecuteFunction) { if (TArray* InstancesOfType = ConstSharedFragmentsContainer.Find(T::StaticStruct())) { for (const FConstSharedStruct& SharedStruct : *InstancesOfType) { const T& StructInstanceRef = SharedStruct.Get(); if (ConditionFunction(StructInstanceRef)) { ExecuteFunction(StructInstanceRef); } } } } inline const UE::Mass::FTypeManager& FMassEntityManager::GetTypeManager() const { return *TypeManager; } inline UE::Mass::FTypeManager& FMassEntityManager::GetTypeManager() { return *TypeManager; } inline bool FMassEntityManager::IsDuringEntityCreation() const { return ObserverManager.GetCreationContext().IsValid(); } inline TSharedRef FMassEntityManager::GetOrMakeObserversLock() { return ObserverManager.GetOrMakeObserverLock(); } #undef UE_API