Files
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

497 lines
16 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#if UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_6
#include "MassEntityManager.h"
#endif // UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_6
#include "MassTranslator.h"
#include "Templates/SharedPointer.h"
#include "Misc/Guid.h"
#include "Hash/CityHash.h"
#if UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_6
#include "MassCommonTypes.h"
#include "MassProcessingTypes.h"
#endif // UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_6
#include "MassEntityTemplate.generated.h"
class UMassEntityTraitBase;
struct FMassEntityView;
struct FMassEntityTemplateIDFactory;
struct FMassEntityTemplate;
struct FMassEntityManager;
namespace UE::Mass
{
struct FEntityBuilder;
}
//ID of the template an entity is using
USTRUCT()
struct FMassEntityTemplateID
{
GENERATED_BODY()
FMassEntityTemplateID()
: FlavorHash(0), TotalHash(InvalidHash)
{}
private:
friend FMassEntityTemplateIDFactory;
// use FMassEntityTemplateIDFactory to access this constructor flavor
explicit FMassEntityTemplateID(const FGuid& InGuid, const int32 InFlavorHash = 0)
: ConfigGuid(InGuid), FlavorHash(InFlavorHash)
{
const uint64 GuidHash = CityHash64((char*)&ConfigGuid, sizeof(FGuid));
TotalHash = CityHash128to64({GuidHash, (uint64)InFlavorHash});
}
public:
uint64 GetHash64() const
{
return TotalHash;
}
void Invalidate()
{
TotalHash = InvalidHash;
}
bool operator==(const FMassEntityTemplateID& Other) const
{
return (TotalHash == Other.TotalHash);
}
bool operator!=(const FMassEntityTemplateID& Other) const
{
return !(*this == Other);
}
/**
* Note that since the function is 32-hashing a 64-bit value it's not guaranteed to produce globally unique values.
* But also note that it's still fine to use FMassEntityTemplateID as a TMap key type, since TMap is using 32bit hash
* to assign buckets rather than identify individual values.
*/
friend uint32 GetTypeHash(const FMassEntityTemplateID& TemplateID)
{
return GetTypeHash(TemplateID.TotalHash);
}
bool IsValid() const
{
return (TotalHash != InvalidHash);
}
MASSSPAWNER_API FString ToString() const;
protected:
UPROPERTY(VisibleAnywhere, Category="Mass")
FGuid ConfigGuid;
UPROPERTY()
uint32 FlavorHash;
UPROPERTY()
uint64 TotalHash;
private:
static constexpr uint64 InvalidHash = 0;
};
/**
* Serves as data used to define and build finalized FMassEntityTemplate instances. Describes composition and initial
* values of fragments for entities created with this data, and lets users modify and extend the data. Once finalized as
* FMassEntityTemplate the data will become immutable.
*/
USTRUCT()
struct FMassEntityTemplateData
{
GENERATED_BODY()
typedef TFunction<void(UObject& /*Owner*/, FMassEntityView& /*EntityView*/, const EMassTranslationDirection /*CurrentDirection*/)> FObjectFragmentInitializerFunction;
FMassEntityTemplateData() = default;
MASSSPAWNER_API explicit FMassEntityTemplateData(const FMassEntityTemplate& InFinalizedTemplate);
bool IsEmpty() const;
TConstArrayView<FObjectFragmentInitializerFunction> GetObjectFragmentInitializers() const;
const FString& GetTemplateName() const;
const FMassArchetypeCompositionDescriptor& GetCompositionDescriptor() const;
const FMassArchetypeSharedFragmentValues& GetSharedFragmentValues() const;
TConstArrayView<FInstancedStruct> GetInitialFragmentValues() const;
TArray<FMassEntityTemplateData::FObjectFragmentInitializerFunction>& GetMutableObjectFragmentInitializers();
void SetTemplateName(const FString& Name);
template<typename T>
void AddFragment()
{
static_assert(UE::Mass::CFragment<T>, MASS_INVALID_FRAGMENT_MSG);
Composition.GetFragments().Add<T>();
}
void AddFragment(const UScriptStruct& FragmentType)
{
checkf(UE::Mass::IsA<FMassFragment>(&FragmentType), TEXT(MASS_INVALID_FRAGMENT_MSG_F), *FragmentType.GetName());
Composition.GetFragments().Add(FragmentType);
}
// @todo this function is doing nothing if a given fragment's initial value has already been created. This seems inconsistent with the other AddFragment functions (especially AddFragment_GetRef).
void AddFragment(FConstStructView Fragment)
{
const UScriptStruct* FragmentType = Fragment.GetScriptStruct();
checkf(UE::Mass::IsA<FMassFragment>(FragmentType), TEXT(MASS_INVALID_FRAGMENT_MSG_F), *GetNameSafe(FragmentType));
if (!Composition.GetFragments().Contains(*FragmentType))
{
Composition.GetFragments().Add(*FragmentType);
InitialFragmentValues.Emplace(Fragment);
}
else if (!InitialFragmentValues.ContainsByPredicate(FStructTypeEqualOperator(FragmentType)))
{
InitialFragmentValues.Emplace(Fragment);
}
}
template<typename T>
T& AddFragment_GetRef()
{
static_assert(UE::Mass::CFragment<T>, MASS_INVALID_FRAGMENT_MSG);
if (!Composition.GetFragments().Contains<T>())
{
Composition.GetFragments().Add<T>();
}
else if (FInstancedStruct* Fragment = InitialFragmentValues.FindByPredicate(FStructTypeEqualOperator(T::StaticStruct())))
{
return Fragment->template GetMutable<T>();
}
// Add a default initial fragment value
return InitialFragmentValues.Emplace_GetRef(T::StaticStruct()).template GetMutable<T>();
}
template<typename T>
T* GetMutableFragment()
{
static_assert(UE::Mass::CFragment<T>, MASS_INVALID_FRAGMENT_MSG);
FInstancedStruct* Fragment = InitialFragmentValues.FindByPredicate(FStructTypeEqualOperator(T::StaticStruct()));
return Fragment ? &Fragment->template GetMutable<T>() : (T*)nullptr;
}
template<typename T>
void AddTag()
{
static_assert(UE::Mass::CTag<T>, "Given struct doesn't represent a valid mass tag type. Make sure to inherit from FMassTag or one of its child-types.");
Composition.GetTags().Add<T>();
}
void AddTag(const UScriptStruct& TagType)
{
checkf(UE::Mass::IsA<FMassTag>(&TagType), TEXT("Given struct doesn't represent a valid mass tag type. Make sure to inherit from FMassTag or one of its child-types."));
Composition.GetTags().Add(TagType);
}
template<typename T>
void RemoveTag()
{
static_assert(UE::Mass::CTag<T>, "Given struct doesn't represent a valid mass tag type. Make sure to inherit from FMassTag or one of its child-types.");
Composition.GetTags().Remove<T>();
}
void RemoveTag(const UScriptStruct& TagType)
{
checkf(UE::Mass::IsA<FMassTag>(&TagType), TEXT("Given struct doesn't represent a valid mass tag type. Make sure to inherit from FMassTag or one of its child-types."));
Composition.GetTags().Remove(TagType);
}
const FMassTagBitSet& GetTags() const;
FMassTagBitSet& GetMutableTags();
template<typename T>
void AddChunkFragment()
{
static_assert(UE::Mass::CChunkFragment<T>, "Given struct doesn't represent a valid chunk fragment type. Make sure to inherit from FMassChunkFragment or one of its child-types.");
Composition.GetChunkFragments().Add<T>();
}
void AddConstSharedFragment(const FConstSharedStruct& SharedFragment)
{
const UScriptStruct* FragmentType = SharedFragment.GetScriptStruct();
if(ensureMsgf(UE::Mass::IsA<FMassConstSharedFragment>(FragmentType), TEXT("Given struct doesn't represent a valid const shared fragment type. Make sure to inherit from FMassConstSharedFragment or one of its child-types.")))
{
if (!Composition.GetConstSharedFragments().Contains(*FragmentType))
{
Composition.GetConstSharedFragments().Add(*FragmentType);
SharedFragmentValues.Add(SharedFragment);
}
#if DO_ENSURE
else
{
const FConstSharedStruct* Struct = SharedFragmentValues.GetConstSharedFragments().FindByPredicate(FStructTypeEqualOperator(SharedFragment));
ensureMsgf(Struct && *Struct == SharedFragment, TEXT("Adding 2 different const shared fragment of the same type is not allowed"));
}
#endif // DO_ENSURE
}
}
void AddSharedFragment(const FSharedStruct& SharedFragment)
{
const UScriptStruct* FragmentType = SharedFragment.GetScriptStruct();
if(ensureMsgf(UE::Mass::IsA<FMassSharedFragment>(FragmentType), TEXT("Given struct doesn't represent a valid shared fragment type. Make sure to inherit from FMassSharedFragment or one of its child-types.")))
{
if (!Composition.GetSharedFragments().Contains(*FragmentType))
{
Composition.GetSharedFragments().Add(*FragmentType);
SharedFragmentValues.Add(SharedFragment);
}
#if DO_ENSURE
else
{
const FSharedStruct* Struct = SharedFragmentValues.GetSharedFragments().FindByPredicate(FStructTypeEqualOperator(SharedFragment));
ensureMsgf(Struct && *Struct == SharedFragment, TEXT("Adding 2 different shared fragment of the same type is not allowed"));
}
#endif // DO_ENSURE
}
}
template<typename T>
bool HasFragment() const
{
return Composition.GetFragments().Contains<T>();
}
bool HasFragment(const UScriptStruct& ScriptStruct) const
{
return Composition.GetFragments().Contains(ScriptStruct);
}
template<typename T>
bool HasTag() const
{
return Composition.GetTags().Contains<T>();
}
template<typename T>
bool HasChunkFragment() const
{
return Composition.GetChunkFragments().Contains<T>();
}
template<typename T>
bool HasSharedFragment() const
{
return Composition.GetSharedFragments().Contains<T>();
}
bool HasSharedFragment(const UScriptStruct& ScriptStruct) const
{
return Composition.GetSharedFragments().Contains(ScriptStruct);
}
template<typename T>
bool HasConstSharedFragment() const
{
return Composition.GetConstSharedFragments().Contains<T>();
}
bool HasConstSharedFragment(const UScriptStruct& ScriptStruct) const
{
return Composition.GetConstSharedFragments().Contains(ScriptStruct);
}
void Sort()
{
SharedFragmentValues.Sort();
}
/** Compares contents of two archetypes (this and Other). Returns whether both are equivalent.
* @Note that the function can be slow, depending on how elaborate the template is. This function is meant for debugging purposes. */
MASSSPAWNER_API bool SlowIsEquivalent(const FMassEntityTemplateData& Other) const;
FMassArchetypeCreationParams& GetArchetypeCreationParams();
MASSSPAWNER_API UE::Mass::FEntityBuilder CreateEntityBuilder(const TSharedRef<FMassEntityManager>& InEntityManager) const;
protected:
FMassArchetypeCompositionDescriptor Composition;
FMassArchetypeSharedFragmentValues SharedFragmentValues;
// Initial fragment values, this is not part of the archetype as it is the spawner job to set them.
TArray<FInstancedStruct> InitialFragmentValues;
// These functions will be called to initialize entity's UObject-based fragments
TArray<FObjectFragmentInitializerFunction> ObjectInitializers;
FMassArchetypeCreationParams CreationParams;
FString TemplateName;
};
/**
* A finalized and const wrapper for FMassEntityTemplateData, associated with a Mass archetype and template ID.
* Designed to never be changed. If a change is needed a copy of the hosted FMassEntityTemplateData needs to be made and
* used to create another finalized FMassEntityTemplate (via FMassEntityTemplateManager).
*/
struct FMassEntityTemplate final : public TSharedFromThis<FMassEntityTemplate>
{
friend TSharedFromThis<FMassEntityTemplate>;
FMassEntityTemplate() = default;
MASSSPAWNER_API FMassEntityTemplate(const FMassEntityTemplateData& InData, FMassEntityManager& EntityManager, FMassEntityTemplateID InTemplateID);
MASSSPAWNER_API FMassEntityTemplate(FMassEntityTemplateData&& InData, FMassEntityManager& EntityManager, FMassEntityTemplateID InTemplateID);
/** InArchetype is expected to be valid. The function will crash-check it. */
MASSSPAWNER_API void SetArchetype(const FMassArchetypeHandle& InArchetype);
const FMassArchetypeHandle& GetArchetype() const;
bool IsValid() const;
void SetTemplateID(FMassEntityTemplateID InTemplateID);
FMassEntityTemplateID GetTemplateID() const;
MASSSPAWNER_API FString DebugGetDescription(FMassEntityManager* EntityManager = nullptr) const;
MASSSPAWNER_API FString DebugGetArchetypeDescription(FMassEntityManager& EntityManager) const;
static MASSSPAWNER_API TSharedRef<FMassEntityTemplate> MakeFinalTemplate(FMassEntityManager& EntityManager, FMassEntityTemplateData&& TempTemplateData, FMassEntityTemplateID InTemplateID);
//-----------------------------------------------------------------------------
// FMassEntityTemplateData getters
//-----------------------------------------------------------------------------
TConstArrayView<FMassEntityTemplateData::FObjectFragmentInitializerFunction> GetObjectFragmentInitializers() const;
const FString& GetTemplateName() const;
const FMassArchetypeCompositionDescriptor& GetCompositionDescriptor() const;
const FMassArchetypeSharedFragmentValues& GetSharedFragmentValues() const;
TConstArrayView<FInstancedStruct> GetInitialFragmentValues() const;
const FMassEntityTemplateData& GetTemplateData() const;
MASSSPAWNER_API UE::Mass::FEntityBuilder CreateEntityBuilder(const TSharedRef<FMassEntityManager>& InEntityManager) const;
private:
FMassEntityTemplateData TemplateData;
FMassArchetypeHandle Archetype;
FMassEntityTemplateID TemplateID;
};
struct FMassEntityTemplateIDFactory
{
static MASSSPAWNER_API FMassEntityTemplateID Make(const FGuid& ConfigGuid);
static MASSSPAWNER_API FMassEntityTemplateID MakeFlavor(const FMassEntityTemplateID& SourceTemplateID, const int32 Flavor);
};
//-----------------------------------------------------------------------------
// INLINES
//-----------------------------------------------------------------------------
inline bool FMassEntityTemplateData::IsEmpty() const
{
return Composition.IsEmpty();
}
inline TConstArrayView<FMassEntityTemplateData::FObjectFragmentInitializerFunction> FMassEntityTemplateData::GetObjectFragmentInitializers() const
{
return ObjectInitializers;
}
inline const FString& FMassEntityTemplateData::GetTemplateName() const
{
return TemplateName;
}
inline const FMassArchetypeCompositionDescriptor& FMassEntityTemplateData::GetCompositionDescriptor() const
{
return Composition;
}
inline const FMassArchetypeSharedFragmentValues& FMassEntityTemplateData::GetSharedFragmentValues() const
{
return SharedFragmentValues;
}
inline TConstArrayView<FInstancedStruct> FMassEntityTemplateData::GetInitialFragmentValues() const
{
return InitialFragmentValues;
}
inline TArray<FMassEntityTemplateData::FObjectFragmentInitializerFunction>& FMassEntityTemplateData::GetMutableObjectFragmentInitializers()
{
return ObjectInitializers;
}
inline void FMassEntityTemplateData::SetTemplateName(const FString& Name)
{
TemplateName = Name;
}
inline const FMassTagBitSet& FMassEntityTemplateData::GetTags() const
{
return Composition.GetTags();
}
inline FMassTagBitSet& FMassEntityTemplateData::GetMutableTags()
{
return Composition.GetTags();
}
inline FMassArchetypeCreationParams& FMassEntityTemplateData::GetArchetypeCreationParams()
{
return CreationParams;
}
inline const FMassArchetypeHandle& FMassEntityTemplate::GetArchetype() const
{
return Archetype;
}
inline bool FMassEntityTemplate::IsValid() const
{
return Archetype.IsValid();
}
inline void FMassEntityTemplate::SetTemplateID(FMassEntityTemplateID InTemplateID)
{
TemplateID = InTemplateID;
}
inline FMassEntityTemplateID FMassEntityTemplate::GetTemplateID() const
{
return TemplateID;
}
inline TConstArrayView<FMassEntityTemplateData::FObjectFragmentInitializerFunction> FMassEntityTemplate::GetObjectFragmentInitializers() const
{
return TemplateData.GetObjectFragmentInitializers();
}
inline const FString& FMassEntityTemplate::GetTemplateName() const
{
return TemplateData.GetTemplateName();
}
inline const FMassArchetypeCompositionDescriptor& FMassEntityTemplate::GetCompositionDescriptor() const
{
return TemplateData.GetCompositionDescriptor();
}
inline const FMassArchetypeSharedFragmentValues& FMassEntityTemplate::GetSharedFragmentValues() const
{
return TemplateData.GetSharedFragmentValues();
}
inline TConstArrayView<FInstancedStruct> FMassEntityTemplate::GetInitialFragmentValues() const
{
return TemplateData.GetInitialFragmentValues();
}
inline const FMassEntityTemplateData& FMassEntityTemplate::GetTemplateData() const
{
return TemplateData;
}