Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

771 lines
21 KiB

  1. //===== Copyright 1996-2009, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "mm_framework.h"
  7. #include "vstdlib/random.h"
  8. #include "fmtstr.h"
  9. // memdbgon must be the last include file in a .cpp file!!!
  10. #include "tier0/memdbgon.h"
  11. static ConVar mm_ignored_sessions_forget_time( "mm_ignored_sessions_forget_time", "600", FCVAR_DEVELOPMENTONLY );
  12. static ConVar mm_ignored_sessions_forget_pass( "mm_ignored_sessions_forget_pass", "5", FCVAR_DEVELOPMENTONLY );
  13. class CIgnoredSessionsMgr
  14. {
  15. public:
  16. CIgnoredSessionsMgr();
  17. public:
  18. void Reset();
  19. void OnSearchStarted();
  20. bool IsIgnored( XNKID xid );
  21. void Ignore( XNKID xid );
  22. protected:
  23. struct SessionSearchPass_t
  24. {
  25. double m_flTime;
  26. int m_nSearchCounter;
  27. };
  28. static bool XNKID_LessFunc( const XNKID &lhs, const XNKID &rhs )
  29. {
  30. return ( (uint64 const&) lhs ) < ( (uint64 const&) rhs );
  31. }
  32. CUtlMap< XNKID, SessionSearchPass_t > m_IgnoredSessionsAndTime;
  33. int m_nSearchCounter;
  34. };
  35. static CIgnoredSessionsMgr g_IgnoredSessionsMgr;
  36. static CUtlMap< uint32, float > g_mapValidatedWhitelistCacheTimestamps( DefLessFunc( uint32 ) );
  37. CON_COMMAND_F( mm_ignored_sessions_reset, "Reset ignored sessions", FCVAR_DEVELOPMENTONLY )
  38. {
  39. g_IgnoredSessionsMgr.Reset();
  40. DevMsg( "Reset ignored sessions" );
  41. }
  42. CIgnoredSessionsMgr::CIgnoredSessionsMgr() :
  43. m_IgnoredSessionsAndTime( XNKID_LessFunc ),
  44. m_nSearchCounter( 0 )
  45. {
  46. }
  47. void CIgnoredSessionsMgr::Reset()
  48. {
  49. m_nSearchCounter = 0;
  50. m_IgnoredSessionsAndTime.RemoveAll();
  51. }
  52. void CIgnoredSessionsMgr::OnSearchStarted()
  53. {
  54. ++ m_nSearchCounter;
  55. double fNow = Plat_FloatTime();
  56. double const fKeepIgnoredTime = mm_ignored_sessions_forget_time.GetFloat();
  57. int const numIgnoredSearches = mm_ignored_sessions_forget_pass.GetInt();
  58. // Keep sessions for only so long...
  59. for ( int x = m_IgnoredSessionsAndTime.FirstInorder();
  60. x != m_IgnoredSessionsAndTime.InvalidIndex(); )
  61. {
  62. SessionSearchPass_t ssp = m_IgnoredSessionsAndTime.Element( x );
  63. int xNext = m_IgnoredSessionsAndTime.NextInorder( x );
  64. if ( fabs( fNow - ssp.m_flTime ) > fKeepIgnoredTime &&
  65. m_nSearchCounter - ssp.m_nSearchCounter > numIgnoredSearches )
  66. {
  67. m_IgnoredSessionsAndTime.RemoveAt( x );
  68. }
  69. x = xNext;
  70. }
  71. }
  72. bool CIgnoredSessionsMgr::IsIgnored( XNKID xid )
  73. {
  74. return ( m_IgnoredSessionsAndTime.Find( xid ) != m_IgnoredSessionsAndTime.InvalidIndex() );
  75. }
  76. void CIgnoredSessionsMgr::Ignore( XNKID xid )
  77. {
  78. if ( ( const uint64 & )xid == 0ull )
  79. return;
  80. SessionSearchPass_t ssp = { Plat_FloatTime(), m_nSearchCounter };
  81. m_IgnoredSessionsAndTime.InsertOrReplace( xid, ssp );
  82. }
  83. //
  84. // CMatchSessionOnlineSearch
  85. //
  86. // Implementation of an online session search (aka matchmaking)
  87. //
  88. CMatchSessionOnlineSearch::CMatchSessionOnlineSearch( KeyValues *pSettings ) :
  89. m_pSettings( pSettings->MakeCopy() ),
  90. m_autodelete_pSettings( m_pSettings ),
  91. m_eState( STATE_INIT ),
  92. m_pSysSession( NULL ),
  93. m_pMatchSearcher( NULL ),
  94. m_result( RESULT_UNDEFINED ),
  95. m_pSysSessionConTeam (NULL),
  96. #if !defined( NO_STEAM )
  97. m_pServerListListener( NULL ),
  98. #endif
  99. m_flInitializeTimestamp( 0.0f )
  100. {
  101. DevMsg( "Created CMatchSessionOnlineSearch:\n" );
  102. KeyValuesDumpAsDevMsg( m_pSettings, 1 );
  103. }
  104. CMatchSessionOnlineSearch::CMatchSessionOnlineSearch() :
  105. m_pSettings( NULL ),
  106. m_autodelete_pSettings( (KeyValues*)NULL ),
  107. m_eState( STATE_INIT ),
  108. m_pSysSession( NULL ),
  109. m_pMatchSearcher( NULL ),
  110. m_result( RESULT_UNDEFINED ),
  111. m_pSysSessionConTeam (NULL),
  112. m_flInitializeTimestamp( 0.0f )
  113. {
  114. }
  115. CMatchSessionOnlineSearch::~CMatchSessionOnlineSearch()
  116. {
  117. if ( m_pMatchSearcher )
  118. m_pMatchSearcher->Destroy();
  119. m_pMatchSearcher = NULL;
  120. DevMsg( "Destroying CMatchSessionOnlineSearch:\n" );
  121. KeyValuesDumpAsDevMsg( m_pSettings, 1 );
  122. }
  123. KeyValues * CMatchSessionOnlineSearch::GetSessionSettings()
  124. {
  125. return m_pSettings;
  126. }
  127. void CMatchSessionOnlineSearch::UpdateSessionSettings( KeyValues *pSettings )
  128. {
  129. Warning( "CMatchSessionOnlineSearch::UpdateSessionSettings is unavailable in state %d!\n", m_eState );
  130. Assert( !"CMatchSessionOnlineSearch::UpdateSessionSettings is unavailable!\n" );
  131. }
  132. void CMatchSessionOnlineSearch::Command( KeyValues *pCommand )
  133. {
  134. Warning( "CMatchSessionOnlineSearch::Command is unavailable!\n" );
  135. Assert( !"CMatchSessionOnlineSearch::Command is unavailable!\n" );
  136. }
  137. uint64 CMatchSessionOnlineSearch::GetSessionID()
  138. {
  139. return 0;
  140. }
  141. #if !defined( NO_STEAM )
  142. extern volatile uint32 *g_hRankingSetupCallHandle;
  143. void CMatchSessionOnlineSearch::SetupSteamRankingConfiguration()
  144. {
  145. KeyValues *kvNotification = new KeyValues( "SetupSteamRankingConfiguration" );
  146. kvNotification->SetPtr( "settingsptr", m_pSettings );
  147. kvNotification->SetPtr( "callhandleptr", ( void * ) &g_hRankingSetupCallHandle );
  148. g_pMatchEventsSubscription->BroadcastEvent( kvNotification );
  149. }
  150. bool CMatchSessionOnlineSearch::IsSteamRankingConfigured() const
  151. {
  152. return !g_hRankingSetupCallHandle || !*g_hRankingSetupCallHandle;
  153. }
  154. #endif
  155. extern ConVar mm_session_sys_ranking_timeout;
  156. extern ConVar mm_session_search_qos_timeout;
  157. void CMatchSessionOnlineSearch::Update()
  158. {
  159. switch ( m_eState )
  160. {
  161. case STATE_INIT:
  162. if ( !m_flInitializeTimestamp )
  163. {
  164. m_flInitializeTimestamp = Plat_FloatTime();
  165. #if !defined( NO_STEAM )
  166. SetupSteamRankingConfiguration();
  167. #endif
  168. }
  169. #if !defined( NO_STEAM )
  170. if ( !IsSteamRankingConfigured() && ( Plat_FloatTime() < m_flInitializeTimestamp + mm_session_sys_ranking_timeout.GetFloat() ) )
  171. break;
  172. #endif
  173. m_eState = STATE_SEARCHING;
  174. // Kick off the search
  175. g_IgnoredSessionsMgr.OnSearchStarted();
  176. m_pMatchSearcher = OnStartSearching();
  177. // Update our settings with match searcher
  178. m_pSettings->deleteThis();
  179. m_pSettings = m_pMatchSearcher->GetSearchSettings()->MakeCopy();
  180. m_autodelete_pSettings.Assign( m_pSettings );
  181. // Run the first frame update on the searcher
  182. m_pMatchSearcher->Update();
  183. break;
  184. case STATE_SEARCHING:
  185. // Waiting for session search to complete
  186. m_pMatchSearcher->Update();
  187. break;
  188. case STATE_JOIN_NEXT:
  189. StartJoinNextFoundSession();
  190. break;
  191. #if !defined( NO_STEAM )
  192. case STATE_VALIDATING_WHITELIST:
  193. if ( Plat_FloatTime() > m_flInitializeTimestamp + mm_session_search_qos_timeout.GetFloat() )
  194. {
  195. DevWarning( "Steam whitelist validation timed out.\n" );
  196. Steam_OnDedicatedServerListFetched();
  197. }
  198. break;
  199. #endif
  200. case STATE_JOINING:
  201. // Waiting for the join negotiation
  202. if ( m_pSysSession )
  203. {
  204. m_pSysSession->Update();
  205. }
  206. if (m_pSysSessionConTeam)
  207. {
  208. m_pSysSessionConTeam->Update();
  209. switch ( m_pSysSessionConTeam->GetResult() )
  210. {
  211. case CSysSessionConTeamHost::RESULT_SUCCESS:
  212. OnSearchCompletedSuccess( NULL, m_pSettings );
  213. break;
  214. case CSysSessionConTeamHost::RESULT_FAIL:
  215. m_pSysSessionConTeam->Destroy();
  216. m_pSysSessionConTeam = NULL;
  217. // Try next session
  218. m_eState = STATE_JOIN_NEXT;
  219. break;
  220. }
  221. }
  222. break;
  223. }
  224. }
  225. void CMatchSessionOnlineSearch::Destroy()
  226. {
  227. // Stop the search
  228. if ( m_pMatchSearcher )
  229. {
  230. m_pMatchSearcher->Destroy();
  231. m_pMatchSearcher = NULL;
  232. }
  233. // If we are in the middle of connecting,
  234. // abort
  235. if ( m_pSysSession )
  236. {
  237. m_pSysSession->Destroy();
  238. m_pSysSession = NULL;
  239. }
  240. if ( m_pSysSessionConTeam )
  241. {
  242. m_pSysSessionConTeam->Destroy();
  243. m_pSysSessionConTeam = NULL;
  244. }
  245. #if !defined( NO_STEAM )
  246. if ( m_pServerListListener )
  247. {
  248. m_pServerListListener->Destroy();
  249. m_pServerListListener = NULL;
  250. }
  251. #endif
  252. delete this;
  253. }
  254. void CMatchSessionOnlineSearch::DebugPrint()
  255. {
  256. DevMsg( "CMatchSessionOnlineSearch [ state=%d ]\n", m_eState );
  257. DevMsg( "System data:\n" );
  258. KeyValuesDumpAsDevMsg( GetSessionSystemData(), 1 );
  259. DevMsg( "Settings data:\n" );
  260. KeyValuesDumpAsDevMsg( GetSessionSettings(), 1 );
  261. if ( m_pSysSession )
  262. m_pSysSession->DebugPrint();
  263. else
  264. DevMsg( "SysSession is NULL\n" );
  265. DevMsg( "Search results outstanding: %d\n", m_arrSearchResults.Count() );
  266. }
  267. void CMatchSessionOnlineSearch::OnEvent( KeyValues *pEvent )
  268. {
  269. char const *szEvent = pEvent->GetName();
  270. if ( !Q_stricmp( "mmF->SysSessionUpdate", szEvent ) )
  271. {
  272. if ( m_pSysSession && pEvent->GetPtr( "syssession", NULL ) == m_pSysSession )
  273. {
  274. // This is our session
  275. switch ( m_eState )
  276. {
  277. case STATE_JOINING:
  278. // Session was creating
  279. if ( char const *szError = pEvent->GetString( "error", NULL ) )
  280. {
  281. // Destroy the session
  282. m_pSysSession->Destroy();
  283. m_pSysSession = NULL;
  284. // Go ahead and join next available session
  285. m_eState = STATE_JOIN_NEXT;
  286. }
  287. else
  288. {
  289. // We have received an entirely new "settings" data and copied that to our "settings" data
  290. m_eState = STATE_CLOSING;
  291. // Now we need to create a new client session
  292. CSysSessionClient *pSysSession = m_pSysSession;
  293. KeyValues *pSettings = m_pSettings;
  294. // Release ownership of the resources since new match session now owns them
  295. m_pSysSession = NULL;
  296. m_pSettings = NULL;
  297. m_autodelete_pSettings.Assign( NULL );
  298. OnSearchCompletedSuccess( pSysSession, pSettings );
  299. return;
  300. }
  301. break;
  302. }
  303. }
  304. }
  305. }
  306. void CMatchSessionOnlineSearch::OnSearchEvent( KeyValues *pNotify )
  307. {
  308. g_pMatchEventsSubscription->BroadcastEvent( pNotify );
  309. }
  310. CSysSessionClient * CMatchSessionOnlineSearch::OnBeginJoiningSearchResult()
  311. {
  312. return new CSysSessionClient( m_pSettings );
  313. }
  314. void CMatchSessionOnlineSearch::OnSearchDoneNoResultsMatch()
  315. {
  316. m_eState = STATE_CLOSING;
  317. // Reset ignored session tracker
  318. g_IgnoredSessionsMgr.Reset();
  319. // Just go ahead and create the session
  320. KeyValues *pSettings = m_pSettings;
  321. m_pSettings = NULL;
  322. m_autodelete_pSettings.Assign( NULL );
  323. OnSearchCompletedEmpty( pSettings );
  324. }
  325. void CMatchSessionOnlineSearch::OnSearchCompletedSuccess( CSysSessionClient *pSysSession, KeyValues *pSettings )
  326. {
  327. m_result = RESULT_SUCCESS;
  328. // Note that m_pSysSessionConTeam will be NULL if this is an individual joining a
  329. // match that was started with con team
  330. KeyValues *teamMatch = pSettings->FindKey( "options/conteammatch" );
  331. if ( teamMatch && m_pSysSessionConTeam )
  332. {
  333. DevMsg( "OnlineSearch - ConTeam host reserved session\n" );
  334. KeyValuesDumpAsDevMsg( pSettings );
  335. int numPlayers, sides[10];
  336. uint64 playerIds[10];
  337. if ( !m_pSysSessionConTeam->GetPlayerSidesAssignment( &numPlayers, playerIds, sides ) )
  338. {
  339. // Something went badly wrong, bail
  340. m_result = RESULT_FAIL;
  341. }
  342. else
  343. {
  344. // Send out a "joinsession" event. This is picked up by the host and sent out
  345. // to all machines machines in lobby.
  346. KeyValues *joinSession = new KeyValues(
  347. "OnMatchSessionUpdate",
  348. "state", "joinconteamsession"
  349. );
  350. #if defined (_X360)
  351. uint64 sessionId = pSettings->GetUint64( "options/sessionid", 0 );
  352. const char *sessionInfo = pSettings->GetString( "options/sessioninfo", "" );
  353. joinSession->SetUint64( "sessionid", sessionId );
  354. joinSession->SetString( "sessioninfo", sessionInfo );
  355. // Unpack sessionHostData
  356. KeyValues *pSessionHostData = (KeyValues*)pSettings->GetPtr( "options/sessionHostData" );
  357. if ( pSessionHostData )
  358. {
  359. KeyValues *pSessionHostDataUnpacked = joinSession->CreateNewKey();
  360. pSessionHostDataUnpacked->SetName("sessionHostDataUnpacked");
  361. pSessionHostData->CopySubkeys( pSessionHostDataUnpacked );
  362. }
  363. #else
  364. joinSession->SetUint64( "sessionid", m_pSysSessionConTeam->GetSessionID() );
  365. #endif
  366. KeyValues *pTeamMembers = joinSession->CreateNewKey();
  367. pTeamMembers->SetName( "teamMembers" );
  368. pTeamMembers->SetInt( "numPlayers", numPlayers );
  369. // Assign players to different teams
  370. for ( int i = 0; i < numPlayers; i++ )
  371. {
  372. KeyValues *pTeamPlayer = pTeamMembers->CreateNewKey();
  373. pTeamPlayer->SetName( CFmtStr( "player%d", i ) );
  374. pTeamPlayer->SetUint64( "xuid", playerIds[i] );
  375. pTeamPlayer->SetInt( "team", sides[i] );
  376. }
  377. OnSearchEvent( joinSession );
  378. }
  379. }
  380. else
  381. {
  382. // Destroy our instance and point at the new match interface
  383. CMatchSessionOnlineClient *pNewSession = new CMatchSessionOnlineClient( pSysSession, pSettings );
  384. g_pMMF->SetCurrentMatchSession( pNewSession );
  385. this->Destroy();
  386. DevMsg( "OnlineSearch - client fully connected to session, search finished.\n" );
  387. pNewSession->OnClientFullyConnectedToSession();
  388. }
  389. }
  390. void CMatchSessionOnlineSearch::OnSearchCompletedEmpty( KeyValues *pSettings )
  391. {
  392. KeyValues::AutoDelete autodelete_pSettings( pSettings );
  393. m_result = RESULT_FAIL;
  394. KeyValues *notify = new KeyValues(
  395. "OnMatchSessionUpdate",
  396. "state", "progress",
  397. "progress", "searchempty"
  398. );
  399. notify->SetPtr( "settingsptr", pSettings );
  400. OnSearchEvent( notify );
  401. if ( !Q_stricmp( pSettings->GetString( "options/searchempty" ), "close" ) )
  402. {
  403. g_pMatchFramework->CloseSession();
  404. return;
  405. }
  406. // If this is a team session then stop here and let the team host decide what
  407. // to do next
  408. KeyValues *teamMatch = pSettings->FindKey( "options/conteammatch" );
  409. if ( teamMatch )
  410. {
  411. return;
  412. }
  413. // Preserve the "options/bypasslobby" key
  414. bool bypassLobby = pSettings->GetBool( "options/bypasslobby", false );
  415. // Preserve the "options/server" key
  416. char serverType[64];
  417. const char *prevServerType = pSettings->GetString( "options/server", NULL );
  418. if ( prevServerType )
  419. {
  420. Q_strncpy( serverType, prevServerType, sizeof( serverType ) );
  421. }
  422. // Remove "options" key
  423. if ( KeyValues *kvOptions = pSettings->FindKey( "options" ) )
  424. {
  425. pSettings->RemoveSubKey( kvOptions );
  426. kvOptions->deleteThis();
  427. }
  428. pSettings->SetString( "options/createreason", "searchempty" );
  429. if ( bypassLobby )
  430. {
  431. pSettings->SetBool( "options/bypasslobby", bypassLobby );
  432. }
  433. if ( prevServerType )
  434. {
  435. pSettings->SetString( "options/server", serverType );
  436. }
  437. DevMsg( "Search completed empty - creating a new session\n" );
  438. KeyValuesDumpAsDevMsg( pSettings );
  439. g_pMatchFramework->CreateSession( pSettings );
  440. }
  441. void CMatchSessionOnlineSearch::UpdateTeamProperties( KeyValues *pTeamProperties )
  442. {
  443. }
  444. void CMatchSessionOnlineSearch::StartJoinNextFoundSession()
  445. {
  446. if ( !m_arrSearchResults.Count() )
  447. {
  448. OnSearchDoneNoResultsMatch();
  449. return;
  450. }
  451. // Session is joining
  452. KeyValues *notify = new KeyValues(
  453. "OnMatchSessionUpdate",
  454. "state", "progress",
  455. "progress", "searchresult"
  456. );
  457. notify->SetInt( "numResults", m_arrSearchResults.Count() );
  458. OnSearchEvent( notify );
  459. // Peek at the next search result
  460. CMatchSearcher::SearchResult_t const &sr = *m_arrSearchResults.Head();
  461. // Register it into the ignored session pool
  462. g_IgnoredSessionsMgr.Ignore( sr.GetXNKID() );
  463. // Make a validation query
  464. ValidateSearchResultWhitelist();
  465. }
  466. static uint32 OfficialWhitelistClientCachedAddress( uint32 uiServerIP )
  467. {
  468. /** Removed for partner depot **/
  469. return 0;
  470. }
  471. void CMatchSessionOnlineSearch::ValidateSearchResultWhitelist()
  472. {
  473. #if !defined( NO_STEAM )
  474. // In case of official matchmaking we need to validate that the server
  475. // that we are about to join actually is whitelisted official server
  476. if ( !m_pSettings->GetInt( "game/hosted" ) )
  477. {
  478. // Peek at the next search result
  479. CMatchSearcher::SearchResult_t const &sr = *m_arrSearchResults.Head();
  480. if ( ( g_mapValidatedWhitelistCacheTimestamps.Find( sr.m_svAdr.GetIPHostByteOrder() ) == g_mapValidatedWhitelistCacheTimestamps.InvalidIndex() ) &&
  481. !OfficialWhitelistClientCachedAddress( sr.m_svAdr.GetIPHostByteOrder() )
  482. )
  483. {
  484. // This server needs to be validated
  485. m_flInitializeTimestamp = Plat_FloatTime();
  486. m_eState = STATE_VALIDATING_WHITELIST;
  487. CUtlVector< MatchMakingKeyValuePair_t > filters;
  488. filters.EnsureCapacity( 10 );
  489. filters.AddToTail( MatchMakingKeyValuePair_t( "gamedir", COM_GetModDirectory() ) );
  490. filters.AddToTail( MatchMakingKeyValuePair_t( "addr", sr.m_svAdr.ToString() ) );
  491. filters.AddToTail( MatchMakingKeyValuePair_t( "white", "1" ) );
  492. m_pServerListListener = new CServerListListener( this, filters );
  493. return;
  494. }
  495. }
  496. #endif
  497. ConnectJoinLobbyNextFoundSession();
  498. }
  499. #if !defined( NO_STEAM )
  500. CMatchSessionOnlineSearch::CServerListListener::CServerListListener( CMatchSessionOnlineSearch *pDsSearcher, CUtlVector< MatchMakingKeyValuePair_t > &filters ) :
  501. m_pOuter( pDsSearcher ),
  502. m_hRequest( NULL )
  503. {
  504. MatchMakingKeyValuePair_t *pFilter = filters.Base();
  505. DevMsg( 1, "Requesting dedicated whitelist validation...\n" );
  506. for (int i = 0; i < filters.Count(); i++)
  507. {
  508. DevMsg("Filter %d: %s=%s\n", i, filters.Element(i).m_szKey, filters.Element(i).m_szValue);
  509. }
  510. m_hRequest = steamapicontext->SteamMatchmakingServers()->RequestInternetServerList(
  511. ( AppId_t ) g_pMatchFramework->GetMatchTitle()->GetTitleID(), &pFilter,
  512. filters.Count(), this );
  513. }
  514. void CMatchSessionOnlineSearch::CServerListListener::Destroy()
  515. {
  516. m_pOuter = NULL;
  517. if ( m_hRequest )
  518. steamapicontext->SteamMatchmakingServers()->ReleaseRequest( m_hRequest );
  519. m_hRequest = NULL;
  520. delete this;
  521. }
  522. void CMatchSessionOnlineSearch::CServerListListener::HandleServerResponse( HServerListRequest hReq, int iServer, bool bResponded )
  523. {
  524. // Register the result
  525. if ( bResponded )
  526. {
  527. gameserveritem_t *pServer = steamapicontext->SteamMatchmakingServers()
  528. ->GetServerDetails( hReq, iServer );
  529. DevMsg( 1, "Successfully validated whitelist for %s...\n", pServer->m_NetAdr.GetConnectionAddressString() );
  530. g_mapValidatedWhitelistCacheTimestamps.InsertOrReplace( pServer->m_NetAdr.GetIP(), Plat_FloatTime() );
  531. }
  532. }
  533. void CMatchSessionOnlineSearch::CServerListListener::RefreshComplete( HServerListRequest hReq, EMatchMakingServerResponse response )
  534. {
  535. if ( m_pOuter )
  536. {
  537. m_pOuter->Steam_OnDedicatedServerListFetched();
  538. }
  539. }
  540. void CMatchSessionOnlineSearch::Steam_OnDedicatedServerListFetched()
  541. {
  542. if ( m_pServerListListener )
  543. {
  544. m_pServerListListener->Destroy();
  545. m_pServerListListener = NULL;
  546. }
  547. // Peek at the next search result
  548. if ( m_arrSearchResults.Count() )
  549. {
  550. CMatchSearcher::SearchResult_t const &sr = *m_arrSearchResults.Head();
  551. if ( g_mapValidatedWhitelistCacheTimestamps.Find( sr.m_svAdr.GetIPHostByteOrder() ) == g_mapValidatedWhitelistCacheTimestamps.InvalidIndex() )
  552. {
  553. DevWarning( 1, "Failed to validate whitelist for %s...\n", sr.m_svAdr.ToString( true ) );
  554. FOR_EACH_VEC_BACK( m_arrSearchResults, itSR )
  555. {
  556. if ( m_arrSearchResults[itSR]->m_svAdr.GetIPHostByteOrder() == sr.m_svAdr.GetIPHostByteOrder() )
  557. {
  558. m_arrSearchResults.Remove( itSR );
  559. }
  560. }
  561. }
  562. }
  563. m_eState = STATE_JOIN_NEXT;
  564. }
  565. #endif
  566. void CMatchSessionOnlineSearch::ConnectJoinLobbyNextFoundSession()
  567. {
  568. // Pop the search result
  569. CMatchSearcher::SearchResult_t const &sr = *m_arrSearchResults.Head();
  570. m_arrSearchResults.RemoveMultipleFromHead( 1 );
  571. // Set the settings to connect with
  572. if ( KeyValues *kvOptions = m_pSettings->FindKey( "options", true ) )
  573. {
  574. #ifdef _X360
  575. kvOptions->SetUint64( "sessionid", ( const uint64 & ) sr.m_info.sessionID );
  576. char chSessionInfo[ XSESSION_INFO_STRING_LENGTH ] = {0};
  577. MMX360_SessionInfoToString( sr.m_info, chSessionInfo );
  578. kvOptions->SetString( "sessioninfo", chSessionInfo );
  579. kvOptions->SetPtr( "sessionHostData", sr.GetGameDetails() );
  580. KeyValuesDumpAsDevMsg( sr.GetGameDetails(), 1, 2 );
  581. #else
  582. kvOptions->SetUint64( "sessionid", sr.m_uiLobbyId );
  583. #endif
  584. }
  585. // Trigger client session creation
  586. Msg( "[MM] Joining session %llx, %d search results remaining...\n",
  587. m_pSettings->GetUint64( "options/sessionid", 0ull ),
  588. m_arrSearchResults.Count() );
  589. KeyValues *teamMatch = m_pSettings->FindKey( "options/conteammatch" );
  590. if ( teamMatch )
  591. {
  592. m_pSysSessionConTeam = new CSysSessionConTeamHost( m_pSettings );
  593. }
  594. else
  595. {
  596. m_pSysSession = OnBeginJoiningSearchResult();
  597. }
  598. m_eState = STATE_JOINING;
  599. }
  600. CMatchSearcher_OnlineSearch::CMatchSearcher_OnlineSearch( CMatchSessionOnlineSearch *pSession, KeyValues *pSettings ) :
  601. CMatchSearcher( pSettings ),
  602. m_pSession( pSession )
  603. {
  604. }
  605. void CMatchSearcher_OnlineSearch::OnSearchEvent( KeyValues *pNotify )
  606. {
  607. m_pSession->OnSearchEvent( pNotify );
  608. }
  609. void CMatchSearcher_OnlineSearch::OnSearchDone()
  610. {
  611. // Let the base searcher finalize results
  612. CMatchSearcher::OnSearchDone();
  613. // Iterate over search results
  614. for ( int k = 0, kNum = GetNumSearchResults(); k < kNum; ++ k )
  615. {
  616. SearchResult_t const &sr = GetSearchResult( k );
  617. if ( !g_IgnoredSessionsMgr.IsIgnored( sr.GetXNKID() ) )
  618. m_pSession->m_arrSearchResults.AddToTail( &sr );
  619. }
  620. if ( !m_pSession->m_arrSearchResults.Count() )
  621. {
  622. m_pSession->OnSearchDoneNoResultsMatch();
  623. return;
  624. }
  625. // Go ahead and start joining the results
  626. DevMsg( "Establishing connection with %d search results.\n", m_pSession->m_arrSearchResults.Count() );
  627. m_pSession->m_eState = m_pSession->STATE_JOIN_NEXT;
  628. }
  629. CMatchSearcher * CMatchSessionOnlineSearch::OnStartSearching()
  630. {
  631. CMatchSearcher *pMS = new CMatchSearcher_OnlineSearch( this, m_pSettings->MakeCopy() );
  632. // Let the title extend the game settings
  633. g_pMMF->GetMatchTitleGameSettingsMgr()->InitializeGameSettings( pMS->GetSearchSettings(), "search_online" );
  634. DevMsg( "CMatchSearcher_OnlineSearch title adjusted settings:\n" );
  635. KeyValuesDumpAsDevMsg( pMS->GetSearchSettings(), 1 );
  636. return pMS;
  637. }