Files
UnrealEngine/Engine/Plugins/Online/OnlineSubsystemSteam/Source/Private/OnlineSessionInterfaceSteam.cpp
2025-05-18 13:04:45 +08:00

1982 lines
63 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "OnlineSessionInterfaceSteam.h"
#include "Misc/CommandLine.h"
#include "Online/OnlineBase.h"
#include "UObject/CoreNet.h"
#include "Online/OnlineSessionNames.h"
#include "SocketSubsystem.h"
#include "OnlineSessionAsyncLobbySteam.h"
#include "OnlineSessionAsyncServerSteam.h"
#include "OnlineLeaderboardInterfaceSteam.h"
#include "OnlineAuthInterfaceSteam.h"
#include "Online/LANBeacon.h"
#include "NboSerializerSteam.h"
#include "Interfaces/VoiceInterface.h"
#include <steam/steamuniverse.h>
#if WITH_ENGINE
#include "Engine/EngineBaseTypes.h"
#endif
/** Constructor for non-lobby sessions */
FOnlineSessionInfoSteam::FOnlineSessionInfoSteam(ESteamSession::Type InSessionType) :
SessionType(InSessionType),
HostAddr(nullptr),
SteamP2PAddr(nullptr),
SessionId(FUniqueNetIdSteam::Create((uint64)0)),
ConnectionMethod((InSessionType == ESteamSession::LANSession) ? FSteamConnectionMethod::Direct : FSteamConnectionMethod::None)
{
}
/** Constructor for sessions that represent a Steam lobby or server */
FOnlineSessionInfoSteam::FOnlineSessionInfoSteam(ESteamSession::Type InSessionType, const FUniqueNetIdSteam& InSessionId) :
SessionType(InSessionType),
HostAddr(nullptr),
SteamP2PAddr(nullptr),
SessionId(InSessionId.AsShared()),
ConnectionMethod(FSteamConnectionMethod::None)
{
}
void FOnlineSessionInfoSteam::Init()
{
}
void FOnlineSessionInfoSteam::InitLAN()
{
#if WITH_ENGINE
SessionType = ESteamSession::LANSession;
ConnectionMethod = FSteamConnectionMethod::Direct;
uint64 Nonce = 0;
GenerateNonce((uint8*)&Nonce, 8);
SessionId = FUniqueNetIdSteam::Create(Nonce);
// Read the IP from the system
bool bCanBindAll;
HostAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetLocalHostAddr(*GLog, bCanBindAll);
// Now set the port that was configured
HostAddr->SetPort(FURL::UrlConfig.DefaultPort);
Init();
#else //WITH_ENGINE
ensure(false); //Not supported on non-engine
#endif //WITH_ENGINE
}
/**
* Async task for ending a Steam online session
*/
class FOnlineAsyncTaskSteamEndSession : public FOnlineAsyncTaskSteam
{
private:
/** Name of session ending */
FName SessionName;
public:
FOnlineAsyncTaskSteamEndSession(FOnlineSubsystemSteam* InSubsystem, FName InSessionName) :
FOnlineAsyncTaskSteam(InSubsystem, k_uAPICallInvalid),
SessionName(InSessionName)
{
}
~FOnlineAsyncTaskSteamEndSession()
{
}
/**
* Get a human readable description of task
*/
virtual FString ToString() const override
{
return FString::Printf(TEXT("FOnlineAsyncTaskSteamEndSession bWasSuccessful: %d SessionName: %s"), WasSuccessful(), *SessionName.ToString());
}
/**
* Give the async task time to do its work
* Can only be called on the async task manager thread
*/
virtual void Tick() override
{
bIsComplete = true;
bWasSuccessful = true;
}
/**
* Give the async task a chance to marshal its data back to the game thread
* Can only be called on the game thread by the async task manager
*/
virtual void Finalize() override
{
IOnlineSessionPtr SessionInt = Subsystem->GetSessionInterface();
FNamedOnlineSession* Session = SessionInt->GetNamedSession(SessionName);
if (Session)
{
Session->SessionState = EOnlineSessionState::Ended;
}
}
/**
* Async task is given a chance to trigger it's delegates
*/
virtual void TriggerDelegates() override
{
IOnlineSessionPtr SessionInt = Subsystem->GetSessionInterface();
if (SessionInt.IsValid())
{
SessionInt->TriggerOnEndSessionCompleteDelegates(SessionName, bWasSuccessful);
}
}
};
/**
* Async task for destroying a Steam online session
*/
class FOnlineAsyncTaskSteamDestroySession : public FOnlineAsyncTaskSteam
{
private:
/** Name of session ending */
FName SessionName;
/** */
FOnDestroySessionCompleteDelegate CompletionDelegate;
public:
FOnlineAsyncTaskSteamDestroySession(FOnlineSubsystemSteam* InSubsystem, FName InSessionName, const FOnDestroySessionCompleteDelegate& InCompletionDelegate) :
FOnlineAsyncTaskSteam(InSubsystem, k_uAPICallInvalid),
SessionName(InSessionName),
CompletionDelegate(InCompletionDelegate)
{
}
~FOnlineAsyncTaskSteamDestroySession()
{
}
/**
* Get a human readable description of task
*/
virtual FString ToString() const override
{
return FString::Printf(TEXT("FOnlineAsyncTaskSteamDestroySession bWasSuccessful: %d SessionName: %s"), WasSuccessful(), *SessionName.ToString());
}
/**
* Give the async task time to do its work
* Can only be called on the async task manager thread
*/
virtual void Tick() override
{
bIsComplete = true;
bWasSuccessful = true;
}
/**
* Give the async task a chance to marshal its data back to the game thread
* Can only be called on the game thread by the async task manager
*/
virtual void Finalize() override
{
IOnlineSessionPtr SessionInt = Subsystem->GetSessionInterface();
if (SessionInt.IsValid())
{
FNamedOnlineSession* Session = SessionInt->GetNamedSession(SessionName);
if (Session)
{
SessionInt->RemoveNamedSession(SessionName);
if (SessionInt->GetNumSessions() == 0)
{
IOnlineVoicePtr VoiceInt = Subsystem->GetVoiceInterface();
if (VoiceInt.IsValid())
{
if (!Subsystem->IsDedicated())
{
// Stop local talkers
VoiceInt->UnregisterLocalTalkers();
}
// Stop remote voice
VoiceInt->RemoveAllRemoteTalkers();
}
FOnlineAuthSteamPtr AuthInt = Subsystem->GetAuthInterface();
if (AuthInt.IsValid())
{
AuthInt->RevokeAllTickets();
}
}
}
}
}
/**
* Async task is given a chance to trigger it's delegates
*/
virtual void TriggerDelegates() override
{
IOnlineSessionPtr SessionInt = Subsystem->GetSessionInterface();
if (SessionInt.IsValid())
{
CompletionDelegate.ExecuteIfBound(SessionName, bWasSuccessful);
SessionInt->TriggerOnDestroySessionCompleteDelegates(SessionName, bWasSuccessful);
}
}
};
bool FOnlineSessionSteam::CreateSession(int32 HostingPlayerNum, FName SessionName, const FOnlineSessionSettings& NewSessionSettings)
{
// In Steam, bUsesPresence and bUseLobbiesIfAvailable have equivalent meaning and should have the same value
if (NewSessionSettings.bUsesPresence != NewSessionSettings.bUseLobbiesIfAvailable)
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("[%hs] The values of FOnlineSessionSettings::bUsesPresence and FOnlineSessionSettings::bUseLobbiesIfAvailable are treated as equal and have to match"), __FUNCTION__);
TriggerOnCreateSessionCompleteDelegates(SessionName, false);
return false;
}
uint32 Result = ONLINE_FAIL;
// Check for an existing session
FNamedOnlineSession* Session = GetNamedSession(SessionName);
if (Session == nullptr)
{
// Create a new session and deep copy the game settings
Session = AddNamedSession(SessionName, NewSessionSettings);
check(Session);
Session->SessionState = EOnlineSessionState::Creating;
Session->NumOpenPrivateConnections = NewSessionSettings.NumPrivateConnections;
Session->NumOpenPublicConnections = NewSessionSettings.bIsDedicated ? NewSessionSettings.NumPublicConnections : NewSessionSettings.NumPublicConnections - 1;
Session->HostingPlayerNum = HostingPlayerNum;
Session->OwningUserId = SteamUser() ? FUniqueNetIdSteamPtr(FUniqueNetIdSteam::Create(SteamUser()->GetSteamID())) : nullptr;
Session->OwningUserName = SteamFriends() ? UTF8_TO_TCHAR(SteamFriends()->GetPersonaName()) : GetCustomDedicatedServerName();
// Unique identifier of this build for compatibility
Session->SessionSettings.BuildUniqueId = GetBuildUniqueId();
// Create Internet or LAN match
if (!NewSessionSettings.bIsLANMatch)
{
if (Session->SessionSettings.bUseLobbiesIfAvailable)
{
Result = CreateLobbySession(HostingPlayerNum, Session);
}
else
{
Result = CreateInternetSession(HostingPlayerNum, Session);
}
}
else
{
Result = CreateLANSession(HostingPlayerNum, Session);
}
if (Result != ONLINE_IO_PENDING)
{
// Set the game state as pending (not started)
Session->SessionState = EOnlineSessionState::Pending;
if (Result != ONLINE_SUCCESS)
{
// Clean up the session info so we don't get into a confused state
RemoveNamedSession(SessionName);
}
else
{
RegisterLocalPlayers(Session);
}
}
}
else
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("Cannot create session '%s': session already exists."), *SessionName.ToString());
}
if (Result != ONLINE_IO_PENDING)
{
TriggerOnCreateSessionCompleteDelegates(SessionName, (Result == ONLINE_SUCCESS) ? true : false);
}
return Result == ONLINE_IO_PENDING || Result == ONLINE_SUCCESS;
}
bool FOnlineSessionSteam::CreateSession(const FUniqueNetId& HostingPlayerId, FName SessionName, const FOnlineSessionSettings& NewSessionSettings)
{
// @todo: use proper HostingPlayerId
return CreateSession(0, SessionName, NewSessionSettings);
}
uint32 FOnlineSessionSteam::CreateLobbySession(int32 HostingPlayerNum, FNamedOnlineSession* Session)
{
uint32 Result = ONLINE_FAIL;
if (Session)
{
/** Max lobby size is sum of private/public (@TODO ONLINE - not sure we can differentiate) */
int32 MaxLobbySize = Session->SessionSettings.NumPrivateConnections + Session->SessionSettings.NumPublicConnections;
/** Generate the proper lobby type from our session settings */
ELobbyType SteamLobbyType = BuildLobbyType(&Session->SessionSettings);
FOnlineAsyncTaskSteamCreateLobby* NewTask = new FOnlineAsyncTaskSteamCreateLobby(SteamSubsystem, Session->SessionName, SteamLobbyType, MaxLobbySize);
SteamSubsystem->QueueAsyncTask(NewTask);
Result = ONLINE_IO_PENDING;
}
else
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("CreateLobbySession: NULL Session"));
}
return Result;
}
uint32 FOnlineSessionSteam::CreateInternetSession(int32 HostingPlayerNum, FNamedOnlineSession* Session)
{
uint32 Result = ONLINE_FAIL;
// Only allowed one published session with Steam
FNamedOnlineSession* ListedSession = GetGameServerSession();
if (ListedSession == nullptr)
{
if (SteamSubsystem->IsSteamServerAvailable())
{
// Reset the policy response
bPolicyResponseReceived = false;
FOnlineAsyncTaskSteamCreateServer* NewTask = new FOnlineAsyncTaskSteamCreateServer(SteamSubsystem, Session->SessionName);
SteamSubsystem->QueueAsyncTask(NewTask);
Result = ONLINE_IO_PENDING;
}
else
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("Failed to initialize game server with Steam!"));
}
}
else
{
UE_LOG_ONLINE_SESSION(Verbose, TEXT("Advertised session %s already exists, unable to create another."), *Session->SessionName.ToString());
}
return Result;
}
uint32 FOnlineSessionSteam::CreateLANSession(int32 HostingPlayerNum, FNamedOnlineSession* Session)
{
check(Session);
uint32 Result = ONLINE_SUCCESS;
// Setup the host session info
FOnlineSessionInfoSteam* NewSessionInfo = new FOnlineSessionInfoSteam(ESteamSession::LANSession);
NewSessionInfo->InitLAN();
if (!Session->OwningUserId.IsValid())
{
// Use the lan user id, requires us to advertise the host ip and port
Session->OwningUserId = FUniqueNetIdSteam::Create(k_steamIDLanModeGS);
}
Session->SessionInfo = MakeShareable(NewSessionInfo);
// Don't create the LAN beacon if advertising is off
if (Session->SessionSettings.bShouldAdvertise)
{
if (!LANSession)
{
LANSession = new FLANSession();
}
FOnValidQueryPacketDelegate QueryPacketDelegate = FOnValidQueryPacketDelegate::CreateRaw(this, &FOnlineSessionSteam::OnValidQueryPacketReceived);
if (!LANSession->Host(QueryPacketDelegate))
{
Result = ONLINE_FAIL;
}
}
return Result;
}
bool FOnlineSessionSteam::StartSession(FName SessionName)
{
uint32 Result = ONLINE_FAIL;
// Grab the session information by name
FNamedOnlineSession* Session = GetNamedSession(SessionName);
if (Session)
{
// Can't start a match multiple times
if (Session->SessionState == EOnlineSessionState::Pending ||
Session->SessionState == EOnlineSessionState::Ended)
{
if (!Session->SessionSettings.bIsLANMatch)
{
Result = ONLINE_SUCCESS;
Session->SessionState = EOnlineSessionState::InProgress;
if (SteamFriends() != nullptr)
{
for (int32 PlayerIdx=0; PlayerIdx < Session->RegisteredPlayers.Num(); PlayerIdx++)
{
FUniqueNetIdSteam& Player = (FUniqueNetIdSteam&)Session->RegisteredPlayers[PlayerIdx].Get();
SteamFriends()->SetPlayedWith(Player);
}
}
}
else
{
// If this lan match has join in progress disabled, shut down the beacon
if (!Session->SessionSettings.bAllowJoinInProgress)
{
LANSession->StopLANSession();
}
Result = ONLINE_SUCCESS;
Session->SessionState = EOnlineSessionState::InProgress;
}
}
else
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("Can't start an online session (%s) in state %s"),
*SessionName.ToString(),
EOnlineSessionState::ToString(Session->SessionState));
}
}
else
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("Can't start an online game for session (%s) that hasn't been created"), *SessionName.ToString());
}
if (Result != ONLINE_IO_PENDING)
{
// Just trigger the delegate
TriggerOnStartSessionCompleteDelegates(SessionName, (Result == ONLINE_SUCCESS) ? true : false);
}
return Result == ONLINE_SUCCESS || Result == ONLINE_IO_PENDING;
}
bool FOnlineSessionSteam::UpdateSession(FName SessionName, FOnlineSessionSettings& UpdatedSessionSettings, bool bShouldRefreshOnlineData)
{
bool bWasSuccessful = true;
// Grab the session information by name
FNamedOnlineSession* Session = GetNamedSession(SessionName);
if (Session)
{
if (!Session->SessionSettings.bIsLANMatch)
{
FOnlineSessionInfoSteam* SessionInfo = (FOnlineSessionInfoSteam*)(Session->SessionInfo.Get());
if (SessionInfo)
{
if (SessionInfo->SessionType == ESteamSession::LobbySession && SessionInfo->SessionId->IsValid())
{
// Lobby update
FOnlineAsyncTaskSteamUpdateLobby* NewTask = new FOnlineAsyncTaskSteamUpdateLobby(SteamSubsystem, SessionName, bShouldRefreshOnlineData, UpdatedSessionSettings);
SteamSubsystem->QueueAsyncTask(NewTask);
}
else if (SessionInfo->SessionType == ESteamSession::AdvertisedSessionHost)
{
// Gameserver update
FOnlineAsyncTaskSteamUpdateServer* NewTask = new FOnlineAsyncTaskSteamUpdateServer(SteamSubsystem, SessionName, bShouldRefreshOnlineData, UpdatedSessionSettings);
SteamSubsystem->QueueAsyncTask(NewTask);
}
}
else
{
bWasSuccessful = false;
}
}
else
{
// @TODO ONLINE update LAN settings
Session->SessionSettings = UpdatedSessionSettings;
TriggerOnUpdateSessionCompleteDelegates(SessionName, bWasSuccessful);
}
}
return bWasSuccessful;
}
bool FOnlineSessionSteam::EndSession(FName SessionName)
{
uint32 Result = ONLINE_FAIL;
// Grab the session information by name
FNamedOnlineSession* Session = GetNamedSession(SessionName);
if (Session)
{
// Can't end a match that isn't in progress
if (Session->SessionState == EOnlineSessionState::InProgress)
{
if (!Session->SessionSettings.bIsLANMatch)
{
Result = EndInternetSession(Session);
}
else
{
// If the session should be advertised and the lan beacon was destroyed, recreate
if (Session->SessionSettings.bShouldAdvertise &&
LANSession->LanBeacon == nullptr &&
SteamSubsystem->IsServer())
{
// Recreate the beacon
Result = CreateLANSession(Session->HostingPlayerNum, Session);
}
else
{
Result = ONLINE_SUCCESS;
}
}
}
else
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("Can't end session (%s) in state %s"),
*SessionName.ToString(),
EOnlineSessionState::ToString(Session->SessionState));
}
}
else
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("Can't end an online game for session (%s) that hasn't been created"),
*SessionName.ToString());
}
if (Result != ONLINE_IO_PENDING)
{
if (Session)
{
Session->SessionState = EOnlineSessionState::Ended;
}
TriggerOnEndSessionCompleteDelegates(SessionName, (Result == ONLINE_SUCCESS) ? true : false);
}
return Result == ONLINE_SUCCESS || Result == ONLINE_IO_PENDING;
}
uint32 FOnlineSessionSteam::EndInternetSession(FNamedOnlineSession* Session)
{
// Only called from EndSession/DestroySession and presumes only in InProgress state
check(Session && Session->SessionState == EOnlineSessionState::InProgress);
// Enqueue a flush leaderboard on async task list
FOnlineLeaderboardsSteamPtr Leaderboards = StaticCastSharedPtr<FOnlineLeaderboardsSteam>(SteamSubsystem->GetLeaderboardsInterface());
if (Leaderboards.IsValid())
{
Leaderboards->FlushLeaderboards(Session->SessionName);
}
Session->SessionState = EOnlineSessionState::Ending;
// Guaranteed to be called after the flush is complete
FOnlineAsyncTaskSteamEndSession* NewTask = new FOnlineAsyncTaskSteamEndSession(SteamSubsystem, Session->SessionName);
SteamSubsystem->QueueAsyncTask(NewTask);
return ONLINE_IO_PENDING;
}
bool FOnlineSessionSteam::DestroySession(FName SessionName, const FOnDestroySessionCompleteDelegate& CompletionDelegate)
{
uint32 Result = ONLINE_FAIL;
// Find the session in question
FNamedOnlineSession* Session = GetNamedSession(SessionName);
if (Session)
{
if (Session->SessionState != EOnlineSessionState::Destroying)
{
if (!Session->SessionSettings.bIsLANMatch)
{
if (Session->SessionState == EOnlineSessionState::InProgress)
{
// Enqueue all the end session tasks first
EndInternetSession(Session);
}
if (Session->SessionSettings.bUseLobbiesIfAvailable)
{
Result = DestroyLobbySession(Session, CompletionDelegate);
}
else
{
Result = DestroyInternetSession(Session, CompletionDelegate);
}
}
else
{
if (LANSession)
{
// Tear down the LAN beacon
LANSession->StopLANSession();
delete LANSession;
LANSession = nullptr;
}
Result = ONLINE_SUCCESS;
}
if (Result != ONLINE_IO_PENDING)
{
// The session info is no longer needed
RemoveNamedSession(Session->SessionName);
CompletionDelegate.ExecuteIfBound(SessionName, (Result == ONLINE_SUCCESS) ? true : false);
TriggerOnDestroySessionCompleteDelegates(SessionName, (Result == ONLINE_SUCCESS) ? true : false);
}
}
else
{
// Purposefully skip the delegate call as one should already be in flight
UE_LOG_ONLINE_SESSION(Warning, TEXT("Already in process of destroying session (%s)"), *SessionName.ToString());
}
}
else
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("Can't destroy a null online session (%s)"), *SessionName.ToString());
CompletionDelegate.ExecuteIfBound(SessionName, false);
TriggerOnDestroySessionCompleteDelegates(SessionName, false);
}
return Result == ONLINE_SUCCESS || Result == ONLINE_IO_PENDING;
}
uint32 FOnlineSessionSteam::DestroyLobbySession(FNamedOnlineSession* Session, const FOnDestroySessionCompleteDelegate& CompletionDelegate)
{
Session->SessionState = EOnlineSessionState::Destroying;
if (Session->SessionInfo.IsValid())
{
FOnlineSessionInfoSteam* SessionInfo = (FOnlineSessionInfoSteam*)(Session->SessionInfo.Get());
check(SessionInfo->SessionType == ESteamSession::LobbySession);
FOnlineAsyncTaskSteamLeaveLobby* NewTask = new FOnlineAsyncTaskSteamLeaveLobby(SteamSubsystem, Session->SessionName, *SessionInfo->SessionId);
SteamSubsystem->QueueAsyncTask(NewTask);
}
FOnlineAsyncTaskSteamDestroySession* NewTask = new FOnlineAsyncTaskSteamDestroySession(SteamSubsystem, Session->SessionName, CompletionDelegate);
SteamSubsystem->QueueAsyncTask(NewTask);
return ONLINE_IO_PENDING;
}
uint32 FOnlineSessionSteam::DestroyInternetSession(FNamedOnlineSession* Session, const FOnDestroySessionCompleteDelegate& CompletionDelegate)
{
Session->SessionState = EOnlineSessionState::Destroying;
if (Session->SessionInfo.IsValid())
{
FOnlineSessionInfoSteam* SessionInfo = (FOnlineSessionInfoSteam*)(Session->SessionInfo.Get());
check(SessionInfo->SessionType == ESteamSession::AdvertisedSessionHost || SessionInfo->SessionType == ESteamSession::AdvertisedSessionClient);
}
// Clear any session advertisements this account had with this session.
FOnlineAuthSteamPtr SteamAuth = SteamSubsystem->GetAuthInterface();
if (SteamUser() != nullptr && SteamUser()->BLoggedOn() && (!SteamAuth.IsValid() || !SteamAuth->IsSessionAuthEnabled()))
{
UE_LOG_ONLINE(Warning, TEXT("AUTH: DestroyInternetSession is calling the depricated AdvertiseGame call"));
SteamUser()->AdvertiseGame(k_steamIDNil, 0, 0);
}
if (bSteamworksGameServerConnected && GameServerSteamId->IsValid())
{
// Remove this server from the server list
FOnlineAsyncTaskSteamLogoffServer* LogoffTask = new FOnlineAsyncTaskSteamLogoffServer(SteamSubsystem, Session->SessionName);
SteamSubsystem->QueueAsyncTask(LogoffTask);
}
// Destroy the session
FOnlineAsyncTaskSteamDestroySession* DestroyTask = new FOnlineAsyncTaskSteamDestroySession(SteamSubsystem, Session->SessionName, CompletionDelegate);
SteamSubsystem->QueueAsyncTask(DestroyTask);
return ONLINE_IO_PENDING;
}
bool FOnlineSessionSteam::IsPlayerInSession(FName SessionName, const FUniqueNetId& UniqueId)
{
return IsPlayerInSessionImpl(this, SessionName, UniqueId);
}
bool FOnlineSessionSteam::StartMatchmaking(const TArray< FUniqueNetIdRef >& LocalPlayers, FName SessionName, const FOnlineSessionSettings& NewSessionSettings, TSharedRef<FOnlineSessionSearch>& SearchSettings)
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("StartMatchmaking is not supported on this platform. Use FindSessions or FindSessionById."));
TriggerOnMatchmakingCompleteDelegates(SessionName, false);
return false;
}
bool FOnlineSessionSteam::CancelMatchmaking(int32 SearchingPlayerNum, FName SessionName)
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("CancelMatchmaking is not supported on this platform. Use CancelFindSessions."));
TriggerOnCancelMatchmakingCompleteDelegates(SessionName, false);
return false;
}
bool FOnlineSessionSteam::CancelMatchmaking(const FUniqueNetId& SearchingPlayerId, FName SessionName)
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("CancelMatchmaking is not supported on this platform. Use CancelFindSessions."));
TriggerOnCancelMatchmakingCompleteDelegates(SessionName, false);
return false;
}
bool FOnlineSessionSteam::FindSessions(int32 SearchingPlayerNum, const TSharedRef<FOnlineSessionSearch>& SearchSettings)
{
uint32 Return = ONLINE_FAIL;
// Dedicated servers shouldn't be matchmaking.
if (SteamSubsystem->IsDedicated())
{
SearchSettings->SearchState = EOnlineAsyncTaskState::Failed;
TriggerOnFindSessionsCompleteDelegates(false);
return false;
}
// Don't start another search while one is in progress
if (!CurrentSessionSearch.IsValid() && SearchSettings->SearchState != EOnlineAsyncTaskState::InProgress)
{
// Free up previous results
SearchSettings->SearchResults.Empty();
// Copy the search pointer so we can keep it around
CurrentSessionSearch = SearchSettings;
// Check if its a LAN query
if (SearchSettings->bIsLanQuery == false)
{
Return = FindInternetSession(SearchSettings);
}
else
{
Return = FindLANSession(SearchSettings);
}
if (Return == ONLINE_IO_PENDING)
{
SearchSettings->SearchState = EOnlineAsyncTaskState::InProgress;
}
}
else
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("Ignoring game search request while one is pending"));
Return = ONLINE_IO_PENDING;
}
return Return == ONLINE_SUCCESS || Return == ONLINE_IO_PENDING;
}
bool FOnlineSessionSteam::FindSessions(const FUniqueNetId& SearchingPlayerId, const TSharedRef<FOnlineSessionSearch>& SearchSettings)
{
// @todo: use proper SearchingPlayerId
return FindSessions(0, SearchSettings);
}
bool FOnlineSessionSteam::FindSessionById(const FUniqueNetId& SearchingUserId, const FUniqueNetId& SessionId, const FUniqueNetId& FriendId, const FOnSingleSessionResultCompleteDelegate& CompletionDelegates)
{
FOnlineSessionSearchResult EmptyResult;
CompletionDelegates.ExecuteIfBound(0, false, EmptyResult);
return true;
}
uint32 FOnlineSessionSteam::FindInternetSession(const TSharedRef<FOnlineSessionSearch>& SearchSettings)
{
bool PresenceSearch = false;
bool LobbySearch = false;
if (
PRAGMA_DISABLE_DEPRECATION_WARNINGS
(SearchSettings->QuerySettings.Get(SEARCH_PRESENCE, PresenceSearch) && PresenceSearch)
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|| (SearchSettings->QuerySettings.Get(SEARCH_LOBBIES, LobbySearch) && LobbySearch))
{
FOnlineAsyncTaskSteamFindLobbies* NewTask = new FOnlineAsyncTaskSteamFindLobbies(SteamSubsystem, SearchSettings);
SteamSubsystem->QueueAsyncTask(NewTask);
}
else
{
FOnlineAsyncTaskSteamFindServers* NewTask = new FOnlineAsyncTaskSteamFindServers(SteamSubsystem, SearchSettings, OnFindSessionsCompleteDelegates);
SteamSubsystem->QueueAsyncTask(NewTask);
}
return ONLINE_IO_PENDING;
}
uint32 FOnlineSessionSteam::FindLANSession(const TSharedRef<FOnlineSessionSearch>& SearchSettings)
{
uint32 Return = ONLINE_IO_PENDING;
if (!LANSession)
{
LANSession = new FLANSession();
}
// Recreate the unique identifier for this client
GenerateNonce((uint8*)&LANSession->LanNonce, 8);
FOnValidResponsePacketDelegate ResponseDelegate = FOnValidResponsePacketDelegate::CreateRaw(this, &FOnlineSessionSteam::OnValidResponsePacketReceived);
FOnSearchingTimeoutDelegate TimeoutDelegate = FOnSearchingTimeoutDelegate::CreateRaw(this, &FOnlineSessionSteam::OnLANSearchTimeout);
FNboSerializeToBufferSteam Packet(LAN_BEACON_MAX_PACKET_SIZE);
LANSession->CreateClientQueryPacket(Packet, LANSession->LanNonce);
if (Packet.HasOverflow() || LANSession->Search(Packet, ResponseDelegate, TimeoutDelegate) == false)
{
Return = ONLINE_FAIL;
delete LANSession;
LANSession = nullptr;
CurrentSessionSearch->SearchState = EOnlineAsyncTaskState::Failed;
// Just trigger the delegate as having failed
TriggerOnFindSessionsCompleteDelegates(false);
}
return Return;
}
bool FOnlineSessionSteam::CancelFindSessions()
{
uint32 Return = ONLINE_FAIL;
if (CurrentSessionSearch.IsValid() && CurrentSessionSearch->SearchState == EOnlineAsyncTaskState::InProgress)
{
// Make sure it's the right type
if (CurrentSessionSearch->bIsLanQuery)
{
Return = ONLINE_SUCCESS;
LANSession->StopLANSession();
CurrentSessionSearch->SearchState = EOnlineAsyncTaskState::Failed;
}
else
{
// @TODO ONLINE Server list version
Return = ONLINE_SUCCESS;
// There is no CANCEL lobby query
// NULLing out the object will prevent the async event from adding the results
CurrentSessionSearch->SearchState = EOnlineAsyncTaskState::Failed;
CurrentSessionSearch = nullptr;
}
}
else
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("Can't cancel a search that isn't in progress"));
}
if (Return != ONLINE_IO_PENDING)
{
TriggerOnCancelFindSessionsCompleteDelegates(true);
}
return Return == ONLINE_SUCCESS || Return == ONLINE_IO_PENDING;
}
bool FOnlineSessionSteam::JoinSession(int32 PlayerNum, FName SessionName, const FOnlineSessionSearchResult& DesiredSession)
{
// In Steam, bUsesPresence and bUseLobbiesIfAvailable have equivalent meaning and should have the same value
if(DesiredSession.Session.SessionSettings.bUsesPresence != DesiredSession.Session.SessionSettings.bUseLobbiesIfAvailable)
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("[%hs] The values of FOnlineSessionSettings::bUsesPresence and FOnlineSessionSettings::bUseLobbiesIfAvailable are treated as equal and have to match"), __FUNCTION__);
TriggerOnJoinSessionCompleteDelegates(SessionName, EOnJoinSessionCompleteResult::UnknownError);
return false;
}
uint32 Return = ONLINE_FAIL;
FNamedOnlineSession* Session = GetNamedSession(SessionName);
// Don't join a session if already in one or hosting one
if (Session == nullptr)
{
// Create a named session from the search result data
Session = AddNamedSession(SessionName, DesiredSession.Session);
Session->HostingPlayerNum = PlayerNum;
// Create Internet or LAN match
if (!Session->SessionSettings.bIsLANMatch)
{
if (DesiredSession.Session.SessionInfo.IsValid())
{
const FOnlineSessionInfoSteam* SearchSessionInfo = (const FOnlineSessionInfoSteam*)DesiredSession.Session.SessionInfo.Get();
if (DesiredSession.Session.SessionSettings.bUseLobbiesIfAvailable)
{
FOnlineSessionInfoSteam* NewSessionInfo = new FOnlineSessionInfoSteam(ESteamSession::LobbySession, *SearchSessionInfo->SessionId);
Session->SessionInfo = MakeShareable(NewSessionInfo);
Return = JoinLobbySession(PlayerNum, Session, &DesiredSession.Session);
}
else
{
FOnlineSessionInfoSteam* NewSessionInfo = new FOnlineSessionInfoSteam(ESteamSession::AdvertisedSessionClient, *SearchSessionInfo->SessionId);
Session->SessionInfo = MakeShareable(NewSessionInfo);
Return = JoinInternetSession(PlayerNum, Session, &DesiredSession.Session);
}
}
else
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("Invalid session info on search result"));
}
}
else
{
FOnlineSessionInfoSteam* NewSessionInfo = new FOnlineSessionInfoSteam(ESteamSession::LANSession);
Session->SessionInfo = MakeShareable(NewSessionInfo);
Return = JoinLANSession(PlayerNum, Session, &DesiredSession.Session);
}
if (Return != ONLINE_IO_PENDING)
{
if (Return != ONLINE_SUCCESS)
{
// Clean up the session info so we don't get into a confused state
RemoveNamedSession(SessionName);
}
else
{
RegisterLocalPlayers(Session);
}
}
}
else
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("Session (%s) already exists, can't join twice"), *SessionName.ToString());
}
if (Return != ONLINE_IO_PENDING)
{
// Just trigger the delegate as having failed
TriggerOnJoinSessionCompleteDelegates(SessionName, Return == ONLINE_SUCCESS ? EOnJoinSessionCompleteResult::Success : EOnJoinSessionCompleteResult::UnknownError);
}
return Return == ONLINE_SUCCESS || Return == ONLINE_IO_PENDING;
}
bool FOnlineSessionSteam::JoinSession(const FUniqueNetId& PlayerId, FName SessionName, const FOnlineSessionSearchResult& DesiredSession)
{
// @todo: use proper PlayerId
return JoinSession(0, SessionName, DesiredSession);
}
uint32 FOnlineSessionSteam::JoinLobbySession(int32 PlayerNum, FNamedOnlineSession* Session, const FOnlineSession* SearchSession)
{
uint32 Result = ONLINE_FAIL;
if (Session->SessionInfo.IsValid())
{
FOnlineSessionInfoSteam* SteamSessionInfo = (FOnlineSessionInfoSteam*)(Session->SessionInfo.Get());
if (SteamSessionInfo->SessionType == ESteamSession::LobbySession && SteamSessionInfo->SessionId->IsValid())
{
// Copy the session info over
const FOnlineSessionInfoSteam* SearchSessionInfo = (const FOnlineSessionInfoSteam*)SearchSession->SessionInfo.Get();
SteamSessionInfo->HostAddr = SearchSessionInfo->HostAddr;
SteamSessionInfo->SteamP2PAddr = SearchSessionInfo->SteamP2PAddr;
SteamSessionInfo->ConnectionMethod = SearchSessionInfo->ConnectionMethod;
// The settings found on the search object will be duplicated again when we enter the lobby, possibly updated
FOnlineAsyncTaskSteamJoinLobby* NewTask = new FOnlineAsyncTaskSteamJoinLobby(SteamSubsystem, Session->SessionName, *SteamSessionInfo->SessionId);
SteamSubsystem->QueueAsyncTask(NewTask);
Result = ONLINE_IO_PENDING;
}
}
return Result;
}
uint32 FOnlineSessionSteam::JoinInternetSession(int32 PlayerNum, FNamedOnlineSession* Session, const FOnlineSession* SearchSession)
{
uint32 Result = ONLINE_FAIL;
Session->SessionState = EOnlineSessionState::Pending;
if (Session->SessionInfo.IsValid())
{
FOnlineSessionInfoSteam* SteamSessionInfo = (FOnlineSessionInfoSteam*)(Session->SessionInfo.Get());
if (SteamSessionInfo->SessionType == ESteamSession::AdvertisedSessionClient && SteamSessionInfo->SessionId->IsValid())
{
// Copy the session info over
const FOnlineSessionInfoSteam* SearchSessionInfo = (const FOnlineSessionInfoSteam*)SearchSession->SessionInfo.Get();
SteamSessionInfo->HostAddr = SearchSessionInfo->HostAddr;
SteamSessionInfo->SteamP2PAddr = SearchSessionInfo->SteamP2PAddr;
SteamSessionInfo->ConnectionMethod = SearchSessionInfo->ConnectionMethod;
if (SearchSession->SessionSettings.bAllowJoinViaPresence)
{
FString ConnectionString = GetSteamConnectionString(Session->SessionName);
if (!SteamFriends()->SetRichPresence("connect", TCHAR_TO_UTF8(*ConnectionString)))
{
UE_LOG_ONLINE_SESSION(Verbose, TEXT("Failed to set rich presence for session %s"), *Session->SessionName.ToString());
}
// SteamAuth will auto advertise any sessions we join.
bool bShouldUseFallback = true;
FOnlineAuthSteamPtr SteamAuth = SteamSubsystem->GetAuthInterface();
if (SteamAuth.IsValid() && SteamAuth->IsSessionAuthEnabled())
{
bShouldUseFallback = false;
}
// Advertise any servers we join (However, if we're using SteamAuth [as determined above], then we should not do this)
if (SteamUser() != nullptr && SteamUser()->BLoggedOn() && bShouldUseFallback)
{
uint32 IpAddr;
uint32 Port = SteamSessionInfo->HostAddr->GetPort();
SteamSessionInfo->HostAddr->GetIp(IpAddr);
SteamUser()->AdvertiseGame(k_steamIDNonSteamGS, IpAddr, Port);
UE_LOG_ONLINE(Warning, TEXT("AUTH: JoinInternetSession is calling the depricated AdvertiseGame call"));
}
}
Result = ONLINE_SUCCESS;
}
}
return Result;
}
uint32 FOnlineSessionSteam::JoinLANSession(int32 PlayerNum, FNamedOnlineSession* Session, const FOnlineSession* SearchSession)
{
uint32 Result = ONLINE_FAIL;
Session->SessionState = EOnlineSessionState::Pending;
if (Session->SessionInfo.IsValid())
{
// Copy the session info over
const FOnlineSessionInfoSteam* SearchSessionInfo = (const FOnlineSessionInfoSteam*)SearchSession->SessionInfo.Get();
FOnlineSessionInfoSteam* SessionInfo = (FOnlineSessionInfoSteam*)Session->SessionInfo.Get();
SessionInfo->HostAddr = SearchSessionInfo->HostAddr->Clone();
SessionInfo->ConnectionMethod = FSteamConnectionMethod::Direct;
Result = ONLINE_SUCCESS;
}
return Result;
}
bool FOnlineSessionSteam::FindFriendSession(int32 LocalUserNum, const FUniqueNetId& Friend)
{
bool bSuccess = false;
const FUniqueNetIdSteam& SteamFriendId = (const FUniqueNetIdSteam&) Friend;
// Don't start another search while one is in progress
if (!CurrentSessionSearch.IsValid())
{
FriendGameInfo_t FriendGameInfo;
if (SteamFriends()->GetFriendGamePlayed(SteamFriendId, &FriendGameInfo))
{
if (FriendGameInfo.m_gameID.AppID() == SteamSubsystem->GetSteamAppId())
{
// Create a search settings object
TSharedRef<FOnlineSessionSearch> SearchSettings = MakeShareable(new FOnlineSessionSearch());
CurrentSessionSearch = SearchSettings;
CurrentSessionSearch->SearchState = EOnlineAsyncTaskState::InProgress;
if (FriendGameInfo.m_steamIDLobby.IsValid())
{
const FUniqueNetIdSteamRef LobbyId = FUniqueNetIdSteam::Create(FriendGameInfo.m_steamIDLobby);
FOnlineAsyncTaskSteamFindLobbiesForFriendSession* NewTask = new FOnlineAsyncTaskSteamFindLobbiesForFriendSession(SteamSubsystem, *LobbyId, CurrentSessionSearch, LocalUserNum, OnFindFriendSessionCompleteDelegates[LocalUserNum]);
SteamSubsystem->QueueAsyncTask(NewTask);
bSuccess = true;
}
else
{
// Search for the session via host ip
#if WITH_ENGINE
TSharedRef<FInternetAddr> IpAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
IpAddr->SetIp(FriendGameInfo.m_unGameIP);
IpAddr->SetPort(FriendGameInfo.m_usGamePort);
CurrentSessionSearch->QuerySettings.Set(FName(SEARCH_STEAM_HOSTIP), IpAddr->ToString(true), EOnlineComparisonOp::Equals);
#else //WITH_ENGINE
ensure(false); //Not currently supported
#endif //WITH_ENGINE
FOnlineAsyncTaskSteamFindServerForFriendSession* NewTask = new FOnlineAsyncTaskSteamFindServerForFriendSession(SteamSubsystem, CurrentSessionSearch, LocalUserNum, OnFindFriendSessionCompleteDelegates[LocalUserNum]);
SteamSubsystem->QueueAsyncTask(NewTask);
bSuccess = true;
}
}
}
}
else
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("Ignoring friend search request while another search is pending"));
}
if (!bSuccess)
{
TArray<FOnlineSessionSearchResult> EmptyResult;
TriggerOnFindFriendSessionCompleteDelegates(LocalUserNum, bSuccess, EmptyResult);
}
return bSuccess;
}
bool FOnlineSessionSteam::FindFriendSession(const FUniqueNetId& LocalUserId, const FUniqueNetId& Friend)
{
// @todo: use proper LocalUserId
return FindFriendSession(0, Friend);
}
bool FOnlineSessionSteam::FindFriendSession(const FUniqueNetId& LocalUserId, const TArray<FUniqueNetIdRef>& FriendList)
{
UE_LOG_ONLINE_SESSION(Display, TEXT("FOnlineSessionSteam::FindFriendSession(const FUniqueNetId& LocalUserId, const TArray<FUniqueNetIdRef>& FriendList) - not implemented"));
// @todo: use proper LocalUserId
TArray<FOnlineSessionSearchResult> EmptyResult;
TriggerOnFindFriendSessionCompleteDelegates(0, false, EmptyResult);
return false;
}
bool FOnlineSessionSteam::PingSearchResults(const FOnlineSessionSearchResult& SearchResult)
{
return false;
}
void FOnlineSessionSteam::CheckPendingSessionInvite()
{
const TCHAR* CmdLine = FCommandLine::Get();
FString CmdLineStr(CmdLine);
const FString LobbyConnectCmd = TEXT("+connect_lobby");
int32 ConnectIdx = CmdLineStr.Find(LobbyConnectCmd, ESearchCase::IgnoreCase, ESearchDir::FromEnd);
if (ConnectIdx != INDEX_NONE)
{
const TCHAR* Str = CmdLine + ConnectIdx + LobbyConnectCmd.Len();
FString LobbyIdStr = FParse::Token(Str, 0);
int64 LobbyId = FCString::Strtoui64(*LobbyIdStr, nullptr, 10);
if (LobbyId > 0)
{
PendingInvite.PendingInviteType = ESteamSession::LobbySession;
PendingInvite.LobbyId = FUniqueNetIdSteam::Create(LobbyId);
}
}
else
{
const FString ServerConnectCmd = TEXT("+connect");
ConnectIdx = CmdLineStr.Find(ServerConnectCmd, ESearchCase::IgnoreCase, ESearchDir::FromEnd);
if (ConnectIdx != INDEX_NONE)
{
const TCHAR* Str = CmdLine + ConnectIdx + ServerConnectCmd.Len();
FString ServerIpAddrStr = FParse::Token(Str, 0);
if (!ServerIpAddrStr.IsEmpty())
{
PendingInvite.PendingInviteType = ESteamSession::AdvertisedSessionClient;
PendingInvite.ServerIp = FString::Printf(TEXT("-SteamConnectIP=%s"), *ServerIpAddrStr);
}
}
}
}
bool FOnlineSessionSteam::SendSessionInviteToFriend(int32 LocalUserNum, FName SessionName, const FUniqueNetId& Friend)
{
TArray<FUniqueNetIdRef> Friends;
Friends.Add(Friend.AsShared());
return SendSessionInviteToFriends(LocalUserNum, SessionName, Friends);
}
bool FOnlineSessionSteam::SendSessionInviteToFriend(const FUniqueNetId& LocalUserId, FName SessionName, const FUniqueNetId& Friend)
{
// @todo: use proper LocalUserId
return SendSessionInviteToFriend(0, SessionName, Friend);
}
bool FOnlineSessionSteam::SendSessionInviteToFriends(int32 LocalUserNum, FName SessionName, const TArray< FUniqueNetIdRef >& Friends)
{
bool bSuccess = false;
FNamedOnlineSession* Session = GetNamedSession(SessionName);
if (Session && Session->SessionInfo.IsValid())
{
FOnlineSessionInfoSteam* SessionInfo = (FOnlineSessionInfoSteam*)(Session->SessionInfo.Get());
if (SessionInfo->SessionType == ESteamSession::LobbySession && SessionInfo->SessionId->IsValid())
{
for (int32 FriendIdx=0; FriendIdx < Friends.Num(); FriendIdx++)
{
const FUniqueNetIdSteam& FriendId = FUniqueNetIdSteam::Cast(*Friends[FriendIdx]);
// Outside game accept -> +connect_lobby <64-bit lobby id> on client commandline
// Inside game accept -> GameLobbyJoinRequested_t callback on client
if (SteamMatchmaking()->InviteUserToLobby(*SessionInfo->SessionId, FriendId))
{
bSuccess = true;
}
else
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("Error inviting %s to session %s, not connected to Steam"), *FriendId.ToDebugString(), *SessionName.ToString());
}
}
}
else if (SessionInfo->SessionType == ESteamSession::AdvertisedSessionHost || SessionInfo->SessionType == ESteamSession::AdvertisedSessionClient)
{
// Create the connection string
FString ConnectionURL = GetSteamConnectionString(SessionName);
for (int32 FriendIdx=0; FriendIdx < Friends.Num(); FriendIdx++)
{
FUniqueNetIdSteam& FriendId = (FUniqueNetIdSteam&)(Friends[FriendIdx].Get());
// Outside game accept -> the ConnectionURL gets added on client commandline
// Inside game accept -> GameRichPresenceJoinRequested_t callback on client
if (SteamFriends()->InviteUserToGame(FriendId, TCHAR_TO_UTF8(*ConnectionURL)))
{
UE_LOG_ONLINE_SESSION(Verbose, TEXT("Inviting %s to session %s with %s"), *FriendId.ToDebugString(), *SessionName.ToString(), *ConnectionURL);
bSuccess = true;
}
else
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("Error inviting %s to session %s"), *FriendId.ToDebugString(), *SessionName.ToString());
}
}
}
else
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("Invalid session info for invite %s"), *SessionName.ToString());
}
}
else
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("Missing or invalid session %s for invite request"), *SessionName.ToString());
}
return bSuccess;
}
bool FOnlineSessionSteam::SendSessionInviteToFriends(const FUniqueNetId& LocalUserId, FName SessionName, const TArray< FUniqueNetIdRef >& Friends)
{
// @todo: use proper LocalUserId
return SendSessionInviteToFriends(0, SessionName, Friends);
}
FString FOnlineSessionSteam::GetSteamConnectionString(FName SessionName)
{
FString ConnectionString;
FNamedOnlineSession* Session = GetNamedSession(SessionName);
if (Session && Session->SessionInfo.IsValid())
{
FOnlineSessionInfoSteam* SessionInfo = (FOnlineSessionInfoSteam*)(Session->SessionInfo.Get());
if (SessionInfo->SessionType == ESteamSession::AdvertisedSessionHost || SessionInfo->SessionType == ESteamSession::AdvertisedSessionClient)
{
ConnectionString = FString::Printf(TEXT("-SteamConnectIP=%s"), *SessionInfo->HostAddr->ToString(true));
}
}
return ConnectionString;
}
/** Get a resolved connection string from a session info */
static bool GetConnectStringFromSessionInfo(TSharedPtr<FOnlineSessionInfoSteam>& SessionInfo, FString& ConnectInfo, int32 PortOverride=0)
{
bool bSuccess = false;
if (SessionInfo.IsValid())
{
bool bP2PDataValid = (SessionInfo->SteamP2PAddr.IsValid() && SessionInfo->SteamP2PAddr->IsValid());
bool bHostDataValid = (SessionInfo->HostAddr.IsValid() && SessionInfo->HostAddr->IsValid());
// If we have host data, attempt to use it.
if (bHostDataValid && SessionInfo->ConnectionMethod == FSteamConnectionMethod::Direct)
{
UE_LOG_ONLINE_SESSION(Log, TEXT("Using Host Data for Connection Serialization"));
int32 HostPort = SessionInfo->HostAddr->GetPort();
if (PortOverride > 0)
{
HostPort = PortOverride;
}
ConnectInfo = FString::Printf(TEXT("%s:%d"), *SessionInfo->HostAddr->ToString(false), HostPort);
bSuccess = true;
}
else if (bP2PDataValid)
{
UE_LOG_ONLINE_SESSION(Log, TEXT("Using P2P Data for Connection Serialization"));
int32 SteamPort = SessionInfo->SteamP2PAddr->GetPort();
if (PortOverride > 0)
{
SteamPort = PortOverride;
}
ConnectInfo = FString::Printf(STEAM_URL_PREFIX TEXT("%s:%d"), *SessionInfo->SteamP2PAddr->ToString(false), SteamPort);
bSuccess = true;
}
else
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("Steam could not resolve session info! ValidP2P[%d] ValidHost[%d] ConnectionMethod[%s]"),
bP2PDataValid, bHostDataValid, *LexToString(SessionInfo->ConnectionMethod));
return false;
}
}
return bSuccess;
}
bool FOnlineSessionSteam::GetResolvedConnectString(FName SessionName, FString& ConnectInfo, FName PortType)
{
bool bSuccess = false;
// Find the session
FNamedOnlineSession* Session = GetNamedSession(SessionName);
if (Session != nullptr)
{
TSharedPtr<FOnlineSessionInfoSteam> SessionInfo = StaticCastSharedPtr<FOnlineSessionInfoSteam>(Session->SessionInfo);
if (PortType == NAME_BeaconPort)
{
int32 BeaconListenPort = GetBeaconPortFromSessionSettings(Session->SessionSettings);
bSuccess = GetConnectStringFromSessionInfo(SessionInfo, ConnectInfo, BeaconListenPort);
}
else if (PortType == NAME_GamePort)
{
bSuccess = GetConnectStringFromSessionInfo(SessionInfo, ConnectInfo);
}
if (!bSuccess)
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("Invalid session info for session %s in GetResolvedConnectString()"), *SessionName.ToString());
}
}
else
{
UE_LOG_ONLINE_SESSION(Warning,
TEXT("Unknown session name (%s) specified to GetResolvedConnectString()"),
*SessionName.ToString());
}
return bSuccess;
}
bool FOnlineSessionSteam::GetResolvedConnectString(const FOnlineSessionSearchResult& SearchResult, const FName PortType, FString& ConnectInfo)
{
bool bSuccess = false;
if (SearchResult.Session.SessionInfo.IsValid())
{
TSharedPtr<FOnlineSessionInfoSteam> SessionInfo = StaticCastSharedPtr<FOnlineSessionInfoSteam>(SearchResult.Session.SessionInfo);
if (PortType == NAME_BeaconPort)
{
int32 BeaconListenPort = GetBeaconPortFromSessionSettings(SearchResult.Session.SessionSettings);
bSuccess = GetConnectStringFromSessionInfo(SessionInfo, ConnectInfo, BeaconListenPort);
}
else if (PortType == NAME_GamePort)
{
bSuccess = GetConnectStringFromSessionInfo(SessionInfo, ConnectInfo);
}
}
if (!bSuccess || ConnectInfo.IsEmpty())
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("Invalid session info in search result to GetResolvedConnectString()"));
}
return bSuccess;
}
FString FOnlineSessionSteam::GetCustomDedicatedServerName() const
{
FString ServerName;
if (!FParse::Value(FCommandLine::Get(), TEXT("-SteamServerName="), ServerName))
{
GConfig->GetString(TEXT("OnlineSubsystemSteam"), TEXT("SteamServerName"), ServerName, GEngineIni);
}
if (ServerName.Len() >= k_cbMaxGameServerName)
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("SteamServerName overflows the maximum amount of characters %d allowed, truncating."), k_cbMaxGameServerName);
// Must have space for the null terminator
ServerName.LeftInline(k_cbMaxGameServerName - 1);
}
return ServerName;
}
FUniqueNetIdPtr FOnlineSessionSteam::CreateSessionIdFromString(const FString& SessionIdStr)
{
if (!SessionIdStr.IsEmpty())
{
return FUniqueNetIdSteam::Create(SessionIdStr);
}
return nullptr;
}
FOnlineSessionSettings* FOnlineSessionSteam::GetSessionSettings(FName SessionName)
{
FNamedOnlineSession* Session = GetNamedSession(SessionName);
if (Session)
{
return &Session->SessionSettings;
}
return nullptr;
}
void FOnlineSessionSteam::RegisterVoice(const FUniqueNetId& PlayerId)
{
if (!SteamSubsystem->IsDedicated())
{
if (PlayerId.IsValid())
{
IOnlineVoicePtr VoiceInt = SteamSubsystem->GetVoiceInterface();
if (VoiceInt.IsValid())
{
if (!SteamSubsystem->IsLocalPlayer(PlayerId))
{
VoiceInt->RegisterRemoteTalker(PlayerId);
}
else
{
// This is a local player. In case their PlayerState came last during replication, reprocess muting
VoiceInt->ProcessMuteChangeNotification();
}
}
}
}
}
void FOnlineSessionSteam::UnregisterVoice(const FUniqueNetId& PlayerId)
{
if (!SteamSubsystem->IsDedicated())
{
IOnlineVoicePtr VoiceInt = SteamSubsystem->GetVoiceInterface();
if (VoiceInt.IsValid())
{
if (PlayerId.IsValid() && !SteamSubsystem->IsLocalPlayer(PlayerId))
{
VoiceInt->UnregisterRemoteTalker(PlayerId);
}
}
}
}
bool FOnlineSessionSteam::RegisterPlayer(FName SessionName, const FUniqueNetId& PlayerId, bool bWasInvited)
{
TArray< FUniqueNetIdRef > Players;
Players.Add(PlayerId.AsShared());
return RegisterPlayers(SessionName, Players, bWasInvited);
}
bool FOnlineSessionSteam::RegisterPlayers(FName SessionName, const TArray< FUniqueNetIdRef >& Players, bool bWasInvited)
{
bool bSuccess = false;
FNamedOnlineSession* Session = GetNamedSession(SessionName);
if (Session)
{
if (Session->SessionInfo.IsValid())
{
FOnlineSessionInfoSteam* SessionInfo = (FOnlineSessionInfoSteam*)(Session->SessionInfo.Get());
ISteamFriends* SteamFriendsPtr = SteamFriends();
for (int32 PlayerIdx=0; PlayerIdx < Players.Num(); PlayerIdx++)
{
const FUniqueNetIdRef& PlayerId = Players[PlayerIdx];
const FUniqueNetIdSteam& SteamId = (const FUniqueNetIdSteam&)*PlayerId;
FUniqueNetIdMatcher PlayerMatch(SteamId);
if (Session->RegisteredPlayers.IndexOfByPredicate(PlayerMatch) == INDEX_NONE)
{
Session->RegisteredPlayers.Add(PlayerId);
// Determine if this player is really remote or not
if (!SteamSubsystem->IsLocalPlayer(SteamId))
{
if (SteamFriendsPtr)
{
SteamFriendsPtr->RequestUserInformation(SteamId, true);
}
}
}
else
{
UE_LOG_ONLINE_SESSION(Log, TEXT("Player %s already registered in session %s"), *Players[PlayerIdx]->ToDebugString(), *SessionName.ToString());
}
RegisterVoice(SteamId);
}
bSuccess = true;
}
else
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("No session info to join for session (%s)"), *SessionName.ToString());
}
}
else
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("No game present to join for session (%s)"), *SessionName.ToString());
}
TriggerOnRegisterPlayersCompleteDelegates(SessionName, Players, bSuccess);
return bSuccess;
}
void FOnlineSessionSteam::RegisterLocalPlayers(FNamedOnlineSession* Session)
{
if (!SteamSubsystem->IsDedicated())
{
IOnlineVoicePtr VoiceInt = SteamSubsystem->GetVoiceInterface();
if (VoiceInt.IsValid())
{
for (int32 Index = 0; Index < MAX_LOCAL_PLAYERS; Index++)
{
// Register the local player as a local talker
VoiceInt->RegisterLocalTalker(Index);
}
}
}
}
bool FOnlineSessionSteam::UnregisterPlayer(FName SessionName, const FUniqueNetId& PlayerId)
{
TArray< FUniqueNetIdRef > Players;
Players.Add(PlayerId.AsShared());
return UnregisterPlayers(SessionName, Players);
}
bool FOnlineSessionSteam::UnregisterPlayers(FName SessionName, const TArray< FUniqueNetIdRef >& Players)
{
bool bSuccess = false;
FNamedOnlineSession* Session = GetNamedSession(SessionName);
if (Session)
{
if (Session->SessionInfo.IsValid())
{
FOnlineSessionInfoSteam* SessionInfo = (FOnlineSessionInfoSteam*)(Session->SessionInfo.Get());
for (int32 PlayerIdx=0; PlayerIdx < Players.Num(); PlayerIdx++)
{
const FUniqueNetIdRef& PlayerId = Players[PlayerIdx];
FUniqueNetIdMatcher PlayerMatch(*PlayerId);
int32 RegistrantIndex = Session->RegisteredPlayers.IndexOfByPredicate(PlayerMatch);
if (RegistrantIndex != INDEX_NONE)
{
Session->RegisteredPlayers.RemoveAtSwap(RegistrantIndex);
UnregisterVoice(*PlayerId);
}
else
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("Player %s is not part of session (%s)"), *PlayerId->ToDebugString(), *SessionName.ToString());
}
}
bSuccess = true;
}
else
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("No session info to leave for session (%s)"), *SessionName.ToString());
}
}
else
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("No game present to leave for session (%s)"), *SessionName.ToString());
}
TriggerOnUnregisterPlayersCompleteDelegates(SessionName, Players, bSuccess);
return bSuccess;
}
void FOnlineSessionSteam::Tick(float DeltaTime)
{
SCOPE_CYCLE_COUNTER(STAT_Session_Interface);
TickLanTasks(DeltaTime);
TickPendingInvites(DeltaTime);
}
void FOnlineSessionSteam::TickLanTasks(float DeltaTime)
{
if (LANSession != nullptr && LANSession->GetBeaconState() > ELanBeaconState::NotUsingLanBeacon)
{
LANSession->Tick(DeltaTime);
}
}
void FOnlineSessionSteam::TickPendingInvites(float DeltaTime)
{
if (PendingInvite.PendingInviteType != ESteamSession::None)
{
if (OnSessionUserInviteAcceptedDelegates.IsBound())
{
FOnlineAsyncItem* NewEvent = nullptr;
if (PendingInvite.PendingInviteType == ESteamSession::LobbySession)
{
NewEvent = new FOnlineAsyncEventSteamLobbyInviteAccepted(SteamSubsystem, *FUniqueNetIdSteam::EmptyId(), *PendingInvite.LobbyId);
}
else
{
NewEvent = new FOnlineAsyncEventSteamInviteAccepted(SteamSubsystem, *FUniqueNetIdSteam::EmptyId(), PendingInvite.ServerIp);
}
if (NewEvent)
{
UE_LOG_ONLINE_SESSION(Verbose, TEXT("%s"), *NewEvent->ToString());
SteamSubsystem->QueueAsyncOutgoingItem(NewEvent);
}
// Clear the invite
PendingInvite.PendingInviteType = ESteamSession::None;
}
}
}
void FOnlineSessionSteam::AppendSessionToPacket(FNboSerializeToBufferSteam& Packet, FOnlineSession* Session)
{
/** Owner of the session */
((FNboSerializeToBuffer&)Packet) << StaticCastSharedPtr<const FUniqueNetIdSteam>(Session->OwningUserId)->UniqueNetId
<< Session->OwningUserName
<< Session->NumOpenPrivateConnections
<< Session->NumOpenPublicConnections;
// Write host info (host addr, session id, and key)
Packet << *StaticCastSharedPtr<FOnlineSessionInfoSteam>(Session->SessionInfo);
// Now append per game settings
AppendSessionSettingsToPacket(Packet, &Session->SessionSettings);
}
void FOnlineSessionSteam::AppendSessionSettingsToPacket(FNboSerializeToBufferSteam& Packet, FOnlineSessionSettings* SessionSettings)
{
#if DEBUG_LAN_BEACON
UE_LOG_ONLINE_SESSION(Verbose, TEXT("Sending session settings to client"));
#endif
// Members of the session settings class
((FNboSerializeToBuffer&)Packet) << SessionSettings->NumPublicConnections
<< SessionSettings->NumPrivateConnections
<< (uint8)SessionSettings->bShouldAdvertise
<< (uint8)SessionSettings->bIsLANMatch
<< (uint8)SessionSettings->bIsDedicated
<< (uint8)SessionSettings->bUsesStats
<< (uint8)SessionSettings->bAllowJoinInProgress
<< (uint8)SessionSettings->bAllowInvites
<< (uint8)SessionSettings->bUsesPresence
<< (uint8)SessionSettings->bUseLobbiesIfAvailable
<< (uint8)SessionSettings->bAllowJoinViaPresence
<< (uint8)SessionSettings->bAllowJoinViaPresenceFriendsOnly
<< (uint8)SessionSettings->bAntiCheatProtected
<< SessionSettings->BuildUniqueId;
// First count number of advertised keys
int32 NumAdvertisedProperties = 0;
for (FSessionSettings::TConstIterator It(SessionSettings->Settings); It; ++It)
{
const FOnlineSessionSetting& Setting = It.Value();
if (Setting.AdvertisementType >= EOnlineDataAdvertisementType::ViaOnlineService)
{
NumAdvertisedProperties++;
}
}
// Add count of advertised keys and the data
((FNboSerializeToBuffer&)Packet) << (int32)NumAdvertisedProperties;
for (FSessionSettings::TConstIterator It(SessionSettings->Settings); It; ++It)
{
const FOnlineSessionSetting& Setting = It.Value();
if (Setting.AdvertisementType >= EOnlineDataAdvertisementType::ViaOnlineService)
{
((FNboSerializeToBuffer&)Packet) << It.Key();
Packet << Setting;
#if DEBUG_LAN_BEACON
UE_LOG_ONLINE_SESSION(Verbose, TEXT("%s"), *Setting.ToString());
#endif
}
}
}
void FOnlineSessionSteam::OnValidQueryPacketReceived(uint8* PacketData, int32 PacketLength, uint64 ClientNonce)
{
// Iterate through all registered sessions and respond for each LAN match
FScopeLock ScopeLock(&SessionLock);
for (int32 SessionIndex = 0; SessionIndex < Sessions.Num(); SessionIndex++)
{
FNamedOnlineSession& Session = Sessions[SessionIndex];
const FOnlineSessionSettings& Settings = Session.SessionSettings;
const bool bIsMatchInProgress = Session.SessionState == EOnlineSessionState::InProgress;
const bool bIsMatchJoinable = Settings.bIsLANMatch &&
(!bIsMatchInProgress || Settings.bAllowJoinInProgress) &&
Settings.NumPublicConnections > 0;
// Don't respond to query if the session is not a joinable LAN match.
if (bIsMatchJoinable)
{
FNboSerializeToBufferSteam Packet(LAN_BEACON_MAX_PACKET_SIZE);
// Create the basic header before appending additional information
LANSession->CreateHostResponsePacket(Packet, ClientNonce);
// Add all the session details
AppendSessionToPacket(Packet, &Session);
// Broadcast this response so the client can see us
LANSession->BroadcastPacket(Packet, Packet.GetByteCount());
}
}
}
void FOnlineSessionSteam::ReadSessionFromPacket(FNboSerializeFromBufferSteam& Packet, FOnlineSession* Session)
{
#if DEBUG_LAN_BEACON
UE_LOG_ONLINE_SESSION(Verbose, TEXT("Reading session information from server"));
#endif
uint64 OwningUserId;
Packet >> OwningUserId
>> Session->OwningUserName
>> Session->NumOpenPrivateConnections
>> Session->NumOpenPublicConnections;
Session->OwningUserId = FUniqueNetIdSteam::Create(OwningUserId);
// Allocate and read the connection data
FOnlineSessionInfoSteam* SteamSessionInfo = new FOnlineSessionInfoSteam(ESteamSession::LANSession);
SteamSessionInfo->HostAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
Packet >> *SteamSessionInfo;
Session->SessionInfo = MakeShareable(SteamSessionInfo);
// Read any per object data using the server object
ReadSettingsFromPacket(Packet, Session->SessionSettings);
}
void FOnlineSessionSteam::ReadSettingsFromPacket(FNboSerializeFromBufferSteam& Packet, FOnlineSessionSettings& SessionSettings)
{
#if DEBUG_LAN_BEACON
UE_LOG_ONLINE_SESSION(Verbose, TEXT("Reading game settings from server"));
#endif
// Clear out any old settings
SessionSettings.Settings.Empty();
// Members of the session settings class
Packet >> SessionSettings.NumPublicConnections
>> SessionSettings.NumPrivateConnections;
uint8 Read = 0;
// Read all the bools as bytes
Packet >> Read;
SessionSettings.bShouldAdvertise = !!Read;
Packet >> Read;
SessionSettings.bIsLANMatch = !!Read;
Packet >> Read;
SessionSettings.bIsDedicated = !!Read;
Packet >> Read;
SessionSettings.bUsesStats = !!Read;
Packet >> Read;
SessionSettings.bAllowJoinInProgress = !!Read;
Packet >> Read;
SessionSettings.bAllowInvites = !!Read;
Packet >> Read;
SessionSettings.bUsesPresence = !!Read;
Packet >> Read;
SessionSettings.bUseLobbiesIfAvailable = !!Read;
Packet >> Read;
SessionSettings.bAllowJoinViaPresence = !!Read;
Packet >> Read;
SessionSettings.bAllowJoinViaPresenceFriendsOnly = !!Read;
Packet >> Read;
SessionSettings.bAntiCheatProtected = !!Read;
// BuildId
Packet >> SessionSettings.BuildUniqueId;
// Now read the contexts and properties from the settings class
int32 NumAdvertisedProperties = 0;
// First, read the number of advertised properties involved, so we can presize the array
Packet >> NumAdvertisedProperties;
if (Packet.HasOverflow() == false)
{
FName Key;
// Now read each context individually
for (int32 Index = 0;
Index < NumAdvertisedProperties && Packet.HasOverflow() == false;
Index++)
{
FOnlineSessionSetting Setting;
Packet >> Key;
Packet >> Setting;
SessionSettings.Set(Key, Setting);
#if DEBUG_LAN_BEACON
UE_LOG_ONLINE_SESSION(Verbose, TEXT("%s"), *Setting->ToString());
#endif
}
}
// If there was an overflow, treat the string settings/properties as broken
if (Packet.HasOverflow())
{
SessionSettings.Settings.Empty();
UE_LOG_ONLINE_SESSION(Verbose, TEXT("Packet overflow detected in ReadGameSettingsFromPacket()"));
}
}
void FOnlineSessionSteam::OnValidResponsePacketReceived(uint8* PacketData, int32 PacketLength)
{
// Create an object that we'll copy the data to
FOnlineSessionSettings NewServer;
if (CurrentSessionSearch.IsValid())
{
// Add space in the search results array
FOnlineSessionSearchResult* NewResult = new (CurrentSessionSearch->SearchResults) FOnlineSessionSearchResult();
FOnlineSession* NewSession = &NewResult->Session;
// Prepare to read data from the packet
FNboSerializeFromBufferSteam Packet(PacketData, PacketLength);
ReadSessionFromPacket(Packet, NewSession);
// NOTE: we don't notify until the timeout happens
}
else
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("Failed to create new online game settings object"));
}
}
void FOnlineSessionSteam::OnLANSearchTimeout()
{
// See if there were any sessions that were marked as hosting before the search started
bool bWasHosting = false;
{
FScopeLock ScopeLock(&SessionLock);
for (int32 SessionIdx=0; SessionIdx < Sessions.Num(); SessionIdx++)
{
FNamedOnlineSession& Session = Sessions[SessionIdx];
if (Session.SessionSettings.bShouldAdvertise &&
Session.SessionSettings.bIsLANMatch &&
SteamSubsystem->IsServer())
{
bWasHosting = true;
break;
}
}
}
if (bWasHosting)
{
FOnValidQueryPacketDelegate QueryPacketDelegate = FOnValidQueryPacketDelegate::CreateRaw(this, &FOnlineSessionSteam::OnValidQueryPacketReceived);
// Maintain lan beacon if there was a session that was marked as hosting
if (LANSession->Host(QueryPacketDelegate))
{
UE_LOG_ONLINE_SESSION(Warning, TEXT("Failed to restart hosted LAN session after search completion"));
}
}
else
{
// Stop future timeouts since we aren't searching any more
LANSession->StopLANSession();
}
if (CurrentSessionSearch.IsValid())
{
if (CurrentSessionSearch->SearchResults.Num() > 0)
{
// Allow game code to sort the servers
CurrentSessionSearch->SortSearchResults();
}
CurrentSessionSearch->SearchState = EOnlineAsyncTaskState::Done;
CurrentSessionSearch = nullptr;
}
// Trigger the delegate as complete
TriggerOnFindSessionsCompleteDelegates(true);
}
void FOnlineSessionSteam::SyncLobbies()
{
UE_LOG_ONLINE_SESSION(Verbose, TEXT("Member of %d lobbies"), JoinedLobbyList.Num());
TArray<FUniqueNetIdSteamRef> LobbiesToRemove = JoinedLobbyList;
{
FScopeLock ScopeLock(&SessionLock);
for (int32 SessionIdx=0; SessionIdx < Sessions.Num(); SessionIdx++)
{
const FNamedOnlineSession& Session = Sessions[SessionIdx];
FOnlineSessionInfoSteam* SessionInfo = (FOnlineSessionInfoSteam*)(Session.SessionInfo.Get());
if (SessionInfo->SessionType == ESteamSession::LobbySession && SessionInfo->SessionId->IsValid())
{
LobbiesToRemove.RemoveSingleSwap(SessionInfo->SessionId);
}
}
}
for (int32 LobbyIdx=0; LobbyIdx < LobbiesToRemove.Num(); LobbyIdx++)
{
const FUniqueNetIdSteam& LobbyId = *LobbiesToRemove[LobbyIdx];
UE_LOG_ONLINE_SESSION(Verbose, TEXT("Lobby %s out of sync, removing..."), *LobbyId.ToDebugString());
FOnlineAsyncTaskSteamLeaveLobby* NewTask = new FOnlineAsyncTaskSteamLeaveLobby(SteamSubsystem, TEXT("OUTOFSYNC"), LobbyId);
SteamSubsystem->QueueAsyncTask(NewTask);
}
}
int32 FOnlineSessionSteam::GetNumSessions()
{
FScopeLock ScopeLock(&SessionLock);
return Sessions.Num();
}
void FOnlineSessionSteam::DumpSessionState()
{
FScopeLock ScopeLock(&SessionLock);
UE_LOG_ONLINE_SESSION(Verbose, TEXT("Member of %d lobbies"), JoinedLobbyList.Num());
TArray<FUniqueNetIdSteamRef> OutOfSyncLobbies = JoinedLobbyList;
for (int32 SessionIdx=0; SessionIdx < Sessions.Num(); SessionIdx++)
{
const FNamedOnlineSession& Session = Sessions[SessionIdx];
FOnlineSessionInfoSteam* SessionInfo = (FOnlineSessionInfoSteam*)(Session.SessionInfo.Get());
if (SessionInfo->SessionType == ESteamSession::LobbySession && SessionInfo->SessionId->IsValid())
{
OutOfSyncLobbies.RemoveSingleSwap(SessionInfo->SessionId);
}
}
if (OutOfSyncLobbies.Num() > 0)
{
UE_LOG_ONLINE_SESSION(Verbose, TEXT("Out of sync lobbies: %d"), OutOfSyncLobbies.Num());
for (int32 LobbyIdx=0; LobbyIdx < OutOfSyncLobbies.Num(); LobbyIdx++)
{
UE_LOG_ONLINE_SESSION(Verbose, TEXT("%s"), *OutOfSyncLobbies[LobbyIdx]->ToDebugString());
}
}
for (int32 SessionIdx=0; SessionIdx < Sessions.Num(); SessionIdx++)
{
DumpNamedSession(&Sessions[SessionIdx]);
}
}
void FOnlineSessionSteam::RegisterLocalPlayer(const FUniqueNetId& PlayerId, FName SessionName, const FOnRegisterLocalPlayerCompleteDelegate& Delegate)
{
Delegate.ExecuteIfBound(PlayerId, EOnJoinSessionCompleteResult::Success);
}
void FOnlineSessionSteam::UnregisterLocalPlayer(const FUniqueNetId& PlayerId, FName SessionName, const FOnUnregisterLocalPlayerCompleteDelegate& Delegate)
{
Delegate.ExecuteIfBound(PlayerId, true);
}
/** Implementation of the ConnectionMethod converters */
FString LexToString(const FSteamConnectionMethod Method)
{
switch (Method)
{
default:
case FSteamConnectionMethod::None:
return TEXT("None");
case FSteamConnectionMethod::Direct:
return TEXT("Direct");
case FSteamConnectionMethod::P2P:
return TEXT("P2P");
case FSteamConnectionMethod::PartnerHosted:
return TEXT("PartnerHosted");
}
}
FSteamConnectionMethod ToConnectionMethod(const FString& InString)
{
if (InString == TEXT("Direct"))
{
return FSteamConnectionMethod::Direct;
}
else if (InString == TEXT("P2P"))
{
return FSteamConnectionMethod::P2P;
}
else if (InString == TEXT("PartnerHosted"))
{
return FSteamConnectionMethod::PartnerHosted;
}
else
{
return FSteamConnectionMethod::None;
}
}