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.

865 lines
24 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 "protocol.h"
  9. #include "proto_oob.h"
  10. #include "filesystem.h"
  11. // memdbgon must be the last include file in a .cpp file!!!
  12. #include "tier0/memdbgon.h"
  13. ConVar mm_dedicated_xlsp_max_dcs( "mm_dedicated_xlsp_max_dcs", "25", FCVAR_DEVELOPMENTONLY, "Max number of XLSP datacenters supported" );
  14. ConVar mm_dedicated_xlsp_force_dc( "mm_dedicated_xlsp_force_dc", "", FCVAR_DEVELOPMENTONLY, "Name of XLSP datacenter to force connection to" );
  15. ConVar mm_dedicated_xlsp_timeout( "mm_dedicated_xlsp_timeout", "20", FCVAR_DEVELOPMENTONLY, "Timeout for XLSP operations" );
  16. ConVar mm_dedicated_xlsp_command_timeout( "mm_dedicated_xlsp_command_timeout", "10", FCVAR_DEVELOPMENTONLY, "Timeout for XLSP command" );
  17. ConVar mm_dedicated_xlsp_force_dc_offset( "mm_dedicated_xlsp_force_dc_offset", "0", FCVAR_DEVELOPMENTONLY, "Offset of XLSP datacenter master to debug master servers" );
  18. static int s_nXlspConnectionCmdReplyId = 0x10000000;
  19. static int GetNextXlspConnectionCmdReplyId()
  20. {
  21. return ++ s_nXlspConnectionCmdReplyId;
  22. }
  23. #ifdef _X360
  24. //
  25. // Datacenter implementation
  26. //
  27. static int GetBucketedRTT( int iRTT )
  28. {
  29. static int s_latencyBucketLevels[] = { 5, 25, 50, 100, 200, 0xFFFFFF };
  30. for ( int k = 0; k < ARRAYSIZE( s_latencyBucketLevels ); ++ k )
  31. {
  32. if ( iRTT <= s_latencyBucketLevels[ k ] )
  33. return s_latencyBucketLevels[ k ];
  34. }
  35. return s_latencyBucketLevels[ ARRAYSIZE( s_latencyBucketLevels ) - 1 ];
  36. }
  37. int CXlspDatacenter::Compare( CXlspDatacenter const *dc1, CXlspDatacenter const *dc2 )
  38. {
  39. int nPing1 = dc1->m_nPingBucket, nPing2 = dc2->m_nPingBucket;
  40. if ( nPing1 != nPing2 )
  41. return ( nPing1 < nPing2 ) ? -1 : 1;
  42. else
  43. return 0;
  44. }
  45. bool CXlspDatacenter::ParseServerInfo()
  46. {
  47. char *pToken = V_stristr( m_xsi.szServerInfo, "**" );
  48. if ( !pToken )
  49. return false;
  50. // Get our bare gateway name
  51. int nTokenLength = pToken - m_xsi.szServerInfo;
  52. nTokenLength = MIN( sizeof( m_szGatewayName ) - 1, nTokenLength );
  53. sprintf( m_szGatewayName, "%.*s", nTokenLength, m_xsi.szServerInfo );
  54. // parse out the gateway information
  55. // get the master server's port and range
  56. pToken += 2;
  57. int nSupportsPII = 0;
  58. sscanf( pToken, "%d_%d_%d", &m_nMasterServerPortStart, &m_numMasterServers, &nSupportsPII );
  59. m_bSupportsPII = ( nSupportsPII != 0 );
  60. if ( !m_nMasterServerPortStart || !m_numMasterServers )
  61. return false;
  62. return true;
  63. }
  64. void CXlspDatacenter::Destroy()
  65. {
  66. if ( m_adrSecure.s_addr )
  67. {
  68. g_pMatchExtensions->GetIXOnline()->XNetUnregisterInAddr( m_adrSecure );
  69. }
  70. Q_memset( this, 0, sizeof( *this ) );
  71. }
  72. //
  73. // XLSP title servers enumeration implementation
  74. //
  75. CXlspTitleServers::CXlspTitleServers( int nPingLimit, bool bMustSupportPII ) :
  76. m_hEnumerate( NULL ),
  77. m_numServers( 0 ),
  78. m_pQos( NULL ),
  79. m_eState( STATE_INIT ),
  80. m_flTimeout( 0.0f ),
  81. m_nPingLimit( nPingLimit ),
  82. m_bMustSupportPII( bMustSupportPII ),
  83. m_pCancelOverlappedJob( NULL )
  84. {
  85. ZeroMemory( &m_xOverlapped, sizeof( m_xOverlapped ) );
  86. }
  87. CXlspTitleServers::~CXlspTitleServers()
  88. {
  89. }
  90. void CXlspTitleServers::Destroy()
  91. {
  92. switch ( m_eState )
  93. {
  94. case STATE_XLSP_ENUMERATE_DCS:
  95. m_pCancelOverlappedJob = ThreadExecute( MMX360_CancelOverlapped, &m_xOverlapped ); // UpdateDormantOperations will clean the rest
  96. break;
  97. case STATE_XLSP_QOS_DCS:
  98. g_pMatchExtensions->GetIXOnline()->XNetQosRelease( m_pQos );
  99. m_pQos = NULL;
  100. break;
  101. }
  102. if ( !m_pCancelOverlappedJob )
  103. delete this;
  104. else
  105. MMX360_RegisterDormant( this ); // keep running UpdateDormantOperation frame loop
  106. }
  107. bool CXlspTitleServers::UpdateDormantOperation()
  108. {
  109. if ( !m_pCancelOverlappedJob->IsFinished() )
  110. return true; // keep running dormant
  111. m_pCancelOverlappedJob->Release();
  112. m_pCancelOverlappedJob = NULL;
  113. CloseHandle( m_hEnumerate );
  114. m_hEnumerate = NULL;
  115. delete this;
  116. return false; // destroyed the object, remove from dormant list
  117. }
  118. void CXlspTitleServers::Update()
  119. {
  120. switch ( m_eState )
  121. {
  122. case STATE_INIT:
  123. m_flTimeout = Plat_FloatTime() + mm_dedicated_xlsp_timeout.GetFloat();
  124. if ( EnumerateDcs( m_hEnumerate, m_xOverlapped, m_bufXlspEnumerateDcs ) )
  125. m_eState = STATE_XLSP_ENUMERATE_DCS;
  126. else
  127. m_eState = STATE_FINISHED;
  128. break;
  129. case STATE_XLSP_ENUMERATE_DCS:
  130. if ( !XHasOverlappedIoCompleted( &m_xOverlapped ) )
  131. {
  132. if ( Plat_FloatTime() > m_flTimeout )
  133. {
  134. // Enumeration timeout elapsed
  135. m_pCancelOverlappedJob = ThreadExecute( MMX360_CancelOverlapped, &m_xOverlapped ); // UpdateDormantOperations will clean the rest
  136. m_eState = STATE_FINISHED;
  137. }
  138. return;
  139. }
  140. m_flTimeout = Plat_FloatTime() + mm_dedicated_xlsp_timeout.GetFloat();
  141. if ( ExecuteQosDcs( m_hEnumerate, m_xOverlapped, m_bufXlspEnumerateDcs, m_numServers, m_pQos ) )
  142. m_eState = STATE_XLSP_QOS_DCS;
  143. else
  144. m_eState = STATE_FINISHED;
  145. break;
  146. case STATE_XLSP_QOS_DCS:
  147. if ( CommandLine()->FindParm( "-xlsp_fake_gateway" ) || Plat_FloatTime() > m_flTimeout ||
  148. !m_pQos->cxnqosPending )
  149. {
  150. ParseDatacentersFromQos( m_arrDcs, m_bufXlspEnumerateDcs, m_pQos );
  151. m_eState = STATE_FINISHED;
  152. }
  153. break;
  154. }
  155. }
  156. bool CXlspTitleServers::EnumerateDcs( HANDLE &hEnumerate, XOVERLAPPED &xOverlapped, CUtlBuffer &bufResults )
  157. {
  158. // If we're using a fake xlsp server, then we don't want to do any of this.
  159. if ( CommandLine()->FindParm( "-xlsp_fake_gateway" ) )
  160. {
  161. return true;
  162. }
  163. int numDcs = mm_dedicated_xlsp_max_dcs.GetInt();
  164. DevMsg( "Enumerating XLSP datacenters (%d max supported)...\n", numDcs );
  165. //
  166. // Create enumerator
  167. //
  168. DWORD numBytes = 0;
  169. DWORD ret = g_pMatchExtensions->GetIXOnline()->XTitleServerCreateEnumerator( NULL, numDcs, &numBytes, &hEnumerate );
  170. if ( ret != ERROR_SUCCESS )
  171. {
  172. DevWarning( "XTitleServerCreateEnumerator failed (code = 0x%08X)\n", ret );
  173. hEnumerate = NULL;
  174. return false;
  175. }
  176. // Allocate results buffer
  177. bufResults.EnsureCapacity( numBytes );
  178. XTITLE_SERVER_INFO *pXSI = ( XTITLE_SERVER_INFO * ) bufResults.Base();
  179. ZeroMemory( pXSI, numBytes );
  180. ZeroMemory( &xOverlapped, sizeof( XOVERLAPPED ) );
  181. //
  182. // Enumerate
  183. //
  184. ret = XEnumerate( hEnumerate, pXSI, numBytes, NULL, &xOverlapped );
  185. if ( ret != ERROR_IO_PENDING )
  186. {
  187. DevWarning( "XEnumerate of XTitleServerCreateEnumerator failed (code = 0x%08X)\n", ret );
  188. CloseHandle( hEnumerate );
  189. hEnumerate = NULL;
  190. return false;
  191. }
  192. return true;
  193. }
  194. bool CXlspTitleServers::ExecuteQosDcs( HANDLE &hEnumerate, XOVERLAPPED &xOverlapped, CUtlBuffer &bufResults, DWORD &numServers, XNQOS *&pQos )
  195. {
  196. // If we're using a fake xlsp server, then we don't want to do any of this.
  197. if ( CommandLine()->FindParm( "-xlsp_fake_gateway" ) )
  198. {
  199. return true;
  200. }
  201. numServers = 0;
  202. XGetOverlappedResult( &xOverlapped, &numServers, TRUE );
  203. CloseHandle( hEnumerate );
  204. hEnumerate = NULL;
  205. DevMsg( "Xlsp_OnEnumerateDcsCompleted found %d datacenters.\n", numServers );
  206. if ( !numServers )
  207. {
  208. return false;
  209. }
  210. //
  211. // Prepare for QOS lookup to the datacenters
  212. //
  213. XTITLE_SERVER_INFO *pXSI = ( XTITLE_SERVER_INFO * ) bufResults.Base();
  214. DWORD dwServiceId = g_pMatchFramework->GetMatchTitle()->GetTitleServiceID();
  215. CUtlVector< IN_ADDR > qosAddr;
  216. CUtlVector< DWORD > qosServiceId;
  217. for ( DWORD k = 0; k < numServers; ++ k )
  218. {
  219. qosAddr.AddToTail( pXSI[k].inaServer );
  220. qosServiceId.AddToTail( dwServiceId );
  221. }
  222. //
  223. // Submit QOS lookup
  224. //
  225. pQos = NULL;
  226. DWORD ret = g_pMatchExtensions->GetIXOnline()->XNetQosLookup( 0, NULL, NULL, NULL,
  227. numServers, qosAddr.Base(), qosServiceId.Base(),
  228. 8, 0, 0,
  229. NULL, &pQos );
  230. if ( ret != ERROR_SUCCESS )
  231. {
  232. DevWarning( "XNetQosLookup failed to start for XLSP datacenters, code = 0x%08X!\n", ret );
  233. return false;
  234. }
  235. return true;
  236. }
  237. void CXlspTitleServers::ParseDatacentersFromQos( CUtlVector< CXlspDatacenter > &arrDcs, CUtlBuffer &bufResults, XNQOS *&pQos )
  238. {
  239. // If we're using a fake xlsp server, then we don't want to do any of this and instead add our fake server to the datacenter list.
  240. if ( CommandLine()->FindParm( "-xlsp_fake_gateway" ) )
  241. {
  242. netadr_t gatewayAdr;
  243. gatewayAdr.SetFromString( CommandLine()->GetParm( CommandLine()->FindParm( "-xlsp_fake_gateway" ) + 1) );
  244. char szSG[200];
  245. sprintf(szSG, "%s**%d_1", gatewayAdr.ToString(true), gatewayAdr.GetPort() );
  246. CXlspDatacenter dc;
  247. ZeroMemory( &dc, sizeof( dc ) );
  248. Q_strncpy( dc.m_xsi.szServerInfo, szSG, sizeof(dc.m_xsi.szServerInfo) );
  249. dc.m_xsi.inaServer.S_un.S_addr = gatewayAdr.GetIPNetworkByteOrder();
  250. dc.ParseServerInfo();
  251. arrDcs.AddToTail( dc );
  252. return;
  253. }
  254. if ( !pQos )
  255. return;
  256. XTITLE_SERVER_INFO *pXSI = ( XTITLE_SERVER_INFO * ) bufResults.Base();
  257. for ( DWORD k = 0; k < pQos->cxnqos; ++ k )
  258. {
  259. // Datacenter info
  260. CXlspDatacenter dc;
  261. ZeroMemory( &dc, sizeof( dc ) );
  262. dc.m_xsi = pXSI[k];
  263. dc.m_qos = pQos->axnqosinfo[k];
  264. // Cull centers that failed to be contacted
  265. uint uiRequiredQosFlags = ( XNET_XNQOSINFO_COMPLETE | XNET_XNQOSINFO_TARGET_CONTACTED );
  266. if ( ( ( dc.m_qos.bFlags & uiRequiredQosFlags ) != uiRequiredQosFlags ) ||
  267. ( dc.m_qos.bFlags & XNET_XNQOSINFO_TARGET_DISABLED ) )
  268. {
  269. DevWarning( "XLSP datacenter %d `%s` failed connection probe (0x%08X).\n", k, dc.m_xsi.szServerInfo, dc.m_qos.bFlags );
  270. continue;
  271. }
  272. // Cull any non-conformant XLSP center names
  273. if ( !dc.ParseServerInfo() )
  274. {
  275. DevWarning( "XLSP datacenter %d `%s` has non-conformant server info.\n", k, dc.m_xsi.szServerInfo );
  276. continue;
  277. }
  278. // Check if PII is required
  279. if ( m_bMustSupportPII && !dc.m_bSupportsPII )
  280. {
  281. DevWarning( "XLSP datacenter %d `%s` does not support PII.\n", k, dc.m_xsi.szServerInfo );
  282. continue;
  283. }
  284. // Check if we are forcing a specific datacenter
  285. bool bForcedUse = false;
  286. if ( char const *szForcedDcName = mm_dedicated_xlsp_force_dc.GetString() )
  287. {
  288. if ( *szForcedDcName && !Q_stricmp( szForcedDcName, dc.m_szGatewayName ) )
  289. bForcedUse = true;
  290. if ( *szForcedDcName && !bForcedUse )
  291. {
  292. // Gateway doesn't match forced datacenter
  293. DevWarning( "XLSP datacenter %d `%s` is ignored because we are forcing datacenter name `%s`.\n", k, dc.m_xsi.szServerInfo, szForcedDcName );
  294. continue;
  295. }
  296. }
  297. // Check ping
  298. if ( m_nPingLimit > 0 && dc.m_qos.wRttMedInMsecs > m_nPingLimit && !bForcedUse )
  299. {
  300. DevWarning( "XLSP datacenter %d `%s` is ignored because its ping %d is greater than max allowed %d.\n",
  301. k, dc.m_xsi.szServerInfo, dc.m_qos.wRttMedInMsecs, m_nPingLimit );
  302. continue;
  303. }
  304. // Remeber the datacenter as a potential candidate
  305. dc.m_nPingBucket = GetBucketedRTT( dc.m_qos.wRttMedInMsecs );
  306. DevMsg( "XLSP datacenter %d `%s` accepted, ping %d [<= %d]\n",
  307. k, dc.m_szGatewayName, dc.m_qos.wRttMedInMsecs, dc.m_nPingBucket );
  308. arrDcs.AddToTail( dc );
  309. }
  310. // Release the QOS query
  311. g_pMatchExtensions->GetIXOnline()->XNetQosRelease( pQos );
  312. pQos = NULL;
  313. }
  314. bool CXlspTitleServers::IsSearchCompleted() const
  315. {
  316. return m_eState == STATE_FINISHED;
  317. }
  318. CUtlVector< CXlspDatacenter > & CXlspTitleServers::GetDatacenters()
  319. {
  320. Assert( IsSearchCompleted() );
  321. return m_arrDcs;
  322. }
  323. //
  324. // XLSP connection implementation
  325. //
  326. CXlspConnection::CXlspConnection( bool bMustSupportPII ) :
  327. m_pTitleServers( NULL ),
  328. m_pCmdResult( NULL ),
  329. m_flTimeout( 0.0f ),
  330. m_eState( STATE_INIT ),
  331. m_idCmdReplyId( 0 ),
  332. m_numCmdRetriesAllowed( 0 ),
  333. m_flCmdRetryTimeout( 0 ),
  334. m_bMustSupportPII( bMustSupportPII )
  335. {
  336. ZeroMemory( &m_dc, sizeof( m_dc ) );
  337. }
  338. CXlspConnection::~CXlspConnection()
  339. {
  340. ;
  341. }
  342. void CXlspConnection::Destroy()
  343. {
  344. if ( m_pTitleServers )
  345. m_pTitleServers->Destroy();
  346. m_pTitleServers = NULL;
  347. if ( m_eState >= STATE_CONNECTED )
  348. m_dc.Destroy();
  349. if ( m_eState == STATE_RUNNINGCMD )
  350. g_pMatchEventsSubscription->Unsubscribe( this );
  351. if ( m_pCmdResult )
  352. m_pCmdResult->deleteThis();
  353. m_pCmdResult = NULL;
  354. delete this;
  355. }
  356. bool CXlspConnection::IsConnected() const
  357. {
  358. return m_eState == STATE_CONNECTED || m_eState == STATE_RUNNINGCMD;
  359. }
  360. bool CXlspConnection::HasError() const
  361. {
  362. return m_eState == STATE_ERROR;
  363. }
  364. static int XlspConnection_CompareDcs( CXlspDatacenter const *dc1, CXlspDatacenter const *dc2 )
  365. {
  366. int nPing1 = dc1->m_nPingBucket, nPing2 = dc2->m_nPingBucket;
  367. if ( nPing1 != nPing2 )
  368. return ( nPing1 < nPing2 ) ? -1 : 1;
  369. // Compare by name
  370. if ( int iNameCmp = Q_stricmp( dc1->m_szGatewayName, dc2->m_szGatewayName ) )
  371. return iNameCmp;
  372. // Compare by IP addr
  373. int iAddrCmp = dc1->m_xsi.inaServer.s_addr - dc2->m_xsi.inaServer.s_addr;
  374. if ( iAddrCmp )
  375. return iAddrCmp;
  376. return 0;
  377. }
  378. void CXlspConnection::ConnectXlspDc()
  379. {
  380. CUtlVector< CXlspDatacenter > &arrDcs = m_pTitleServers->GetDatacenters();
  381. arrDcs.Sort( XlspConnection_CompareDcs );
  382. for ( int k = 0; k < arrDcs.Count(); ++ k )
  383. {
  384. m_dc = arrDcs[k];
  385. DevMsg( "[XLSP] Connecting to %s:%d (%d masters) - ping %d [<= %d]\n"
  386. " ProbesXmit=%3d ProbesRecv=%3d\n"
  387. " RttMinInMsecs=%3d RttMedInMsecs=%3d\n"
  388. " UpBitsPerSec=%6d DnBitsPerSec=%6d\n",
  389. m_dc.m_szGatewayName, m_dc.m_nMasterServerPortStart, m_dc.m_numMasterServers, m_dc.m_qos.wRttMedInMsecs, m_dc.m_nPingBucket,
  390. m_dc.m_qos.cProbesXmit, m_dc.m_qos.cProbesRecv,
  391. m_dc.m_qos.wRttMinInMsecs, m_dc.m_qos.wRttMedInMsecs,
  392. m_dc.m_qos.dwUpBitsPerSec, m_dc.m_qos.dwDnBitsPerSec );
  393. //
  394. // Resolve the secure address
  395. //
  396. DWORD ret = ERROR_SUCCESS;
  397. if ( CommandLine()->FindParm( "-xlsp_fake_gateway" ) )
  398. {
  399. m_dc.m_adrSecure = m_dc.m_xsi.inaServer;
  400. }
  401. else
  402. {
  403. ret = g_pMatchExtensions->GetIXOnline()->XNetServerToInAddr( m_dc.m_xsi.inaServer, g_pMatchFramework->GetMatchTitle()->GetTitleServiceID(), &m_dc.m_adrSecure );
  404. }
  405. if ( ret != ERROR_SUCCESS )
  406. {
  407. DevWarning( "Failed to resolve XLSP secure address (code = 0x%08X)!\n", ret );
  408. continue;
  409. }
  410. else
  411. {
  412. DevMsg( "Resolved XLSP server address\n" );
  413. m_eState = STATE_CONNECTED;
  414. m_pTitleServers->Destroy();
  415. m_pTitleServers = NULL;
  416. return;
  417. }
  418. }
  419. ZeroMemory( &m_dc, sizeof( m_dc ) );
  420. m_eState = STATE_ERROR;
  421. }
  422. CXlspDatacenter const & CXlspConnection::GetDatacenter() const
  423. {
  424. return m_dc;
  425. }
  426. void CXlspConnection::ResolveCmdSystemValues( KeyValues *pCommand )
  427. {
  428. // Expand the command data based on DC fields
  429. if ( KeyValues *pExp = pCommand->FindKey( "*dcpgmi" ) )
  430. pExp->SetInt( NULL, m_dc.m_qos.wRttMinInMsecs );
  431. if ( KeyValues *pExp = pCommand->FindKey( "*dcpgme" ) )
  432. pExp->SetInt( NULL, m_dc.m_qos.wRttMedInMsecs );
  433. if ( KeyValues *pExp = pCommand->FindKey( "*dcpgbu" ) )
  434. pExp->SetInt( NULL, m_dc.m_nPingBucket );
  435. if ( KeyValues *pExp = pCommand->FindKey( "*dcbwup" ) )
  436. pExp->SetInt( NULL, m_dc.m_qos.dwUpBitsPerSec );
  437. if ( KeyValues *pExp = pCommand->FindKey( "*dcbwdn" ) )
  438. pExp->SetInt( NULL, m_dc.m_qos.dwDnBitsPerSec );
  439. if ( KeyValues *pExp = pCommand->FindKey( "*net" ) )
  440. pExp->SetInt( NULL, g_pMatchExtensions->GetIXOnline()->XNetGetEthernetLinkStatus() );
  441. if ( KeyValues *pExp = pCommand->FindKey( "*nat" ) )
  442. pExp->SetInt( NULL, g_pMatchExtensions->GetIXOnline()->XOnlineGetNatType() );
  443. if ( KeyValues *pExp = pCommand->FindKey( "*mac" ) )
  444. {
  445. // Console MAC address
  446. XNADDR xnaddr;
  447. uint64 uiMachineId = 0ull;
  448. if ( XNET_GET_XNADDR_PENDING == g_pMatchExtensions->GetIXOnline()->XNetGetTitleXnAddr( &xnaddr ) ||
  449. g_pMatchExtensions->GetIXOnline()->XNetXnAddrToMachineId( &xnaddr, &uiMachineId ) )
  450. uiMachineId = 0ull;
  451. pExp->SetUint64( NULL, uiMachineId );
  452. }
  453. if ( KeyValues *pExp = pCommand->FindKey( "*diskDsn" ) )
  454. {
  455. // Serial number of GAME volume
  456. struct VolumeInformation_t {
  457. char chVolumeName[128];
  458. char chFsName[128];
  459. DWORD dwVolumeSerial;
  460. DWORD dwMaxComponentLen;
  461. DWORD dwFsFlags;
  462. } vi;
  463. memset( &vi, 0, sizeof( vi ) );
  464. if ( !GetVolumeInformation( "d:\\",
  465. vi.chVolumeName, sizeof( vi.chVolumeName ) - 1,
  466. &vi.dwVolumeSerial, &vi.dwMaxComponentLen, &vi.dwFsFlags,
  467. vi.chFsName, sizeof( vi.chFsName ) - 1
  468. ) )
  469. {
  470. memset( &vi, 0, sizeof( vi ) );
  471. }
  472. uint64 uiDsn = vi.dwVolumeSerial;
  473. if ( g_pFullFileSystem && g_pFullFileSystem->IsDVDHosted() )
  474. uiDsn |= ( 1ull << 33 ); // DVD hosted
  475. pExp->SetUint64( NULL, uiDsn );
  476. }
  477. if ( KeyValues *pExp = pCommand->FindKey( "*diskDnfo" ) )
  478. {
  479. // Space information of GAME volume
  480. struct FreeSpace_t
  481. {
  482. ULARGE_INTEGER ulFreeTitle, ulTotal, ulFree;
  483. } fs;
  484. memset( &fs, 0, sizeof( fs ) );
  485. if ( !GetDiskFreeSpaceEx( "d:\\",
  486. &fs.ulFreeTitle, &fs.ulTotal, &fs.ulFree ) )
  487. {
  488. memset( &fs, 0, sizeof( fs ) );
  489. }
  490. uint32 uiTotalMbs = fs.ulTotal.QuadPart / ( 1024 * 1024 );
  491. uint32 uiFreeMbs = fs.ulFree.QuadPart / ( 1024 * 1024 );
  492. pExp->SetUint64( NULL, uint64( uiTotalMbs ) | ( uint64( uiFreeMbs ) << 32 ) );
  493. }
  494. if ( KeyValues *pExp = pCommand->FindKey( "*diskCnfo" ) )
  495. {
  496. // Space information of CACHE partition
  497. struct FreeSpace_t
  498. {
  499. ULARGE_INTEGER ulFreeTitle, ulTotal, ulFree;
  500. } fs;
  501. memset( &fs, 0, sizeof( fs ) );
  502. if ( !GetDiskFreeSpaceEx( "cache:\\",
  503. &fs.ulFreeTitle, &fs.ulTotal, &fs.ulFree ) )
  504. {
  505. memset( &fs, 0, sizeof( fs ) );
  506. }
  507. uint32 uiTotalMbs = fs.ulTotal.QuadPart / ( 1024 * 1024 );
  508. uint32 uiFreeMbs = fs.ulFree.QuadPart / ( 1024 * 1024 );
  509. pExp->SetUint64( NULL, uint64( uiTotalMbs ) | ( uint64( uiFreeMbs ) << 32 ) );
  510. }
  511. if ( KeyValues *pExp = pCommand->FindKey( "*diskHnfo" ) )
  512. {
  513. // Space information of HDD volume
  514. XDEVICE_DATA xdd;
  515. memset( &xdd, 0, sizeof( xdd ) );
  516. xdd.DeviceID = 1;
  517. if ( XContentGetDeviceData( xdd.DeviceID, &xdd ) )
  518. {
  519. memset( &xdd, 0, sizeof( xdd ) );
  520. }
  521. uint32 uiTotalMbs = xdd.ulDeviceBytes / ( 1024 * 1024 );
  522. uint32 uiFreeMbs = xdd.ulDeviceFreeBytes / ( 1024 * 1024 );
  523. pExp->SetUint64( NULL, uint64( uiTotalMbs ) | ( uint64( uiFreeMbs ) << 32 ) );
  524. }
  525. if ( KeyValues *pExp = pCommand->FindKey( "*disk1nfo" ) )
  526. {
  527. // Space information of user-chosen storage device
  528. XDEVICE_DATA xdd;
  529. memset( &xdd, 0, sizeof( xdd ) );
  530. xdd.DeviceID = XBX_GetStorageDeviceId( XBX_GetPrimaryUserId() );
  531. if ( XContentGetDeviceData( xdd.DeviceID, &xdd ) )
  532. {
  533. memset( &xdd, 0, sizeof( xdd ) );
  534. }
  535. uint32 uiTotalMbs = xdd.ulDeviceBytes / ( 1024 * 1024 );
  536. uint32 uiFreeMbs = xdd.ulDeviceFreeBytes / ( 1024 * 1024 );
  537. pExp->SetUint64( NULL, uint64( uiTotalMbs ) | ( uint64( uiFreeMbs ) << 32 ) );
  538. }
  539. }
  540. netadr_t CXlspConnection::GetXlspDcAddress()
  541. {
  542. //
  543. // Convert DC address to netadr_t on a random master port
  544. //
  545. netadr_t inetAddr;
  546. inetAddr.SetType( NA_IP );
  547. inetAddr.SetIPAndPort( m_dc.m_adrSecure.s_addr,
  548. m_dc.m_nMasterServerPortStart + RandomInt( 0, m_dc.m_numMasterServers - 1 )
  549. + mm_dedicated_xlsp_force_dc_offset.GetInt() );
  550. return inetAddr;
  551. }
  552. bool CXlspConnection::ExecuteCmd( KeyValues *pCommand, int numRetriesAllowed, float flCmdRetryTimeout )
  553. {
  554. if ( !pCommand )
  555. return false;
  556. if ( m_eState != STATE_CONNECTED )
  557. {
  558. Assert( !"CXlspConnection::ExecuteCmd while not connected to XLSP server!\n" );
  559. return false;
  560. }
  561. ResolveCmdSystemValues( pCommand );
  562. // Serialize the command
  563. CUtlBuffer bufBinData;
  564. bufBinData.ActivateByteSwapping( !CByteswap::IsMachineBigEndian() );
  565. if ( !pCommand->WriteAsBinary( bufBinData ) )
  566. return false;
  567. // Destroy the previous result data
  568. if ( m_pCmdResult )
  569. m_pCmdResult->deleteThis();
  570. m_pCmdResult = NULL;
  571. m_idCmdReplyId = GetNextXlspConnectionCmdReplyId();
  572. m_numCmdRetriesAllowed = numRetriesAllowed;
  573. m_flCmdRetryTimeout = ( ( flCmdRetryTimeout > 0 ) ? flCmdRetryTimeout : mm_dedicated_xlsp_command_timeout.GetFloat() );
  574. //
  575. // Prepare the request payload
  576. //
  577. char msg_buffer[ INetSupport::NC_MAX_ROUTABLE_PAYLOAD ];
  578. bf_write msg( msg_buffer, sizeof( msg_buffer ) );
  579. msg.WriteByte( A2A_KV_CMD );
  580. msg.WriteByte( A2A_KV_VERSION );
  581. // Xbox 360 -> Master server
  582. msg.WriteLong( MAKE_4BYTES( 'X', '-', 'M', '1' ) );
  583. msg.WriteLong( m_idCmdReplyId );
  584. msg.WriteLong( m_dc.m_adrSecure.s_addr ); // datacenter's challenge
  585. msg.WriteLong( 0 );
  586. msg.WriteLong( bufBinData.TellMaxPut() );
  587. msg.WriteBytes( bufBinData.Base(), bufBinData.TellMaxPut() ); // datacenter command
  588. DevMsg( 2, "Xbox->XLSP: 0x%08X 0x%08X ( %u bytes, %d retries allowed )\n", m_idCmdReplyId, (uint32) m_dc.m_adrSecure.s_addr, msg.GetNumBytesWritten(), m_numCmdRetriesAllowed );
  589. KeyValuesDumpAsDevMsg( pCommand, 1, 2 );
  590. g_pMatchExtensions->GetINetSupport()->SendPacket( NULL, INetSupport::NS_SOCK_CLIENT,
  591. GetXlspDcAddress(), msg.GetData(), msg.GetNumBytesWritten() );
  592. if ( m_numCmdRetriesAllowed > 0 )
  593. {
  594. m_bufCmdRetry.SetCount( msg.GetNumBytesWritten() );
  595. memcpy( m_bufCmdRetry.Base(), msg.GetData(), msg.GetNumBytesWritten() );
  596. }
  597. else
  598. {
  599. m_bufCmdRetry.Purge();
  600. }
  601. g_pMatchEventsSubscription->Subscribe( this );
  602. m_eState = STATE_RUNNINGCMD;
  603. m_flTimeout = Plat_FloatTime() + m_flCmdRetryTimeout;
  604. return true;
  605. }
  606. void CXlspConnection::OnEvent( KeyValues *pEvent )
  607. {
  608. char const *szName = pEvent->GetName();
  609. if ( !Q_stricmp( "A2A_KV_CMD", szName ) )
  610. {
  611. // Master server -> Xbox 360
  612. if ( pEvent->GetInt( "header" ) == MAKE_4BYTES( 'M', '-', 'X', '1' ) &&
  613. pEvent->GetInt( "replyid" ) == m_idCmdReplyId )
  614. {
  615. g_pMatchEventsSubscription->Unsubscribe( this );
  616. m_eState = STATE_CONNECTED;
  617. m_pCmdResult = pEvent->GetFirstTrueSubKey();
  618. if ( !m_pCmdResult )
  619. m_pCmdResult = pEvent;
  620. // Keep it as a copy
  621. m_pCmdResult = m_pCmdResult->MakeCopy();
  622. DevMsg( 2, "Xbox<<XLSP: 0x%08X ( %u bytes )\n", m_idCmdReplyId, pEvent->GetInt( "size" ) );
  623. // KeyValuesDumpAsDevMsg( m_pCmdResult, 1, 2 );
  624. }
  625. }
  626. }
  627. void CXlspConnection::Update()
  628. {
  629. switch ( m_eState )
  630. {
  631. case STATE_INIT:
  632. m_eState = STATE_CONNECTING;
  633. m_pTitleServers = new CXlspTitleServers( 0, m_bMustSupportPII ); // no ping limitation
  634. break;
  635. case STATE_CONNECTING:
  636. m_pTitleServers->Update();
  637. if ( m_pTitleServers->IsSearchCompleted() )
  638. ConnectXlspDc();
  639. break;
  640. case STATE_RUNNINGCMD:
  641. if ( Plat_FloatTime() > m_flTimeout )
  642. {
  643. if ( m_numCmdRetriesAllowed > 0 )
  644. {
  645. // Retry and increase timeout
  646. -- m_numCmdRetriesAllowed;
  647. m_flTimeout = Plat_FloatTime() + m_flCmdRetryTimeout;
  648. DevMsg( 2, "Xbox->XLSP: 0x%08X 0x%08X ( %u bytes, %d retries remaining )\n", m_idCmdReplyId, (uint32) m_dc.m_adrSecure.s_addr, m_bufCmdRetry.Count(), m_numCmdRetriesAllowed );
  649. g_pMatchExtensions->GetINetSupport()->SendPacket( NULL, INetSupport::NS_SOCK_CLIENT,
  650. GetXlspDcAddress(), m_bufCmdRetry.Base(), m_bufCmdRetry.Count() );
  651. return;
  652. }
  653. DevWarning( 2, "Xbox->XLSP: 0x%08X 0x%08X - TIMED OUT!\n", m_idCmdReplyId, (uint32) m_dc.m_adrSecure.s_addr );
  654. g_pMatchEventsSubscription->Unsubscribe( this );
  655. m_eState = STATE_ERROR;
  656. }
  657. break;
  658. }
  659. }
  660. //
  661. // Connection batch implementation
  662. //
  663. CXlspConnectionCmdBatch::CXlspConnectionCmdBatch( CXlspConnection *pConnection, CUtlVector<KeyValues*> &arrCommands, int numRetriesAllowedPerEachCmd, float flCommandTimeout ) :
  664. m_eState( STATE_BATCH_WAITING ),
  665. m_iCommand( 0 ),
  666. m_pXlspConn( pConnection ),
  667. m_numRetriesAllowedPerEachCmd( numRetriesAllowedPerEachCmd ),
  668. m_flCommandTimeout( ( flCommandTimeout > 0 ) ? flCommandTimeout : mm_dedicated_xlsp_command_timeout.GetFloat() )
  669. {
  670. m_arrCommands.Swap( arrCommands );
  671. }
  672. CXlspConnectionCmdBatch::~CXlspConnectionCmdBatch()
  673. {
  674. }
  675. bool CXlspConnectionCmdBatch::IsFinished() const
  676. {
  677. return m_eState >= STATE_FINISHED;
  678. }
  679. bool CXlspConnectionCmdBatch::HasAllResults() const
  680. {
  681. return IsFinished() && m_arrCommands.Count() == m_arrResults.Count();
  682. }
  683. void CXlspConnectionCmdBatch::Update()
  684. {
  685. m_pXlspConn->Update();
  686. if ( m_pXlspConn->HasError() )
  687. {
  688. m_eState = STATE_FINISHED;
  689. }
  690. switch ( m_eState )
  691. {
  692. case STATE_BATCH_WAITING:
  693. if ( m_pXlspConn->IsConnected() )
  694. {
  695. RunNextCmd();
  696. }
  697. break;
  698. case STATE_RUNNINGCMD:
  699. if ( KeyValues *pCmdResult = m_pXlspConn->GetCmdResult() )
  700. {
  701. m_arrResults.AddToTail( pCmdResult->MakeCopy() );
  702. RunNextCmd();
  703. }
  704. break;
  705. }
  706. }
  707. void CXlspConnectionCmdBatch::RunNextCmd()
  708. {
  709. if ( m_iCommand >= m_arrCommands.Count() ||
  710. m_pXlspConn->HasError() ||
  711. !m_pXlspConn->ExecuteCmd( m_arrCommands[ m_iCommand ], m_numRetriesAllowedPerEachCmd, m_flCommandTimeout ) )
  712. {
  713. m_eState = STATE_FINISHED;
  714. }
  715. else
  716. {
  717. ++ m_iCommand;
  718. m_eState = STATE_RUNNINGCMD;
  719. }
  720. }
  721. void CXlspConnectionCmdBatch::Destroy()
  722. {
  723. for ( int k = 0; k < m_arrCommands.Count(); ++ k )
  724. m_arrCommands[k]->deleteThis();
  725. for ( int k = 0; k < m_arrResults.Count(); ++ k )
  726. m_arrResults[k]->deleteThis();
  727. m_arrCommands.Purge();
  728. m_arrResults.Purge();
  729. m_pXlspConn = NULL;
  730. delete this;
  731. }
  732. #endif // _X360