Files
UnrealEngine/Engine/Source/Runtime/MassEntity/Public/MassEntityManager.h
2025-05-18 13:04:45 +08:00

1372 lines
66 KiB
C++

// 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<FMassEntityManager>).
* IMPORTANT: if you create your own FMassEntityManager instance remember to call Initialize() before using it.
*/
struct FMassEntityManager : public TSharedFromThis<FMassEntityManager>, 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<int32>& InProcessingScopeCount) : ScopedProcessingCount(InProcessingScopeCount)
{
++ScopedProcessingCount;
}
~FScopedProcessing()
{
--ScopedProcessingCount;
}
private:
std::atomic<int32>& ScopedProcessingCount;
};
using FStructInitializationCallback = TFunctionRef<void(void* Fragment, const UScriptStruct& FragmentType)>;
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<const UScriptStruct*> 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<const UScriptStruct*> 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<const UScriptStruct*> 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<FMassArchetypeData>& 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<FMassArchetypeData>& 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<FMassArchetypeHandle>& 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<FInstancedStruct> 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<FEntityCreationContext> 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<UE::Mass::ObserverManager::FObserverLock> 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<FEntityCreationContext> BatchCreateReservedEntities(const FMassArchetypeHandle& ArchetypeHandle,
const FMassArchetypeSharedFragmentValues& SharedFragmentValues, TConstArrayView<FMassEntityHandle> ReservedEntities);
FORCEINLINE TSharedRef<FEntityCreationContext> BatchCreateReservedEntities(const FMassArchetypeHandle& ArchetypeHandle,
TConstArrayView<FMassEntityHandle> 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<FEntityCreationContext> BatchCreateEntities(const FMassArchetypeHandle& ArchetypeHandle, const FMassArchetypeSharedFragmentValues& SharedFragmentValues, const int32 Count, TArray<FMassEntityHandle>& InOutEntities);
FORCEINLINE TSharedRef<FEntityCreationContext> BatchCreateEntities(const FMassArchetypeHandle& ArchetypeHandle, const int32 Count, TArray<FMassEntityHandle>& 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<FInstancedStruct> 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<FMassEntityHandle> 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<FMassArchetypeEntityCollection> 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<FMassArchetypeEntityCollection> 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<FMassEntityHandle> 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<const UScriptStruct*> FragmentList);
UE_API void AddFragmentInstanceListToEntity(FMassEntityHandle EntityHandle, TConstArrayView<FInstancedStruct> FragmentInstanceList);
UE_API void RemoveFragmentFromEntity(FMassEntityHandle EntityHandle, const UScriptStruct* FragmentType);
UE_API void RemoveFragmentListFromEntity(FMassEntityHandle EntityHandle, TConstArrayView<const UScriptStruct*> 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<FMassEntityHandle> BatchReserveEntities(const int32 Count, TArray<FMassEntityHandle>& 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<FMassEntityHandle> InOutEntities);
UE_API TSharedRef<FEntityCreationContext> BatchBuildEntities(const FMassArchetypeEntityCollectionWithPayload& EncodedEntitiesWithPayload, const FMassFragmentBitSet& FragmentsAffected
, const FMassArchetypeSharedFragmentValues& SharedFragmentValues = {}, const FMassArchetypeCreationParams& CreationParams = FMassArchetypeCreationParams());
UE_API TSharedRef<FEntityCreationContext> BatchBuildEntities(const FMassArchetypeEntityCollectionWithPayload& EncodedEntitiesWithPayload, const FMassArchetypeCompositionDescriptor& Composition
, const FMassArchetypeSharedFragmentValues& SharedFragmentValues = {}, const FMassArchetypeCreationParams& CreationParams = FMassArchetypeCreationParams());
UE_API void BatchChangeTagsForEntities(TConstArrayView<FMassArchetypeEntityCollection> EntityCollections, const FMassTagBitSet& TagsToAdd, const FMassTagBitSet& TagsToRemove);
UE_API void BatchChangeFragmentCompositionForEntities(TConstArrayView<FMassArchetypeEntityCollection> EntityCollections, const FMassFragmentBitSet& FragmentsToAdd, const FMassFragmentBitSet& FragmentsToRemove);
UE_API void BatchAddFragmentInstancesForEntities(TConstArrayView<FMassArchetypeEntityCollectionWithPayload> EntityCollections, const FMassFragmentBitSet& FragmentsAffected);
/**
* Adds a new const and non-const shared fragments to all entities provided via EntityCollections
*/
UE_API void BatchAddSharedFragmentsForEntities(TConstArrayView<FMassArchetypeEntityCollection> 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<const FInstancedStruct> 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<const FInstancedStruct> FragmentInstanceList);
UE_API void BatchSetEntityFragmentValues(TConstArrayView<FMassArchetypeEntityCollection> EntityCollections, TArrayView<const FInstancedStruct> 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<typename FragmentType>
FragmentType& GetFragmentDataChecked(FMassEntityHandle EntityHandle) const
{
static_assert(UE::Mass::CFragment<FragmentType>
, "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<typename FragmentType>
FragmentType* GetFragmentDataPtr(FMassEntityHandle EntityHandle) const
{
static_assert(UE::Mass::CFragment<FragmentType>
, "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<FMassFragment>(FragmentType)
, TEXT("GetFragmentDataStruct called with an invalid fragment type '%s'"), *GetPathNameSafe(FragmentType));
return FStructView(FragmentType, static_cast<uint8*>(InternalGetFragmentDataPtr(EntityHandle, FragmentType)));
}
template<typename ConstSharedFragmentType>
ConstSharedFragmentType* GetConstSharedFragmentDataPtr(FMassEntityHandle EntityHandle) const
{
static_assert(UE::Mass::CConstSharedFragment<ConstSharedFragmentType>, "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<typename ConstSharedFragmentType>
ConstSharedFragmentType& GetConstSharedFragmentDataChecked(FMassEntityHandle EntityHandle) const
{
ConstSharedFragmentType* TypePtr = GetConstSharedFragmentDataPtr<ConstSharedFragmentType>(EntityHandle);
check(TypePtr);
return *TypePtr;
}
FConstStructView GetConstSharedFragmentDataStruct(FMassEntityHandle EntityHandle, const UScriptStruct* ConstSharedFragmentType) const
{
checkf(UE::Mass::IsA<FMassConstSharedFragment>(ConstSharedFragmentType)
, TEXT("GetConstSharedFragmentDataStruct called with an invalid fragment type '%s'"), *GetPathNameSafe(ConstSharedFragmentType));
const FConstSharedStruct* ConstSharedStruct = InternalGetConstSharedFragmentPtr(EntityHandle, ConstSharedFragmentType);
return ConstSharedStruct
? FConstStructView(*ConstSharedStruct)
: FConstStructView();
}
template<typename SharedFragmentType>
TConstArrayView<FSharedStruct> GetSharedFragmentsOfType()
{
static_assert(UE::Mass::CSharedFragment<SharedFragmentType>
, "Given struct doesn't represent a valid shared fragment type. Make sure to inherit from FMassSharedFragment or one of its child-types.");
TArray<FSharedStruct>* InstancesOfType = SharedFragmentsContainer.Find(SharedFragmentType::StaticStruct());
return InstancesOfType ? *InstancesOfType : TConstArrayView<FSharedStruct>();
}
template<typename SharedFragmentType>
SharedFragmentType* GetSharedFragmentDataPtr(FMassEntityHandle EntityHandle) const
{
static_assert(UE::Mass::CSharedFragment<SharedFragmentType>
, "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<typename SharedFragmentType>
SharedFragmentType& GetSharedFragmentDataChecked(FMassEntityHandle EntityHandle) const
{
SharedFragmentType* TypePtr = GetSharedFragmentDataPtr<SharedFragmentType>(EntityHandle);
check(TypePtr);
return *TypePtr;
}
FConstStructView GetSharedFragmentDataStruct(FMassEntityHandle EntityHandle, const UScriptStruct* SharedFragmentType) const
{
checkf(UE::Mass::IsA<FMassSharedFragment>(SharedFragmentType)
, TEXT("GetSharedFragmentDataStruct called with an invalid fragment type '%s'"), *GetPathNameSafe(SharedFragmentType));
const FSharedStruct* FragmentPtr = InternalGetSharedFragmentPtr(EntityHandle, SharedFragmentType);
return FragmentPtr
? FConstStructView(*FragmentPtr)
: FConstStructView();
}
template<typename T>
FConstStructView GetElementDataStruct(FMassEntityHandle EntityHandle, TNotNull<const UScriptStruct*> FragmentType) const
{
if constexpr (std::is_same_v<T, FMassFragment>)
{
return GetFragmentDataStruct(EntityHandle, FragmentType);
}
else if constexpr (std::is_same_v<T, FMassSharedFragment>)
{
return GetSharedFragmentDataStruct(EntityHandle, FragmentType);
}
else if constexpr (std::is_same_v<T, FMassConstSharedFragment>)
{
return GetConstSharedFragmentDataStruct(EntityHandle, FragmentType);
}
else
{
static_assert(UE::Mass::TAlwaysFalse<T>, "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<FMassCommandBuffer>& 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<FMassCommandBuffer>& InOutCommandBuffer);
template<typename T>
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<T>, "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<const uint8*>(&Fragment));
}
private:
template<typename T>
const FSharedStruct& GetOrCreateSharedFragmentByHash(const uint32 Hash, const T& Fragment)
{
static_assert(UE::Mass::CSharedFragment<T>, "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<const uint8*>(&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<typename T, typename... TArgs>
const FConstSharedStruct& GetOrCreateConstSharedFragmentByHash(const uint32 Hash, TArgs&&... InArgs)
{
static_assert(UE::Mass::CConstSharedFragment<T>, "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<T>(Hash, Forward<TArgs>(InArgs)...);
}
public:
#if WITH_EDITOR || WITH_MASSENTITY_DEBUG
template<typename CallableT>
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<typename T, typename... TArgs>
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<T>, "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<T>(Hash, Forward<TArgs>(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<typename T>
const FConstSharedStruct& GetOrCreateConstSharedFragment(const T& Fragment)
{
static_assert(UE::Mass::CConstSharedFragment<T>,
"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<typename T>
const FSharedStruct& GetOrCreateSharedFragment(const T& Fragment)
{
static_assert(UE::Mass::CSharedFragment<T>,
"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<FIntConstSharedFragment>(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<typename T, typename... TArgs>
const FConstSharedStruct& GetOrCreateConstSharedFragment(TArgs&&... InArgs)
{
static_assert(UE::Mass::CConstSharedFragment<T>,
"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<TArgs>(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<FIntSharedFragment>(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<typename T, typename... TArgs>
const FSharedStruct& GetOrCreateSharedFragment(TArgs&&... InArgs)
{
static_assert(UE::Mass::CSharedFragment<T>,
"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<TArgs>(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<FMassConstSharedFragment>::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<FMassSharedFragment>::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<FIntConstSharedFragment>(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<typename T, typename... TArgs>
const FConstSharedStruct& GetOrCreateConstSharedFragment(const FConstStructView HashingHelperStruct, TArgs&&... InArgs)
{
static_assert(UE::Mass::CConstSharedFragment<T>,
"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<TArgs>(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<FIntSharedFragment>(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<typename T, typename... TArgs>
const FSharedStruct& GetOrCreateSharedFragment(const FConstStructView HashingHelperStruct, TArgs&&... InArgs)
{
static_assert(UE::Mass::CSharedFragment<T>,
"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<TArgs>(InArgs)...);
const uint32 Hash = UE::StructUtils::GetStructCrc32(HashingHelperStruct);
return GetOrCreateSharedFragmentByHash(Hash, MoveTemp(Fragment));
}
template<UE::Mass::CSharedFragment T>
void ForEachSharedFragment(TFunctionRef< void(T& /*SharedFragment*/) > ExecuteFunction);
template<UE::Mass::CSharedFragment T>
void ForEachSharedFragmentConditional(TFunctionRef< bool(T& /*SharedFragment*/) > ConditionFunction, TFunctionRef< void(T& /*SharedFragment*/) > ExecuteFunction);
template<UE::Mass::CConstSharedFragment T>
void ForEachConstSharedFragment(TFunctionRef< void(const T& /*ConstSharedFragment*/) > ExecuteFunction);
template<UE::Mass::CConstSharedFragment T>
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<const UScriptStruct*>& 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<FName>& OutFragmentNames, TArray<FName>& 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<FMassArchetypeHandle>& 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<FMassArchetypeData>& SourceArchetype, const FMassTagBitSet& OverrideTags);
UE_API FMassArchetypeHandle InternalCreateSimilarArchetype(const TSharedPtr<FMassArchetypeData>& SourceArchetype, const FMassFragmentBitSet& OverrideFragments);
UE_API FMassArchetypeHandle InternalCreateSimilarArchetype(const TSharedPtr<FMassArchetypeData>& 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<const UScriptStruct*> 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<FEntityCreationContext> InternalBatchCreateReservedEntities(const FMassArchetypeHandle& ArchetypeHandle,
const FMassArchetypeSharedFragmentValues& SharedFragmentValues, TConstArrayView<FMassEntityHandle> 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<UE::Mass::FSingleThreadedEntityStorage>();
#endif
}
bool DebugDoCollectionsOverlapCreationContext(TConstArrayView<FMassArchetypeEntityCollection> 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<int32> 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<uint32, TArray<TSharedPtr<FMassArchetypeData>>> FragmentHashToArchetypeMap;
// Map to list of archetypes that contain the specified fragment type
TMap<const UScriptStruct*, TArray<TSharedPtr<FMassArchetypeData>>> 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<TSharedPtr<FMassArchetypeData>> 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<typename TSharedStructType>
struct TSharedFragmentsContainer
{
TArray<TSharedStructType>* 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<typename T, typename... TArgs>
TSharedStructType& FindOrAdd(const uint32 Hash, TArgs&&... InArgs)
{
int32& Index = HashToInstanceIndexMap.FindOrAddByHash(Hash, Hash, INDEX_NONE);
if (Index == INDEX_NONE)
{
Index = Add(TSharedStructType::template Make<T>(Forward<TArgs>(InArgs)...));
}
return Instances[Index];
}
TSharedStructType& operator[](const int32 Index)
{
return Instances[Index];
}
TArrayView<TSharedStructType> GetAllInstances()
{
return Instances;
}
private:
int32 Add(TSharedStructType&& SharedStruct)
{
TArray<TSharedStructType>& InstancesOfType = TypeToInstanceMap.FindOrAdd(SharedStruct.GetScriptStruct(), {});
const int32 Index = Instances.Add(Forward<TSharedStructType>(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<TSharedStructType> Instances;
// Hash/Index in array pair
TMap<uint32, int32> HashToInstanceIndexMap;
// Maps specific struct type to a collection of FSharedStruct instances of that type
TMap<const UScriptStruct*, TArray<TSharedStructType>> TypeToInstanceMap;
};
TSharedFragmentsContainer<FConstSharedStruct> ConstSharedFragmentsContainer;
TSharedFragmentsContainer<FSharedStruct> SharedFragmentsContainer;
FMassObserverManager ObserverManager;
TSharedRef<UE::Mass::FTypeManager> TypeManager;
TMap<const FName, const int32> GroupNameToTypeIndex;
// @todo we'll probably have some "GroupTypeInformation" here in the future
TArray<const FName> GroupTypes;
#if WITH_MASSENTITY_DEBUG
FMassRequirementAccessDetector RequirementAccessDetector;
FString DebugName;
EDebugFeatures EnabledDebugFeatures = EDebugFeatures::All;
#endif // WITH_MASSENTITY_DEBUG
TWeakObjectPtr<UObject> 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<TSharedPtr<FMassCommandBuffer>, NumCommandBuffers> DeferredCommandBuffers;
uint8 OpenedCommandBufferIndex = 0;
std::atomic<bool> 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<const FInstancedStruct> 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<const FInstancedStruct> 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<FMassArchetypeEntityCollection> EntityCollections, TArrayView<const FInstancedStruct> FragmentInstanceList);
template<UE::Mass::CConstSharedFragment T>
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::Mass::CConstSharedFragment T>
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<UE::Mass::CSharedFragment T>
void FMassEntityManager::ForEachSharedFragment(TFunctionRef< void(T& /*SharedFragment*/) > ExecuteFunction)
{
if (TArray<FSharedStruct>* InstancesOfType = SharedFragmentsContainer.Find(T::StaticStruct()))
{
for (const FSharedStruct& SharedStruct : *InstancesOfType)
{
ExecuteFunction(SharedStruct.Get<T>());
}
}
}
template<UE::Mass::CSharedFragment T>
void FMassEntityManager::ForEachSharedFragmentConditional(TFunctionRef< bool(T& /*SharedFragment*/) > ConditionFunction, TFunctionRef< void(T& /*SharedFragment*/) > ExecuteFunction)
{
if (TArray<FSharedStruct>* InstancesOfType = SharedFragmentsContainer.Find(T::StaticStruct()))
{
for (const FSharedStruct& SharedStruct : *InstancesOfType)
{
T& StructInstanceRef = SharedStruct.Get<T>();
if (ConditionFunction(StructInstanceRef))
{
ExecuteFunction(StructInstanceRef);
}
}
}
}
template<UE::Mass::CConstSharedFragment T>
void FMassEntityManager::ForEachConstSharedFragment(TFunctionRef< void(const T& /*ConstSharedFragment*/) > ExecuteFunction)
{
if (TArray<FConstSharedStruct>* InstancesOfType = ConstSharedFragmentsContainer.Find(T::StaticStruct()))
{
for (const FConstSharedStruct& SharedStruct : *InstancesOfType)
{
ExecuteFunction(SharedStruct.Get<const T>());
}
}
}
template<UE::Mass::CConstSharedFragment T>
void FMassEntityManager::ForEachConstSharedFragmentConditional(TFunctionRef< bool(const T& /*ConstSharedFragment*/) > ConditionFunction, TFunctionRef< void(const T& /*ConstSharedFragment*/) > ExecuteFunction)
{
if (TArray<FConstSharedStruct>* InstancesOfType = ConstSharedFragmentsContainer.Find(T::StaticStruct()))
{
for (const FConstSharedStruct& SharedStruct : *InstancesOfType)
{
const T& StructInstanceRef = SharedStruct.Get<const T>();
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<FMassObserverManager::FObserverLock> FMassEntityManager::GetOrMakeObserversLock()
{
return ObserverManager.GetOrMakeObserverLock();
}
#undef UE_API