/* ======================== idLobby::BecomeHost ======================== */ void idLobby::BecomeHost() { if( !verify( migrationInfo.state == MIGRATE_PICKING_HOST ) ) { idLib::Printf( "BecomeHost: Must be called from PickNewHost.\n" ); EndMigration(); return; } if( IsHost() ) { idLib::Printf( "BecomeHost: Already host of session.\n" ); EndMigration(); return; } if( !sessionCB->BecomingHost( *this ) ) { EndMigration(); return; } idLib::Printf( "BecomeHost: Sending %i invites on %s.\n", migrationInfo.invites.Num(), GetLobbyName() ); migrationInfo.state = MIGRATE_BECOMING_HOST; migrationInfo.migrationStartTime = Sys_Milliseconds(); if( lobbyBackend == NULL ) { // If we don't have a lobbyBackend, then just create one Shutdown(); StartCreating(); return; } // Shutdown the current lobby, but keep the lobbyBackend (we'll migrate it) Shutdown( true ); // Migrate the lobbyBackend to host lobbyBackend->BecomeHost( migrationInfo.invites.Num() ); // Wait for it to complete SetState( STATE_CREATE_LOBBY_BACKEND ); }
/* ======================== idLobby::UpdateHostMigration ======================== */ void idLobby::UpdateHostMigration() { int time = Sys_Milliseconds(); // If we are picking a new host, then update that if( migrationInfo.state == MIGRATE_PICKING_HOST ) { const int MIGRATION_PICKING_HOST_TIMEOUT_IN_SECONDS = 20; // FIXME: set back to 5 // Give other hosts 5 seconds if( time - migrationInfo.migrationStartTime > session->GetTitleStorageInt( "MIGRATION_PICKING_HOST_TIMEOUT_IN_SECONDS", MIGRATION_PICKING_HOST_TIMEOUT_IN_SECONDS ) * 1000 ) { // Just become the host if we haven't heard from a host in awhile BecomeHost(); } else { return; } } // See if we are a new migrated host that needs to invite the original members back if( migrationInfo.state != MIGRATE_BECOMING_HOST ) { return; } if( lobbyBackend == NULL || lobbyBackend->GetState() != idLobbyBackend::STATE_READY ) { return; } if( state != STATE_IDLE ) { return; } if( !IsHost() ) { return; } const int MIGRATION_TIMEOUT_IN_SECONDS = 30; // FIXME: setting to 30 for dev purposes. 10 seems more reasonable. Need to make unloading game / loading lobby async const int MIGRATION_INVITE_TIME_IN_SECONDS = 2; if( migrationInfo.invites.Num() == 0 || time - migrationInfo.migrationStartTime > session->GetTitleStorageInt( "MIGRATION_TIMEOUT_IN_SECONDS", MIGRATION_TIMEOUT_IN_SECONDS ) * 1000 ) { // Either everyone acked, or we timed out, just keep who we have, and stop sending invites EndMigration(); return; } // Send invites to anyone who hasn't responded for( int i = 0; i < migrationInfo.invites.Num(); i++ ) { if( time - migrationInfo.invites[i].lastInviteTime < session->GetTitleStorageInt( "MIGRATION_INVITE_TIME_IN_SECONDS", MIGRATION_INVITE_TIME_IN_SECONDS ) * 1000 ) { continue; // Not enough time passed } // Mark the time migrationInfo.invites[i].lastInviteTime = time; byte buffer[ idPacketProcessor::MAX_PACKET_SIZE - 2 ]; idBitMsg outmsg( buffer, sizeof( buffer ) ); // Have lobbyBackend fill out msg with connection info lobbyConnectInfo_t connectInfo = lobbyBackend->GetConnectInfo(); connectInfo.WriteToMsg( outmsg ); // Let them know whether or not this was from in game outmsg.WriteBool( migrationInfo.persistUntilGameEndsData.wasMigratedGame ); NET_VERBOSE_PRINT( "NET: Sending migration invite to %s\n", migrationInfo.invites[i].address.ToString() ); // Send the migration invite SendConnectionLess( migrationInfo.invites[i].address, OOB_MIGRATE_INVITE, outmsg.GetReadData(), outmsg.GetSize() ); } }
//----------------------------------------------------------------------------- // 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; }