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

293 lines
9.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "StateTreeModule.h"
#include "StateTreeModuleImpl.h"
#include "StateTreeTypes.h"
#if WITH_STATETREE_TRACE
#include "Debugger/StateTreeTrace.h"
#include "Debugger/StateTreeTraceTypes.h"
#include "HAL/IConsoleManager.h"
#include "ProfilingDebugging/TraceAuxiliary.h"
#include "StateTreeDelegates.h"
#include "StateTreeSettings.h"
#endif // WITH_STATETREE_TRACE
#if WITH_STATETREE_TRACE_DEBUGGER
#include "Debugger/StateTreeDebuggerTypes.h"
#include "Debugger/StateTreeTraceModule.h"
#include "Features/IModularFeatures.h"
#include "Misc/CoreDelegates.h"
#include "Trace/StoreClient.h"
#include "TraceServices/AnalysisService.h"
#include "TraceServices/ITraceServicesModule.h"
#endif // WITH_STATETREE_TRACE_DEBUGGER
#if WITH_EDITORONLY_DATA
#include "StateTreeInstanceData.h"
#endif // WITH_EDITORONLY_DATA
#if WITH_EDITOR
#include "StructUtilsDelegates.h"
#endif
#define LOCTEXT_NAMESPACE "StateTree"
#if WITH_EDITOR
FStateTreeModule::FOnObjectsReinstanced FStateTreeModule::OnObjectsReinstanced;
FStateTreeModule::FOnPIEEvent FStateTreeModule::OnPreBeginPIE;
FStateTreeModule::FOnUserDefinedStructReinstanced FStateTreeModule::OnUserDefinedStructReinstanced;
#endif
#if WITH_STATETREE_TRACE_DEBUGGER
UE::Trace::FStoreClient* FStateTreeModule::GetStoreClient()
{
if (!StoreClient.IsValid())
{
StoreClient = TUniquePtr<UE::Trace::FStoreClient>(UE::Trace::FStoreClient::Connect(TEXT("localhost")));
}
return StoreClient.Get();
}
#endif // WITH_STATETREE_TRACE_DEBUGGER
FStateTreeModule::FStateTreeModule()
#if WITH_STATETREE_TRACE
: StartDebuggerTracesCommand(FAutoConsoleCommand(
TEXT("statetree.startdebuggertraces"),
TEXT("Turns on StateTree debugger traces if not already active."),
FConsoleCommandDelegate::CreateLambda([]
{
int32 TraceId = 0;
IStateTreeModule::Get().StartTraces(TraceId);
})))
, StopDebuggerTracesCommand(FAutoConsoleCommand(
TEXT("statetree.stopdebuggertraces"),
TEXT("Turns off StateTree debugger traces if active."),
FConsoleCommandDelegate::CreateLambda([]
{
IStateTreeModule::Get().StopTraces();
})))
#endif // WITH_STATETREE_TRACE
{
}
void FStateTreeModule::StartupModule()
{
#if WITH_STATETREE_TRACE_DEBUGGER
ITraceServicesModule& TraceServicesModule = FModuleManager::LoadModuleChecked<ITraceServicesModule>("TraceServices");
TraceAnalysisService = TraceServicesModule.GetAnalysisService();
TraceModuleService = TraceServicesModule.GetModuleService();
IModularFeatures::Get().RegisterModularFeature(TraceServices::ModuleFeatureName, &StateTreeTraceModule);
#endif // WITH_STATETREE_TRACE_DEBUGGER
#if WITH_STATETREE_TRACE
UE::StateTreeTrace::RegisterGlobalDelegates();
#if !WITH_EDITOR
// We don't automatically start traces for Editor targets since we rely on the debugger
// to start recording either on user action or on PIE session start.
if (UStateTreeSettings::Get().bAutoStartDebuggerTracesOnNonEditorTargets)
{
int32 TraceId = INDEX_NONE;
StartTraces(TraceId);
}
#endif // !WITH_EDITOR
#endif // WITH_STATETREE_TRACE
#if WITH_EDITORONLY_DATA
UE::StateTree::RegisterInstanceDataForLocalization();
#endif // WITH_EDITORONLY_DATA
#if WITH_EDITOR
// Register thread safe delegates, which allows the StateTree objects to safely register to these delegates e.g. in PostInitProperties() which may be called from another thread.
OnObjectsReinstancedHandle = FCoreUObjectDelegates::OnObjectsReinstanced.AddRaw(this, &FStateTreeModule::HandleObjectsReinstanced);
OnUserDefinedStructReinstancedHandle = UE::StructUtils::Delegates::OnUserDefinedStructReinstanced.AddRaw(this, &FStateTreeModule::HandleUserDefinedStructReinstanced);
OnPreBeginPIEHandle = FEditorDelegates::PreBeginPIE.AddRaw(this, &FStateTreeModule::HandlePreBeginPIE);
UE::PropertyBinding::PropertyBindingIndex16ConversionFuncList.Add([](const FPropertyTag& Tag, FStructuredArchive::FSlot Slot, TNotNull<FPropertyBindingIndex16*> Index)
{
static FName FStateTreeIndex16TypeName = FStateTreeIndex16::StaticStruct()->GetFName();
const FName StructFName = Tag.GetType().GetParameter(0).GetName();
if (FStateTreeIndex16TypeName == StructFName)
{
FStateTreeIndex16 StateTreeIndex16;
FStateTreeIndex16::StaticStruct()->SerializeItem(Slot, &StateTreeIndex16, /*Defaults*/nullptr);
*Index = StateTreeIndex16;
return true;
}
return false;
});
#endif //WITH_EDITOR
}
void FStateTreeModule::ShutdownModule()
{
#if WITH_EDITOR
FCoreUObjectDelegates::OnObjectsReinstanced.Remove(OnObjectsReinstancedHandle);
UE::StructUtils::Delegates::OnUserDefinedStructReinstanced.Remove(OnUserDefinedStructReinstancedHandle);
FEditorDelegates::PreBeginPIE.Remove(OnPreBeginPIEHandle);
#endif //WITH_EDITOR
#if WITH_STATETREE_TRACE
StopTraces();
UE::StateTreeTrace::UnregisterGlobalDelegates();
#endif // WITH_STATETREE_TRACE
#if WITH_STATETREE_TRACE_DEBUGGER
if (StoreClient.IsValid())
{
StoreClient.Reset();
}
IModularFeatures::Get().UnregisterModularFeature(TraceServices::ModuleFeatureName, &StateTreeTraceModule);
#endif // WITH_STATETREE_TRACE_DEBUGGER
}
bool FStateTreeModule::StartTraces(int32& OutTraceId)
{
OutTraceId = INDEX_NONE;
#if WITH_STATETREE_TRACE
if (IsRunningCommandlet() || bIsTracing)
{
return false;
}
FGuid SessionGuid, TraceGuid;
const bool bAlreadyConnected = FTraceAuxiliary::IsConnected(SessionGuid, TraceGuid);
#if WITH_STATETREE_TRACE_DEBUGGER
if (const UE::Trace::FStoreClient* Client = GetStoreClient())
{
const UE::Trace::FStoreClient::FSessionInfo* SessionInfo = Client->GetSessionInfoByGuid(TraceGuid);
// Note that 0 is returned instead of INDEX_NONE to match default invalid value for GetTraceId
OutTraceId = SessionInfo != nullptr ? SessionInfo->GetTraceId(): 0;
}
#endif // WITH_STATETREE_TRACE_DEBUGGER
// If trace is already connected let's keep track of enabled channels to restore them when we stop recording
if (bAlreadyConnected)
{
UE::Trace::EnumerateChannels([](const ANSICHAR* Name, const bool bIsEnabled, void* Channels)
{
TArray<FString>* EnabledChannels = static_cast<TArray<FString>*>(Channels);
if (bIsEnabled)
{
EnabledChannels->Emplace(ANSI_TO_TCHAR(Name));
}
}, &ChannelsToRestore);
}
else
{
// Disable all channels and then enable only those we need to minimize trace file size.
UE::Trace::EnumerateChannels([](const ANSICHAR* ChannelName, const bool bEnabled, void*)
{
if (bEnabled)
{
FString ChannelNameFString(ChannelName);
UE::Trace::ToggleChannel(ChannelNameFString.GetCharArray().GetData(), false);
}
}
, nullptr);
}
UE::Trace::ToggleChannel(TEXT("StateTreeDebugChannel"), true);
UE::Trace::ToggleChannel(TEXT("FrameChannel"), true);
bool bAreTracesStarted = false;
if (bAlreadyConnected == false)
{
FTraceAuxiliary::FOptions Options;
Options.bExcludeTail = true;
bAreTracesStarted = FTraceAuxiliary::Start(FTraceAuxiliary::EConnectionType::Network, TEXT("localhost"), TEXT(""), &Options, LogStateTree);
}
bIsTracing = true;
if (UE::StateTree::Delegates::OnTracingStateChanged.IsBound())
{
UE_LOG(LogStateTree, Log, TEXT("StateTree traces enabled"));
UE::StateTree::Delegates::OnTracingStateChanged.Broadcast(EStateTreeTraceStatus::TracesStarted);
}
return bAreTracesStarted;
#else
return false;
#endif // WITH_STATETREE_TRACE
}
bool FStateTreeModule::IsTracing() const
{
#if WITH_STATETREE_TRACE
return bIsTracing;
#else
return false;
#endif // WITH_STATETREE_TRACE
}
void FStateTreeModule::StopTraces()
{
#if WITH_STATETREE_TRACE
if (bIsTracing == false)
{
return;
}
if (UE::StateTree::Delegates::OnTracingStateChanged.IsBound())
{
UE_LOG(LogStateTree, Log, TEXT("Stopping StateTree traces..."));
UE::StateTree::Delegates::OnTracingStateChanged.Broadcast(EStateTreeTraceStatus::StoppingTrace);
}
UE::Trace::ToggleChannel(TEXT("StateTreeDebugChannel"), false);
UE::Trace::ToggleChannel(TEXT("FrameChannel"), false);
// When we have channels to restore it also indicates that the trace were active
// so we only toggle the channels back (i.e. not calling FTraceAuxiliary::Stop)
if (ChannelsToRestore.Num() > 0)
{
for (const FString& ChannelName : ChannelsToRestore)
{
UE::Trace::ToggleChannel(ChannelName.GetCharArray().GetData(), true);
}
ChannelsToRestore.Reset();
}
else
{
FTraceAuxiliary::Stop();
}
bIsTracing = false;
if (UE::StateTree::Delegates::OnTracingStateChanged.IsBound())
{
UE_LOG(LogStateTree, Log, TEXT("StateTree traces stopped"));
UE::StateTree::Delegates::OnTracingStateChanged.Broadcast(EStateTreeTraceStatus::TracesStopped);
}
#endif // WITH_STATETREE_TRACE
}
#if WITH_EDITOR
void FStateTreeModule::HandleObjectsReinstanced(const FReplacementObjectMap& ObjectMap)
{
OnObjectsReinstanced.Broadcast(ObjectMap);
}
void FStateTreeModule::HandlePreBeginPIE(const bool bIsSimulating)
{
OnPreBeginPIE.Broadcast(bIsSimulating);
}
void FStateTreeModule::HandleUserDefinedStructReinstanced(const UUserDefinedStruct& UserDefinedStruct)
{
OnUserDefinedStructReinstanced.Broadcast(UserDefinedStruct);
}
#endif
IMPLEMENT_MODULE(FStateTreeModule, StateTreeModule)
#undef LOCTEXT_NAMESPACE