Files
funnygame/external/steamworks/steamworksexample/Lobby.cpp
2025-07-13 15:47:42 +03:00

383 lines
13 KiB
C++

//========= Copyright © 1996-2008, Valve LLC, All rights reserved. ============
//
// Purpose: Class for handling finding & creating lobbies, getting their details,
// and seeing other users in the current lobby
//
//=============================================================================
#include "stdafx.h"
#include "Lobby.h"
#include "SpaceWarClient.h"
#include "p2pauth.h"
//-----------------------------------------------------------------------------
// Purpose: Menu that shows a list of other users in a lobby
//-----------------------------------------------------------------------------
class CLobbyMenu : public CBaseMenu<LobbyMenuItem_t>
{
public:
// Constructor
CLobbyMenu( IGameEngine *pGameEngine ) : CBaseMenu<LobbyMenuItem_t>( pGameEngine ) {}
void Rebuild( const CSteamID &steamIDLobby )
{
PushSelectedItem();
ClearMenuItems();
if ( !steamIDLobby.IsValid() )
{
LobbyMenuItem_t menuItem = { CSteamID(), LobbyMenuItem_t::k_ELobbyMenuItemLeaveLobby };
AddMenuItem( CLobbyMenu::MenuItem_t( "Lobby Disconnected - Return to main menu", menuItem ) );
return;
}
// list of users in lobby
// iterate all the users in the lobby and show their details
int cLobbyMembers = SteamMatchmaking()->GetNumLobbyMembers( steamIDLobby );
for ( int i = 0; i < cLobbyMembers; i++ )
{
CSteamID steamIDLobbyMember = SteamMatchmaking()->GetLobbyMemberByIndex( steamIDLobby, i ) ;
// we get the details of a user from the ISteamFriends interface
const char *pchName = SteamFriends()->GetFriendPersonaName( steamIDLobbyMember );
// we may not know the name of the other users in the lobby immediately; but we'll receive
// a PersonaStateUpdate_t callback when they do, and we'll rebuild the list then
if ( pchName && *pchName )
{
const char *pchReady = SteamMatchmaking()->GetLobbyMemberData( steamIDLobby, steamIDLobbyMember, "ready" );
bool bReady = ( pchReady && atoi( pchReady ) == 1);
LobbyMenuItem_t menuItem = { steamIDLobbyMember, LobbyMenuItem_t::k_ELobbyMenuItemUser };
char rgchMenuText[256];
sprintf_safe( rgchMenuText, "%s %s", pchName, bReady ? "(READY)" : "" );
AddMenuItem( MenuItem_t( std::string( rgchMenuText ), menuItem ) );
}
}
// ready/not ready toggle
{
const char *pchReady = SteamMatchmaking()->GetLobbyMemberData( steamIDLobby, SteamUser()->GetSteamID(), "ready" );
bool bReady = ( pchReady && atoi( pchReady ) == 1 );
LobbyMenuItem_t menuItem = { CSteamID(), LobbyMenuItem_t::k_ELobbyMenuItemToggleReadState };
if ( bReady )
AddMenuItem( CLobbyMenu::MenuItem_t( "Set myself as Not Ready", menuItem ) );
else
AddMenuItem( CLobbyMenu::MenuItem_t( "Set myself as Ready", menuItem ) );
}
// see if the local user is the owner of this lobby
bool bLobbyOwner = false;
if ( SteamUser()->GetSteamID() == SteamMatchmaking()->GetLobbyOwner( steamIDLobby ) )
{
bLobbyOwner = true;
}
// start game
if ( bLobbyOwner )
{
LobbyMenuItem_t menuItem = { CSteamID(), LobbyMenuItem_t::k_ELobbyMenuItemStartGame };
AddMenuItem( CLobbyMenu::MenuItem_t( "Start game", menuItem ) );
}
// invite friend
{
LobbyMenuItem_t menuItem = { CSteamID(), LobbyMenuItem_t::k_ELobbyMenuItemInviteToLobby, steamIDLobby };
AddMenuItem( CLobbyMenu::MenuItem_t( "Invite Friend", menuItem ) );
}
// exit lobby
{
LobbyMenuItem_t menuItem = { CSteamID(), LobbyMenuItem_t::k_ELobbyMenuItemLeaveLobby };
AddMenuItem( CLobbyMenu::MenuItem_t( "Return to main menu", menuItem ) );
}
// reset selection
PopSelectedItem();
}
};
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CLobby::CLobby( IGameEngine *pGameEngine ) :
m_pGameEngine( pGameEngine ),
m_CallbackPersonaStateChange( this, &CLobby::OnPersonaStateChange ),
m_CallbackLobbyDataUpdate( this, &CLobby::OnLobbyDataUpdate ),
m_CallbackChatDataUpdate( this, &CLobby::OnLobbyChatUpdate )
{
m_pMenu = new CLobbyMenu( pGameEngine );
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CLobby::~CLobby()
{
}
//-----------------------------------------------------------------------------
// Purpose: Sets the ID of the lobby to display
//-----------------------------------------------------------------------------
void CLobby::SetLobbySteamID( const CSteamID &steamIDLobby )
{
m_steamIDLobby = steamIDLobby;
m_pMenu->Rebuild( m_steamIDLobby );
}
//-----------------------------------------------------------------------------
// Purpose: Draws the lobby
//-----------------------------------------------------------------------------
void CLobby::RunFrame()
{
m_pMenu->RunFrame();
}
//-----------------------------------------------------------------------------
// Purpose: Handles a user in the lobby changing their name or details
// ( note: joining and leaving is handled below by CLobby::OnLobbyChatUpdate() )
//-----------------------------------------------------------------------------
void CLobby::OnPersonaStateChange( PersonaStateChange_t *pCallback )
{
// callbacks are broadcast to all listeners, so we'll get this for every friend who changes state
// so make sure the user is in the lobby before acting
if ( !SteamFriends()->IsUserInSource( pCallback->m_ulSteamID, m_steamIDLobby ) )
return;
// rebuild the menu
m_pMenu->Rebuild( m_steamIDLobby );
}
//-----------------------------------------------------------------------------
// Purpose: Handles lobby data changing
//-----------------------------------------------------------------------------
void CLobby::OnLobbyDataUpdate( LobbyDataUpdate_t *pCallback )
{
// callbacks are broadcast to all listeners, so we'll get this for every lobby we're requesting
if ( m_steamIDLobby != pCallback->m_ulSteamIDLobby )
return;
// set the heading
m_pMenu->SetHeading( SteamMatchmaking()->GetLobbyData( m_steamIDLobby, "name" ) );
// rebuild the menu
m_pMenu->Rebuild( m_steamIDLobby );
}
//-----------------------------------------------------------------------------
// Purpose: Handles users in the lobby joining or leaving
//-----------------------------------------------------------------------------
void CLobby::OnLobbyChatUpdate( LobbyChatUpdate_t *pCallback )
{
// callbacks are broadcast to all listeners, so we'll get this for every lobby we're requesting
if ( m_steamIDLobby != pCallback->m_ulSteamIDLobby )
return;
if ( pCallback->m_ulSteamIDUserChanged == SteamUser()->GetSteamID().ConvertToUint64() &&
( pCallback->m_rgfChatMemberStateChange &
( k_EChatMemberStateChangeLeft|
k_EChatMemberStateChangeDisconnected|
k_EChatMemberStateChangeKicked|
k_EChatMemberStateChangeBanned ) ) )
{
// we've left the lobby, so it is now invalid
m_steamIDLobby = CSteamID();
}
// rebuild the menu
m_pMenu->Rebuild( m_steamIDLobby );
int cLobbyMembers = SteamMatchmaking()->GetNumLobbyMembers( m_steamIDLobby );
for ( int i = 0; i < cLobbyMembers; i++ )
{
CSteamID steamIDLobbyMember = SteamMatchmaking()->GetLobbyMemberByIndex( m_steamIDLobby, i ) ;
// ignore yourself.
if ( SteamUser()->GetSteamID() == steamIDLobbyMember )
continue;
}
}
//-----------------------------------------------------------------------------
// Purpose: Menu that shows a list of lobbies to choose from
//-----------------------------------------------------------------------------
class CLobbyBrowserMenu : public CBaseMenu<LobbyBrowserMenuItem_t>
{
public:
// Constructor
CLobbyBrowserMenu( IGameEngine *pGameEngine ) : CBaseMenu<LobbyBrowserMenuItem_t>( pGameEngine ) {}
void ShowSearching()
{
PushSelectedItem();
ClearMenuItems();
LobbyBrowserMenuItem_t data;
data.m_eStateToTransitionTo = k_EClientGameMenu;
AddMenuItem( CLobbyBrowserMenu::MenuItem_t( "Searching...", data ) );
data.m_eStateToTransitionTo = k_EClientGameMenu;
AddMenuItem( CLobbyBrowserMenu::MenuItem_t( "Return to main menu", data ) );
PopSelectedItem();
}
void Rebuild( std::list<Lobby_t> &listLobbies )
{
PushSelectedItem();
ClearMenuItems();
LobbyBrowserMenuItem_t data;
std::list<Lobby_t>::iterator iter;
for( iter = listLobbies.begin(); iter != listLobbies.end(); ++iter )
{
data.m_eStateToTransitionTo = k_EClientJoiningLobby;
data.m_steamIDLobby = iter->m_steamIDLobby;
if ( iter->m_rgchName[0] )
{
AddMenuItem( MenuItem_t( std::string( iter->m_rgchName ), data ) );
}
}
data.m_eStateToTransitionTo = k_EClientGameMenu;
AddMenuItem( CLobbyBrowserMenu::MenuItem_t( "Return to main menu", data ) );
// reset selection
PopSelectedItem();
}
};
//-----------------------------------------------------------------------------
// Purpose: Constructor
// just initializes base data
//-----------------------------------------------------------------------------
CLobbyBrowser::CLobbyBrowser( IGameEngine *pGameEngine )
: m_CallbackLobbyDataUpdated( this, &CLobbyBrowser::OnLobbyDataUpdatedCallback )
{
m_pGameEngine = pGameEngine;
m_pMenu = new CLobbyBrowserMenu( pGameEngine );
m_pMenu->Rebuild( m_ListLobbies );
m_pMenu->SetHeading( "Lobby browser" );
m_bRequestingLobbies = false;
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CLobbyBrowser::~CLobbyBrowser()
{
delete m_pMenu;
}
//-----------------------------------------------------------------------------
// Purpose: Run a frame (to handle KB input and such as well as render)
//-----------------------------------------------------------------------------
void CLobbyBrowser::RunFrame()
{
m_pMenu->RunFrame();
}
//-----------------------------------------------------------------------------
// Purpose: Starts rebuilding the lobby list
//-----------------------------------------------------------------------------
void CLobbyBrowser::Refresh()
{
if ( !m_bRequestingLobbies )
{
m_bRequestingLobbies = true;
// request all lobbies for this game
SteamAPICall_t hSteamAPICall = SteamMatchmaking()->RequestLobbyList();
// set the function to call when this API call has completed
m_SteamCallResultLobbyMatchList.Set( hSteamAPICall, this, &CLobbyBrowser::OnLobbyMatchListCallback );
m_pMenu->ShowSearching();
}
}
//-----------------------------------------------------------------------------
// Purpose: Callback, on a list of lobbies being received from the Steam back-end
//-----------------------------------------------------------------------------
void CLobbyBrowser::OnLobbyMatchListCallback( LobbyMatchList_t *pCallback, bool bIOFailure )
{
m_ListLobbies.clear();
m_bRequestingLobbies = false;
if ( bIOFailure )
{
// we had a Steam I/O failure - we probably timed out talking to the Steam back-end servers
// doesn't matter in this case, we can just act if no lobbies were received
}
// lobbies are returned in order of closeness to the user, so add them to the list in that order
for ( uint32 iLobby = 0; iLobby < pCallback->m_nLobbiesMatching; iLobby++ )
{
CSteamID steamIDLobby = SteamMatchmaking()->GetLobbyByIndex( iLobby );
// add the lobby to the list
Lobby_t lobby;
lobby.m_steamIDLobby = steamIDLobby;
// pull the name from the lobby metadata
const char *pchLobbyName = SteamMatchmaking()->GetLobbyData( steamIDLobby, "name" );
if ( pchLobbyName && pchLobbyName[0] )
{
// set the lobby name
sprintf_safe( lobby.m_rgchName, "%s", pchLobbyName );
}
else
{
// we don't have info about the lobby yet, request it
SteamMatchmaking()->RequestLobbyData( steamIDLobby );
// results will be returned via LobbyDataUpdate_t callback
sprintf_safe( lobby.m_rgchName, "Lobby %d", steamIDLobby.GetAccountID() );
}
m_ListLobbies.push_back( lobby );
}
m_pMenu->Rebuild( m_ListLobbies );
}
//-----------------------------------------------------------------------------
// Purpose: Callback, on a list of lobbies being received from the Steam back-end
//-----------------------------------------------------------------------------
void CLobbyBrowser::OnLobbyDataUpdatedCallback( LobbyDataUpdate_t *pCallback )
{
// find the lobby in our local list
std::list<Lobby_t>::iterator iter;
for( iter = m_ListLobbies.begin(); iter != m_ListLobbies.end(); ++iter )
{
// update the name of the lobby
if ( iter->m_steamIDLobby == pCallback->m_ulSteamIDLobby )
{
// extract the display name from the lobby metadata
const char *pchLobbyName = SteamMatchmaking()->GetLobbyData( iter->m_steamIDLobby, "name" );
if ( pchLobbyName[0] )
{
sprintf_safe( iter->m_rgchName, "%s", pchLobbyName );
// update the menu
m_pMenu->Rebuild( m_ListLobbies );
}
return;
}
}
}