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.

868 lines
25 KiB

  1. //===== Copyright � 1996-2009, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "mm_framework.h"
  7. #include "fmtstr.h"
  8. #include "vstdlib/random.h"
  9. #include "protocol.h"
  10. #include "proto_oob.h"
  11. #include "bitbuf.h"
  12. #include "checksum_crc.h"
  13. // memdbgon must be the last include file in a .cpp file!!!
  14. #include "tier0/memdbgon.h"
  15. ConVar mm_dedicated_allow( "mm_dedicated_allow", "1", FCVAR_DEVELOPMENTONLY, "1 = allow searches for dedicated servers" );
  16. ConVar mm_dedicated_fake( "mm_dedicated_fake", "0", FCVAR_DEVELOPMENTONLY, "1 = pretend like search is going, but abort after some time" );
  17. ConVar mm_dedicated_force_servers( "mm_dedicated_force_servers", "", FCVAR_RELEASE,
  18. "Comma delimited list of ip:port of servers used to search for dedicated servers instead of searching for public servers.\n"
  19. "Use syntax `publicip1:port|privateip1:port,publicip2:port|privateip2:port` if your server is behind NAT.\n"
  20. "If the server is behind NAT, you can specify `0.0.0.0|privateip:port` and if server port is in the list of `mm_server_search_lan_ports` its public address should be automatically detected." );
  21. ConVar mm_dedicated_ip( "mm_dedicated_ip", "", FCVAR_DEVELOPMENTONLY, "IP address of dedicated servers to consider available" );
  22. ConVar mm_dedicated_timeout_request( "mm_dedicated_timeout_request", "20", FCVAR_DEVELOPMENTONLY );
  23. ConVar mm_dedicated_search_maxping( "mm_dedicated_search_maxping", IsX360() ? "200" : "150", FCVAR_RELEASE | FCVAR_ARCHIVE, "Longest preferred ping to dedicated servers for games", true, 25.f, true, 350.f );
  24. ConVar mm_dedicated_search_maxresults( "mm_dedicated_search_maxresults", "75", FCVAR_DEVELOPMENTONLY );
  25. extern ConVar mm_dedicated_xlsp_timeout;
  26. CDsSearcher::CDsSearcher( KeyValues *pSettings, uint64 uiReserveCookie, IMatchSession *pMatchSession, uint64 ullCrypt ) :
  27. m_pSettings( pSettings ),
  28. m_uiReserveCookie( uiReserveCookie ),
  29. m_pReserveSettings( g_pMatchFramework->GetMatchNetworkMsgController()->PackageGameDetailsForReservation( m_pSettings ) ),
  30. m_autodelete_pReserveSettings( m_pReserveSettings ),
  31. #ifdef _X360
  32. m_pTitleServers( NULL ),
  33. #elif !defined( NO_STEAM )
  34. m_pServerListListener( NULL ),
  35. m_nSearchPass( 0 ),
  36. #endif
  37. m_eState( STATE_INIT ),
  38. m_flTimeout( 0.0f ),
  39. m_pAsyncOperation( NULL ),
  40. m_pMatchSession( pMatchSession ),
  41. m_ullCrypt( ullCrypt )
  42. {
  43. #ifdef _X360
  44. ZeroMemory( m_chDatacenterQuery, sizeof( m_chDatacenterQuery ) );
  45. ZeroMemory( &m_dc, sizeof( m_dc ) );
  46. #endif
  47. DevMsg( "Created DS searcher\n" );
  48. KeyValuesDumpAsDevMsg( m_pSettings );
  49. memset( &m_Result, 0, sizeof( m_Result ) );
  50. // Build the reservation settings
  51. // Load test
  52. m_bLoadTest = (m_pSettings->GetInt( "options/sv_load_test", 0) == 1);
  53. }
  54. CDsSearcher::~CDsSearcher()
  55. {
  56. ;
  57. }
  58. void CDsSearcher::Update()
  59. {
  60. switch ( m_eState )
  61. {
  62. case STATE_INIT:
  63. {
  64. char const *szNetwork = m_pSettings->GetString( "system/network", "" );
  65. char const *szServer = m_pSettings->GetString( "options/server", "listen" );
  66. if ( m_pSettings->GetString( "server/server", NULL ) )
  67. {
  68. InitWithKnownServer();
  69. }
  70. else if ( mm_dedicated_allow.GetBool() &&
  71. !Q_stricmp( "LIVE", szNetwork ) &&
  72. Q_stricmp( "listen", szServer ) &&
  73. !( g_pMatchFramework->GetMatchTitle()->GetTitleSettingsFlags() & MATCHTITLE_SETTING_NODEDICATED ) )
  74. {
  75. InitDedicatedSearch();
  76. }
  77. else
  78. {
  79. m_eState = STATE_FINISHED;
  80. }
  81. }
  82. break;
  83. case STATE_WAITING:
  84. {
  85. if ( Plat_FloatTime() > m_flTimeout )
  86. m_eState = STATE_FINISHED;
  87. }
  88. break;
  89. #ifdef _X360
  90. case STATE_XLSP_ENUMERATE_DCS:
  91. m_pTitleServers->Update();
  92. if ( m_pTitleServers->IsSearchCompleted() )
  93. Xlsp_OnEnumerateDcsCompleted();
  94. break;
  95. case STATE_XLSP_NEXT_DC:
  96. Xlsp_StartNextDc();
  97. break;
  98. case STATE_XLSP_REQUESTING_SERVERS:
  99. if ( Plat_FloatTime() > m_flTimeout )
  100. {
  101. DevWarning( "XLSP datacenter `%s` timed out.\n", m_dc.m_szGatewayName );
  102. m_dc.Destroy();
  103. m_eState = STATE_XLSP_NEXT_DC;
  104. }
  105. break;
  106. #elif !defined( NO_STEAM )
  107. case STATE_STEAM_REQUESTING_SERVERS:
  108. if ( Plat_FloatTime() > m_flTimeout )
  109. {
  110. DevWarning( "Steam search for dedicated servers timed out.\n" );
  111. Steam_OnDedicatedServerListFetched();
  112. }
  113. break;
  114. case STATE_STEAM_NEXT_SEARCH_PASS:
  115. Steam_SearchPass();
  116. break;
  117. #endif
  118. }
  119. }
  120. void CDsSearcher::OnEvent( KeyValues *pEvent )
  121. {
  122. char const *szEvent = pEvent->GetName();
  123. #ifdef _X360
  124. if ( m_eState == STATE_XLSP_REQUESTING_SERVERS &&
  125. !Q_stricmp( "M2A_SERVER_BATCH", szEvent ) )
  126. {
  127. void const *pData = pEvent->GetPtr( "ptr" );
  128. int numBytes = pEvent->GetInt( "size" );
  129. Xlsp_OnDcServerBatch( pData, numBytes );
  130. }
  131. #elif !defined( NO_STEAM )
  132. szEvent;
  133. #endif
  134. }
  135. void CDsSearcher::Destroy()
  136. {
  137. if ( m_pAsyncOperation )
  138. {
  139. m_pAsyncOperation->Release();
  140. m_pAsyncOperation = NULL;
  141. }
  142. #ifdef _X360
  143. switch ( m_eState )
  144. {
  145. case STATE_XLSP_ENUMERATE_DCS:
  146. if ( m_pTitleServers )
  147. m_pTitleServers->Destroy();
  148. m_pTitleServers = NULL;
  149. break;
  150. case STATE_XLSP_REQUESTING_SERVERS:
  151. case STATE_RESERVING:
  152. m_dc.Destroy();
  153. break;
  154. }
  155. #elif !defined( NO_STEAM )
  156. if ( m_pServerListListener )
  157. {
  158. m_pServerListListener->Destroy();
  159. m_pServerListListener = NULL;
  160. }
  161. #endif
  162. delete this;
  163. }
  164. bool CDsSearcher::IsFinished()
  165. {
  166. return m_eState == STATE_FINISHED;
  167. }
  168. CDsSearcher::DsResult_t const & CDsSearcher::GetResult()
  169. {
  170. return m_Result;
  171. }
  172. void CDsSearcher::DsResult_t::CopyToServerKey( KeyValues *pKvServer, uint64 ullCrypt ) const
  173. {
  174. Assert( m_bDedicated );
  175. pKvServer->SetString( "server", "dedicated" );
  176. #ifdef _X360
  177. pKvServer->SetString( "adrInsecure", m_szInsecureSendableServerAddress );
  178. #elif !defined( NO_STEAM )
  179. if ( char const *szEncrypted = MatchSession_EncryptAddressString( m_szPublicConnectionString, ullCrypt ) )
  180. pKvServer->SetString( "adronline", szEncrypted );
  181. else
  182. pKvServer->SetString( "adronline", m_szPublicConnectionString );
  183. if ( m_szPrivateConnectionString[0] )
  184. {
  185. if ( char const *szEncrypted = MatchSession_EncryptAddressString( m_szPrivateConnectionString, ullCrypt ) )
  186. pKvServer->SetString( "adrlocal", szEncrypted );
  187. else
  188. pKvServer->SetString( "adrlocal", m_szPrivateConnectionString );
  189. }
  190. #endif
  191. }
  192. //
  193. // Implementation
  194. //
  195. void CDsSearcher::InitDedicatedSearch()
  196. {
  197. if ( mm_dedicated_fake.GetBool() )
  198. {
  199. // Special fake of the search - it just spins for some time and
  200. // pretends like it was aborted
  201. m_flTimeout = Plat_FloatTime() + mm_dedicated_timeout_request.GetFloat();
  202. m_eState = STATE_WAITING;
  203. m_Result.m_bAborted = true;
  204. return;
  205. }
  206. #ifdef _X360
  207. Xlsp_EnumerateDcs();
  208. #elif !defined( NO_STEAM )
  209. m_flTimeout = Plat_FloatTime() + mm_dedicated_timeout_request.GetFloat();
  210. Steam_SearchPass();
  211. #endif
  212. }
  213. void CDsSearcher::InitWithKnownServer()
  214. {
  215. #ifdef _X360
  216. Assert( 0 );
  217. m_eState = STATE_FINISHED;
  218. return;
  219. #elif !defined( NO_STEAM )
  220. if ( m_pSettings->GetInt( "server/reserved" ) )
  221. {
  222. m_Result.m_bDedicated = true;
  223. char const *szAdrOnline = m_pSettings->GetString( "server/adronline", "" );
  224. if ( char const *szDecrypted = MatchSession_DecryptAddressString( szAdrOnline, m_ullCrypt ) )
  225. szAdrOnline = szDecrypted;
  226. Q_strncpy( m_Result.m_szConnectionString, szAdrOnline, ARRAYSIZE( m_Result.m_szConnectionString ) );
  227. Q_strncpy( m_Result.m_szPublicConnectionString, szAdrOnline, ARRAYSIZE( m_Result.m_szPublicConnectionString ) );
  228. char const *szAdrLocal = m_pSettings->GetString( "server/adrlocal", "" );
  229. if ( char const *szDecrypted = MatchSession_DecryptAddressString( szAdrLocal, m_ullCrypt ) )
  230. szAdrLocal = szDecrypted;
  231. Q_strncpy( m_Result.m_szPrivateConnectionString, szAdrLocal, ARRAYSIZE( m_Result.m_szPrivateConnectionString ) );
  232. m_eState = STATE_FINISHED;
  233. }
  234. else
  235. {
  236. DsServer_t dsResult(
  237. m_pSettings->GetString( "server/adronline", "" ),
  238. m_pSettings->GetString( "server/adrlocal", "" ),
  239. 0
  240. );
  241. if ( char const *szDecrypted = MatchSession_DecryptAddressString( dsResult.m_szConnectionString, m_ullCrypt ) )
  242. Q_strncpy( dsResult.m_szConnectionString, szDecrypted, ARRAYSIZE( dsResult.m_szConnectionString ) );
  243. if ( char const *szDecrypted = MatchSession_DecryptAddressString( dsResult.m_szPrivateConnectionString, m_ullCrypt ) )
  244. Q_strncpy( dsResult.m_szPrivateConnectionString, szDecrypted, ARRAYSIZE( dsResult.m_szPrivateConnectionString ) );
  245. m_arrServerList.AddToTail( dsResult );
  246. ReserveNextServer();
  247. }
  248. #endif
  249. }
  250. #ifdef _X360
  251. void CDsSearcher::Xlsp_EnumerateDcs()
  252. {
  253. m_eState = STATE_XLSP_ENUMERATE_DCS;
  254. m_pTitleServers = new CXlspTitleServers( mm_dedicated_search_maxping.GetInt(), false );
  255. }
  256. void CDsSearcher::Xlsp_OnEnumerateDcsCompleted()
  257. {
  258. DevMsg( "Xlsp_OnEnumerateDcsCompleted - analyzing QOS results...\n" );
  259. CUtlVector< CXlspDatacenter > &arrDcs = m_pTitleServers->GetDatacenters();
  260. m_arrDatacenters.AddMultipleToTail( arrDcs.Count(), arrDcs.Base() );
  261. m_pTitleServers->Destroy();
  262. m_pTitleServers = NULL;
  263. //
  264. // Sort and randomize the accepted results
  265. //
  266. m_arrDatacenters.Sort( CXlspDatacenter::Compare );
  267. for ( int k = 0; k < m_arrDatacenters.Count() - 1; ++ k )
  268. {
  269. CXlspDatacenter &dc1 = m_arrDatacenters[ k ];
  270. CXlspDatacenter &dc2 = m_arrDatacenters[ k + 1 ];
  271. if ( dc1.m_nPingBucket == dc2.m_nPingBucket && RandomInt( 0, 1 ) )
  272. {
  273. CXlspDatacenter dcSwap = dc1;
  274. dc1 = dc2;
  275. dc2 = dcSwap;
  276. }
  277. }
  278. DevMsg( "Xlsp_OnEnumerateDcsCompleted - accepted %d datacenters.\n", m_arrDatacenters.Count() );
  279. for ( int k = 0; k < m_arrDatacenters.Count(); ++ k )
  280. {
  281. DevMsg( " %d. `%s`\n", k, m_arrDatacenters[k].m_szGatewayName );
  282. }
  283. // Prepare the datacenter query
  284. Xlsp_PrepareDatacenterQuery();
  285. // Go to the next datacenter
  286. m_eState = STATE_XLSP_NEXT_DC;
  287. }
  288. void CDsSearcher::Xlsp_PrepareDatacenterQuery()
  289. {
  290. // Compute CRC of primary user's gamertag
  291. byte bSult = RandomInt( 5, 100 );
  292. CRC32_t crc32 = 0;
  293. if ( IPlayerLocal * player = g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() ) )
  294. {
  295. char const *szPlayerName = player->GetName();
  296. crc32 = CRC32_ProcessSingleBuffer( szPlayerName, strlen( szPlayerName ) );
  297. uint32 sult32 = bSult | ( bSult << 8 ) | ( bSult << 16 ) | ( bSult << 24 );
  298. crc32 ^= sult32;
  299. }
  300. if ( !crc32 )
  301. bSult = 0;
  302. // Search key
  303. static ConVarRef sv_search_key( "sv_search_key" );
  304. char const *szPrivateKey = sv_search_key.IsValid() ? sv_search_key.GetString() : "";
  305. if ( !*szPrivateKey )
  306. szPrivateKey = "default";
  307. //
  308. // Build query
  309. //
  310. Q_snprintf( m_chDatacenterQuery, ARRAYSIZE( m_chDatacenterQuery ),
  311. "\\empty\\1"
  312. "\\private\\%s"
  313. "\\players\\%d"
  314. "\\slots\\%d"
  315. "\\perm\\%s"
  316. "\\acct\\%02x%08x",
  317. szPrivateKey,
  318. m_pSettings->GetInt( "members/numPlayers", 0 ),
  319. m_pSettings->GetInt( "members/numSlots", 0 ),
  320. m_pSettings->GetString( "system/access", "public" ),
  321. bSult, crc32
  322. );
  323. DevMsg( "Datacenters query: %s\n", m_chDatacenterQuery );
  324. }
  325. void CDsSearcher::Xlsp_StartNextDc()
  326. {
  327. if ( !m_arrDatacenters.Count() )
  328. {
  329. m_eState = STATE_FINISHED;
  330. return;
  331. }
  332. //
  333. // Get the next datacenter off the list
  334. //
  335. m_dc = m_arrDatacenters.Head();
  336. m_arrDatacenters.RemoveMultipleFromHead( 1 );
  337. m_flTimeout = Plat_FloatTime() + mm_dedicated_xlsp_timeout.GetFloat();
  338. DevMsg( "[XLSP] Requesting server batch from %s:%d (%d masters) - ping %d [<= %d]\n"
  339. " ProbesXmit=%3d ProbesRecv=%3d\n"
  340. " RttMinInMsecs=%3d RttMedInMsecs=%3d\n"
  341. " UpBitsPerSec=%6d DnBitsPerSec=%6d\n",
  342. m_dc.m_szGatewayName, m_dc.m_nMasterServerPortStart, m_dc.m_numMasterServers, m_dc.m_qos.wRttMedInMsecs, m_dc.m_nPingBucket,
  343. m_dc.m_qos.cProbesXmit, m_dc.m_qos.cProbesRecv,
  344. m_dc.m_qos.wRttMinInMsecs, m_dc.m_qos.wRttMedInMsecs,
  345. m_dc.m_qos.dwUpBitsPerSec, m_dc.m_qos.dwDnBitsPerSec );
  346. if ( CommandLine()->FindParm( "-xlsp_fake_gateway" ) )
  347. {
  348. m_dc.m_adrSecure = m_dc.m_xsi.inaServer;
  349. }
  350. else
  351. {
  352. //
  353. // Resolve the secure address
  354. //
  355. DWORD ret = g_pMatchExtensions->GetIXOnline()->XNetServerToInAddr( m_dc.m_xsi.inaServer, g_pMatchFramework->GetMatchTitle()->GetTitleServiceID(), &m_dc.m_adrSecure );
  356. if ( ret != ERROR_SUCCESS )
  357. {
  358. DevWarning( "Failed to resolve XLSP secure address (code = 0x%08X)!\n", ret );
  359. return;
  360. }
  361. }
  362. // Convert to netadr_t on a random master port
  363. netadr_t inetAddr;
  364. inetAddr.SetType( NA_IP );
  365. inetAddr.SetIPAndPort( m_dc.m_adrSecure.s_addr,
  366. m_dc.m_nMasterServerPortStart + RandomInt( 0, m_dc.m_numMasterServers - 1 ) );
  367. //
  368. // Prepare the request payload
  369. //
  370. char msg_buffer[ INetSupport::NC_MAX_ROUTABLE_PAYLOAD ];
  371. bf_write msg( msg_buffer, sizeof( msg_buffer ) );
  372. msg.WriteByte( A2M_GET_SERVERS_BATCH2 );
  373. msg.WriteByte( '\n' );
  374. msg.WriteLong( 0 ); // batch starts at 0
  375. msg.WriteLong( m_dc.m_adrSecure.s_addr ); // datacenter's challenge
  376. msg.WriteString( m_chDatacenterQuery ); // datacenter query
  377. msg.WriteByte( '\n' );
  378. g_pMatchExtensions->GetINetSupport()->SendPacket( NULL, INetSupport::NS_SOCK_CLIENT,
  379. inetAddr, msg.GetData(), msg.GetNumBytesWritten() );
  380. m_eState = STATE_XLSP_REQUESTING_SERVERS;
  381. }
  382. void CDsSearcher::Xlsp_OnDcServerBatch( void const *pData, int numBytes )
  383. {
  384. if ( numBytes < 8 )
  385. return;
  386. bf_read msg( pData, numBytes );
  387. int nNextId = msg.ReadLong();
  388. nNextId;
  389. uint nChallenge = msg.ReadLong();
  390. if ( nChallenge != m_dc.m_adrSecure.s_addr )
  391. return;
  392. //
  393. // Get master server reply message or Secure Gateway name (must match request)
  394. //
  395. char szReply[ MAX_PATH ] = {0};
  396. msg.ReadString( szReply, ARRAYSIZE( szReply ), true );
  397. if ( !szReply[0] )
  398. {
  399. DevWarning( "XLSP master server: empty response.\n" );
  400. m_dc.Destroy();
  401. m_eState = STATE_XLSP_NEXT_DC;
  402. return;
  403. }
  404. if ( !Q_stricmp( "##full", szReply ) )
  405. {
  406. DevWarning( "XLSP master server: full.\n" );
  407. m_dc.Destroy();
  408. m_eState = STATE_XLSP_NEXT_DC;
  409. return;
  410. }
  411. if ( !Q_stricmp( "##local", szReply ) )
  412. {
  413. DevWarning( "XLSP master server: game is not eligible for dedicated server.\n" );
  414. m_dc.Destroy();
  415. m_eState = STATE_FINISHED;
  416. return;
  417. }
  418. // Bypass the gateway name check if we're faking it.
  419. if ( !CommandLine()->FindParm( "-xlsp_fake_gateway" ) )
  420. {
  421. if ( Q_stricmp( m_dc.m_szGatewayName, szReply ) )
  422. {
  423. DevWarning( "XLSP master server: wrong reply `%s`, expected gateway `%s`.\n", szReply, m_dc.m_szGatewayName );
  424. m_dc.Destroy();
  425. m_eState = STATE_XLSP_NEXT_DC;
  426. return;
  427. }
  428. }
  429. //
  430. // Process all the servers in the batch
  431. //
  432. m_arrServerPorts.RemoveAll();
  433. for ( ; ; )
  434. {
  435. uint16 nPort = msg.ReadWord();
  436. if ( !nPort || nPort == 0xFFFF )
  437. {
  438. // end of list
  439. break;
  440. }
  441. m_arrServerPorts.AddToTail( nPort );
  442. }
  443. DevWarning( "XLSP master server: returned %d servers in batch.\n", m_arrServerPorts.Count() );
  444. // Go ahead and start reserving
  445. ReserveNextServer();
  446. }
  447. #elif !defined( NO_STEAM )
  448. void CDsSearcher::Steam_SearchPass()
  449. {
  450. if ( mm_dedicated_force_servers.GetString()[0] )
  451. {
  452. // if convar is on to force dedicated server choices, pretend we got search results of just those servers
  453. CSplitString serverList( mm_dedicated_force_servers.GetString(), "," );
  454. for ( int i = 0; i < serverList.Count(); i++ )
  455. {
  456. // Check if the specification has a private IP address
  457. char const * adrsStrings[2] = { serverList[i], "" };
  458. if ( char *pchDelim = strchr( serverList[i], '|' ) )
  459. {
  460. *( pchDelim ++ ) = 0;
  461. adrsStrings[1] = pchDelim;
  462. }
  463. netadr_t adrsForced[2];
  464. adrsForced[0].SetFromString( adrsStrings[0] );
  465. adrsForced[1].SetFromString( adrsStrings[1] );
  466. // Check if a locally discovered server is known with
  467. // either public or private address that is being forced
  468. int numServers = g_pServerManager->GetNumServers();
  469. for ( int iServer = 0; iServer < numServers; ++ iServer )
  470. {
  471. IMatchServer *pMatchServer = g_pServerManager->GetServerByIndex( iServer );
  472. if ( !pMatchServer )
  473. continue;
  474. KeyValues *pServerDetails = pMatchServer->GetGameDetails();
  475. netadr_t adrsKnown[2];
  476. char const * adrsStringsKnown[2] = { pServerDetails->GetString( "server/adronline" ), pServerDetails->GetString( "server/adrlocal" ) };
  477. adrsKnown[0].SetFromString( adrsStringsKnown[0] );
  478. adrsKnown[1].SetFromString( adrsStringsKnown[1] );
  479. for ( int iAdrForced = 0; iAdrForced < ARRAYSIZE( adrsForced ); ++ iAdrForced )
  480. {
  481. for ( int iAdrKnown = 0; iAdrKnown < ARRAYSIZE( adrsKnown ); ++ iAdrKnown )
  482. {
  483. if ( adrsForced[iAdrForced].GetIPHostByteOrder() && adrsKnown[iAdrKnown].GetIPHostByteOrder() &&
  484. adrsForced[iAdrForced].CompareAdr( adrsKnown[iAdrKnown] ) )
  485. {
  486. if ( !adrsForced[!iAdrForced].GetIPHostByteOrder() ) // user not forcing other address, but we know it
  487. adrsStrings[!iAdrForced] = adrsStringsKnown[!iAdrKnown];
  488. goto finished_server_lookup;
  489. }
  490. }
  491. }
  492. }
  493. finished_server_lookup:
  494. m_arrServerList.AddToTail( CDsSearcher::DsServer_t(
  495. adrsStrings[0], adrsStrings[1],
  496. 0 ) ); // you get no accurate ping to forced servers
  497. }
  498. Steam_OnDedicatedServerListFetched();
  499. return;
  500. }
  501. CUtlVector< MatchMakingKeyValuePair_t > filters;
  502. filters.EnsureCapacity( 10 );
  503. // filter by game and require empty server
  504. filters.AddToTail( MatchMakingKeyValuePair_t( "gamedir", COM_GetModDirectory() ) );
  505. filters.AddToTail( MatchMakingKeyValuePair_t( "noplayers", "1" ) );
  506. // Official servers
  507. bool bNeedOfficialServer = false;
  508. char const *szServerType = m_pSettings->GetString( "options/server", NULL );
  509. if ( szServerType && !Q_stricmp( szServerType, "official" ) )
  510. {
  511. bNeedOfficialServer = true;
  512. filters.AddToTail( MatchMakingKeyValuePair_t( "white", "1" ) );
  513. }
  514. // Allow the game to extend the filters
  515. if ( KeyValues *pExtra = g_pMMF->GetMatchTitleGameSettingsMgr()->DefineDedicatedSearchKeys( m_pSettings, bNeedOfficialServer, m_nSearchPass ) )
  516. {
  517. if ( !mm_dedicated_ip.GetString()[0] )
  518. {
  519. for ( KeyValues *val = pExtra->GetFirstValue(); val; val = val->GetNextValue() )
  520. {
  521. char const *szValue = val->GetString();
  522. if ( !szValue || !*szValue )
  523. continue;
  524. filters.AddToTail( MatchMakingKeyValuePair_t( val->GetName(), szValue ) );
  525. }
  526. }
  527. pExtra->deleteThis();
  528. }
  529. // Load test
  530. if (m_bLoadTest && m_nSearchPass == 0)
  531. {
  532. // Add to the "gametype" filter
  533. for ( int i=0; i < filters.Count(); i++ )
  534. {
  535. MatchMakingKeyValuePair_t *pKV = &(filters[i]);
  536. if ( !Q_stricmp( pKV->m_szKey, "gametype" ) )
  537. {
  538. Q_strncat( pKV->m_szValue, ",sv_load_test", sizeof(pKV->m_szValue) );
  539. break;
  540. }
  541. }
  542. }
  543. // request the server list. We will get called back at ServerResponded, ServerFailedToRespond, and RefreshComplete
  544. m_eState = STATE_STEAM_REQUESTING_SERVERS;
  545. m_pServerListListener = new CServerListListener( this, filters );
  546. ++m_nSearchPass;
  547. }
  548. CDsSearcher::CServerListListener::CServerListListener( CDsSearcher *pDsSearcher, CUtlVector< MatchMakingKeyValuePair_t > &filters ) :
  549. m_pOuter( pDsSearcher ),
  550. m_hRequest( NULL )
  551. {
  552. MatchMakingKeyValuePair_t *pFilter = filters.Base();
  553. DevMsg( 1, "Requesting dedicated server list...\n" );
  554. for (int i = 0; i < filters.Count(); i++)
  555. {
  556. DevMsg("Filter %d: %s=%s\n", i, filters.Element(i).m_szKey, filters.Element(i).m_szValue);
  557. }
  558. m_hRequest = steamapicontext->SteamMatchmakingServers()->RequestInternetServerList(
  559. ( AppId_t ) g_pMatchFramework->GetMatchTitle()->GetTitleID(), &pFilter,
  560. filters.Count(), this );
  561. }
  562. void CDsSearcher::CServerListListener::Destroy()
  563. {
  564. m_pOuter = NULL;
  565. if ( m_hRequest )
  566. steamapicontext->SteamMatchmakingServers()->ReleaseRequest( m_hRequest );
  567. m_hRequest = NULL;
  568. delete this;
  569. }
  570. void CDsSearcher::CServerListListener::ServerResponded( HServerListRequest hReq, int iServer )
  571. {
  572. gameserveritem_t *pServer = steamapicontext->SteamMatchmakingServers()
  573. ->GetServerDetails( hReq, iServer );
  574. Msg( "[MM] Server responded '%s', dist %d\n",
  575. pServer->m_NetAdr.GetConnectionAddressString(), pServer->m_nPing );
  576. // Check if the server IP address matches
  577. char const *szDedicatedIp = mm_dedicated_ip.GetString();
  578. if ( szDedicatedIp && szDedicatedIp[0] )
  579. {
  580. char const *szServerConnString = pServer->m_NetAdr.GetConnectionAddressString();
  581. szServerConnString = StringAfterPrefix( szServerConnString, szDedicatedIp );
  582. if ( !szServerConnString ||
  583. ( szServerConnString[0] &&
  584. szServerConnString[0] != ':' ) )
  585. {
  586. DevMsg( " rejected dedicated server '%s' due to ip filter '%s'\n",
  587. pServer->m_NetAdr.GetConnectionAddressString(), szDedicatedIp );
  588. return;
  589. }
  590. }
  591. // Register the dedicated server as acceptable
  592. // netadr_t adrPublic, adrPrivate;
  593. // adrPublic.SetFromString( pServer->m_NetAdr.GetConnectionAddressString() );
  594. // TODO: bool bHasPrivate = FindLANServerPrivateIPByPublicIP( adrPublic, adrPrivate );
  595. // Don't reserve servers with human players playing
  596. int nHumanPlayers = pServer->m_nPlayers - pServer->m_nBotPlayers;
  597. if ( nHumanPlayers > 0 )
  598. return;
  599. // Don't reserve servers with too high ping
  600. if ( ( mm_dedicated_search_maxping.GetInt() > 0 ) && ( pServer->m_nPing > mm_dedicated_search_maxping.GetInt() ) )
  601. return;
  602. // Register the result
  603. if ( m_pOuter )
  604. {
  605. // See if maybe we know about a private address for this server
  606. char const *szPrivateAddress = "";
  607. netadr_t adrPublic;
  608. adrPublic.SetFromString( pServer->m_NetAdr.GetConnectionAddressString() );
  609. XUID xuidOnline = uint64( adrPublic.GetIPNetworkByteOrder() ) | ( uint64( adrPublic.GetPort() ) << 32ull );
  610. if ( IMatchServer *pMatchServer = g_pServerManager->GetServerByOnlineId( xuidOnline ) )
  611. {
  612. szPrivateAddress = pMatchServer->GetGameDetails()->GetString( "server/adrlocal" );
  613. }
  614. m_pOuter->m_arrServerList.AddToTail( CDsSearcher::DsServer_t(
  615. pServer->m_NetAdr.GetConnectionAddressString(),
  616. szPrivateAddress,
  617. pServer->m_nPing ) );
  618. if ( m_pOuter->m_arrServerList.Count() > mm_dedicated_search_maxresults.GetInt() ||
  619. Plat_FloatTime() > m_pOuter->m_flTimeout )
  620. {
  621. steamapicontext->SteamMatchmakingServers()->CancelQuery( hReq );
  622. }
  623. }
  624. }
  625. void CDsSearcher::CServerListListener::RefreshComplete( HServerListRequest hReq, EMatchMakingServerResponse response )
  626. {
  627. if ( m_pOuter )
  628. {
  629. m_pOuter->Steam_OnDedicatedServerListFetched();
  630. }
  631. }
  632. void CDsSearcher::Steam_OnDedicatedServerListFetched()
  633. {
  634. if ( m_bLoadTest && m_nSearchPass < 2 )
  635. {
  636. m_eState = STATE_INIT;
  637. }
  638. else
  639. {
  640. if ( m_pServerListListener )
  641. {
  642. m_pServerListListener->Destroy();
  643. m_pServerListListener = NULL;
  644. }
  645. DevMsg( 1, "Dedicated server list fetched %d servers.\n", m_arrServerList.Count() );
  646. ReserveNextServer();
  647. }
  648. }
  649. #endif
  650. void CDsSearcher::ReserveNextServer()
  651. {
  652. m_eState = STATE_RESERVING;
  653. #ifdef _X360
  654. if ( !m_arrServerPorts.Count() )
  655. {
  656. m_dc.Destroy();
  657. m_eState = STATE_XLSP_NEXT_DC;
  658. return;
  659. }
  660. uint16 nPort = m_arrServerPorts.Head();
  661. m_arrServerPorts.RemoveMultipleFromHead( 1 );
  662. netadr_t inetAddrSecure;
  663. inetAddrSecure.SetType( NA_IP );
  664. inetAddrSecure.SetIPAndPort( m_dc.m_adrSecure.s_addr, nPort );
  665. netadr_t inetAddrInsecureSendable;
  666. inetAddrInsecureSendable.SetType( NA_IP );
  667. inetAddrInsecureSendable.SetIPAndPort( m_dc.m_xsi.inaServer.s_addr, nPort );
  668. Q_strncpy( m_Result.m_szConnectionString, inetAddrSecure.ToString(), ARRAYSIZE( m_Result.m_szConnectionString ) );
  669. Q_strncpy( m_Result.m_szInsecureSendableServerAddress, inetAddrInsecureSendable.ToString(), ARRAYSIZE( m_Result.m_szInsecureSendableServerAddress ) );
  670. netadr_t addrPublic, addrPrivate;
  671. addrPrivate.SetType( NA_NULL );
  672. addrPublic = inetAddrSecure;
  673. #elif !defined( NO_STEAM )
  674. if ( !m_arrServerList.Count() )
  675. {
  676. if ( Plat_FloatTime() < m_flTimeout )
  677. {
  678. m_eState = STATE_STEAM_NEXT_SEARCH_PASS;
  679. return;
  680. }
  681. else
  682. {
  683. m_eState = STATE_FINISHED;
  684. return;
  685. }
  686. }
  687. DsServer_t dss = m_arrServerList.Head();
  688. m_arrServerList.RemoveMultipleFromHead( 1 );
  689. Q_strncpy( m_Result.m_szPublicConnectionString, dss.m_szConnectionString, ARRAYSIZE( m_Result.m_szPublicConnectionString ) );
  690. Q_strncpy( m_Result.m_szPrivateConnectionString, dss.m_szPrivateConnectionString, ARRAYSIZE( m_Result.m_szPrivateConnectionString ) );
  691. netadr_t addrPublic, addrPrivate;
  692. addrPublic.SetFromString( dss.m_szConnectionString );
  693. if ( dss.m_szPrivateConnectionString[0] )
  694. addrPrivate.SetFromString( dss.m_szPrivateConnectionString );
  695. else
  696. addrPrivate.SetType( NA_NULL );
  697. #else
  698. netadr_t addrPublic, addrPrivate;
  699. #endif
  700. g_pMatchExtensions->GetINetSupport()->ReserveServer( addrPublic, addrPrivate,
  701. m_uiReserveCookie, m_pReserveSettings,
  702. this, &m_pAsyncOperation );
  703. }
  704. void CDsSearcher::OnOperationFinished( IMatchAsyncOperation *pOperation )
  705. {
  706. Assert( pOperation == m_pAsyncOperation );
  707. uint64 uiResult = m_pAsyncOperation->GetResult();
  708. if ( m_pAsyncOperation->GetState() == AOS_FAILED || !uiResult )
  709. {
  710. ReserveNextServer();
  711. }
  712. else
  713. {
  714. m_Result.m_bDedicated = true;
  715. char const *szReservedAddr = ( char const * )( int )uiResult;
  716. Q_strncpy( m_Result.m_szConnectionString, szReservedAddr, ARRAYSIZE( m_Result.m_szConnectionString ) );
  717. // If this server reservation reported number of game slots then
  718. // force that setting into session data
  719. uint32 numGameSlots = uint32( pOperation->GetResultExtraInfo() & 0xFF );
  720. if ( m_pMatchSession && ( numGameSlots > 0 ) )
  721. {
  722. KeyValues *kvUpdate = new KeyValues( "update" );
  723. KeyValues::AutoDelete autodelete( kvUpdate );
  724. kvUpdate->SetInt( "update/members/numSlots", numGameSlots );
  725. m_pMatchSession->UpdateSessionSettings( kvUpdate );
  726. }
  727. m_eState = STATE_FINISHED;
  728. }
  729. }