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

366 lines
8.2 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SessionManager.h"
#include "HAL/PlatformProcess.h"
#include "Misc/CommandLine.h"
#include "Containers/Ticker.h"
#include "EngineServiceMessages.h"
#include "MessageEndpointBuilder.h"
#include "SessionServiceMessages.h"
#include "SessionInfo.h"
/** Defines the interval in seconds in which devices are being pinged by the proxy manager. */
#define SESSION_MANAGER_PING_INTERVAL 5.0f
/* FSessionManager structors
*****************************************************************************/
FSessionManager::FSessionManager(const TSharedRef<IMessageBus, ESPMode::ThreadSafe>& InMessageBus)
: MessageBusPtr(InMessageBus)
{
// fill in the owner array
FString Filter;
if (FParse::Value(FCommandLine::Get(), TEXT("SessionFilter="), Filter))
{
// Allow support for -SessionFilter=Filter1+Filter2+Filter3
int32 PlusIdx = Filter.Find(TEXT("+"), ESearchCase::CaseSensitive);
while (PlusIdx != INDEX_NONE)
{
FString Owner = Filter.Left(PlusIdx);
FilteredOwners.Add(Owner);
Filter.RightInline(Filter.Len() - (PlusIdx + 1), EAllowShrinking::No);
PlusIdx = Filter.Find(TEXT("+"), ESearchCase::CaseSensitive);
}
FilteredOwners.Add(Filter);
}
// connect to message bus
MessageEndpoint = FMessageEndpoint::Builder("FSessionManager", InMessageBus)
.Handling<FEngineServicePong>(this, &FSessionManager::HandleEnginePongMessage)
.Handling<FSessionServicePong>(this, &FSessionManager::HandleSessionPongMessage);
// initialize ticker
TickDelegateHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateRaw(this, &FSessionManager::HandleTicker), SESSION_MANAGER_PING_INTERVAL);
SendPing();
}
FSessionManager::~FSessionManager()
{
FTSTicker::GetCoreTicker().RemoveTicker(TickDelegateHandle);
FMessageEndpoint::SafeRelease(MessageEndpoint);
}
/* ISessionManager interface
*****************************************************************************/
void FSessionManager::AddOwner(const FString& InOwner)
{
FilteredOwners.Add(InOwner);
}
const TArray<TSharedPtr<ISessionInstanceInfo>>& FSessionManager::GetSelectedInstances() const
{
return SelectedInstances;
}
const TSharedPtr<ISessionInfo>& FSessionManager::GetSelectedSession() const
{
return SelectedSession;
}
void FSessionManager::GetSessions(TArray<TSharedPtr<ISessionInfo>>& OutSessions) const
{
OutSessions.Empty(Sessions.Num());
for (TMap<FGuid, TSharedPtr<FSessionInfo> >::TConstIterator It(Sessions); It; ++It)
{
OutSessions.Add(It.Value());
}
}
bool FSessionManager::IsInstanceSelected(const TSharedRef<ISessionInstanceInfo>& Instance) const
{
return ((Instance->GetOwnerSession() == SelectedSession) && SelectedInstances.Contains(Instance));
}
FSimpleMulticastDelegate& FSessionManager::OnSessionsUpdated()
{
return SessionsUpdatedDelegate;
}
FSimpleMulticastDelegate& FSessionManager::OnSessionInstanceUpdated()
{
return SessionInstanceUpdatedDelegate;
}
void FSessionManager::RemoveOwner(const FString& InOwner)
{
FilteredOwners.Remove(InOwner);
RefreshSessions();
}
bool FSessionManager::SelectSession(const TSharedPtr<ISessionInfo>& Session)
{
// already selected?
if (Session == SelectedSession)
{
return true;
}
// do we own the session?
if (Session.IsValid() && !Sessions.Contains(Session->GetSessionId()))
{
return false;
}
// allowed to de/select?
bool CanSelect = true;
CanSelectSessionDelegate.Broadcast(Session, CanSelect);
if (!CanSelect)
{
return false;
}
// Notify listeners that previously selected sessions have been deselected.
for (TSharedPtr<ISessionInstanceInfo> Instance : SelectedInstances)
{
InstanceSelectionChangedDelegate.Broadcast(Instance, false);
}
// set selection
SelectedInstances.Empty();
SelectedSession = Session;
SelectedSessionChangedEvent.Broadcast(Session);
return true;
}
bool FSessionManager::SetInstanceSelected(const TSharedRef<ISessionInstanceInfo>& Instance, bool Selected)
{
if (Instance->GetOwnerSession() != SelectedSession)
{
return false;
}
if (Selected)
{
if (!SelectedInstances.Contains(Instance))
{
SelectedInstances.Add(Instance);
InstanceSelectionChangedDelegate.Broadcast(Instance, true);
}
}
else
{
if (SelectedInstances.Remove(Instance) > 0)
{
InstanceSelectionChangedDelegate.Broadcast(Instance, false);
}
}
return true;
}
/* FSessionManager implementation
*****************************************************************************/
void FSessionManager::FindExpiredSessions(const FDateTime& Now)
{
bool Dirty = false;
for (TMap<FGuid, TSharedPtr<FSessionInfo> >::TIterator It(Sessions); It; ++It)
{
if (Now > It.Value()->GetLastUpdateTime() + FTimespan::FromSeconds(10.0))
{
It.RemoveCurrent();
Dirty = true;
}
}
if (Dirty)
{
SessionsUpdatedDelegate.Broadcast();
}
}
bool FSessionManager::IsValidOwner(const FString& Owner)
{
if (Owner == FPlatformProcess::UserName(false))
{
return true;
}
for (const auto& FilteredOwner : FilteredOwners)
{
if (FilteredOwner == Owner)
{
return true;
}
}
return false;
}
void FSessionManager::RefreshSessions()
{
bool Dirty = false;
for (TMap<FGuid, TSharedPtr<FSessionInfo> >::TIterator It(Sessions); It; ++It)
{
if (!IsValidOwner(It.Value()->GetSessionOwner()))
{
It.RemoveCurrent();
Dirty = true;
}
}
if (Dirty)
{
SessionsUpdatedDelegate.Broadcast();
}
}
void FSessionManager::SendPing()
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_FSessionmanager_SendPing);
if (MessageEndpoint.IsValid())
{
MessageEndpoint->Publish(FMessageEndpoint::MakeMessage<FEngineServicePing>(), EMessageScope::Network);
MessageEndpoint->Publish(FMessageEndpoint::MakeMessage<FSessionServicePing>(FPlatformProcess::UserName(false)), EMessageScope::Network);
}
LastPingTime = FDateTime::UtcNow();
}
/* FSessionManager callbacks
*****************************************************************************/
void FSessionManager::HandleEnginePongMessage(const FEngineServicePong& Message, const TSharedRef<IMessageContext, ESPMode::ThreadSafe>& Context)
{
if (!Message.SessionId.IsValid())
{
return;
}
// update instance
TSharedPtr<FSessionInfo> Session = Sessions.FindRef(Message.SessionId);
if (Session.IsValid())
{
Session->UpdateFromMessage(Message, Context);
SessionInstanceUpdatedDelegate.Broadcast();
}
}
void FSessionManager::HandleLogReceived(const TSharedRef<ISessionInfo>& Session, const TSharedRef<ISessionInstanceInfo>& Instance, const TSharedRef<FSessionLogMessage>& Message)
{
if (Session == SelectedSession)
{
LogReceivedEvent.Broadcast(Session, Instance, Message);
}
}
void FSessionManager::HandleSessionPongMessage(const FSessionServicePong& Message, const TSharedRef<IMessageContext, ESPMode::ThreadSafe>& Context)
{
if (!Message.SessionId.IsValid())
{
return;
}
if (!Message.Standalone && !IsValidOwner(Message.SessionOwner))
{
return;
}
auto MessageBus = MessageBusPtr.Pin();
if (!MessageBus.IsValid())
{
return;
}
// update session
TSharedPtr<FSessionInfo>& Session = Sessions.FindOrAdd(Message.SessionId);
if (Session.IsValid())
{
if (Session->GetSessionOwner() != Message.SessionOwner)
{
Session->UpdateFromMessage(Message, Context);
SessionsUpdatedDelegate.Broadcast();
}
else
{
Session->UpdateFromMessage(Message, Context);
}
}
else
{
Session = MakeShareable(new FSessionInfo(Message.SessionId, MessageBus.ToSharedRef()));
Session->OnLogReceived().AddSP(this, &FSessionManager::HandleLogReceived);
Session->UpdateFromMessage(Message, Context);
SessionsUpdatedDelegate.Broadcast();
}
}
bool FSessionManager::HandleTicker(float DeltaTime)
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_FSessionmanager_HandleTicker);
FDateTime Now = FDateTime::UtcNow();
// @todo gmp: don't expire sessions for now
// FindExpiredSessions(Now);
if (Now >= LastPingTime + FTimespan::FromSeconds(SESSION_MANAGER_PING_INTERVAL))
{
SendPing();
}
return true;
}
TSharedPtr<ISessionInstanceInfo> FSessionManager::GetInstance(const FGuid& Id) const
{
for (const auto& SessionPair : Sessions)
{
const auto& Session = SessionPair.Value;
TArray<TSharedPtr<ISessionInstanceInfo>> Instances;
Session->GetInstances(Instances);
for (const auto& Instance : Instances)
{
if (Instance->GetInstanceId() == Id)
{
return Instance;
}
}
}
return nullptr;
}