|
|
//===== Copyright � 1996-2009, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
//===========================================================================//
#include "mm_framework.h"
#include "fmtstr.h"
#include "netmessages_signon.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
// Disable the creation of a listen server for online play.
ConVar mm_disable_listen_server( "mm_disable_listen_server", "0", FCVAR_DEVELOPMENTONLY );
//
// CMatchSessionOnlineHost
//
// Implementation of an online session of a host machine
//
CMatchSessionOnlineHost::CMatchSessionOnlineHost( KeyValues *pSettings ) : m_pSettings( pSettings->MakeCopy() ), m_autodelete_pSettings( m_pSettings ), m_pSysData( new KeyValues( "SysSessionData", "type", "host" ) ), m_autodelete_pSysData( m_pSysData ), m_eState( STATE_INIT ), m_pSysSession( NULL ), m_pDsSearcher( NULL ), m_pTeamSearcher( NULL ), m_pMatchSearcher( NULL ) { DevMsg( "Created CMatchSessionOnlineHost:\n" ); KeyValuesDumpAsDevMsg( m_pSettings, 1 );
// Generate an encryption cookie
unsigned char chEncryptionCookie[ sizeof( uint64 ) ] = {}; for ( int j = 0; j < ARRAYSIZE( chEncryptionCookie ); ++ j ) chEncryptionCookie[j] = RandomInt( 1, 254 ); m_pSysData->SetUint64( "crypt", * reinterpret_cast< uint64 * >( chEncryptionCookie ) );
InitializeGameSettings(); }
CMatchSessionOnlineHost::CMatchSessionOnlineHost( CSysSessionClient *pSysSession, KeyValues *pExtendedSettings ) : m_pSettings( pExtendedSettings->FindKey( "settings" ) ), m_autodelete_pSettings( m_pSettings ), m_pSysData( new KeyValues( "SysSessionData", "type", "host" ) ), m_autodelete_pSysData( m_pSysData ), m_eState( STATE_LOBBY ), // it's at least lobby, we'll figure out later
m_pSysSession( NULL ), m_pDsSearcher( NULL ), m_pTeamSearcher( NULL ), m_pMatchSearcher( NULL ) { Assert( m_pSettings );
KeyValues::AutoDelete autodelete( pExtendedSettings ); pExtendedSettings->RemoveSubKey( m_pSettings ); // it's now our settings
// Carry over encryption cookie
uint64 ullCrypt = pExtendedSettings->GetUint64( "crypt" ); if ( ullCrypt ) m_pSysData->SetUint64( "crypt", ullCrypt );
// Install our session
g_pMMF->SetCurrentMatchSession( this );
// Check if the game is in a non-lobby state
char const *szState = pExtendedSettings->GetString( "state", "" ); if ( !Q_stricmp( szState, "game" ) ) m_eState = STATE_GAME; else if ( !Q_stricmp( szState, "ending" ) ) m_eState = STATE_ENDING;
// Now we need to create the system session to reflect the client session passed
m_pSysSession = new CSysSessionHost( pSysSession, m_pSettings ); if ( ullCrypt ) m_pSysSession->SetCryptKey( ullCrypt ); pSysSession->Destroy();
// Now we need to clean up some transient leftovers from incomplete operations
// started by previous host
MigrateGameSettings();
// Show the state
DevMsg( "Migrated into CMatchSessionOnlineHost:\n" ); KeyValuesDumpAsDevMsg( m_pSettings, 1 ); }
CMatchSessionOnlineHost::~CMatchSessionOnlineHost() { DevMsg( "Destroying CMatchSessionOnlineHost:\n" ); KeyValuesDumpAsDevMsg( m_pSettings, 1 ); }
KeyValues * CMatchSessionOnlineHost::GetSessionSystemData() { // Setup our sys data
m_pSysData->SetUint64( "xuidReserve", m_pSysSession ? m_pSysSession->GetReservationCookie() : 0ull ); m_pSysData->SetUint64( "xuidHost", m_pSysSession ? m_pSysSession->GetHostXuid() : 0ull );
switch ( m_eState ) { case STATE_LOBBY: m_pSysData->SetString( "state", "lobby" ); break; case STATE_GAME: m_pSysData->SetString( "state", "game" ); break; default: m_pSysData->SetString( "state", "" ); break; }
return m_pSysData; }
KeyValues * CMatchSessionOnlineHost::GetSessionSettings() { return m_pSettings; }
void CMatchSessionOnlineHost::UpdateSessionSettings( KeyValues *pSettings ) { if ( m_eState < STATE_LOBBY ) { Warning( "CMatchSessionOnlineHost::UpdateSessionSettings is unavailable in state %d!\n", m_eState ); Assert( !"CMatchSessionOnlineHost::UpdateSessionSettings is unavailable!\n" ); return; }
// Let the title extend the game update keys
g_pMMF->GetMatchTitleGameSettingsMgr()->ExtendGameSettingsUpdateKeys( m_pSettings, pSettings ); m_pSettings->MergeFrom( pSettings );
DevMsg( "CMatchSessionOnlineHost::UpdateSessionSettings\n"); KeyValuesDumpAsDevMsg( m_pSettings );
if ( m_pSysSession ) { m_pSysSession->UpdateMembersInfo(); m_pSysSession->OnUpdateSessionSettings( pSettings ); }
// Broadcast the update to everybody interested
MatchSession_BroadcastSessionSettingsUpdate( pSettings ); }
void CMatchSessionOnlineHost::UpdateTeamProperties( KeyValues *pSettings ) { m_pSysSession->UpdateTeamProperties( pSettings ); }
void CMatchSessionOnlineHost::Command( KeyValues *pCommand ) { char const *szCommand, *szRun; szCommand = pCommand->GetName(); szRun = pCommand->GetString( "run", "" );
if ( !Q_stricmp( szRun, "all" ) || !Q_stricmp( szRun, "clients" ) || !Q_stricmp( szRun, "xuid" ) ) { if ( m_pSysSession ) { m_pSysSession->Command( pCommand ); return; } } else if ( !*szRun || !Q_stricmp( szRun, "local" ) || !Q_stricmp( szRun, "host" ) ) { OnRunCommand( pCommand ); return; }
Warning( "CMatchSessionOnlineClient::Command( %s ) unhandled!\n", szCommand ); Assert( !"CMatchSessionOnlineClient::Command" ); }
void CMatchSessionOnlineHost::OnRunCommand( KeyValues *pCommand ) { char const *szCommand = pCommand->GetName();
if ( !Q_stricmp( "Start", szCommand ) ) { if ( ( m_eState == STATE_LOBBY ) && !pCommand->GetUint64( "_remote_xuidsrc" ) ) { OnRunCommand_Start(); return; } } if ( !Q_stricmp( "Match", szCommand ) ) { if ( ( m_eState == STATE_LOBBY ) && !pCommand->GetUint64( "_remote_xuidsrc" ) ) { OnRunCommand_Match(); return; } } if ( !Q_stricmp( "Cancel", szCommand ) ) { if ( !pCommand->GetUint64( "_remote_xuidsrc" ) ) { switch ( m_eState ) { case STATE_STARTING: OnRunCommand_Cancel_DsSearch(); return; case STATE_MATCHING: OnRunCommand_Cancel_Match(); return; } } } if ( !Q_stricmp( "Kick", szCommand ) ) { if ( m_pSysSession && !pCommand->GetUint64( "_remote_xuidsrc" ) ) { m_pSysSession->KickPlayer( pCommand ); return; } } if ( !Q_stricmp( "Migrate", szCommand ) ) { if ( m_pSysSession ) // TODO: research who sends the "Migrate" command and how secure it is?
{ m_pSysSession->Migrate( pCommand ); return; } } if ( !Q_stricmp( "QueueConnect", szCommand ) ) { if ( ( m_eState == STATE_LOBBY ) && !pCommand->GetUint64( "_remote_xuidsrc" ) ) { OnRunCommand_QueueConnect( pCommand ); return; } }
//
// Let the title-specific matchmaking handle the command
//
CUtlVector< KeyValues * > arrPlayersUpdated; arrPlayersUpdated.SetCount( m_pSettings->GetInt( "members/numPlayers", 0 ) ); memset( arrPlayersUpdated.Base(), 0, arrPlayersUpdated.Count() * sizeof( KeyValues * ) );
g_pMMF->GetMatchTitleGameSettingsMgr()->ExecuteCommand( pCommand, GetSessionSystemData(), m_pSettings, arrPlayersUpdated.Base() );
// Now notify the framework about player updated
for ( int k = 0; k < arrPlayersUpdated.Count(); ++ k ) { if ( !arrPlayersUpdated[k] ) break;
Assert( m_pSysSession ); if ( m_pSysSession ) { m_pSysSession->OnPlayerUpdated( arrPlayersUpdated[k] ); } }
//
// Send the command as event for handling
//
KeyValues *pEvent = pCommand->MakeCopy(); pEvent->SetName( CFmtStr( "Command::%s", pCommand->GetName() ) ); g_pMatchEventsSubscription->BroadcastEvent( pEvent ); }
void CMatchSessionOnlineHost::OnRunCommand_Start() { // First of all flip our state
m_eState = STATE_STARTING;
// Now modify the state of our game
UpdateSessionSettings( KeyValues::AutoDeleteInline ( KeyValues::FromString( "update", " update { " " system { " " lock starting " " } " " } " ) ) );
// Prepare lobby for game
OnGamePrepareLobbyForGame();
// Search for a server
m_pDsSearcher = new CDsSearcher( m_pSettings, m_pSysSession->GetReservationCookie(), this ); }
void CMatchSessionOnlineHost::OnRunCommand_Match() { // First of all flip our state
m_eState = STATE_MATCHING;
// Now modify the state of our game
UpdateSessionSettings( KeyValues::AutoDeleteInline ( KeyValues::FromString( "update", " update { " " system { " " lock matching " " } " " } " ) ) );
// Prepare lobby for game
OnGamePrepareLobbyForGame();
// Search for opponents
if ( m_pTeamSearcher ) { m_pTeamSearcher->Destroy(); m_pTeamSearcher = NULL; }
if ( m_pMatchSearcher ) { m_pMatchSearcher->Destroy(); m_pMatchSearcher = NULL; }
KeyValues *teamMatch = m_pSettings->FindKey( "options/conteammatch" ); if ( teamMatch ) { const char *serverType = m_pSettings->GetString( "options/server", "official" ); if ( Q_stricmp( serverType, "listen" ) ) { m_pMatchSearcher = new CMatchSessionOnlineSearch( m_pSettings ); } else { // Transition into loading state
m_eState = STATE_LOADING; StartListenServerMap(); } } else { m_pTeamSearcher = new CMatchSessionOnlineTeamSearch( m_pSettings, this ); } }
void CMatchSessionOnlineHost::OnRunCommand_Cancel_Match() { // Destroy the matchmaking search state
Assert( m_pTeamSearcher ); if ( m_pTeamSearcher ) { m_pTeamSearcher->Destroy(); m_pTeamSearcher = NULL; }
if ( m_pMatchSearcher ) { m_pMatchSearcher->Destroy(); m_pMatchSearcher = NULL; }
// Flip the state back to lobby
m_eState = STATE_LOBBY;
// Now unlock the state of our game
UpdateSessionSettings( KeyValues::AutoDeleteInline ( KeyValues::FromString( "update", " update { " " system { " " lock #empty# " " } " " } " ) ) ); }
void CMatchSessionOnlineHost::OnRunCommand_Cancel_DsSearch() { // Destroy the dedicated search state
Assert( m_pDsSearcher ); if ( m_pDsSearcher ) { m_pDsSearcher->Destroy(); m_pDsSearcher = NULL; }
// Flip the state back to lobby
m_eState = STATE_LOBBY;
// Now unlock the state of our game
UpdateSessionSettings( KeyValues::AutoDeleteInline ( KeyValues::FromString( "update", " update { " " system { " " lock #empty# " " } " " } " ) ) ); }
void CMatchSessionOnlineHost::OnRunCommand_StartDsSearchFinished() { // Retrieve the result and destroy the searcher
Assert( m_pDsSearcher );
CDsSearcher::DsResult_t dsResult = m_pDsSearcher->GetResult();
if ( dsResult.m_bAborted ) { OnRunCommand_Cancel_DsSearch(); return; }
m_pDsSearcher->Destroy(); m_pDsSearcher = NULL;
// Handle console team matchmaking case here - if we did not find a ds then
// just go back to the lobby
KeyValues *teamMatch = m_pSettings->FindKey( "options/conteammatch" ); if ( teamMatch && !dsResult.m_bDedicated ) { OnRunCommand_Cancel_DsSearch(); OnRunCommand_Match(); // restart the search
return; }
#if defined _GAMECONSOLE
if ( !dsResult.m_bDedicated && m_pSettings->GetString( "server/server", NULL ) ) { // We should be connecting to the dedicated server, but we
// failed to reserve it, just bail out with an error
KeyValues *notify = new KeyValues( "mmF->SysSessionUpdate" ); notify->SetPtr( "syssession", m_pSysSession ); notify->SetString( "error", "n/a" ); g_pMatchEventsSubscription->BroadcastEvent( notify ); return; }
if ( !dsResult.m_bDedicated ) { // Transition into loading state
m_eState = STATE_LOADING; StartListenServerMap(); return; }
#else
// On PC if we fail to find or reserve a DS then bail
if ( !dsResult.m_bDedicated ) { // We should be connecting to the dedicated server, but we
// failed to reserve it, just bail out with an error
KeyValues *notify = new KeyValues( "mmF->SysSessionUpdate" ); notify->SetPtr( "syssession", m_pSysSession ); notify->SetString( "error", "Could not find or connect to a DS" ); g_pMatchEventsSubscription->BroadcastEvent( notify ); return; }
#endif
//
// We have reserved a dedicated server
//
// Prepare the update - creating the "server" key signals that
// a game server is available
KeyValues *kvUpdate = KeyValues::FromString( "update", " update { " " system { " " lock #empty# " " } " " server { " " server dedicated " " } " " } " ); KeyValues::AutoDelete autodelete( kvUpdate ); KeyValues *kvServer = kvUpdate->FindKey( "update/server" );
//
// Publish the addresses of the server
//
bool bWasEncrypted = ( '$' == m_pSettings->GetString( "server/adronline" )[0] ); dsResult.CopyToServerKey( kvServer, bWasEncrypted ? m_pSysData->GetUint64( "crypt" ) : 0ull );
// Remove the lock from the session and allow joins and trigger clients connect
UpdateSessionSettings( kvUpdate );
// Add server info to lobby settings
m_pSysSession->UpdateServerInfo( m_pSettings );
//
// Actually connect to game server
//
ConnectGameServer( &dsResult ); }
void CMatchSessionOnlineHost::OnRunCommand_StartListenServerStarted( uint32 externalIP ) { // If this is a team match and we do not yet have a public IP, because the server
// logon is not complete, then return and wait for another call when the
// server public IP is available
// KeyValues *teamMatch = m_pSettings->FindKey( "options/conteammatch" );
// if ( teamMatch != NULL ) // <vitaliy - we probably don't care here, we'll use P2P anyways in public> && externalIP == 0)
// {
// return;
// }
// Switch the state
m_eState = STATE_GAME;
// Prepare the update - creating the "server" key signals that
// a game server is available
KeyValues *kvUpdate = KeyValues::FromString( "update", " update { " " system { " " lock #empty# " " } " " server { " " server listen " " } " " } " ); KeyValues::AutoDelete autodelete( kvUpdate ); KeyValues *kvServer = kvUpdate->FindKey( "update/server" );
if ( !IsX360() ) { // Let everybody know server info of the server that they should join
INetSupport::ServerInfo_t si; memset( &si, 0, sizeof( si ) ); g_pMatchExtensions->GetINetSupport()->GetServerInfo( &si );
if ( externalIP != 0) { si.m_netAdrOnline.SetIP( externalIP ); }
kvServer->SetString( "adrlocal", MatchSession_EncryptAddressString( si.m_netAdr.ToString(), m_pSysData->GetUint64( "crypt" ) ) ); kvServer->SetString( "adronline", MatchSession_EncryptAddressString( si.m_netAdrOnline.ToString(), m_pSysData->GetUint64( "crypt" ) ) );
// For listen servers we also expose our Steam ID for libjingle
kvServer->SetUint64( "xuid", g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() )->GetXUID() ); } // Set the server reservation appropriately for clients to connect
uint64 uiReservationCookie = m_pSysSession->GetReservationCookie(); g_pMatchExtensions->GetINetSupport()->UpdateServerReservation( uiReservationCookie ); kvServer->SetUint64( "reservationid", uiReservationCookie ); if ( m_pTeamSearcher ) // for team-on-team game the listen server is team 1
kvServer->SetInt( "team", 1 );
if ( m_pTeamSearcher ) { if ( CSysSessionHost *pSysSessionHost = dynamic_cast< CSysSessionHost * >( m_pTeamSearcher->LinkSysSession() ) ) { #ifdef _X360
char chSessionInfo[ XSESSION_INFO_STRING_LENGTH ] = {0}; pSysSessionHost->GetHostSessionInfo( chSessionInfo ); kvServer->SetString( "sessioninfo", chSessionInfo ); #endif
} }
// Remove the lock from the session and allow joins and trigger clients connect
UpdateSessionSettings( kvUpdate );
// Add server info to lobby settings
m_pSysSession->UpdateServerInfo( m_pSettings ); // Mark the local session as active
SetSessionActiveGameplayState( true, NULL );
// Run the extra Update on the teamsearcher if it is alive
if ( m_pTeamSearcher ) m_pTeamSearcher->Update();
InviteTeam(); }
void CMatchSessionOnlineHost::OnRunCommand_QueueConnect( KeyValues *pCommand ) { char const *szConnectAddress = pCommand->GetString( "adronline", "0.0.0.0" ); uint64 uiReservationId = pCommand->GetUint64( "reservationid" ); bool bAutoCloseSession = pCommand->GetBool( "auto_close_session" );
// Switch the state
m_eState = STATE_GAME;
MatchSession_PrepareClientForConnect( m_pSettings, uiReservationId );
// Mark gameplay state as active
SetSessionActiveGameplayState( true, szConnectAddress );
// Close the session, potentially resetting a bunch of state
if ( bAutoCloseSession ) g_pMatchFramework->CloseSession();
// Determine reservation settings required
g_pMatchExtensions->GetINetSupport()->UpdateClientReservation( uiReservationId, 0ull );
// Issue the connect command
g_pMatchExtensions->GetIVEngineClient()->StartLoadingScreenForCommand( CFmtStr( "connect %s", szConnectAddress ) ); }
void CMatchSessionOnlineHost::ConnectGameServer( CDsSearcher::DsResult_t *pDsResult ) { // Switch the state
m_eState = STATE_GAME;
MatchSession_PrepareClientForConnect( m_pSettings );
//
// Resolve server information
//
MatchSessionServerInfo_t msInfo = {0}; if ( pDsResult ) msInfo.m_dsResult = *pDsResult;
// Flags
uint uiResolveServerInfoFlags = msInfo.RESOLVE_CONNECTSTRING | msInfo.RESOLVE_QOS_RATE_PROBE; if ( !pDsResult ) uiResolveServerInfoFlags |= msInfo.RESOLVE_DSRESULT; if ( m_pTeamSearcher ) uiResolveServerInfoFlags |= msInfo.RESOLVE_ALLOW_EXTPEER;
// Client session
CSysSessionBase *pSysSessionForGameServer = m_pSysSession; if ( m_pTeamSearcher ) pSysSessionForGameServer = m_pTeamSearcher->LinkSysSession();
if ( !MatchSession_ResolveServerInfo( m_pSettings, pSysSessionForGameServer, msInfo, uiResolveServerInfoFlags, m_pSysData->GetUint64( "crypt" ) ) ) { if ( m_pTeamSearcher ) { m_eState = STATE_MATCHINGRESTART; return; }
// Destroy the session
m_pSysSession->Destroy(); m_pSysSession = NULL;
// Handle error
g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchSessionUpdate", "state", "error", "error", "connect" ) ); return; }
// Determine reservation settings required
g_pMatchExtensions->GetINetSupport()->UpdateClientReservation( msInfo.m_uiReservationCookie, msInfo.m_xuidJingle );
// Mark gameplay state as active
SetSessionActiveGameplayState( true, msInfo.m_szSecureServerAddress );
// Issue the connect command
g_pMatchExtensions->GetIVEngineClient()->StartLoadingScreenForCommand( msInfo.m_szConnectCmd );
// Tell the rest of the team
InviteTeam(); }
void CMatchSessionOnlineHost::StartListenServerMap() { UpdateSessionSettings( KeyValues::AutoDeleteInline ( KeyValues::FromString( "update", " update { " " system { " " lock loading " " } " " } " " delete { " " server delete " " } " ) ) );
// Note: in case of a team-on-team game we don't yet have "server" key
// and don't put the explicit team nomination, but the title must designate
// the listen server host to be on team 1.
MatchSession_PrepareClientForConnect( m_pSettings );
// Before starting a listen server map ensure we have the map name set
g_pMMF->GetMatchTitleGameSettingsMgr()->SetBspnameFromMapgroup( m_pSettings );
if ( !mm_disable_listen_server.GetBool() ) { bool bResult = g_pMatchFramework->GetMatchTitle()->StartServerMap( m_pSettings ); if ( !bResult ) { Warning( "Failed to start server map!\n" ); KeyValuesDumpAsDevMsg( m_pSettings, 1 ); Assert( 0 ); g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchSessionUpdate", "state", "error", "error", "nomap" ) ); } Msg( "Succeeded in starting server map!\n" ); } else { Msg( "Failed to start server map because mm_disable_listen_server was enabled.\n" ); g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchSessionUpdate", "state", "error", "error", "listen server disabled" ) ); } }
void CMatchSessionOnlineHost::OnEndGameToLobby() { m_eState = STATE_ENDING;
// Remove server information
UpdateSessionSettings( KeyValues::AutoDeleteInline( KeyValues::FromString( "updatedelete", " update { " " system { " " lock endgame " " } " " } " " delete { " " server delete " " } " ) ) );
// Issue the disconnect command
g_pMatchExtensions->GetIVEngineClient()->ExecuteClientCmd( "disconnect" ); g_pMatchExtensions->GetINetSupport()->UpdateServerReservation( 0ull );
// Mark gameplay state as inactive
SetSessionActiveGameplayState( false, NULL ); }
void CMatchSessionOnlineHost::SetSessionActiveGameplayState( bool bActive, char const *szSecureServerAddress ) { if ( m_pTeamSearcher ) { // Mark gameplay state as inactive
if ( !bActive ) { m_pTeamSearcher->Destroy(); m_pTeamSearcher = NULL; } else { // TODO: m_pTeamSearcher->SetSessionActiveGameplayState
} }
m_pSysSession->SetSessionActiveGameplayState( bActive, szSecureServerAddress ); }
void CMatchSessionOnlineHost::OnGamePrepareLobbyForGame() { // Remember which players will get updated
CUtlVector< KeyValues * > arrPlayersUpdated; arrPlayersUpdated.SetCount( m_pSettings->GetInt( "members/numPlayers", 0 ) ); memset( arrPlayersUpdated.Base(), 0, arrPlayersUpdated.Count() * sizeof( KeyValues * ) );
g_pMMF->GetMatchTitleGameSettingsMgr()->PrepareLobbyForGame( m_pSettings, arrPlayersUpdated.Base() );
//
// Now notify the framework about player updated
//
for ( int k = 0; k < arrPlayersUpdated.Count(); ++ k ) { if ( !arrPlayersUpdated[k] ) break;
Assert( m_pSysSession ); if ( m_pSysSession ) { m_pSysSession->OnPlayerUpdated( arrPlayersUpdated[k] ); } } }
void CMatchSessionOnlineHost::OnGamePlayerMachinesConnected( int numMachines ) { if ( m_eState != STATE_GAME ) return;
// Remember which players will get updated
CUtlVector< KeyValues * > arrPlayersUpdated; arrPlayersUpdated.SetCount( m_pSettings->GetInt( "members/numPlayers", 0 ) ); memset( arrPlayersUpdated.Base(), 0, arrPlayersUpdated.Count() * sizeof( KeyValues * ) );
g_pMMF->GetMatchTitleGameSettingsMgr()->PrepareLobbyForGame( m_pSettings, arrPlayersUpdated.Base() );
#ifdef _DEBUG
// Theoretically only new machines should be affected by the callback,
// so in debug mode we are verifying that.
// The logic is actually somewhat complicated - see sys_session implementation
// of order in which OnPlayerMachinesConnected and OnPlayerUpdated are fired.
// All adjustments to the connecting players should be made before OnPlayerUpdated
// gets fired so that OnPlayerUpdated("joined") was fired with all valid settings.
// Current total number of machines
int numMachinesTotal = m_pSettings->GetInt( "members/numMachines", 0 );
// For the players from the old machines we would need to send updates
for ( int k = 0; k < arrPlayersUpdated.Count(); ++ k ) { if ( !arrPlayersUpdated[k] ) break; bool bNewMachine = false; XUID xuidPlayer = arrPlayersUpdated[k]->GetUint64( "xuid" ); KeyValues *pMachine = NULL; SessionMembersFindPlayer( m_pSettings, xuidPlayer, &pMachine ); if ( pMachine ) { char const *szMachine = pMachine->GetName(); if ( char const *szMachineNumber = StringAfterPrefix( szMachine, "machine" ) ) { int iMachineNumber = atoi( szMachineNumber ); if ( iMachineNumber >= numMachinesTotal - numMachines ) bNewMachine = true; } }
Assert( bNewMachine ); if ( !bNewMachine ) { Assert( m_pSysSession ); if ( m_pSysSession ) { m_pSysSession->OnPlayerUpdated( arrPlayersUpdated[k] ); } } } #endif
}
uint64 CMatchSessionOnlineHost::GetSessionID() { if( m_pSysSession ) { return m_pSysSession->GetSessionID(); }
return 0; }
void CMatchSessionOnlineHost::InviteTeam() { return; // this path is no longer needed
DevMsg( "InviteTeam\n" ); KeyValuesDumpAsDevMsg( m_pSettings );
KeyValues *teamMatch = m_pSettings->FindKey( "options/conteammatch" ); if ( teamMatch == NULL ) { return; }
// Reserve session for team
int numTeamPlayers = m_pSettings->GetInt( "members/numPlayers" ); uint64 teamResKey = m_pSettings->GetUint64( "members/machine0/id", 0 ); m_pSysSession->ReserveTeamSession( teamResKey, numTeamPlayers - 1);
// Update lobby settings so that non-team members can join via matchmaking
KeyValues *pUpdate = g_pMMF->GetMatchTitleGameSettingsMgr()->ExtendTeamLobbyToGame( m_pSettings ); UpdateSessionSettings( pUpdate ); pUpdate->deleteThis();
g_pMMF->GetMatchTitleGameSettingsMgr()->PrepareForSessionCreate( m_pSettings ); // Set all the properties of the new session and send them across
KeyValues *pSettings = KeyValues::FromString( "update", " update { " " options { " " action joinsession " " } " " } " ); KeyValues::AutoDelete autodelete_settings( pSettings );
pUpdate = pSettings->FindKey( "update" );
KeyValues *pOptions = pUpdate->FindKey( "options" ); pOptions->SetUint64( "teamResKey", teamResKey ); #ifdef _X360
char chSessionInfo[ XSESSION_INFO_STRING_LENGTH ] = {0}; m_pSysSession->GetHostSessionInfo( chSessionInfo ); pOptions->SetString( "sessioninfo", chSessionInfo ); pOptions->SetUint64( "sessionid", m_pSysSession->GetHostSessionId() ); #else
pOptions->SetUint64( "sessionid", m_pSysSession->GetSessionID() );
#endif
// Set up "teammembers" key
KeyValues *pTeamMembers = pUpdate->CreateNewKey(); pTeamMembers->SetName( "teamMembers" );
// Iterate "members" key
KeyValues *pSessionMembers = m_pSettings->FindKey( "members" ); pTeamMembers->SetInt( "numPlayers", numTeamPlayers );
// Choose a side to join - in keyvalues, team CT = 1, team T = 2
int team = RandomInt(1, 2); int otherTeam = 3 - team; for ( int i = 0; i < numTeamPlayers; i++ ) { if ( i == 5) { // Switch teams
team = otherTeam; }
KeyValues *pTeamPlayer = pTeamMembers->CreateNewKey(); pTeamPlayer->SetName( CFmtStr( "player%d", i ) );
KeyValues *pSessionMember = pSessionMembers->FindKey( CFmtStr( "machine%d", i ) );
uint64 playerId = pSessionMember->GetUint64( "id" ); pTeamPlayer->SetUint64( "xuid", playerId ); pTeamPlayer->SetInt( "team", team ); } KeyValues *notify = new KeyValues( "SysSession::OnUpdate" ); KeyValues::AutoDelete autodelete_notify( notify );
notify->AddSubKey( pUpdate->MakeCopy() );
m_pSysSession->SendMessage( notify );
// Make sure we update host settings to include "conteam" otherwise host
// won't know what side to join
m_pSettings->SetInt( "conteam", team ); }
void CMatchSessionOnlineHost::Update() { switch ( m_eState ) { case STATE_INIT: m_eState = STATE_CREATING;
// Session is creating
g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchSessionUpdate", "state", "progress", "progress", "creating" ) );
// Trigger session creation
m_pSysSession = new CSysSessionHost( m_pSettings ); m_pSysSession->SetCryptKey( m_pSysData->GetUint64( "crypt" ) ); break;
case STATE_STARTING: Assert( m_pDsSearcher ); if ( m_pDsSearcher ) { m_pDsSearcher->Update(); if ( m_pDsSearcher->IsFinished() ) { OnRunCommand_StartDsSearchFinished(); return; } } break;
case STATE_MATCHING: Assert( m_pTeamSearcher ); if ( m_pTeamSearcher ) { m_pTeamSearcher->Update(); }
if ( m_pMatchSearcher ) { m_pMatchSearcher->Update(); CMatchSessionOnlineSearch::Result result = m_pMatchSearcher->GetResult();
if ( result == CMatchSessionOnlineSearch::RESULT_FAIL ) { KeyValues *teamMatch = m_pSettings->FindKey( "options/conteammatch" ); if ( teamMatch ) { OnRunCommand_Cancel_Match(); OnRunCommand_Start(); } } else if (result == CMatchSessionOnlineSearch::RESULT_SUCCESS ) { m_pMatchSearcher->Destroy(); m_pMatchSearcher = NULL; } }
break;
case STATE_MATCHINGRESTART: OnRunCommand_Match(); break;
case STATE_LOBBY: // If we're in the lobby and are setup to bypass the lobby, then send a start command now.
if ( m_pSettings->GetBool( "options/bypasslobby", false ) ) { OnRunCommand_Start(); } break; }
if ( m_pSysSession ) { m_pSysSession->Update(); } }
void CMatchSessionOnlineHost::Destroy() { g_pMatchExtensions->GetINetSupport()->UpdateClientReservation( 0ull, 0ull );
if ( m_eState > STATE_LOBBY ) { char const *szServerType = m_pSettings->GetString( "server/server", "listen" ); if ( !Q_stricmp( szServerType, "listen" ) ) { if ( IGameEvent *pEvent = g_pMatchExtensions->GetIGameEventManager2()->CreateEvent( "server_pre_shutdown" ) ) { pEvent->SetString( "reason", "quit" ); g_pMatchExtensions->GetIGameEventManager2()->FireEvent( pEvent ); } } g_pMatchExtensions->GetIVEngineClient()->ExecuteClientCmd( "disconnect" ); }
if ( m_pMatchSearcher ) { m_pMatchSearcher->Destroy(); m_pMatchSearcher = NULL; }
if ( m_pTeamSearcher ) { m_pTeamSearcher->Destroy(); m_pTeamSearcher = NULL; }
if ( m_pDsSearcher ) { m_pDsSearcher->Destroy(); m_pDsSearcher = NULL; }
if ( m_pSysSession ) { m_pSysSession->Destroy(); m_pSysSession = NULL; }
delete this;
g_pMatchExtensions->GetINetSupport()->UpdateServerReservation( 0ull ); }
void CMatchSessionOnlineHost::DebugPrint() { DevMsg( "CMatchSessionOnlineHost [ state=%d ]\n", m_eState ); DevMsg( "System data:\n" ); KeyValuesDumpAsDevMsg( GetSessionSystemData(), 1 ); DevMsg( "Settings data:\n" ); KeyValuesDumpAsDevMsg( GetSessionSettings(), 1 );
if ( m_pDsSearcher ) DevMsg( "Dedicated search in progress\n" ); else DevMsg( "Dedicated search not active\n" ); if ( m_pSysSession ) m_pSysSession->DebugPrint(); else DevMsg( "SysSession is NULL\n" );
if ( m_pTeamSearcher ) m_pTeamSearcher->DebugPrint(); else DevMsg( "TeamSearch is NULL\n" ); }
bool CMatchSessionOnlineHost::IsAnotherSessionJoinable( const char *pszAnotherSessionInfo ) { #ifdef _X360
if ( m_pSysSession ) { char chHostInfo[ XSESSION_INFO_STRING_LENGTH ] = {0}; m_pSysSession->GetHostSessionInfo( chHostInfo ); XSESSION_INFO xsi, xsiAnother; MMX360_SessionInfoFromString( xsi, chHostInfo ); MMX360_SessionInfoFromString( xsiAnother, pszAnotherSessionInfo ); if ( !memcmp( &xsiAnother.sessionID, &xsi.sessionID, sizeof( xsi.sessionID ) ) ) return false; } #endif
return true; }
void CMatchSessionOnlineHost::OnEvent( KeyValues *pEvent ) { char const *szEvent = pEvent->GetName();
if ( m_pDsSearcher ) m_pDsSearcher->OnEvent( pEvent );
if ( m_pTeamSearcher ) m_pTeamSearcher->OnEvent( pEvent );
if ( m_pMatchSearcher ) m_pMatchSearcher->OnEvent( pEvent );
if ( !Q_stricmp( "OnEngineClientSignonStateChange", szEvent ) ) { int iOldState = pEvent->GetInt( "old", 0 ); int iNewState = pEvent->GetInt( "new", 0 );
if ( iOldState >= SIGNONSTATE_CONNECTED && iNewState < SIGNONSTATE_CONNECTED ) { if ( m_eState == STATE_LOADING || m_eState == STATE_GAME ) { // Lost connection from server or explicit disconnect
DevMsg( "OnEngineClientSignonStateChange\n" ); g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "mmF->CloseSession" ) ); } else if ( m_eState == STATE_ENDING ) { // The game has successfully ended and we are back to lobby
m_eState = STATE_LOBBY; KeyValues *pUpdate = KeyValues::FromString( "update", " update { " " system { " " lock #empty# " " } " " } " " delete { " " game { " " mmqueue #empty# " " } " " } " ); g_pMMF->GetMatchTitleGameSettingsMgr()->ExtendGameSettingsForLobbyTransition( m_pSettings, pUpdate->FindKey( "update" ), true ); UpdateSessionSettings( KeyValues::AutoDeleteInline( pUpdate ) );
g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchSessionUpdate", "state", "ready", "transition", "hostendgame" ) ); } } } else if ( !Q_stricmp( "OnEngineDisconnectReason", szEvent ) ) { if ( m_eState == STATE_LOADING || m_eState == STATE_GAME ) { // Lost connection from server or explicit disconnect
char const *szReason = pEvent->GetString( "reason", "" ); DevMsg( "OnEngineDisconnectReason %s\n", szReason );
bool bLobbySalvagable = StringHasPrefix( szReason, "Connection to server timed out" ) || StringHasPrefix( szReason, "Server shutting down" );
if ( KeyValues *pDisconnectHdlr = g_pMMF->GetMatchTitleGameSettingsMgr()->PrepareClientLobbyForGameDisconnect( m_pSettings, pEvent ) ) { KeyValues::AutoDelete autodelete( pDisconnectHdlr ); char const *szDisconnectHdlr = pDisconnectHdlr->GetString( "disconnecthdlr", "" ); if ( !Q_stricmp( szDisconnectHdlr, "destroy" ) ) bLobbySalvagable = false; else if ( !Q_stricmp( szDisconnectHdlr, "lobby" ) ) bLobbySalvagable = true; }
if ( !bLobbySalvagable ) { g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "mmF->CloseSession" ) ); } else { // Server shutting down, try to retain the lobby
pEvent->SetString( "disconnecthdlr", "lobby" ); g_pMatchEventsSubscription->RegisterEventData( pEvent->MakeCopy() ); OnEndGameToLobby(); } } } else if ( !Q_stricmp( "OnEngineEndGame", szEvent ) ) { if ( m_eState == STATE_LOADING || m_eState == STATE_GAME ) { OnEndGameToLobby(); } } else if ( !Q_stricmp( "OnEngineListenServerStarted", szEvent ) ) { if ( m_eState == STATE_LOADING ) { uint32 externalIP = pEvent->GetInt( "externalIP", 0 ); OnRunCommand_StartListenServerStarted( externalIP ); } } else if ( !Q_stricmp( "OnPlayerMachinesConnected", szEvent ) ) { OnGamePlayerMachinesConnected( pEvent->GetInt( "numMachines" ) ); } else if ( !Q_stricmp( "OnNetLanConnectionlessPacket", szEvent ) ) { char const *szPacketType = pEvent->GetFirstTrueSubKey()->GetName(); if ( m_pSysSession && m_eState > STATE_CREATING && !Q_stricmp( szPacketType, "LanSearch" ) ) { m_pSysSession->ReplyLanSearch( pEvent ); } } else if ( !Q_stricmp( "OnSysMuteListChanged", szEvent ) ) { if ( m_pSysSession ) m_pSysSession->Voice_UpdateMutelist(); } else if ( !Q_stricmp( "OnMatchSessionUpdate", szEvent ) ) { const char *state = pEvent->GetString( "state", "" );
if ( !Q_stricmp( "progress", state) && ( m_pTeamSearcher || m_pMatchSearcher) && ( m_eState == STATE_MATCHING ) ) { char const *szProgress = pEvent->GetString( "progress" ); int numResults = pEvent->GetInt( "numResults", 0 );
// Special case when communication between team gets aborted and the process needs
// to be restarted
if ( !Q_stricmp( "restart", szProgress ) ) { m_eState = STATE_MATCHINGRESTART; return; }
KeyValues *pUpdate = KeyValues::FromString( "update", " update { " " system { " " lock = " " } " " } " );
if ( numResults > 0 ) { pUpdate->SetString( "update/system/lock", CFmtStr( "matching%s%d", szProgress, numResults ) ); } else { pUpdate->SetString( "update/system/lock", CFmtStr( "matching%s", szProgress ) ); }
// Now modify the state of our game
UpdateSessionSettings( KeyValues::AutoDeleteInline ( pUpdate ) ); } else if ( !Q_stricmp( "joinconteamsession", state) ) { KeyValues *pSettings = KeyValues::FromString( "update", " update { " " system { " " network LIVE " " } " " options { " " action joinsession " " } " " } " );
KeyValues *pUpdate = pSettings->FindKey( "update" );
uint64 sessionId = pEvent->GetUint64( "sessionid", 0 ); const char *sessionInfo = pEvent->GetString( "sessioninfo", "" ); KeyValues *pOptions = pUpdate->FindKey( "options" ); pOptions->SetUint64( "sessionid", sessionId ); pOptions->SetString( "sessioninfo", sessionInfo );
uint64 teamResKey = m_pSettings->GetUint64( "members/machine0/id", 0 ); pOptions->SetUint64( "teamResKey", teamResKey );
KeyValues *pSessionHostDataSrc = pEvent->FindKey( "sessionHostDataUnpacked" ); if ( pSessionHostDataSrc ) { KeyValues *pSessionHostDataDst = pUpdate->CreateNewKey(); pSessionHostDataDst->SetName( "sessionHostDataUnpacked" );
pSessionHostDataSrc->CopySubkeys( pSessionHostDataDst ); }
KeyValues *pTeamMembersSrc = pEvent->FindKey( "teamMembers" ); KeyValues *pTeamMembersDst = pUpdate->CreateNewKey(); pTeamMembersDst->SetName( "teamMembers" );
pTeamMembersSrc->CopySubkeys( pTeamMembersDst );
// Now modify the state of our game
UpdateSessionSettings( KeyValues::AutoDeleteInline ( pSettings ) ); } } else if ( !Q_stricmp( "TeamSearchResult::ListenHost", szEvent ) ) { m_eState = STATE_LOADING; StartListenServerMap(); } else if ( !Q_stricmp( "TeamSearchResult::Dedicated", szEvent ) || !Q_stricmp( "TeamSearchResult::ListenClient", szEvent ) ) { KeyValues *pServerInfo = pEvent->FindKey( "server", false ); if ( ( m_eState == STATE_MATCHING ) && pServerInfo && m_pTeamSearcher ) { // We found our dedicated server to play on, let the game commence!
// Prepare the update - creating the "server" key signals that
// a game server is available
KeyValues *kvUpdate = KeyValues::FromString( "update", " update { " " system { " " lock #empty# " " } " " server { " " server dedicated " " } " " } " ); KeyValues::AutoDelete autodelete( kvUpdate ); kvUpdate->FindKey( "update/server" )->MergeFrom( pServerInfo, KeyValues::MERGE_KV_UPDATE );
// Remove the lock from the session and allow joins and trigger clients connect
UpdateSessionSettings( kvUpdate );
//
// Actually connect to game server
//
if ( void *pDsResult = pEvent->GetPtr( "dsresult" ) ) { // we have a resolved secure server XNADDR
ConnectGameServer( reinterpret_cast< CDsSearcher::DsResult_t * >( pDsResult ) ); } else { // we need to resolve secure server XNADDR just like clients do
ConnectGameServer( NULL ); } } } else if ( !Q_stricmp( "mmF->SysSessionUpdate", szEvent ) ) { if ( m_pSysSession && pEvent->GetPtr( "syssession", NULL ) == m_pSysSession ) { // We had a session error
if ( char const *szError = pEvent->GetString( "error", NULL ) ) { State_t eSavedState = m_eState;
// Destroy the session
m_pSysSession->Destroy(); m_pSysSession = NULL;
// Handle error
m_eState = STATE_CREATING; g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchSessionUpdate", "state", "error", "error", szError ) );
// If we were in active gameplay state during migration then also disconnect
if ( !Q_stricmp( szError, "migrate" ) && ( eSavedState == STATE_GAME || eSavedState == STATE_ENDING ) ) { g_pMatchExtensions->GetIVEngineClient()->ExecuteClientCmd( "disconnect" ); } return; }
// This is our session
switch ( m_eState ) { case STATE_CREATING: g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchSessionUpdate", "state", "created" ) );
// Session created successfully and we were straight on the way to a server
if ( char const *szServer = m_pSettings->GetString( "server/server", NULL ) ) { OnRunCommand_Start(); } // Session created successfully and we are in the lobby
else { m_eState = STATE_LOBBY; g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchSessionUpdate", "state", "ready", "transition", "hostinit" ) ); } break;
default: if ( char const *szAction = pEvent->GetString( "action", NULL ) ) { if ( !Q_stricmp( "client", szAction ) ) { KeyValues *pExtendedSettings = new KeyValues( "ExtendedSettings" ); char const *szMigrateState = "lobby"; switch ( m_eState ) { case STATE_GAME: szMigrateState = "game"; break; case STATE_ENDING: szMigrateState = "ending"; break; } pExtendedSettings->SetString( "state", szMigrateState ); pExtendedSettings->AddSubKey( m_pSettings );
// Release ownership of the resources since new match session now owns them
m_pSettings = NULL; m_autodelete_pSettings.Assign( NULL );
CSysSessionHost *pSysSession = m_pSysSession; m_pSysSession = NULL;
// Destroy our instance and create the new match interface
m_eState = STATE_MIGRATE; g_pMMF->SetCurrentMatchSession( NULL ); this->Destroy();
// Now we need to create the new client session that will install itself
IMatchSession *pNewClient = new CMatchSessionOnlineClient( pSysSession, pExtendedSettings ); Assert( g_pMMF->GetMatchSession() == pNewClient ); pNewClient; return; } } break; } } } else if ( !Q_stricmp( "mmF->SysSessionCommand", szEvent ) ) { if ( m_pSysSession && pEvent->GetPtr( "syssession", NULL ) == m_pSysSession ) { KeyValues *pCommand = pEvent->GetFirstTrueSubKey(); if ( pCommand ) { OnRunCommand( pCommand ); } } } }
void CMatchSessionOnlineHost::MigrateGameSettings() { // Check if we have not been destroyed due to a failed migration
if ( g_pMatchFramework->GetMatchSession() != this ) { DevWarning( "CMatchSessionOnlineHost::MigrateGameSettings cannot run because our session is not the active session!\n" ); return; }
switch ( m_eState ) { case STATE_LOBBY: { // Previous host was in LOBBY - then nothing to do
// Previous host was in STARTING / LOADING - remove the "lock" field
// on the session
// Previous host was in ENDING, but this client reached lobby after
// reacting to game event from game server - host had the "server"
// key and could have set "lock = endgame" - remove the "lock" field
// and "server" key on the session.
// Check if the server or endgame information was set
char const *szServer = m_pSettings->GetString( "server/server", NULL ); char const *szLockType = m_pSettings->GetString( "system/lock", "" ); // Remove server information
KeyValues *kvFix = KeyValues::FromString( "updatedelete", " update { " " system { " " lock #empty# " " } " " } " " delete { " " game { mmqueue #empty# } " " server delete " " } " );
// Let the title apply title-specific adjustments
bool bEndGameTransition = ( szServer || !Q_stricmp( szLockType, "endgame" ) ); g_pMMF->GetMatchTitleGameSettingsMgr()->ExtendGameSettingsForLobbyTransition( m_pSettings, kvFix->FindKey( "update" ), bEndGameTransition );
UpdateSessionSettings( KeyValues::AutoDeleteInline( kvFix ) ); } break;
case STATE_GAME: { // Game state really just transfers straightly
} break; case STATE_ENDING: { // Fix up the system since this machine is the new host and haven't yet
// reached the lobby
// Remove server information
UpdateSessionSettings( KeyValues::AutoDeleteInline( KeyValues::FromString( "updatedelete", " update { " " system { " " lock endgame " " } " " } " " delete { " " server delete " " } " ) ) ); } break;
default: Assert( !"CMatchSessionOnlineHost::MigrateGameSettings - unreachable!" ); break; } }
void CMatchSessionOnlineHost::InitializeGameSettings() { //
// We need to establish correct keys
// because data coming from callers can contain all sorts
// of auxiliary settings
//
m_pSettings->SetName( "settings" ); int numSlotsCreated = m_pSettings->GetInt( "members/numSlots", 0 ); char const *arrKeys[] = { "system", "game", "options", "server", "conteamlobby" }; for ( KeyValues *valRoot = m_pSettings->GetFirstSubKey(); valRoot; ) { KeyValues *pRootSubkey = valRoot; valRoot = pRootSubkey->GetNextKey();
bool bValidKey = false; char const *szRootSubkeyName = pRootSubkey->GetName(); for ( int k = 0; k < ARRAYSIZE( arrKeys ); ++ k ) { if ( !Q_stricmp( arrKeys[k], szRootSubkeyName ) ) { bValidKey = true; break; } } if ( !bValidKey ) { m_pSettings->RemoveSubKey( pRootSubkey ); pRootSubkey->deleteThis(); } }
// Since the session can be created with a minimal amount of data available
// the session object is responsible for initializing the missing data to defaults
// or saved values or values from gamer progress/profile or etc...
if ( KeyValues *kv = m_pSettings->FindKey( "system", true ) ) { KeyValuesAddDefaultString( kv, "network", "LIVE" ); KeyValuesAddDefaultString( kv, "access", "public" ); }
bool bEncryptedServerAddressRequired = false;
if ( KeyValues *kv = m_pSettings->FindKey( "options", true ) ) { char const *szServerType = kv->GetString( "server", NULL ); if ( !szServerType || Q_stricmp( szServerType, "official") ) szServerType = "official"; if ( Q_stricmp( "LIVE", m_pSettings->GetString( "system/network" ) ) ) szServerType = "listen"; KeyValuesAddDefaultString( kv, "server", szServerType ); // Remove "action" key if it doesn't hold useful data
if ( KeyValues *kvAction = kv->FindKey( "action" ) ) { char const *szAction = kvAction->GetString(); if ( szAction && !Q_stricmp( szAction, "crypt" ) ) bEncryptedServerAddressRequired = true;
if ( !szAction || !*szAction || !Q_stricmp( "create", szAction ) || !Q_stricmp( "crypt", szAction ) ) { kv->RemoveSubKey( kvAction ); kvAction->deleteThis(); } } }
// Reset the members key
if ( KeyValues *pMembers = m_pSettings->FindKey( "members", true ) ) { pMembers->SetInt( "numMachines", 1 );
int numPlayers = 1; #ifdef _GAMECONSOLE
numPlayers = XBX_GetNumGameUsers(); #endif
pMembers->SetInt( "numPlayers", numPlayers ); pMembers->SetInt( "numSlots", MAX( numSlotsCreated, numPlayers ) ); if ( KeyValues *pMachine = pMembers->FindKey( "machine0", true ) ) { XUID machineid = g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() )->GetXUID();
pMachine->SetUint64( "id", machineid ); #if defined( _PS3 ) && !defined( NO_STEAM )
pMachine->SetUint64( "psnid", steamapicontext->SteamUser()->GetConsoleSteamID().ConvertToUint64() ); #endif
pMachine->SetUint64( "flags", MatchSession_GetMachineFlags() ); pMachine->SetInt( "numPlayers", numPlayers ); pMachine->SetUint64( "dlcmask", g_pMatchFramework->GetMatchSystem()->GetDlcManager()->GetDataInfo()->GetUint64( "@info/installed" ) ); pMachine->SetString( "tuver", MatchSession_GetTuInstalledString() ); pMachine->SetInt( "ping", 0 );
for ( int k = 0; k < numPlayers; ++ k ) { if ( KeyValues *pPlayer = pMachine->FindKey( CFmtStr( "player%d", k ), true ) ) { int iController = 0; #ifdef _GAMECONSOLE
iController = XBX_GetUserId( k ); #endif
IPlayerLocal *player = g_pPlayerManager->GetLocalPlayer( iController );
pPlayer->SetUint64( "xuid", player->GetXUID() ); pPlayer->SetString( "name", player->GetName() ); } } } }
if ( bEncryptedServerAddressRequired ) { // We need to encrypt the address if there's one
if ( char const *szAdrOnline = MatchSession_EncryptAddressString( m_pSettings->GetString( "server/adronline" ), m_pSysData->GetUint64( "crypt" ) ) ) m_pSettings->SetString( "server/adronline", szAdrOnline ); if ( char const *szAdrOnline = MatchSession_EncryptAddressString( m_pSettings->GetString( "server/adrlocal" ), m_pSysData->GetUint64( "crypt" ) ) ) m_pSettings->SetString( "server/adrlocal", szAdrOnline ); }
// Let the title extend the game settings
g_pMMF->GetMatchTitleGameSettingsMgr()->InitializeGameSettings( m_pSettings, "host" );
DevMsg( "CMatchSessionOnlineHost::InitializeGameSettings adjusted settings:\n" ); KeyValuesDumpAsDevMsg( m_pSettings, 1 ); }
|