Files
UnrealEngine/Engine/Plugins/Runtime/MassGameplay/Source/MassSpawner/Private/MassEntityTemplate.cpp
2025-05-18 13:04:45 +08:00

189 lines
7.1 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MassEntityTemplate.h"
#include "MassCommonTypes.h"
#include "VisualLogger/VisualLoggerTypes.h"
#include "MassDebugger.h"
#include "Algo/Find.h"
#include "MassEntityBuilder.h"
//----------------------------------------------------------------------//
// FMassEntityTemplateID
//----------------------------------------------------------------------//
FString FMassEntityTemplateID::ToString() const
{
return IsValid() ? FString::Printf(TEXT("[%s:%d]"), *ConfigGuid.ToString(EGuidFormats::DigitsLower), FlavorHash)
: FString::Printf(TEXT("[Invalid]"));
}
//----------------------------------------------------------------------//
// FMassEntityTemplateData
//----------------------------------------------------------------------//
FMassEntityTemplateData::FMassEntityTemplateData(const FMassEntityTemplate& InFinalizedTemplate)
: FMassEntityTemplateData(InFinalizedTemplate.GetTemplateData())
{
}
bool FMassEntityTemplateData::SlowIsEquivalent(const FMassEntityTemplateData& Other) const
{
if (Composition.IsEquivalent(Other.GetCompositionDescriptor()) == false)
{
return false;
}
else if (SharedFragmentValues.IsEquivalent(Other.GetSharedFragmentValues()) == false)
{
return false;
}
TConstArrayView<FInstancedStruct> OtherInitialFragmentValues = Other.GetInitialFragmentValues();
if (OtherInitialFragmentValues.Num() != InitialFragmentValues.Num())
{
return false;
}
for (const FInstancedStruct& InitialValue : InitialFragmentValues)
{
const FInstancedStruct* FoundElement = Algo::FindByPredicate(OtherInitialFragmentValues, [&InitialValue](const FInstancedStruct& Element)
{
return InitialValue == Element;
});
if (FoundElement == nullptr)
{
return false;
}
}
return true;
}
UE::Mass::FEntityBuilder FMassEntityTemplateData::CreateEntityBuilder(const TSharedRef<FMassEntityManager>& InEntityManager) const
{
UE::Mass::FEntityBuilder Builder = UE::Mass::FEntityBuilder::Make(InEntityManager, Composition
, InitialFragmentValues
, SharedFragmentValues.GetConstSharedFragments()
, SharedFragmentValues.GetSharedFragments());
Builder.ConfigureArchetypeCreation(CreationParams);
ensureMsgf(ObjectInitializers.Num() == 0, TEXT("%hs: EntityBuilder doesn't support object initializers (yet)."), __FUNCTION__);
return Builder;
}
uint32 GetTypeHash(const FMassEntityTemplateData& Template)
{
// @todo: using the template name is temporary solution to allow to tell two templates apart based on something
// else than composition. Ideally we would hash the fragment values instead.
const int32 NameHash = GetTypeHash(Template.GetTemplateName());
const uint32 CompositionHash = Template.GetCompositionDescriptor().CalculateHash();
// @todo shared fragments hash is based on pointers - this needs to be changed as well
const uint32 SharedFragmentValuesHash = GetTypeHash(Template.GetSharedFragmentValues());
uint32 InitialValuesHash = 0;
// Initial fragment values, this is not part of the archetype as it is the spawner job to set them.
for (const FInstancedStruct& Struct : Template.GetInitialFragmentValues())
{
InitialValuesHash = UE::StructUtils::GetStructCrc32(Struct, InitialValuesHash);
}
// These functions will be called to initialize entity's UObject-based fragments
// @todo this is very bad for hash creation - no way to make this consistent between client server. Might need some "initializer index"
uint32 InitializersHash = 0;
/* left here on purpose, will address in following CLs
for (const FMassEntityTemplateData::FObjectFragmentInitializerFunction& Initializer : Template.ObjectInitializers)
{
InitializersHash = HashCombine(InitializersHash, GetTypeHash(Initializer));
}*/
// @todo maybe better to have two separate hashes - one for composition and maybe shared fragments, and then the other one for the rest, and use multimap for look up
return HashCombine(NameHash, HashCombine(HashCombine(CompositionHash, SharedFragmentValuesHash), HashCombine(InitialValuesHash, InitializersHash)));
}
//-----------------------------------------------------------------------------
// FMassEntityTemplate
//-----------------------------------------------------------------------------
TSharedRef<FMassEntityTemplate> FMassEntityTemplate::MakeFinalTemplate(FMassEntityManager& EntityManager, FMassEntityTemplateData&& TempTemplateData, FMassEntityTemplateID InTemplateID)
{
return MakeShared<FMassEntityTemplate>(MoveTemp(TempTemplateData), EntityManager, InTemplateID);
}
FMassEntityTemplate::FMassEntityTemplate(const FMassEntityTemplateData& InData, FMassEntityManager& EntityManager, FMassEntityTemplateID InTemplateID)
: TemplateData(InData)
, TemplateID(InTemplateID)
{
// Sort anything there is to sort for later comparison purposes
TemplateData.Sort();
TemplateData.GetArchetypeCreationParams().DebugName = FName(GetTemplateName());
const FMassArchetypeHandle ArchetypeHandle = EntityManager.CreateArchetype(GetCompositionDescriptor(), TemplateData.GetArchetypeCreationParams());
SetArchetype(ArchetypeHandle);
}
FMassEntityTemplate::FMassEntityTemplate(FMassEntityTemplateData&& InData, FMassEntityManager& EntityManager, FMassEntityTemplateID InTemplateID)
: TemplateData(InData)
, TemplateID(InTemplateID)
{
// Sort anything there is to sort for later comparison purposes
TemplateData.Sort();
TemplateData.GetArchetypeCreationParams().DebugName = FName(GetTemplateName());
const FMassArchetypeHandle ArchetypeHandle = EntityManager.CreateArchetype(GetCompositionDescriptor(), TemplateData.GetArchetypeCreationParams());
SetArchetype(ArchetypeHandle);
}
void FMassEntityTemplate::SetArchetype(const FMassArchetypeHandle& InArchetype)
{
check(InArchetype.IsValid());
Archetype = InArchetype;
}
FString FMassEntityTemplate::DebugGetArchetypeDescription(FMassEntityManager& EntityManager) const
{
FStringOutputDevice OutDescription;
#if WITH_MASSGAMEPLAY_DEBUG
FMassDebugger::OutputArchetypeDescription(OutDescription, Archetype);
#endif // WITH_MASSGAMEPLAY_DEBUG
return MoveTemp(OutDescription);
}
FString FMassEntityTemplate::DebugGetDescription(FMassEntityManager* EntityManager) const
{
FStringOutputDevice Ar;
#if WITH_MASSGAMEPLAY_DEBUG
Ar.SetAutoEmitLineTerminator(true);
if (EntityManager)
{
Ar += TEXT("Archetype details:\n");
Ar += DebugGetArchetypeDescription(*EntityManager);
}
else
{
Ar += TEXT("Composition:\n");
GetCompositionDescriptor().DebugOutputDescription(Ar);
}
#endif // WITH_MASSGAMEPLAY_DEBUG
return MoveTemp(Ar);
}
UE::Mass::FEntityBuilder FMassEntityTemplate::CreateEntityBuilder(const TSharedRef<FMassEntityManager>& InEntityManager) const
{
return TemplateData.CreateEntityBuilder(InEntityManager);
}
//-----------------------------------------------------------------------------
// FMassEntityTemplateIDFactory
//-----------------------------------------------------------------------------
FMassEntityTemplateID FMassEntityTemplateIDFactory::Make(const FGuid& ConfigGuid)
{
return FMassEntityTemplateID(ConfigGuid);
}
FMassEntityTemplateID FMassEntityTemplateIDFactory::MakeFlavor(const FMassEntityTemplateID& SourceTemplateID, const int32 Flavor)
{
return FMassEntityTemplateID(SourceTemplateID.ConfigGuid, Flavor);
}