in Source/Services/Multiplayer/Manager/multiplayer_game_client.cpp [629:770]
HRESULT MultiplayerGameClient::JoinGameForAllLocalMembers(
_In_ const XblMultiplayerSessionReference& sessionRefToJoin,
_In_ const String& handleId,
_In_ bool createGameIfFailedToJoin,
_In_ MultiplayerSessionCallback callback
) noexcept
{
RETURN_HR_INVALIDARGUMENT_IF(!XblMultiplayerSessionReferenceIsValid(&sessionRefToJoin) && handleId.empty());
std::shared_ptr<XblContext> primaryContext = m_multiplayerLocalUserManager->GetPrimaryContext();
RETURN_HR_IF_LOG_DEBUG(primaryContext == nullptr, E_UNEXPECTED, "Call add_local_user() before joining.");
// Leave existing game without updating the latest as the leave may comeback after the actual join and overwrite it.
auto cachedSession = Session();
if (cachedSession != nullptr)
{
LeaveRemoteSession(cachedSession, false, false);
}
UpdateSession(nullptr);
// Join either an existing or new game session. If attempting to join an existing session fails,
// optionally creates a new game session from the lobby. When all users have joined the game, a JoinGameCompleted
// event will be raised.
struct JoinGameOperation : public std::enable_shared_from_this<JoinGameOperation>
{
JoinGameOperation(
std::shared_ptr<MultiplayerGameClient> gameClient,
const XblMultiplayerSessionReference& sessionRefToJoin,
String handleIdToJoin,
bool createGameIfFailedToJoin,
MultiplayerSessionCallback&& callback
) noexcept :
m_gameClient{ std::move(gameClient) },
m_sessionRefToJoin{ sessionRefToJoin },
m_handleIdToJoin{ std::move(handleIdToJoin) },
m_createGameIfFailedToJoin{ createGameIfFailedToJoin },
m_localUsers{ m_gameClient->m_multiplayerLocalUserManager->GetLocalUserMap() },
m_callback{ std::move(callback) }
{
}
void Run() noexcept
{
JoinGameWithNextUser();
}
private:
void JoinGameWithNextUser() noexcept
{
assert(!m_localUsers.empty());
auto localUser{ m_localUsers.begin()->second };
m_localUsers.erase(m_localUsers.begin());
auto sessionToCommit = MakeShared<XblMultiplayerSession>(localUser->Xuid(), &m_sessionRefToJoin, nullptr);
HRESULT hr = m_gameClient->JoinHelper(localUser, sessionToCommit, true, m_handleIdToJoin,
[
this,
sharedThis{ shared_from_this() },
localUser
]
(Result<std::shared_ptr<XblMultiplayerSession>> joinSessionResult)
{
if (Succeeded(joinSessionResult))
{
localUser->SetGameState(MultiplayerLocalUserGameState::InSession);
}
if (Failed(joinSessionResult) || m_localUsers.empty())
{
OnJoinCompleted(std::move(joinSessionResult));
}
else
{
JoinGameWithNextUser();
}
});
if (FAILED(hr))
{
OnJoinCompleted(hr);
}
}
void OnJoinCompleted(Result<std::shared_ptr<XblMultiplayerSession>>&& joinResult) noexcept
{
if (auto lobbyClient{ m_gameClient->LobbyClient() })
{
if (joinResult.Hresult() == HTTP_E_STATUS_NOT_FOUND && !m_handleIdToJoin.empty())
{
// We tried to join an existing session but it didn't exist. Create a new game session
// from the lobby and clear the existing handleId since it must be invalid.
if (m_createGameIfFailedToJoin)
{
m_callback(lobbyClient->CreateGameFromLobby());
return;
}
lobbyClient->ClearGameSessionFromLobby();
}
else if (Failed(joinResult) && m_handleIdToJoin.empty())
{
// Tried to create a new game and we failed. Clear the transfer handle pending state (GameSessionTransferHandle=pending~xuid).
lobbyClient->ClearGameSessionFromLobby();
}
}
if (Succeeded(joinResult))
{
m_gameClient->UpdateSession(joinResult.Payload());
m_gameClient->m_multiplayerLocalUserManager->ChangeAllLocalUserGameState(MultiplayerLocalUserGameState::InSession);
}
{
std::lock_guard<std::mutex> lock(m_gameClient->m_clientRequestLock);
m_gameClient->m_multiplayerEventQueue.AddEvent(
XblMultiplayerEventType::JoinGameCompleted,
XblMultiplayerSessionType::GameSession,
MakeShared<XblMultiplayerEventArgs>(),
joinResult
);
}
m_callback(joinResult);
}
std::shared_ptr<MultiplayerGameClient> m_gameClient;
XblMultiplayerSessionReference m_sessionRefToJoin;
String m_handleIdToJoin;
bool m_createGameIfFailedToJoin;
Map<uint64_t, std::shared_ptr<MultiplayerLocalUser>> m_localUsers;
MultiplayerSessionCallback m_callback;
};
auto operation = MakeShared<JoinGameOperation>(
shared_from_this(),
sessionRefToJoin,
handleId,
createGameIfFailedToJoin,
std::move(callback)
);
operation->Run();
return S_OK;
}