//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Handles host migration for a session (not for the game server) // //=============================================================================// #include "matchmaking.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- // Purpose: Start a Matchmaking session as the host //----------------------------------------------------------------------------- CClientInfo *CMatchmaking::SelectNewHost() { // For now, just grab the first guy in the list CClientInfo *pClient = &m_Local; for ( int i = 0; i < m_Remote.Count(); ++i ) { if ( m_Remote[i]->m_id > pClient->m_id ) { pClient = m_Remote[i]; } } return pClient; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CMatchmaking::StartHostMigration() { SwitchToState( MMSTATE_HOSTMIGRATE_STARTINGMIGRATION ); m_pNewHost = SelectNewHost(); if ( m_pNewHost == &m_Local ) { // We're the new host, so start hosting Msg( "Starting new host" ); BeginHosting(); } else { Msg( "Waiting for a new host" ); SwitchToNewHost(); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CMatchmaking::BeginHosting() { m_Session.SetIsHost( true ); m_Host = m_Local; // Move into private slots if ( !m_Local.m_bInvited ) { RemovePlayersFromSession( &m_Local ); m_Local.m_bInvited = true; AddPlayersToSession( &m_Local ); } if ( !m_Session.MigrateHost() ) { Warning( "Session migrate failed!\n" ); SessionNotification( SESSION_NOTIFY_FAIL_MIGRATE ); return; } SwitchToState( MMSTATE_HOSTMIGRATE_MIGRATING ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CMatchmaking::TellClientsToMigrate() { Msg( "Sending migrate request\n" ); XSESSION_INFO info; m_Session.GetNewSessionInfo( &info ); MM_Migrate msg; msg.m_MsgType = MM_Migrate::MESSAGE_HOSTING; msg.m_Id = m_Local.m_id; msg.m_sessionId = info.sessionID; msg.m_xnaddr = info.hostAddress; msg.m_key = info.keyExchangeKey; for ( int i = 0; i < m_Remote.Count(); ++i ) { if ( m_Remote[i]->m_bMigrated ) { continue; } SendMessage( &msg, &m_Remote[i]->m_adr ); } m_fSendTimer = GetTime(); ++m_nSendCount; } //----------------------------------------------------------------------------- // Purpose: Handle a migration message from our new host //----------------------------------------------------------------------------- bool CMatchmaking::ProcessMigrate( MM_Migrate *pMsg ) { MM_Migrate reply; int type = pMsg->m_MsgType; if ( m_CurrentState == MMSTATE_HOSTMIGRATE_WAITINGFORHOST ) { if ( type == MM_Migrate::MESSAGE_HOSTING ) { // Make sure this is the host we were expecting if ( !Q_memcmp( &pMsg->m_xnaddr, &m_Host.m_xnaddr, sizeof( m_Host.m_xnaddr ) ) ) { // Reply to the host reply.m_MsgType = MM_Migrate::MESSAGE_MIGRATED; reply.m_xnaddr = m_Local.m_xnaddr; SendMessage( &reply, &m_Host.m_adr ); XSESSION_INFO info; info.sessionID = pMsg->m_sessionId; info.hostAddress = pMsg->m_xnaddr; info.keyExchangeKey = pMsg->m_key; m_Session.SetNewSessionInfo( &info ); m_Session.SetOwnerId( XUSER_INDEX_NONE ); if ( !m_Session.MigrateHost() ) { Warning( "Session migrate failed!\n" ); SessionNotification( SESSION_NOTIFY_FAIL_MIGRATE ); return true; } SwitchToState( MMSTATE_HOSTMIGRATE_MIGRATING ); } else { // Someone else is trying to host reply.m_MsgType = MM_Migrate::MESSAGE_STANDBY; SendMessage( &reply, &m_Host.m_adr ); } } } else if ( m_CurrentState == MMSTATE_HOSTMIGRATE_WAITINGFORCLIENTS ) { if ( type == MM_Migrate::MESSAGE_MIGRATED ) { // Flag the client as having migrated bool bClientsOutstanding = false; for ( int i = 0; i < m_Remote.Count(); ++i ) { if ( m_Remote[i]->m_id == pMsg->m_Id ) { m_Remote[i]->m_bMigrated = true; } bClientsOutstanding = bClientsOutstanding && m_Remote[i]->m_bMigrated; } if ( !bClientsOutstanding ) { // Everyone's migrated! EndMigration(); } } if ( type == MM_Migrate::MESSAGE_STANDBY ) { // Someone requested a standby --m_nSendCount; } } return true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CMatchmaking::SwitchToNewHost() { // Set a timer to wait for the host to contact us m_fWaitTimer = GetTime(); // Get rid of the current host net channel MarkChannelForRemoval( &m_Host.m_adr ); AddRemoteChannel( &m_pNewHost->m_adr ); SwitchToState( MMSTATE_HOSTMIGRATE_WAITINGFORHOST ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CMatchmaking::EndMigration() { Msg( "Migration complete\n" ); if ( m_Session.IsHost() ) { // Drop any clients that failed to migrate for ( int i = m_Remote.Count()-1; i >= 0; --i ) { ClientDropped( m_Remote[i] ); } // Update the lobby to show the new host SendPlayerInfoToLobby( &m_Local, 0 ); // X360TBD: Figure out what state we should be in int newState = m_PreMigrateState; switch( m_PreMigrateState ) { case MMSTATE_SESSION_CONNECTING: newState = MMSTATE_ACCEPTING_CONNECTIONS; break; default: Warning( "Unhandled post-migrate state transition" ); } // Don't use SwitchToState() to set our new state because when changing // from a client to a host the state transition is usually invalid. m_CurrentState = newState; } else { // Still a client, just restore our previous state m_CurrentState = m_PreMigrateState; } } void CMatchmaking::TestStats() { }