Files
UnrealEngine/Engine/Source/Runtime/MassEntity/Private/MassEntitySettings.cpp
2025-05-18 13:04:45 +08:00

216 lines
6.7 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MassEntitySettings.h"
#include "MassProcessingPhaseManager.h"
#include "VisualLogger/VisualLogger.h"
#include "UObject/UObjectHash.h"
#include "Misc/CoreDelegates.h"
#include "MassArchetypeData.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(MassEntitySettings)
#if WITH_EDITOR
#include "ObjectEditorUtils.h"
#include "CoreGlobals.h"
#endif // WITH_EDITOR
//----------------------------------------------------------------------//
// UMassEntitySettings
//----------------------------------------------------------------------//
UMassEntitySettings::UMassEntitySettings(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
for (int i = 0; i < (int)EMassProcessingPhase::MAX; ++i)
{
ProcessingPhasesConfig[i].PhaseName = *UEnum::GetDisplayValueAsText(EMassProcessingPhase(i)).ToString();
}
FCoreDelegates::OnPostEngineInit.AddUObject(this, &UMassEntitySettings::OnPostEngineInit);
}
void UMassEntitySettings::PostInitProperties()
{
Super::PostInitProperties();
ChunkMemorySize = UE::Mass::SanitizeChunkMemorySize(ChunkMemorySize);
}
void UMassEntitySettings::BeginDestroy()
{
FCoreDelegates::OnPostEngineInit.RemoveAll(this);
Super::BeginDestroy();
}
void UMassEntitySettings::OnPostEngineInit()
{
bEngineInitialized = true;
BuildProcessorListAndPhases();
}
void UMassEntitySettings::BuildProcessorListAndPhases()
{
if (bInitialized == true || bEngineInitialized == false)
{
return;
}
BuildProcessorList();
BuildPhases();
bInitialized = true;
OnInitializedEvent.Broadcast();
}
void UMassEntitySettings::BuildPhases()
{
#if WITH_EDITOR
if (GIsEditor)
{
for (int i = 0; i < int(EMassProcessingPhase::MAX); ++i)
{
FMassProcessingPhaseConfig& PhaseConfig = ProcessingPhasesConfig[i];
PhaseConfig.PhaseProcessor = NewObject<UMassCompositeProcessor>(this, PhaseConfig.PhaseGroupClass
, *FString::Printf(TEXT("ProcessingPhase_%s"), *PhaseConfig.PhaseName.ToString()));
PhaseConfig.PhaseProcessor->SetGroupName(PhaseConfig.PhaseName);
PhaseConfig.PhaseProcessor->SetProcessingPhase(EMassProcessingPhase(i));
const FString PhaseDumpDependencyGraphFileName = !DumpDependencyGraphFileName.IsEmpty() ? DumpDependencyGraphFileName + TEXT("_") + PhaseConfig.PhaseName.ToString() : FString();
FMassRuntimePipeline TmpPipeline(EProcessorExecutionFlags::All);
TmpPipeline.CreateFromArray(PhaseConfig.ProcessorCDOs, *PhaseConfig.PhaseProcessor);
PhaseConfig.PhaseProcessor->SetChildProcessors(TmpPipeline.MoveProcessorsArray());
FStringOutputDevice Ar;
PhaseConfig.PhaseProcessor->DebugOutputDescription(Ar);
PhaseConfig.Description = FText::FromString(Ar);
}
}
#endif // WITH_EDITOR
}
void UMassEntitySettings::BuildProcessorList()
{
ProcessorCDOs.Reset();
for (FMassProcessingPhaseConfig& PhaseConfig : ProcessingPhasesConfig)
{
PhaseConfig.ProcessorCDOs.Reset();
}
TArray<UClass*> SubClassess;
GetDerivedClasses(UMassProcessor::StaticClass(), SubClassess);
for (int i = SubClassess.Num() - 1; i >= 0; --i)
{
if (SubClassess[i]->HasAnyClassFlags(CLASS_Abstract))
{
continue;
}
UMassProcessor* ProcessorCDO = GetMutableDefault<UMassProcessor>(SubClassess[i]);
// we explicitly restrict adding UMassCompositeProcessor. If needed by specific project a derived class can be added
if (ProcessorCDO && SubClassess[i] != UMassCompositeProcessor::StaticClass()
#if WITH_EDITOR
&& ProcessorCDO->ShouldShowUpInSettings()
#endif // WITH_EDITOR
)
{
ProcessorCDOs.Add(ProcessorCDO);
if (ProcessorCDO->ShouldAutoAddToGlobalList())
{
ProcessingPhasesConfig[int(ProcessorCDO->GetProcessingPhase())].ProcessorCDOs.Add(ProcessorCDO);
}
}
}
ProcessorCDOs.Sort([](UMassProcessor& LHS, UMassProcessor& RHS) {
return LHS.GetName().Compare(RHS.GetName()) < 0;
});
}
void UMassEntitySettings::AddToActiveProcessorsList(TSubclassOf<UMassProcessor> ProcessorClass)
{
if (UMassProcessor* ProcessorCDO = GetMutableDefault<UMassProcessor>(ProcessorClass))
{
if (ProcessorClass == UMassCompositeProcessor::StaticClass())
{
UE_VLOG_UELOG(this, LogMass, Log, TEXT("%s adding MassCompositeProcessor to the global processor list is unsupported"), ANSI_TO_TCHAR(__FUNCTION__));
}
else if (ProcessorClass->HasAnyClassFlags(CLASS_Abstract))
{
UE_VLOG_UELOG(this, LogMass, Log, TEXT("%s unable to add %s due to it being an abstract class"), ANSI_TO_TCHAR(__FUNCTION__), *ProcessorClass->GetName());
}
else if (ProcessorCDOs.Find(ProcessorCDO) != INDEX_NONE)
{
UE_VLOG_UELOG(this, LogMass, Log, TEXT("%s already in global processor list"), *ProcessorCDO->GetName());
}
else
{
ensureMsgf(ProcessorCDO->ShouldAutoAddToGlobalList() == false, TEXT("%s missing from the global list while it's already marked to be auto-added"), *ProcessorCDO->GetName());
ProcessorCDOs.Add(ProcessorCDO);
ProcessorCDO->SetShouldAutoRegisterWithGlobalList(true);
}
}
}
TConstArrayView<FMassProcessingPhaseConfig> UMassEntitySettings::GetProcessingPhasesConfig()
{
BuildProcessorListAndPhases();
return MakeArrayView(ProcessingPhasesConfig, int32(EMassProcessingPhase::MAX));
}
#if WITH_EDITOR
void UMassEntitySettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
static const FName ProcessorCDOsName = GET_MEMBER_NAME_CHECKED(UMassEntitySettings, ProcessorCDOs);
static const FName ChunkMemorySizeName = GET_MEMBER_NAME_CHECKED(UMassEntitySettings, ChunkMemorySize);
Super::PostEditChangeProperty(PropertyChangedEvent);
if (PropertyChangedEvent.ChangeType == EPropertyChangeType::ArrayAdd)
{
// ignore adding elements to arrays since it would be 'None' at first
return;
}
if (PropertyChangedEvent.Property)
{
const FName PropName = PropertyChangedEvent.Property->GetFName();
if (PropName == ProcessorCDOsName)
{
BuildProcessorList();
}
else if (PropName == ChunkMemorySizeName)
{
ChunkMemorySize = UE::Mass::SanitizeChunkMemorySize(ChunkMemorySize);
}
BuildPhases();
OnSettingsChange.Broadcast(PropertyChangedEvent);
}
}
void UMassEntitySettings::PostEditChangeChainProperty(struct FPropertyChangedChainEvent& PropertyChangedEvent)
{
static const FName AutoRegisterName = TEXT("bAutoRegisterWithProcessingPhases");
Super::PostEditChangeChainProperty(PropertyChangedEvent);
FProperty* Property = PropertyChangedEvent.Property;
FProperty* MemberProperty = nullptr;
FEditPropertyChain::TDoubleLinkedListNode* LastPropertyNode = PropertyChangedEvent.PropertyChain.GetActiveMemberNode();
while (LastPropertyNode && LastPropertyNode->GetNextNode())
{
LastPropertyNode = LastPropertyNode->GetNextNode();
}
if (LastPropertyNode)
{
MemberProperty = LastPropertyNode->GetValue();
}
if (MemberProperty && MemberProperty->GetFName() == AutoRegisterName)
{
BuildProcessorList();
}
}
#endif // WITH_EDITOR