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

706 lines
27 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Chaos/ChaosVDRemoteSessionsManager.h"
#include "ChaosVDRuntimeModule.h"
#include "IMessageBus.h"
#include "IMessagingModule.h"
#include "MessageEndpoint.h"
#include "MessageEndpointBuilder.h"
#include "ChaosVisualDebugger/ChaosVDOptionalDataChannel.h"
#include "ChaosVisualDebugger/ChaosVisualDebuggerTrace.h"
#include "Misc/App.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(ChaosVDRemoteSessionsManager)
const FGuid FChaosVDRemoteSessionsManager::AllRemoteSessionsWrapperGUID = FGuid::NewGuid();
const FGuid FChaosVDRemoteSessionsManager::AllRemoteServersWrapperGUID = FGuid::NewGuid();
const FGuid FChaosVDRemoteSessionsManager::AllRemoteClientsWrapperGUID = FGuid::NewGuid();
const FGuid FChaosVDRemoteSessionsManager::AllSessionsWrapperGUID = FGuid::NewGuid();
const FGuid FChaosVDRemoteSessionsManager::CustomSessionsWrapperGUID = FGuid::NewGuid();
const FGuid FChaosVDRemoteSessionsManager::InvalidSessionGUID = FGuid();
const FGuid FChaosVDRemoteSessionsManager::LocalSessionID = FApp::GetInstanceId();
const FString FChaosVDRemoteSessionsManager::LocalEditorSessionName = TEXT("Local Editor");
const FGuid FChaosVDRemoteSessionsManager::LocalEditorSessionID = IsController() ? FApp::GetInstanceId() : InvalidSessionGUID;
const FName FChaosVDRemoteSessionsManager::MessageBusEndPointName = FName("CVDSessionManagerEndPoint");
const FString FChaosVDRemoteSessionsManager::AllRemoteSessionsTargetName = TEXT("All Remote");
const FString FChaosVDRemoteSessionsManager::AllRemoteServersTargetName = TEXT("All Remote Servers");
const FString FChaosVDRemoteSessionsManager::AllRemoteClientsTargetName = TEXT("All Remote Clients");
const FString FChaosVDRemoteSessionsManager::AllSessionsTargetName = TEXT("All Sessions");
const FString FChaosVDRemoteSessionsManager::CustomSessionsTargetName = TEXT("Custom Selection");
DEFINE_LOG_CATEGORY(LogChaosVDRemoteSession)
const FChaosVDTraceDetails& FChaosVDSessionInfo::GetConnectionDetails()
{
return LastKnownConnectionDetails;
}
bool FChaosVDSessionInfo::IsRecording() const
{
return LastKnownRecordingState.bIsRecording;
}
EChaosVDRecordingMode FChaosVDSessionInfo::GetRecordingMode() const
{
return LastKnownConnectionDetails.IsValid() ? LastKnownConnectionDetails.Mode : LastRequestedRecordingMode;
}
EChaosVDRecordingMode FChaosVDSessionInfo::GetLastRequestedRecordingMode() const
{
return LastRequestedRecordingMode;
}
void FChaosVDSessionInfo::SetLastRequestedRecordingMode(EChaosVDRecordingMode NewRecordingMode)
{
LastRequestedRecordingMode = NewRecordingMode;
}
bool FChaosVDSessionInfo::IsConnected() const
{
return false;
}
bool FChaosVDMultiSessionInfo::IsRecording() const
{
bool bIsRecording = false;
EnumerateInnerSessions([&bIsRecording](const TSharedRef<FChaosVDSessionInfo>& InSessionRef)
{
if (InSessionRef->IsRecording())
{
bIsRecording = true;
return false;
}
return true;
});
return bIsRecording;
}
EChaosVDRecordingMode FChaosVDMultiSessionInfo::GetRecordingMode() const
{
EChaosVDRecordingMode FirstValidInstanceRecordingMode = EChaosVDRecordingMode::Invalid;
EnumerateInnerSessions([&FirstValidInstanceRecordingMode](const TSharedRef<FChaosVDSessionInfo>& InSessionRef)
{
EChaosVDRecordingMode RecordingMode = InSessionRef->GetRecordingMode();
if (RecordingMode == EChaosVDRecordingMode::Invalid)
{
// In multi-session, all recordings will have the same recording mode, but not of them might report connected state at the same time
// Therefore we need to continue searching until one of the session has a valid state before giving up.
return true;
}
FirstValidInstanceRecordingMode = RecordingMode;
return false;
});
return FirstValidInstanceRecordingMode;
}
void FChaosVDRemoteSessionsManager::RegisterBuiltInMessageTypes()
{
SupportedMessageTypes.Emplace(FChaosVDSessionPong::StaticStruct());
SupportedMessageTypes.Emplace(FChaosVDRecordingStatusMessage::StaticStruct());
SupportedMessageTypes.Emplace(FChaosVDSessionPing::StaticStruct());
SupportedMessageTypes.Emplace(FChaosVDStartRecordingCommandMessage::StaticStruct());
SupportedMessageTypes.Emplace(FChaosVDStopRecordingCommandMessage::StaticStruct());
SupportedMessageTypes.Emplace(FChaosVDChannelStateChangeCommandMessage::StaticStruct());
SupportedMessageTypes.Emplace(FChaosVDFullSessionInfoRequestMessage::StaticStruct());
SupportedMessageTypes.Emplace(FChaosVDFullSessionInfoResponseMessage::StaticStruct());
SupportedMessageTypes.Emplace(FChaosVDChannelStateChangeResponseMessage::StaticStruct());
SupportedMessageTypes.Emplace(FChaosVDTraceConnectionDetailsMessage::StaticStruct());
}
FChaosVDRemoteSessionsManager::FChaosVDRemoteSessionsManager()
{
RegisterBuiltInMessageTypes();
}
void FChaosVDRemoteSessionsManager::Initialize(const TSharedPtr<IMessageBus>& InMessageBus)
{
ensureMsgf(!InMessageBus, TEXT("Initialize(MessageBusPtr) is deprecated!. The provided message buss will be ignored"));
Initialize();
}
constexpr bool FChaosVDRemoteSessionsManager::IsController()
{
#if WITH_EDITOR
return true;
#else
return false;
#endif
}
TWeakPtr<FChaosVDSessionInfo> FChaosVDRemoteSessionsManager::GetSessionInfo(FGuid Id)
{
if (TSharedPtr<FChaosVDSessionInfo>* FoundSessionPtrPtr = ActiveSessionsByInstanceId.Find(Id))
{
return *FoundSessionPtrPtr;
}
return nullptr;
}
TSharedPtr<FChaosVDSessionInfo> FChaosVDRemoteSessionsManager::CreatedWrapperSessionInfo(FGuid InstanceId, const FString& SessionName)
{
TSharedPtr<FChaosVDMultiSessionInfo> NewSessionInfo = MakeShared<FChaosVDMultiSessionInfo>();
NewSessionInfo->InstanceId = InstanceId;
NewSessionInfo->SessionName = SessionName;
return NewSessionInfo;
}
TSharedPtr<FMessageEndpoint> FChaosVDRemoteSessionsManager::CreateEndPoint(const TSharedRef<IMessageBus>& InMessageBus)
{
FMessageEndpointBuilder EndPointBuilder = FMessageEndpoint::Builder(MessageBusEndPointName, InMessageBus)
.Handling<FChaosVDSessionPing>(this, &FChaosVDRemoteSessionsManager::HandleSessionPingMessage)
.Handling<FChaosVDStartRecordingCommandMessage>(this, &FChaosVDRemoteSessionsManager::HandleRecordingStartCommandMessage)
.Handling<FChaosVDStopRecordingCommandMessage>(this, &FChaosVDRemoteSessionsManager::HandleRecordingStopCommandMessage)
.Handling<FChaosVDChannelStateChangeCommandMessage>(this, &FChaosVDRemoteSessionsManager::HandleChangeDataChannelStateCommandMessage)
.Handling<FChaosVDFullSessionInfoRequestMessage>(this, &FChaosVDRemoteSessionsManager::HandleFullSessionStateRequestMessage);
if (IsController())
{
EndPointBuilder.Handling<FChaosVDSessionPong>(this, &FChaosVDRemoteSessionsManager::HandleSessionPongMessage)
.Handling<FChaosVDRecordingStatusMessage>(this, &FChaosVDRemoteSessionsManager::HandleRecordingStatusUpdateMessage)
.Handling<FChaosVDFullSessionInfoResponseMessage>(this, &FChaosVDRemoteSessionsManager::HandleFullSessionStateResponseMessage)
.Handling<FChaosVDChannelStateChangeResponseMessage>(this, &FChaosVDRemoteSessionsManager::HandleChangeDataChannelStateResponseMessage)
.Handling<FChaosVDTraceConnectionDetailsMessage>(this, &FChaosVDRemoteSessionsManager::HandleConnectionDetailsUpdateMessage);
}
return EndPointBuilder.Build();
}
void FChaosVDRemoteSessionsManager::ReInitializeMessagingSystem(const TSharedPtr<IMessageBus>& InMessageBus)
{
ShutdownMessagingSystem();
InitializeMessagingSystem(InMessageBus);
}
void FChaosVDRemoteSessionsManager::RegisterExternalSupportedMessageType(const UScriptStruct* ScriptStruct)
{
if (ensure(ScriptStruct))
{
SupportedMessageTypes.Emplace(ScriptStruct);
}
}
void FChaosVDRemoteSessionsManager::InitializeMessagingSystem(const TSharedPtr<IMessageBus>& InMessageBus)
{
if (!ensure(InMessageBus))
{
return;
}
MessageBusPtr = InMessageBus;
MessageEndpoint = CreateEndPoint(InMessageBus.ToSharedRef());
if (!ensure(MessageEndpoint))
{
return;
}
if (IsController())
{
MessageEndpoint->Subscribe<FChaosVDSessionPong>();
MessageEndpoint->Subscribe<FChaosVDRecordingStatusMessage>();
MessageEndpoint->Subscribe<FChaosVDFullSessionInfoResponseMessage>();
MessageEndpoint->Subscribe<FChaosVDChannelStateChangeResponseMessage>();
MessageEndpoint->Subscribe<FChaosVDTraceConnectionDetailsMessage>();
}
MessageEndpoint->Subscribe<FChaosVDSessionPing>();
MessageEndpoint->Subscribe<FChaosVDStartRecordingCommandMessage>();
MessageEndpoint->Subscribe<FChaosVDStopRecordingCommandMessage>();
MessageEndpoint->Subscribe<FChaosVDChannelStateChangeCommandMessage>();
MessageEndpoint->Subscribe<FChaosVDFullSessionInfoRequestMessage>();
if (bInitialized)
{
MessageEndpoint->Enable();
}
else
{
MessageEndpoint->Disable();
}
MessagingInitializedDelegate.Broadcast(InMessageBus, MessageEndpoint);
}
void FChaosVDRemoteSessionsManager::ShutdownMessagingSystem()
{
if (MessageEndpoint)
{
MessageEndpoint->Unsubscribe<FChaosVDSessionPong>();
MessageEndpoint->Unsubscribe<FChaosVDRecordingStatusMessage>();
MessageEndpoint->Unsubscribe<FChaosVDSessionPing>();
MessageEndpoint->Unsubscribe<FChaosVDStartRecordingCommandMessage>();
MessageEndpoint->Unsubscribe<FChaosVDStopRecordingCommandMessage>();
MessageEndpoint->Unsubscribe<FChaosVDChannelStateChangeCommandMessage>();
MessageEndpoint->Unsubscribe<FChaosVDFullSessionInfoRequestMessage>();
MessageEndpoint->Unsubscribe<FChaosVDFullSessionInfoResponseMessage>();
MessageEndpoint->Unsubscribe<FChaosVDChannelStateChangeResponseMessage>();
}
MessageBusPtr.Reset();
MessageEndpoint = nullptr;
}
void FChaosVDRemoteSessionsManager::Initialize()
{
#if !defined(WITH_CHAOS_VISUAL_DEBUGGER_EXTERNAL_MESSAGING) || !WITH_CHAOS_VISUAL_DEBUGGER_EXTERNAL_MESSAGING
InitializeMessagingSystem(IMessagingModule::Get().GetDefaultBus());
#endif
ActiveSessionsByInstanceId.Add(AllRemoteSessionsWrapperGUID, CreatedWrapperSessionInfo(AllRemoteSessionsWrapperGUID, AllRemoteSessionsTargetName));
ActiveSessionsByInstanceId.Add(AllRemoteServersWrapperGUID, CreatedWrapperSessionInfo(AllRemoteServersWrapperGUID, AllRemoteServersTargetName));
ActiveSessionsByInstanceId.Add(AllRemoteClientsWrapperGUID, CreatedWrapperSessionInfo(AllRemoteClientsWrapperGUID, AllRemoteClientsTargetName));
ActiveSessionsByInstanceId.Add(AllSessionsWrapperGUID, CreatedWrapperSessionInfo(AllSessionsWrapperGUID, AllSessionsTargetName));
ActiveSessionsByInstanceId.Add(CustomSessionsWrapperGUID, CreatedWrapperSessionInfo(CustomSessionsWrapperGUID, CustomSessionsTargetName));
if (MessageEndpoint)
{
MessageEndpoint->Enable();
}
bInitialized = true;
}
void FChaosVDRemoteSessionsManager::Shutdown()
{
bInitialized = false;
ShutdownMessagingSystem();
}
void FChaosVDRemoteSessionsManager::EnumerateMessageTypes(const FVisitorFunction& InVisitor)
{
for (const UScriptStruct* MessageType : SupportedMessageTypes)
{
if (MessageType)
{
InVisitor(MessageType);
}
}
}
void FChaosVDRemoteSessionsManager::StartSessionDiscovery()
{
if (TickHandle.IsValid())
{
UE_LOG(LogChaosVDRemoteSession, Warning, TEXT("[%hs] Session discovery already started"), __func__);
return;
}
constexpr float TickInterval = 1.0f;
TickHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateRaw(this, &FChaosVDRemoteSessionsManager::Tick), TickInterval);
}
void FChaosVDRemoteSessionsManager::StopSessionDiscovery()
{
if (TickHandle.IsValid())
{
RemoveExpiredSessions(ERemoveSessionOptions::ForceRemoveAll);
FTSTicker::RemoveTicker(TickHandle);
TickHandle = FTSTicker::FDelegateHandle();
}
}
void FChaosVDRemoteSessionsManager::PublishRecordingStatusUpdate(const FChaosVDRecordingStatusMessage& InUpdateMessage)
{
if (ensure(MessageEndpoint))
{
MessageEndpoint->Publish(FMessageEndpoint::MakeMessage<FChaosVDRecordingStatusMessage>(InUpdateMessage), EMessageScope::Network);
}
}
void FChaosVDRemoteSessionsManager::PublishTraceConnectionDetailsUpdate(const FChaosVDTraceConnectionDetailsMessage& InUpdateMessage)
{
if (ensure(MessageEndpoint))
{
MessageEndpoint->Publish(FMessageEndpoint::MakeMessage<FChaosVDTraceConnectionDetailsMessage>(InUpdateMessage), EMessageScope::Network);
}
}
void FChaosVDRemoteSessionsManager::PublishDataChannelStateChangeUpdate(const FChaosVDChannelStateChangeResponseMessage& InNewStateData)
{
if (ensure(MessageEndpoint))
{
MessageEndpoint->Publish(FMessageEndpoint::MakeMessage<FChaosVDChannelStateChangeResponseMessage>(InNewStateData), EMessageScope::Network);
}
}
void FChaosVDRemoteSessionsManager::SendStartRecordingCommand(const FMessageAddress& InDestinationAddress, const FChaosVDStartRecordingCommandMessage& RecordingStartCommandParams)
{
if (!MessageEndpoint)
{
UE_LOG(LogChaosVDRemoteSession, Error, TEXT("[%hs] Failed to send command | Invalid endpoint."), __func__);
return;
}
MessageEndpoint->Send(
FMessageEndpoint::MakeMessage<FChaosVDStartRecordingCommandMessage>(RecordingStartCommandParams),
FChaosVDStartRecordingCommandMessage::StaticStruct(),
EMessageFlags::Reliable,
nullptr,
TArrayBuilder<FMessageAddress>().Add(InDestinationAddress),
FTimespan::Zero(),
FDateTime::MaxValue());
}
void FChaosVDRemoteSessionsManager::SendStopRecordingCommand(const FMessageAddress& InDestinationAddress)
{
if (!MessageEndpoint)
{
UE_LOG(LogChaosVDRemoteSession, Error, TEXT("[%s] Failed to send command | Invalid endpoint."), ANSI_TO_TCHAR(__FUNCTION__));
return;
}
MessageEndpoint->Send(
FMessageEndpoint::MakeMessage<FChaosVDStopRecordingCommandMessage>(),
FChaosVDStopRecordingCommandMessage::StaticStruct(),
EMessageFlags::Reliable,
nullptr,
TArrayBuilder<FMessageAddress>().Add(InDestinationAddress),
FTimespan::Zero(),
FDateTime::MaxValue());
}
void FChaosVDRemoteSessionsManager::SendDataChannelStateChangeCommand(const FMessageAddress& InDestinationAddress,const FChaosVDChannelStateChangeCommandMessage& InNewStateData)
{
if (!MessageEndpoint)
{
UE_LOG(LogChaosVDRemoteSession, Error, TEXT("[%s] Failed to send command | Invalid endpoint."), ANSI_TO_TCHAR(__FUNCTION__));
return;
}
MessageEndpoint->Send(
FMessageEndpoint::MakeMessage<FChaosVDChannelStateChangeCommandMessage>(InNewStateData),
FChaosVDChannelStateChangeCommandMessage::StaticStruct(),
EMessageFlags::Reliable,
nullptr,
TArrayBuilder<FMessageAddress>().Add(InDestinationAddress),
FTimespan::Zero(),
FDateTime::MaxValue());
}
void FChaosVDRemoteSessionsManager::SendFullSessionStateRequestCommand(const FMessageAddress& InDestinationAddress)
{
if (!MessageEndpoint)
{
UE_LOG(LogChaosVDRemoteSession, Error, TEXT("[%s] Failed to send command | Invalid endpoint."), ANSI_TO_TCHAR(__FUNCTION__));
return;
}
MessageEndpoint->Send(
FMessageEndpoint::MakeMessage<FChaosVDFullSessionInfoRequestMessage>(),
FChaosVDFullSessionInfoRequestMessage::StaticStruct(),
EMessageFlags::Reliable,
nullptr,
TArrayBuilder<FMessageAddress>().Add(InDestinationAddress),
FTimespan::Zero(),
FDateTime::MaxValue());
}
bool FChaosVDRemoteSessionsManager::Tick(float DeltaTime)
{
if (IsController())
{
SendPing();
RemoveExpiredSessions();
}
return true;
}
void FChaosVDRemoteSessionsManager::SendPing()
{
if (ensure(MessageEndpoint))
{
if (FChaosVDSessionPing* SessionPingData = FMessageEndpoint::MakeMessage<FChaosVDSessionPing>())
{
SessionPingData->ControllerInstanceId = FApp::GetInstanceId();
MessageEndpoint->Publish(SessionPingData, EMessageScope::Network);
}
}
}
void FChaosVDRemoteSessionsManager::SendPong(const FChaosVDSessionPing& InMessage)
{
if (!ensure(MessageEndpoint))
{
return;
}
if (FChaosVDSessionPong* PongMessage = FMessageEndpoint::MakeMessage<FChaosVDSessionPong>())
{
PongMessage->InstanceId = FApp::GetInstanceId();
PongMessage->SessionId = FApp::GetSessionId();
PongMessage->BuildTargetType = static_cast<uint8>(FApp::GetBuildTargetType());
if (InMessage.ControllerInstanceId == PongMessage->InstanceId)
{
PongMessage->SessionName = LocalEditorSessionName;
}
else
{
FString AppSessionName = FApp::GetSessionName();
PongMessage->SessionName = AppSessionName == TEXT("None") || AppSessionName.IsEmpty() ? FString::Format(TEXT("{0} {1} {2}"), { FApp::GetProjectName(), FString(LexToString(FApp::GetBuildTargetType())), FString::FromInt(FPlatformProcess::GetCurrentProcessId()) }) : AppSessionName;
}
MessageEndpoint->Publish(PongMessage, EMessageScope::Network);
}
}
void FChaosVDRemoteSessionsManager::RegisterSessionInMultiSessionWrapper(const TSharedRef<FChaosVDSessionInfo>& InSessionInfoRef)
{
if (InSessionInfoRef->SessionName != LocalEditorSessionName )
{
StaticCastSharedPtr<FChaosVDMultiSessionInfo>(ActiveSessionsByInstanceId.FindChecked(AllRemoteSessionsWrapperGUID))->InnerSessionsByInstanceID.Emplace(InSessionInfoRef->InstanceId, InSessionInfoRef);
if (InSessionInfoRef->BuildTargetType == EBuildTargetType::Server)
{
StaticCastSharedPtr<FChaosVDMultiSessionInfo>(ActiveSessionsByInstanceId.FindChecked(AllRemoteServersWrapperGUID))->InnerSessionsByInstanceID.Emplace(InSessionInfoRef->InstanceId, InSessionInfoRef);
}
else
{
StaticCastSharedPtr<FChaosVDMultiSessionInfo>(ActiveSessionsByInstanceId.FindChecked(AllRemoteClientsWrapperGUID))->InnerSessionsByInstanceID.Emplace(InSessionInfoRef->InstanceId, InSessionInfoRef);
}
}
StaticCastSharedPtr<FChaosVDMultiSessionInfo>(ActiveSessionsByInstanceId.FindChecked(AllSessionsWrapperGUID))->InnerSessionsByInstanceID.Emplace(InSessionInfoRef->InstanceId, InSessionInfoRef);
}
void FChaosVDRemoteSessionsManager::DeRegisterSessionInMultiSessionWrapper(const TSharedRef<FChaosVDSessionInfo>& InSessionInfoRef)
{
StaticCastSharedPtr<FChaosVDMultiSessionInfo>(ActiveSessionsByInstanceId.FindChecked(AllRemoteSessionsWrapperGUID))->InnerSessionsByInstanceID.Remove(InSessionInfoRef->InstanceId);
StaticCastSharedPtr<FChaosVDMultiSessionInfo>(ActiveSessionsByInstanceId.FindChecked(AllSessionsWrapperGUID))->InnerSessionsByInstanceID.Remove(InSessionInfoRef->InstanceId);
StaticCastSharedPtr<FChaosVDMultiSessionInfo>(ActiveSessionsByInstanceId.FindChecked(AllRemoteServersWrapperGUID))->InnerSessionsByInstanceID.Remove(InSessionInfoRef->InstanceId);
StaticCastSharedPtr<FChaosVDMultiSessionInfo>(ActiveSessionsByInstanceId.FindChecked(AllRemoteClientsWrapperGUID))->InnerSessionsByInstanceID.Remove(InSessionInfoRef->InstanceId);
}
void FChaosVDRemoteSessionsManager::ProcessPendingMessagesForSession(const FChaosVDSessionPong& InMessage, const TSharedRef<FChaosVDSessionInfo>& InSessionInfoPtr)
{
PendingRecordingStatusMessages.RemoveAndCopyValue(InMessage.InstanceId, InSessionInfoPtr->LastKnownRecordingState);
PendingRecordingConnectionDetailsMessages.RemoveAndCopyValue(InMessage.InstanceId, InSessionInfoPtr->LastKnownConnectionDetails);
}
void FChaosVDRemoteSessionsManager::HandleSessionPongMessage(const FChaosVDSessionPong& InMessage, const TSharedRef<IMessageContext>& InContext)
{
TSharedPtr<FChaosVDSessionInfo>& SessionInfoPtr = ActiveSessionsByInstanceId.FindOrAdd(InMessage.InstanceId);
if (!SessionInfoPtr)
{
SessionInfoPtr = MakeShared<FChaosVDSessionInfo>();
SessionInfoPtr->Address = InContext->GetSender();
SessionInfoPtr->InstanceId = InMessage.InstanceId;
SessionInfoPtr->SessionName = InMessage.SessionName;
SessionInfoPtr->BuildTargetType = static_cast<EBuildTargetType>(InMessage.BuildTargetType);
RegisterSessionInMultiSessionWrapper(SessionInfoPtr.ToSharedRef());
SessionDiscoveredDelegate.Broadcast(SessionInfoPtr->InstanceId);
// This is the first time we see this session, so we need to request the rest of its state so we can properly populate the UI
SendFullSessionStateRequestCommand(SessionInfoPtr->Address);
}
SessionInfoPtr->LastPingTime = FDateTime::UtcNow();
ProcessPendingMessagesForSession(InMessage, SessionInfoPtr.ToSharedRef());
SessionsUpdatedDelegate.Broadcast();
}
void FChaosVDRemoteSessionsManager::HandleSessionPingMessage(const FChaosVDSessionPing& InMessage, const TSharedRef<IMessageContext>& InContext)
{
// If this instance is not running trace, don't answer to the ping as the CVD instance will not be able to do anything useful with it
#if !CHAOS_VISUAL_DEBUGGER_WITHOUT_TRACE
SendPong(InMessage);
#endif
}
void FChaosVDRemoteSessionsManager::HandleRecordingStatusUpdateMessage(const FChaosVDRecordingStatusMessage& Message, const TSharedRef<IMessageContext>& InContext)
{
if (TSharedPtr<FChaosVDSessionInfo>* SessionInfoPtrPtr = ActiveSessionsByInstanceId.Find(Message.InstanceId))
{
TSharedPtr<FChaosVDSessionInfo>& SessionInfoPtr = *SessionInfoPtrPtr;
check(SessionInfoPtr);
if (SessionInfoPtr->LastKnownRecordingState.bIsRecording != Message.bIsRecording)
{
if (Message.bIsRecording)
{
RecordingStartedDelegate.Broadcast(SessionInfoPtr);
}
else
{
RecordingStoppedDelegate.Broadcast(SessionInfoPtr);
}
}
SessionInfoPtr->LastKnownRecordingState = Message;
}
else
{
PendingRecordingStatusMessages.FindOrAdd(Message.InstanceId) = Message;
}
}
void FChaosVDRemoteSessionsManager::HandleConnectionDetailsUpdateMessage(const FChaosVDTraceConnectionDetailsMessage& InMessage, const TSharedRef<IMessageContext>& InContext)
{
if (TSharedPtr<FChaosVDSessionInfo>* SessionInfoPtrPtr = ActiveSessionsByInstanceId.Find(InMessage.InstanceId))
{
TSharedPtr<FChaosVDSessionInfo>& SessionInfoPtr = *SessionInfoPtrPtr;
check(SessionInfoPtr);
SessionInfoPtr->LastKnownConnectionDetails = InMessage.TraceDetails;
}
else
{
PendingRecordingConnectionDetailsMessages.FindOrAdd(InMessage.InstanceId) = InMessage.TraceDetails;
}
}
void FChaosVDRemoteSessionsManager::HandleRecordingStartCommandMessage(const FChaosVDStartRecordingCommandMessage& InMessage, const TSharedRef<IMessageContext>& InContext)
{
#if WITH_CHAOS_VISUAL_DEBUGGER
UE_AUTORTFM_ONCOMMIT(InMessage, InContext)
{
FChaosVisualDebuggerTrace::OverrideDefaultEnabledDataChannels(InMessage.DataChannelsEnabledOverrideList);
FChaosVDRuntimeModule::Get().StartRecording(InMessage);
};
#endif
}
void FChaosVDRemoteSessionsManager::HandleRecordingStopCommandMessage(const FChaosVDStopRecordingCommandMessage& InMessage, const TSharedRef<IMessageContext>& InContext)
{
#if WITH_CHAOS_VISUAL_DEBUGGER
UE_AUTORTFM_ONCOMMIT()
{
FChaosVDRuntimeModule::Get().StopRecording();
};
#endif
}
void FChaosVDRemoteSessionsManager::HandleChangeDataChannelStateCommandMessage(const FChaosVDChannelStateChangeCommandMessage& InMessage, const TSharedRef<IMessageContext>& InContext)
{
#if WITH_CHAOS_VISUAL_DEBUGGER
UE_AUTORTFM_ONCOMMIT(InMessage)
{
using namespace Chaos::VisualDebugger;
if (TSharedPtr<FChaosVDOptionalDataChannel> ChannelInstance = FChaosVDDataChannelsManager::Get().GetChannelById(FName(InMessage.NewState.ChannelName)))
{
ChannelInstance->SetChannelEnabled(InMessage.NewState.bIsEnabled);
}
};
#endif
}
void FChaosVDRemoteSessionsManager::HandleChangeDataChannelStateResponseMessage(const FChaosVDChannelStateChangeResponseMessage& InMessage, const TSharedRef<IMessageContext>& InContext)
{
if (TSharedPtr<FChaosVDSessionInfo>* SessionInfoPtrPtr = ActiveSessionsByInstanceId.Find(InMessage.InstanceID))
{
if (TSharedPtr<FChaosVDSessionInfo>& SessionInfoPtr = *SessionInfoPtrPtr)
{
if (FChaosVDDataChannelState* FoundChannelState = SessionInfoPtr->DataChannelsStatesByName.Find(InMessage.NewState.ChannelName))
{
*FoundChannelState = InMessage.NewState;
}
}
}
}
void FChaosVDRemoteSessionsManager::HandleFullSessionStateRequestMessage(const FChaosVDFullSessionInfoRequestMessage& InMessage, const TSharedRef<IMessageContext>& InContext)
{
if (!ensure(MessageEndpoint))
{
return;
}
if (FChaosVDFullSessionInfoResponseMessage* FullSessionStateResponse = FMessageEndpoint::MakeMessage<FChaosVDFullSessionInfoResponseMessage>())
{
FullSessionStateResponse->InstanceId = FApp::GetInstanceId();
#if WITH_CHAOS_VISUAL_DEBUGGER
FullSessionStateResponse->bIsRecording = FChaosVDRuntimeModule::Get().IsRecording();
using namespace Chaos::VisualDebugger;
FChaosVDDataChannelsManager::Get().EnumerateChannels([&FullSessionStateResponse](const TSharedRef<FChaosVDOptionalDataChannel>& Channel)
{
FChaosVDChannelStateChangeCommandMessage DataChannelStateMessage = {Channel->GetId().ToString(), Channel->IsChannelEnabled(), Channel->CanChangeEnabledState() };
FullSessionStateResponse->DataChannelsStates.Emplace(DataChannelStateMessage.NewState);
return true;
});
#endif
MessageEndpoint->Send(
FullSessionStateResponse,
FChaosVDFullSessionInfoResponseMessage::StaticStruct(),
EMessageFlags::Reliable,
nullptr,
TArrayBuilder<FMessageAddress>().Add(InContext->GetSender()),
FTimespan::Zero(),
FDateTime::MaxValue());
}
}
void FChaosVDRemoteSessionsManager::HandleFullSessionStateResponseMessage(const FChaosVDFullSessionInfoResponseMessage& InMessage, const TSharedRef<IMessageContext>& InContext)
{
if (TSharedPtr<FChaosVDSessionInfo>* SessionInfoPtrPtr = ActiveSessionsByInstanceId.Find(InMessage.InstanceId))
{
if (TSharedPtr<FChaosVDSessionInfo>& SessionInfoPtr = *SessionInfoPtrPtr)
{
SessionInfoPtr->LastKnownRecordingState.bIsRecording = InMessage.bIsRecording;
for (const FChaosVDDataChannelState& ChannelState : InMessage.DataChannelsStates)
{
SessionInfoPtr->DataChannelsStatesByName.Emplace(ChannelState.ChannelName, ChannelState);
}
}
}
}
void FChaosVDRemoteSessionsManager::RemoveExpiredSessions(ERemoveSessionOptions Options)
{
bool bAnySessionRemoved = false;
FDateTime CurrentTime = FDateTime::UtcNow();
for (TMap<FGuid, TSharedPtr<FChaosVDSessionInfo>>::TIterator RemoveIterator = ActiveSessionsByInstanceId.CreateIterator(); RemoveIterator; ++RemoveIterator)
{
TSharedPtr<FChaosVDSessionInfo>& SessionInfoPtr = RemoveIterator.Value();
if (!SessionInfoPtr)
{
RemoveIterator.RemoveCurrent();
bAnySessionRemoved = true;
continue;
}
if (!EnumHasAnyFlags(SessionInfoPtr->GetSessionTypeAttributes(), EChaosVDRemoteSessionAttributes::CanExpire))
{
continue;
}
FTimespan ElapsedTime = CurrentTime - SessionInfoPtr->LastPingTime;
// A session goes into busy state if we are attempting to issue a command that might stall the target, currently that only happens on recording start commands of complex maps
// In these cases, we need to allow more time between pings. If a recording command failed, it is expected the state to be changed to Ready again
const float MaxAllowedTimeBetweenPings = SessionInfoPtr->ReadyState == EChaosVDRemoteSessionReadyState::Busy || SessionInfoPtr->IsRecording() ? 60.0f : 3.0f;
if (EnumHasAnyFlags(Options, ERemoveSessionOptions::ForceRemoveAll) || ElapsedTime > FTimespan::FromSeconds(MaxAllowedTimeBetweenPings))
{
SessionExpiredDelegate.Broadcast(SessionInfoPtr->InstanceId);
DeRegisterSessionInMultiSessionWrapper(SessionInfoPtr.ToSharedRef());
RemoveIterator.RemoveCurrent();
bAnySessionRemoved = true;
}
}
if (bAnySessionRemoved)
{
SessionsUpdatedDelegate.Broadcast();
}
}