// 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::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("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 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* EnabledChannels = static_cast*>(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