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.

1177 lines
34 KiB

  1. //========= Copyright � 1996-2009, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=====================================================================================//
  6. #ifndef _X360
  7. #include "xbox/xboxstubs.h"
  8. #endif
  9. #include "mm_framework.h"
  10. #include "proto_oob.h"
  11. #include "fmtstr.h"
  12. #include "vstdlib/random.h"
  13. #include "mathlib/IceKey.H"
  14. #include "filesystem.h"
  15. #if !defined( NO_STEAM )
  16. #include "steam_datacenterjobs.h"
  17. #endif
  18. // NOTE: This has to be the last file included!
  19. #include "tier0/memdbgon.h"
  20. static ConVar mm_datacenter_update_interval( "mm_datacenter_update_interval", "3600", FCVAR_DEVELOPMENTONLY, "Interval between datacenter stats updates." );
  21. static ConVar mm_datacenter_retry_interval( "mm_datacenter_retry_interval", "75", FCVAR_DEVELOPMENTONLY, "Interval between datacenter stats retries." );
  22. static ConVar mm_datacenter_retry_infochunks_attempts( "mm_datacenter_retry_infochunks_attempts", "3", FCVAR_DEVELOPMENTONLY, "How many times can we retry retrieving each info chunk before failing." );
  23. static ConVar mm_datacenter_query_delay( "mm_datacenter_query_delay", "2", FCVAR_DEVELOPMENTONLY, "Delay after datacenter update is enabled before data is actually queried." );
  24. static ConVar mm_datacenter_report_version( "mm_datacenter_report_version", "5", FCVAR_DEVELOPMENTONLY, "Data version to report to DC." );
  25. static ConVar mm_datacenter_delay_mount_frames( "mm_datacenter_delay_mount_frames", "6", FCVAR_DEVELOPMENTONLY, "How many frames to delay before attempting to mount the xlsp patch." );
  26. static CDatacenter g_Datacenter;
  27. CDatacenter *g_pDatacenter = &g_Datacenter;
  28. CON_COMMAND( mm_datacenter_debugprint, "Shows information retrieved from data center" )
  29. {
  30. KeyValuesDumpAsDevMsg( g_pDatacenter->GetDataInfo(), 1 );
  31. KeyValuesDumpAsDevMsg( g_pDatacenter->GetStats(), 1 );
  32. }
  33. //
  34. // Buffer encryption/decryption
  35. //
  36. #ifdef _GAMECONSOLE
  37. bool DecryptBuffer( CUtlBuffer &bufCypherText, CUtlBuffer &bufPlainText, unsigned int uiXorMask1 = 0 )
  38. {
  39. if ( bufCypherText.TellPut() < 20 )
  40. return false;
  41. int numBytes = bufCypherText.GetInt();
  42. int iRandom = bufCypherText.GetInt() ^ 0xce135ef8 ^ uiXorMask1;
  43. int iRandom2 = bufCypherText.GetInt() ^ 0x7ea55bb0;
  44. if ( numBytes < 0 ||
  45. bufCypherText.TellGet() + (numBytes + 7)/8 + 8 > bufCypherText.TellPut() )
  46. return false;
  47. IceKey cipher(1); /* medium encryption level */
  48. unsigned char ucEncryptionKey[8] = { 0 };
  49. *( int * )&ucEncryptionKey[ 0 ] = iRandom;
  50. *( int * )&ucEncryptionKey[ 4 ] = iRandom2;
  51. cipher.set( ucEncryptionKey );
  52. bufPlainText.EnsureCapacity( numBytes + 8 + 8 );
  53. unsigned char *pvPlainText = ( unsigned char * ) bufPlainText.PeekPut();
  54. unsigned char *pvCypher = ( unsigned char * ) bufCypherText.PeekGet();
  55. for ( int k = 0; k < numBytes;
  56. k += 8, pvPlainText += 8, pvCypher += 8 )
  57. {
  58. cipher.decrypt( pvCypher, pvPlainText );
  59. }
  60. unsigned char ucDecryptedKey[8] = {0};
  61. cipher.decrypt( pvCypher, ucDecryptedKey );
  62. if ( memcmp( ucDecryptedKey, ucEncryptionKey, sizeof( ucDecryptedKey ) ) )
  63. return false;
  64. bufPlainText.SeekPut( bufPlainText.SEEK_HEAD, numBytes );
  65. return true;
  66. }
  67. void EncryptBuffer( CUtlBuffer &bufPlainText, CUtlBuffer &bufCypherText, unsigned int uiXorMask1 = 0 )
  68. {
  69. int numBytes = bufPlainText.TellPut();
  70. float flRandom = Plat_FloatTime();
  71. int iRandom = *reinterpret_cast< int * >( &flRandom );
  72. int iRandom2 = reinterpret_cast< int >( bufPlainText.Base() ) ^ 0x5ef8ce13;
  73. // Function was written to allow bufCypherText to contain pre-existing data; this simply appends
  74. // the encrypted buffer to the end.
  75. // Add ( numBytes + 7 ) bytes since data is encrypted in blocks of 8 bytes, so numBytes is rounded up to multiple of 8
  76. // Add 12 bytes for numBytes and 2 random salt values
  77. // Add 16 bytes for double-encryption of encryption key
  78. // Add 1 byte to ensure there's room for NULL termination at the end (avoid re-allocation)
  79. bufCypherText.EnsureCapacity( bufCypherText.TellPut() + numBytes + 7 + 12 + 16 + 1 );
  80. bufCypherText.PutInt( numBytes ); // TellPut() + 4
  81. bufCypherText.PutInt( iRandom ^ 0xce135ef8 ^ uiXorMask1 ); // TellPut() + 8
  82. bufCypherText.PutInt( iRandom2 ^ 0x7ea55bb0 ); // TellPut() + 12
  83. IceKey cipher(1); /* medium encryption level */
  84. unsigned char ucEncryptionKey[8] = { 0 };
  85. *( int * )&ucEncryptionKey[ 0 ] = iRandom;
  86. *( int * )&ucEncryptionKey[ 4 ] = iRandom2;
  87. cipher.set( ucEncryptionKey );
  88. // Add some padding to the end of bufPlainText so the source data is padded out to 8 bytes
  89. bufPlainText.Put( ucEncryptionKey, sizeof( ucEncryptionKey ) );
  90. // Write directly into cypher buffer and increment Put pointer later
  91. unsigned char *pvPlainText = ( unsigned char * ) bufPlainText.PeekGet();
  92. unsigned char *pvCypher = ( unsigned char * ) bufCypherText.PeekPut(); // getting pointer to TellPut() + 12
  93. int numBytesWritten = 0;
  94. for ( int k = 0; k < numBytes;
  95. k += 8, pvPlainText += 8, pvCypher += 8, numBytesWritten += 8 )
  96. {
  97. cipher.encrypt( pvPlainText, pvCypher ); // writing numBytes rounded to multiple of 8, 8 bytes at a time (TellPut() + 12 + numBytes)
  98. }
  99. cipher.encrypt( ucEncryptionKey, pvCypher ); // TellPut() + 12 + ceil(numBytes, 8)
  100. numBytesWritten += 8;
  101. cipher.encrypt( ucEncryptionKey, pvCypher + 8 ); // TellPut() + 12 + ceil(numBytes, 8) + 8
  102. numBytesWritten += 8;
  103. bufCypherText.SeekPut( bufCypherText.SEEK_CURRENT, numBytesWritten );
  104. }
  105. #ifndef _CERT
  106. CON_COMMAND( mm_datacenter_encrypt_file, "" )
  107. {
  108. if ( args.ArgC() != 5 )
  109. {
  110. Warning( "Incorrect mm_datacenter_encrypt_file syntax!\n" );
  111. Warning( " mm_datacenter_encrypt_file D:\\update\\in.txt D:\\update\\out.bin 20100305 3589\n" );
  112. return;
  113. }
  114. char const *szIn = args.Arg( 1 );
  115. char const *szOut = args.Arg( 2 );
  116. KeyValues *kv = new KeyValues( "" );
  117. KeyValues::AutoDelete autodelete_kv( kv );
  118. if ( !kv->LoadFromFile( g_pFullFileSystem, szIn ) )
  119. {
  120. Warning( "Failed to load '%s'\n", szIn );
  121. return;
  122. }
  123. CUtlBuffer bufOut;
  124. bufOut.ActivateByteSwapping( !CByteswap::IsMachineBigEndian() );
  125. if ( !kv->WriteAsBinary( bufOut ) )
  126. {
  127. Warning( "Failed to serialize kv!\n" );
  128. return;
  129. }
  130. uint uiXorMaskDecrypt = Q_atoi( args.Arg( 3 ) ) ^ Q_atoi( args.Arg( 4 ) );
  131. CUtlBuffer bufDcCrypt;
  132. EncryptBuffer( bufOut, bufDcCrypt, uiXorMaskDecrypt );
  133. if ( !g_pFullFileSystem->WriteFile( szOut, NULL, bufDcCrypt ) )
  134. {
  135. Warning( "Failed to save '%s'\n", szOut );
  136. return;
  137. }
  138. Msg( "Successfully encrypted '%s'\n", szOut );
  139. }
  140. #endif
  141. #endif
  142. //
  143. // Datacenter implementation
  144. //
  145. CDatacenter::CDatacenter() :
  146. m_pInfoChunks( NULL ),
  147. m_pDataInfo( NULL ),
  148. #ifdef _X360
  149. m_pXlspConnection( NULL ),
  150. m_pXlspBatch( NULL ),
  151. m_nVersionStored( 0 ),
  152. m_nVersionApplied( 0 ),
  153. m_numDelayedMountAttempts( 0 ),
  154. m_flDcRequestDelayUntil( 0.0f ),
  155. #elif !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR ) && !defined( SWDS )
  156. m_JobIDDataRequest( k_GIDNil ),
  157. #endif
  158. m_flNextSearchTime( 0.0f ),
  159. m_bCanReachDatacenter( true ),
  160. m_eState( STATE_IDLE )
  161. {
  162. #ifdef _X360
  163. memset( m_bStorageDeviceAvail, 0, sizeof( m_bStorageDeviceAvail ) );
  164. #endif
  165. }
  166. CDatacenter::~CDatacenter()
  167. {
  168. if ( m_pInfoChunks )
  169. m_pInfoChunks->deleteThis();
  170. m_pInfoChunks = NULL;
  171. if ( m_pDataInfo )
  172. m_pDataInfo->deleteThis();
  173. m_pDataInfo = NULL;
  174. #ifdef _X360
  175. Assert( !m_pXlspConnection );
  176. Assert( !m_pXlspBatch );
  177. #elif !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR ) && !defined( SWDS )
  178. if ( GGCClient() )
  179. {
  180. GCSDK::CJob *pJob = GGCClient()->GetJobMgr().GetPJob( m_JobIDDataRequest );
  181. delete pJob;
  182. }
  183. #endif
  184. }
  185. void CDatacenter::PushAwayNextUpdate()
  186. {
  187. // Push away the next update to prevent start/stop updates
  188. float flNextUpdateTime = Plat_FloatTime() + mm_datacenter_query_delay.GetFloat();
  189. if ( flNextUpdateTime > m_flNextSearchTime )
  190. m_flNextSearchTime = flNextUpdateTime;
  191. }
  192. void CDatacenter::EnableUpdate( bool bEnable )
  193. {
  194. DevMsg( "Datacenter::EnableUpdate( %d ), current state = %d\n", bEnable, m_eState );
  195. if ( bEnable && m_eState == STATE_PAUSED )
  196. {
  197. m_eState = STATE_IDLE;
  198. PushAwayNextUpdate();
  199. }
  200. if ( !bEnable )
  201. {
  202. RequestStop();
  203. m_eState = STATE_PAUSED;
  204. }
  205. }
  206. KeyValues * CDatacenter::GetDataInfo()
  207. {
  208. return m_pInfoChunks;
  209. }
  210. KeyValues * CDatacenter::GetStats()
  211. {
  212. return m_pInfoChunks ? m_pInfoChunks->FindKey( "stat" ) : NULL;
  213. }
  214. //
  215. // CreateCmdBatch
  216. // creates a new instance of cmd batch to communicate
  217. // with datacenter backend
  218. //
  219. IDatacenterCmdBatch * CDatacenter::CreateCmdBatch( bool bMustSupportPII )
  220. {
  221. CDatacenterCmdBatchImpl *pBatch = new CDatacenterCmdBatchImpl( this, bMustSupportPII );
  222. m_arrCmdBatchObjects.AddToTail( pBatch );
  223. return pBatch;
  224. }
  225. //
  226. // CanReachDatacenter
  227. // returns true if we were able to establish a connection with the
  228. // datacenter backend regardless if it returned valid data or not.
  229. bool CDatacenter::CanReachDatacenter()
  230. {
  231. return m_bCanReachDatacenter;
  232. }
  233. void CDatacenter::OnCmdBatchReleased( CDatacenterCmdBatchImpl *pCmdBatch )
  234. {
  235. m_arrCmdBatchObjects.FindAndRemove( pCmdBatch );
  236. }
  237. void CDatacenter::OnEvent( KeyValues *pEvent )
  238. {
  239. char const *szEvent = pEvent->GetName();
  240. if ( !V_stricmp( szEvent, "OnProfileStorageAvailable" ) )
  241. {
  242. OnStorageDeviceAvailable( pEvent->GetInt( "iController" ) );
  243. }
  244. #ifdef _X360
  245. else if ( !V_stricmp( szEvent, "OnProfilesChanged" ) )
  246. {
  247. memset( m_bStorageDeviceAvail, 0, sizeof( m_bStorageDeviceAvail ) );
  248. }
  249. #endif
  250. }
  251. void CDatacenter::OnStorageDeviceAvailable( int iCtrlr )
  252. {
  253. #ifdef _X360
  254. DWORD nStorageDevice = XBX_GetStorageDeviceId( iCtrlr );
  255. if ( !XBX_DescribeStorageDevice( nStorageDevice ) )
  256. return;
  257. if ( iCtrlr >= 0 && iCtrlr < XUSER_MAX_COUNT )
  258. m_bStorageDeviceAvail[ iCtrlr ] = true;
  259. // Build the config name we're looking for
  260. char strFileName[MAX_PATH];
  261. XBX_MakeStorageContainerRoot( iCtrlr, XBX_USER_SETTINGS_CONTAINER_DRIVE, strFileName, sizeof( strFileName ) );
  262. int nLen = strlen( strFileName );
  263. // Call through normal API function once the content container is opened
  264. Q_snprintf( strFileName + nLen, sizeof(strFileName) - nLen, ":\\%08X_dc.nfo", g_pMatchFramework->GetMatchTitle()->GetTitleID() );
  265. CUtlBuffer bufDcInfoCrypt, bufDcInfo;
  266. if ( !g_pFullFileSystem->ReadFile( strFileName, NULL, bufDcInfoCrypt ) )
  267. {
  268. DevMsg( "CDatacenter::OnStorageDeviceAvailable - ctrlr%d has no dc.nfo\n", iCtrlr );
  269. return;
  270. }
  271. if ( !DecryptBuffer( bufDcInfoCrypt, bufDcInfo ) )
  272. {
  273. DevMsg( "CDatacenter::OnStorageDeviceAvailable - ctrlr%d dc.nfo decrypt failed\n", iCtrlr );
  274. return;
  275. }
  276. // Try reading key values info
  277. KeyValues *pKv = new KeyValues( "" );
  278. bufDcInfo.ActivateByteSwapping( !CByteswap::IsMachineBigEndian() );
  279. if ( !pKv->ReadAsBinary( bufDcInfo ) )
  280. {
  281. DevMsg( "CDatacenter::OnStorageDeviceAvailable - ctrlr%d kv read failed\n", iCtrlr );
  282. pKv->deleteThis();
  283. return;
  284. }
  285. // Check if the client is running the required TU
  286. char const *szTuRequired = pKv->GetString( "turequired" );
  287. if ( Q_stricmp( szTuRequired, MatchSession_GetTuInstalledString() ) )
  288. {
  289. DevMsg( "CDatacenter::OnStorageDeviceAvailable - ctrlr%d has dc.nfo for wrong TU version (turequired = %s, current tu = %s)\n",
  290. iCtrlr, szTuRequired, MatchSession_GetTuInstalledString() );
  291. pKv->deleteThis();
  292. return;
  293. }
  294. // Check version of the key values
  295. int nVersionStored = pKv->GetInt( "version", 0 );
  296. if ( m_pInfoChunks->GetInt( "version", 0 ) >= nVersionStored )
  297. {
  298. DevMsg( "CDatacenter::OnStorageDeviceAvailable - ctrlr%d has stale dc.nfo (version = %d, current version = %d)\n",
  299. iCtrlr, nVersionStored, m_pInfoChunks->GetInt( "version", 0 ) );
  300. m_nVersionStored = MAX( m_nVersionStored, nVersionStored );
  301. pKv->deleteThis();
  302. return;
  303. }
  304. // Key values obtained from storage are fresh
  305. if ( m_pInfoChunks )
  306. m_pInfoChunks->deleteThis();
  307. m_pInfoChunks = pKv;
  308. DevMsg( "CDatacenter::OnStorageDeviceAvailable - ctrlr%d has valid dc.nfo (version = %d)\n", iCtrlr, nVersionStored );
  309. m_nVersionStored = nVersionStored;
  310. OnDatacenterInfoUpdated();
  311. #endif
  312. }
  313. void CDatacenter::StorageDeviceWriteInfo( int iCtrlr )
  314. {
  315. #ifdef _X360
  316. float flTimeStart;
  317. flTimeStart = Plat_FloatTime();
  318. // Build the config name we're looking for
  319. char strFileName[MAX_PATH];
  320. XBX_MakeStorageContainerRoot( iCtrlr, XBX_USER_SETTINGS_CONTAINER_DRIVE, strFileName, sizeof( strFileName ) );
  321. int nLen = strlen( strFileName );
  322. // Call through normal API function once the content container is opened
  323. Q_snprintf( strFileName + nLen, sizeof(strFileName) - nLen, ":\\%08X_dc.nfo", g_pMatchFramework->GetMatchTitle()->GetTitleID() );
  324. //
  325. // Serialize our data
  326. //
  327. CUtlBuffer bufDcInfo;
  328. bufDcInfo.ActivateByteSwapping( !CByteswap::IsMachineBigEndian() );
  329. if ( !m_pInfoChunks->WriteAsBinary( bufDcInfo ) )
  330. return;
  331. CUtlBuffer bufDcCrypt;
  332. EncryptBuffer( bufDcInfo, bufDcCrypt );
  333. g_pFullFileSystem->WriteFile( strFileName, NULL, bufDcCrypt );
  334. // Finish container writes
  335. g_pMatchExtensions->GetIXboxSystem()->FinishContainerWrites( iCtrlr );
  336. DevMsg( "CDatacenter::StorageDeviceWriteInfo finished in %.2f sec\n", Plat_FloatTime() - flTimeStart );
  337. #endif
  338. }
  339. void CDatacenter::TrySaveInfoToUserStorage()
  340. {
  341. #ifdef _X360
  342. static ConVarRef host_write_last_time( "host_write_last_time" );
  343. for ( int k = 0; k < ( int ) XBX_GetNumGameUsers(); ++ k )
  344. {
  345. int iCtrlr = XBX_GetUserId( k );
  346. if ( iCtrlr >= 0 && iCtrlr < XUSER_MAX_COUNT &&
  347. m_bStorageDeviceAvail[ iCtrlr ] && m_pInfoChunks &&
  348. ( Plat_FloatTime() - host_write_last_time.GetFloat() > 3.05f ) &&
  349. ( m_pInfoChunks->GetInt( "version", 0 ) > m_nVersionStored ) )
  350. {
  351. m_bStorageDeviceAvail[ iCtrlr ] = false;
  352. StorageDeviceWriteInfo( iCtrlr );
  353. return;
  354. }
  355. }
  356. #endif
  357. }
  358. void CDatacenter::Update()
  359. {
  360. #ifdef _X360
  361. #elif !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR ) && !defined( SWDS )
  362. // Give a time-slice to the GCClient, which is used by Steam to communicate with the datacenter
  363. if ( GGCClient() && !IsLocalClientConnectedToServer() )
  364. {
  365. GGCClient()->BMainLoop( k_nThousand );
  366. }
  367. #endif
  368. switch ( m_eState )
  369. {
  370. case STATE_IDLE:
  371. if ( Plat_FloatTime() > m_flNextSearchTime &&
  372. !IsLocalClientConnectedToServer() )
  373. RequestStart();
  374. else
  375. {
  376. TrySaveInfoToUserStorage();
  377. #ifdef _X360
  378. if ( m_numDelayedMountAttempts > 0 )
  379. {
  380. if ( !IsLocalClientConnectedToServer() )
  381. {
  382. if ( m_numDelayedMountAttempts <= 2 )
  383. {
  384. m_numDelayedMountAttempts -= 2; // if knocking it down to 0, then will allow a retry next frame, otherwise will knock it into negative and will not allow a retry
  385. OnDatacenterInfoUpdated();
  386. }
  387. else
  388. {
  389. -- m_numDelayedMountAttempts;
  390. }
  391. }
  392. else
  393. {
  394. // User connected to server, reset delayed mount attempts
  395. m_numDelayedMountAttempts = mm_datacenter_delay_mount_frames.GetInt(); // attempt to mount again when disconnected
  396. }
  397. }
  398. #endif
  399. }
  400. break;
  401. case STATE_REQUESTING_DATA:
  402. case STATE_REQUESTING_CHUNKS:
  403. RequestUpdate();
  404. break;
  405. case STATE_PAUSED:
  406. // paused
  407. break;
  408. }
  409. // Update all the contained cmd batches
  410. for ( int k = 0; k < m_arrCmdBatchObjects.Count(); ++ k )
  411. {
  412. m_arrCmdBatchObjects[k]->Update();
  413. }
  414. }
  415. void CDatacenter::RequestStart()
  416. {
  417. #ifdef _X360
  418. if ( XBX_GetPrimaryUserId() == XBX_INVALID_USER_ID )
  419. return;
  420. if ( !XBX_GetNumGameUsers() || XBX_GetPrimaryUserIsGuest() )
  421. return;
  422. IPlayerLocal *pLocalPlayer = g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() );
  423. if ( !pLocalPlayer )
  424. return;
  425. // We are about to send the request, inject a delay here so that
  426. // we had enough time to discover and mount DLC
  427. if ( !m_flDcRequestDelayUntil )
  428. {
  429. m_flDcRequestDelayUntil = Plat_FloatTime();
  430. g_pMatchFramework->GetMatchSystem()->GetDlcManager()->RequestDlcUpdate();
  431. return;
  432. }
  433. else if ( ( Plat_FloatTime() < m_flDcRequestDelayUntil + mm_datacenter_query_delay.GetFloat() ) ||
  434. ( !g_pMatchFramework->GetMatchSystem()->GetDlcManager()->IsDlcUpdateFinished() ) )
  435. {
  436. return; // waiting for the first-time request delay
  437. }
  438. #elif !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR ) && !defined( SWDS )
  439. if ( !GGCClient() )
  440. return;
  441. // Avoid stacking requests
  442. if ( GGCClient()->GetJobMgr().BJobExists( m_JobIDDataRequest ) )
  443. return;
  444. GCSDK::CJob *pJob = new CGCClientJobDataRequest();
  445. m_JobIDDataRequest = pJob->GetJobID();
  446. pJob->StartJob( NULL );
  447. #else
  448. #endif
  449. #ifdef _X360
  450. m_pXlspConnection = new CXlspConnection( true );
  451. CUtlVector< KeyValues * > arrCommands;
  452. if ( KeyValues *cmd = new KeyValues( "datarequest" ) )
  453. {
  454. // Game title id
  455. cmd->SetInt( "titleid", g_pMatchFramework->GetMatchTitle()->GetTitleID() );
  456. // Data request fields
  457. cmd->SetInt( "version", m_pInfoChunks->GetInt( "version", 0 ) );
  458. cmd->SetInt( "verrprt", mm_datacenter_report_version.GetInt() );
  459. cmd->SetUint64( "dlcmask", g_pMatchFramework->GetMatchSystem()->GetDlcManager()->GetDataInfo()->GetUint64( "@info/installed" ) );
  460. // XUID
  461. cmd->SetUint64( "xuid", pLocalPlayer->GetXUID() );
  462. // Obfuscated name
  463. char chName[ 2 * MAX_PLAYER_NAME_LENGTH ] = {0};
  464. Q_strncpy( chName + 1, pLocalPlayer->GetName(), ARRAYSIZE( chName ) - 1 );
  465. chName[0] = RandomInt( 5, 15 );
  466. for ( char *pch = chName + 1; *pch; ++ pch )
  467. ( *pch ) = ( *pch ) ^ chName[0];
  468. cmd->SetString( "name", chName );
  469. // Prepare user privileges
  470. struct Priv_t { XPRIVILEGE_TYPE ePriv, eFriendsOnly; uint64 iFlag; };
  471. Priv_t arrPrivs[] = {
  472. { XPRIVILEGE_MULTIPLAYER_SESSIONS, XPRIVILEGE_MULTIPLAYER_SESSIONS, 1ull << 0 },
  473. { XPRIVILEGE_PURCHASE_CONTENT, XPRIVILEGE_PURCHASE_CONTENT, 1ull << 4 },
  474. { XPRIVILEGE_TRADE_CONTENT, XPRIVILEGE_TRADE_CONTENT, 1ull << 8 },
  475. { XPRIVILEGE_CONTENT_AUTHOR, XPRIVILEGE_CONTENT_AUTHOR, 1ull << 12 },
  476. { XPRIVILEGE_COMMUNICATIONS, XPRIVILEGE_COMMUNICATIONS_FRIENDS_ONLY, 1ull << 16 },
  477. { XPRIVILEGE_PROFILE_VIEWING, XPRIVILEGE_PROFILE_VIEWING_FRIENDS_ONLY, 1ull << 20 },
  478. { XPRIVILEGE_USER_CREATED_CONTENT, XPRIVILEGE_USER_CREATED_CONTENT_FRIENDS_ONLY, 1ull << 24 },
  479. { XPRIVILEGE_PRESENCE, XPRIVILEGE_PRESENCE_FRIENDS_ONLY, 1ull << 28 },
  480. { XPRIVILEGE_VIDEO_COMMUNICATIONS, XPRIVILEGE_VIDEO_COMMUNICATIONS_FRIENDS_ONLY, 1ull << 32 },
  481. };
  482. uint64 uiPrivilegesMask = 0ull;
  483. for ( int jj = 0; jj < ARRAYSIZE( arrPrivs ); ++ jj )
  484. {
  485. uint64 uiThisPriv = 0ull;
  486. BOOL bPriv = FALSE;
  487. Priv_t const &priv = arrPrivs[jj];
  488. DWORD dwResult = XUserCheckPrivilege( XBX_GetPrimaryUserId(), priv.ePriv, &bPriv );
  489. if ( dwResult == ERROR_SUCCESS )
  490. {
  491. uiThisPriv |= priv.iFlag;
  492. if ( bPriv )
  493. {
  494. uiThisPriv |= ( priv.iFlag << 1 );
  495. }
  496. else if ( priv.eFriendsOnly != priv.ePriv )
  497. {
  498. dwResult = XUserCheckPrivilege( XBX_GetPrimaryUserId(), priv.eFriendsOnly, &bPriv );
  499. if ( dwResult == ERROR_SUCCESS )
  500. {
  501. uiThisPriv |= ( priv.iFlag << 2 );
  502. if ( bPriv )
  503. uiThisPriv |= ( priv.iFlag << 3 );
  504. }
  505. }
  506. }
  507. uiPrivilegesMask |= uiThisPriv;
  508. }
  509. cmd->SetUint64( "priv", uiPrivilegesMask );
  510. // LV
  511. cmd->SetInt( "lv", !!g_pMatchExtensions->GetIVEngineClient()->IsLowViolence() );
  512. // Console rgn/locale
  513. cmd->SetInt( "xrgn", XGetGameRegion() );
  514. cmd->SetInt( "xlng", XGetLanguage() );
  515. cmd->SetInt( "xloc", XGetLocale() );
  516. // Video mode
  517. XVIDEO_MODE xvid;
  518. XGetVideoMode( &xvid );
  519. cmd->SetInt( "scrw", xvid.dwDisplayWidth );
  520. cmd->SetInt( "scrh", xvid.dwDisplayHeight );
  521. cmd->SetInt( "vidi", xvid.fIsInterlaced );
  522. cmd->SetInt( "vidw", xvid.fIsWideScreen );
  523. cmd->SetInt( "vidh", xvid.fIsHiDef );
  524. cmd->SetInt( "vids", xvid.VideoStandard );
  525. cmd->SetFloat( "scrr", xvid.RefreshRate );
  526. // Sound mode
  527. static ConVarRef snd_surround_speakers( "snd_surround_speakers" );
  528. cmd->SetInt( "snd", snd_surround_speakers.GetInt() );
  529. // Controllers
  530. int uMaskControllersConnected = 0;
  531. for ( int k = 0; k < XUSER_MAX_COUNT; ++ k )
  532. {
  533. XINPUT_CAPABILITIES caps;
  534. if ( XInputGetCapabilities( k, XINPUT_FLAG_GAMEPAD, &caps ) == ERROR_SUCCESS )
  535. {
  536. uMaskControllersConnected |= ( 1 << k );
  537. }
  538. }
  539. cmd->SetInt( "joy", uMaskControllersConnected );
  540. // Profile info
  541. const UserProfileData &ups = pLocalPlayer->GetPlayerProfileData();
  542. cmd->SetInt( "urgn", ups.region );
  543. cmd->SetInt( "uach", ups.achearned );
  544. cmd->SetInt( "uzon", ups.zone );
  545. cmd->SetInt( "ucrd", ups.cred );
  546. cmd->SetInt( "utit", ups.titlesplayed );
  547. cmd->SetInt( "udif", ups.difficulty );
  548. cmd->SetInt( "usns", ups.sensitivity );
  549. cmd->SetInt( "uyax", ups.yaxis );
  550. cmd->SetInt( "utia", ups.titleachearned );
  551. cmd->SetInt( "utic", ups.titlecred );
  552. // Datacenter information
  553. cmd->SetInt( "*dcpgmi", 0 );
  554. cmd->SetInt( "*dcpgme", 0 );
  555. cmd->SetInt( "*dcpgbu", 0 );
  556. cmd->SetInt( "*dcbwup", 0 );
  557. cmd->SetInt( "*dcbwdn", 0 );
  558. cmd->SetInt( "*net", 0 );
  559. cmd->SetInt( "*nat", 0 );
  560. cmd->SetUint64( "*mac", 0 );
  561. cmd->SetUint64( "*diskDsn", 0 );
  562. cmd->SetUint64( "*diskDnfo", 0 );
  563. cmd->SetUint64( "*diskCnfo", 0 );
  564. cmd->SetUint64( "*diskHnfo", 0 );
  565. cmd->SetUint64( "*disk1nfo", 0 );
  566. // Let title extend it
  567. g_pMMF->GetMatchTitleGameSettingsMgr()->ExtendDatacenterReport( cmd, "datarequest" );
  568. arrCommands.AddToTail( cmd );
  569. }
  570. m_pXlspBatch = new CXlspConnectionCmdBatch( m_pXlspConnection, arrCommands );
  571. #endif
  572. #if !defined( _PS3 ) && !defined( NO_STEAM_GAMECOORDINATOR )
  573. DevMsg( "Datacenter::RequestStart, time %.2f\n", Plat_FloatTime() );
  574. #endif
  575. m_eState = STATE_REQUESTING_DATA;
  576. }
  577. void CDatacenter::RequestStop()
  578. {
  579. #if !defined( _PS3 ) && !defined( NO_STEAM_GAMECOORDINATOR )
  580. DevMsg( "Datacenter::RequestStop, time %.2f, state %d\n", Plat_FloatTime(), m_eState );
  581. #endif
  582. bool bWasRequestingData = false;
  583. #ifdef _X360
  584. if ( m_pXlspBatch )
  585. {
  586. m_pXlspBatch->Destroy();
  587. m_pXlspBatch = NULL;
  588. }
  589. if ( m_pXlspConnection )
  590. {
  591. m_pXlspConnection->Destroy();
  592. m_pXlspConnection = NULL;
  593. bWasRequestingData = true;
  594. }
  595. #elif !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR ) && !defined( SWDS )
  596. if ( GGCClient() )
  597. {
  598. bWasRequestingData = GGCClient()->GetJobMgr().BJobExists( m_JobIDDataRequest );
  599. }
  600. m_JobIDDataRequest = k_GIDNil;
  601. #endif
  602. if ( bWasRequestingData )
  603. m_flNextSearchTime = Plat_FloatTime() + mm_datacenter_retry_interval.GetFloat();
  604. m_eState = STATE_IDLE;
  605. }
  606. void CDatacenter::RequestUpdate()
  607. {
  608. bool bSuccessfulUpdate = false;
  609. #ifdef _X360
  610. m_pXlspBatch->Update();
  611. if ( !m_pXlspBatch->IsFinished() )
  612. return;
  613. CUtlVector< KeyValues * > &arrResults = m_pXlspBatch->GetResults();
  614. DevMsg( "Datacenter::RequestUpdate, time %.2f, state %d, batch finished: results %d, error %d\n",
  615. Plat_FloatTime(), m_eState, arrResults.Count(), m_pXlspConnection->HasError() );
  616. switch ( m_eState )
  617. {
  618. case STATE_REQUESTING_DATA:
  619. if ( m_pXlspBatch->HasAllResults() )
  620. {
  621. // We received information about chunks and datacenter info
  622. // "datarequest" command succeeded
  623. if ( m_pDataInfo )
  624. m_pDataInfo->deleteThis();
  625. m_pDataInfo = arrResults[0]->MakeCopy();
  626. // Special handling for base disk turequired
  627. char const *szTuRequiredDc = m_pDataInfo->GetString( "turequired" );
  628. if ( !V_strcmp( szTuRequiredDc, "0" ) )
  629. m_pDataInfo->SetString( "turequired", "00000000" );
  630. DevMsg( "Datacenter::RequestUpdate - data info received\n" );
  631. KeyValuesDumpAsDevMsg( m_pDataInfo, 1 );
  632. // Destroy prev batch
  633. m_pXlspBatch->Destroy();
  634. m_pXlspBatch = NULL;
  635. // Prepare the new batch
  636. CUtlVector< KeyValues * > arrBatch;
  637. if ( KeyValues *pBatch2 = m_pDataInfo->FindKey( "chunks" ) )
  638. {
  639. bool bTitleSupportsXlspPatch = !!( g_pMatchFramework->GetMatchTitle()->GetTitleSettingsFlags() & MATCHTITLE_XLSPPATCH_SUPPORTED );
  640. while ( KeyValues *pReq = pBatch2->GetFirstTrueSubKey() )
  641. {
  642. int numChunks = pReq->GetInt( "chunks", 0 );
  643. if ( KeyValues *pChunks = pReq->FindKey( "chunks" ) )
  644. {
  645. pReq->RemoveSubKey( pChunks );
  646. pReq->SetInt( "version", m_pDataInfo->GetInt( "version" ) );
  647. pChunks->deleteThis();
  648. }
  649. bool bShouldRequest = true;
  650. if ( !V_stricmp( "data_patch", pReq->GetName() ) && !bTitleSupportsXlspPatch )
  651. bShouldRequest = false;
  652. if ( bShouldRequest )
  653. {
  654. if ( !numChunks )
  655. {
  656. arrBatch.AddToTail( pReq->MakeCopy() );
  657. }
  658. else
  659. {
  660. for ( int k = 0; k < numChunks; ++ k )
  661. {
  662. pReq->SetInt( "chunk", k + 1 );
  663. arrBatch.AddToTail( pReq->MakeCopy() );
  664. }
  665. }
  666. }
  667. pBatch2->RemoveSubKey( pReq );
  668. pReq->deleteThis();
  669. }
  670. }
  671. if ( !arrBatch.Count() )
  672. {
  673. bSuccessfulUpdate = true;
  674. }
  675. // Start our new batch
  676. else
  677. {
  678. DevMsg( "Datacenter::RequestUpdate - info chunks - requesting %d packets\n", arrBatch.Count() );
  679. m_pXlspBatch = new CXlspConnectionCmdBatch( m_pXlspConnection, arrBatch, mm_datacenter_retry_infochunks_attempts.GetInt() );
  680. m_eState = STATE_REQUESTING_CHUNKS;
  681. return;
  682. }
  683. }
  684. break;
  685. case STATE_REQUESTING_CHUNKS:
  686. bSuccessfulUpdate = m_pXlspBatch->HasAllResults();
  687. DevMsg( "Datacenter::RequestUpdate - info chunks - received %d packets\n", arrResults.Count() );
  688. if ( bSuccessfulUpdate && arrResults.Count() )
  689. {
  690. if ( m_pInfoChunks )
  691. m_pInfoChunks->deleteThis();
  692. m_pInfoChunks = m_pDataInfo->MakeCopy();
  693. for ( int k = 0; k < arrResults.Count(); ++ k )
  694. {
  695. m_pInfoChunks->MergeFrom( arrResults[k], KeyValues::MERGE_KV_UPDATE );
  696. DevMsg( 2, "-- info chunk %d\n", k + 1 );
  697. KeyValuesDumpAsDevMsg( arrResults[k], 1, 2 );
  698. }
  699. DevMsg( 2, "-- Full info chunks:\n" );
  700. KeyValuesDumpAsDevMsg( m_pInfoChunks, 1,2 );
  701. }
  702. break;
  703. }
  704. #elif !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR ) && !defined( SWDS )
  705. if ( GGCClient() )
  706. {
  707. CGCClientJobDataRequest *pJob = (CGCClientJobDataRequest *)GGCClient()->GetJobMgr().GetPJob( m_JobIDDataRequest );
  708. if ( pJob )
  709. {
  710. if ( !pJob->BComplete() )
  711. return;
  712. bSuccessfulUpdate = pJob->BSuccess();
  713. if ( bSuccessfulUpdate )
  714. {
  715. if ( m_pDataInfo )
  716. m_pDataInfo->deleteThis();
  717. if ( m_pInfoChunks )
  718. m_pInfoChunks->deleteThis();
  719. m_pDataInfo = pJob->GetResults()->MakeCopy();
  720. m_pInfoChunks = pJob->GetResults()->MakeCopy();
  721. }
  722. pJob->Finish();
  723. }
  724. }
  725. #else
  726. #endif
  727. RequestStop();
  728. #if !defined( _PS3 ) && !defined( NO_STEAM_GAMECOORDINATOR )
  729. DevMsg( "Datacenter::RequestUpdate %s\n", bSuccessfulUpdate ? "successful" : "failed" );
  730. #endif
  731. m_bCanReachDatacenter = bSuccessfulUpdate;
  732. if ( bSuccessfulUpdate )
  733. {
  734. m_flNextSearchTime = Plat_FloatTime() + mm_datacenter_update_interval.GetFloat();
  735. OnDatacenterInfoUpdated();
  736. }
  737. }
  738. static void UnpackPatchBinary( KeyValues *pPatch, CUtlBuffer &buf )
  739. {
  740. int nSize = pPatch->GetInt( "size" );
  741. if ( !nSize )
  742. {
  743. buf.Purge();
  744. return;
  745. }
  746. buf.EnsureCapacity( nSize + sizeof( uint64 ) ); // include extra room for alignments
  747. buf.SeekPut( buf.SEEK_HEAD, nSize ); // set the data size
  748. unsigned char *pchBuffer = ( unsigned char * ) buf.Base();
  749. memset( pchBuffer, 0, nSize );
  750. for ( KeyValues *sub = pPatch->GetFirstTrueSubKey(); sub; sub = sub->GetNextTrueSubKey() )
  751. {
  752. for( KeyValues *val = sub->GetFirstValue(); val; val = val->GetNextValue() )
  753. {
  754. if ( !val->GetName()[0] && val->GetDataType() == KeyValues::TYPE_UINT64 )
  755. {
  756. if ( !nSize )
  757. {
  758. nSize -= sizeof( uint64 );
  759. goto unpack_error;
  760. }
  761. uint64 ui = val->GetUint64();
  762. for ( int k = 0; k < MIN( nSize, sizeof( ui ) ); ++ k )
  763. {
  764. pchBuffer[k] = ( unsigned char )( ( ui >> ( 8 * k ) ) & 0xFF );
  765. }
  766. nSize -= MIN( nSize, sizeof( ui ) );
  767. pchBuffer += MIN( nSize, sizeof( ui ) );
  768. }
  769. }
  770. }
  771. // If all the bytes were correctly written to buffer, then the unpack succeeded
  772. if ( !nSize )
  773. return;
  774. unpack_error:
  775. // Transmitted patch indicated a size different than transmitted data!
  776. DevWarning( "UnpackPatchBinary error: %d size indicated, but %d bytes failed to unpack!\n", buf.TellPut(), nSize );
  777. buf.Purge();
  778. return;
  779. }
  780. void CDatacenter::OnDatacenterInfoUpdated()
  781. {
  782. #ifdef _X360
  783. // Check if the client is running the required TU
  784. char const *szTuRequired = m_pInfoChunks->GetString( "turequired" );
  785. if ( Q_stricmp( szTuRequired, MatchSession_GetTuInstalledString() ) )
  786. {
  787. DevWarning( "CDatacenter::OnDatacenterInfoUpdated - wrong TU version (datacenter requires = %s, installed = %s)\n",
  788. szTuRequired, MatchSession_GetTuInstalledString() );
  789. return;
  790. }
  791. // Don't try to mount the update in the middle of the game
  792. if ( IsLocalClientConnectedToServer() )
  793. {
  794. m_numDelayedMountAttempts = mm_datacenter_delay_mount_frames.GetInt(); // attempt to mount again when disconnected
  795. return;
  796. }
  797. #endif
  798. // Downloaded update version
  799. int nUpdateVersion = m_pInfoChunks->GetInt( "version", 0 );
  800. bool bXlspPatchFileMounted = false;
  801. #ifdef _X360
  802. // Filesystem needs to mount new patch binary
  803. if ( KeyValues *pPatch = m_pInfoChunks->FindKey( "patch" ) )
  804. {
  805. if ( nUpdateVersion > m_nVersionApplied )
  806. {
  807. CUtlBuffer bufPatchData;
  808. UnpackPatchBinary( pPatch, bufPatchData );
  809. DevMsg( "CDatacenter::OnDatacenterInfoUpdated mounting patch binary data: %d bytes at %p\n", bufPatchData.TellPut(), bufPatchData.Base() );
  810. if ( g_pFullFileSystem->AddXLSPUpdateSearchPath( bufPatchData.Base(), bufPatchData.TellPut() ) )
  811. {
  812. DevMsg( "CDatacenter::OnDatacenterInfoUpdated successfully mounted patch binary data (version %d -> %d)\n", m_nVersionApplied, nUpdateVersion );
  813. m_nVersionApplied = nUpdateVersion;
  814. bXlspPatchFileMounted = true;
  815. // Try to apply cvar section
  816. CUtlBuffer bufCvarCrypt, bufCvarInfo;
  817. bool bMainFilePresent = g_pFullFileSystem->ReadFile( "scripts/main.nut", NULL, bufCvarCrypt );
  818. uint uiXorMaskDecrypt = Q_atoi( szTuRequired ) ^ bufCvarCrypt.TellPut();
  819. if ( bMainFilePresent &&
  820. DecryptBuffer( bufCvarCrypt, bufCvarInfo, uiXorMaskDecrypt ) )
  821. {
  822. KeyValues *cvkv = new KeyValues( "" );
  823. KeyValues::AutoDelete autodelete_cvkv( cvkv );
  824. bufCvarInfo.ActivateByteSwapping( !CByteswap::IsMachineBigEndian() );
  825. if ( cvkv->ReadAsBinary( bufCvarInfo ) )
  826. {
  827. // Process the cvar section and patch them up
  828. for ( KeyValues *kvName = cvkv->FindKey( "cvar" )->GetFirstValue(); kvName; kvName = kvName->GetNextValue() )
  829. {
  830. ConVarRef cvRef( kvName->GetName(), true ); // deliberately non-static, enumerating in a loop
  831. if ( !cvRef.IsValid() )
  832. {
  833. #ifndef _CERT
  834. DevWarning( "CDatacenter::OnDatacenterInfoUpdated failed to update cvar '%s' = '%s'\n", kvName->GetName(), kvName->GetString() );
  835. #endif
  836. }
  837. else
  838. {
  839. #ifndef _CERT
  840. DevMsg( "CDatacenter::OnDatacenterInfoUpdated updating cvar '%s' = '%s' -> '%s'\n", kvName->GetName(), cvRef.GetString(), kvName->GetString() );
  841. #endif
  842. cvRef.SetValue( kvName->GetString() );
  843. }
  844. }
  845. }
  846. }
  847. }
  848. else
  849. {
  850. DevMsg( "CDatacenter::OnDatacenterInfoUpdated failed to mount version %d (was %d)\n", nUpdateVersion, m_nVersionApplied );
  851. // Give ourselves a retry attempt
  852. ++ m_numDelayedMountAttempts;
  853. }
  854. }
  855. }
  856. #endif
  857. // Signal all other subscribers about the update
  858. if ( KeyValues *newEvent = new KeyValues( "OnDatacenterUpdate", "version", nUpdateVersion ) )
  859. {
  860. if ( bXlspPatchFileMounted )
  861. newEvent->SetInt( "filesystempatch", 1 );
  862. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( newEvent );
  863. }
  864. }
  865. //////////////////////////////////////////////////////////////////////////
  866. //
  867. // CDatacenterCmdBatchImpl
  868. //
  869. CDatacenterCmdBatchImpl::CDatacenterCmdBatchImpl( CDatacenter *pParent, bool bMustSupportPII ) :
  870. #ifdef _X360
  871. m_pXlspConnection( NULL ),
  872. m_pXlspBatch( NULL ),
  873. #endif
  874. m_pParent( pParent ),
  875. m_arrCommands(),
  876. m_numRetriesAllowedPerCmd( 0 ),
  877. m_flRetryCmdTimeout( 0 ),
  878. m_bDestroyWhenFinished( true ),
  879. m_bMustSupportPII( bMustSupportPII )
  880. {
  881. }
  882. void CDatacenterCmdBatchImpl::AddCommand( KeyValues *pCommand )
  883. {
  884. if ( !pCommand )
  885. return;
  886. #ifdef _X360
  887. if ( m_pXlspBatch )
  888. {
  889. Warning( "CDatacenterCmdBatchImpl::AddCommand after already initiated batch processing!\n" );
  890. Assert( 0 );
  891. return;
  892. }
  893. #endif
  894. m_arrCommands.AddToTail( pCommand->MakeCopy() );
  895. }
  896. bool CDatacenterCmdBatchImpl::IsFinished()
  897. {
  898. #ifdef _X360
  899. return m_pXlspBatch && m_pXlspBatch->IsFinished();
  900. #else
  901. return true;
  902. #endif
  903. }
  904. int CDatacenterCmdBatchImpl::GetNumResults()
  905. {
  906. #ifdef _X360
  907. return m_pXlspBatch ? m_pXlspBatch->GetResults().Count() : 0;
  908. #else
  909. return 0;
  910. #endif
  911. }
  912. KeyValues * CDatacenterCmdBatchImpl::GetResult( int idx )
  913. {
  914. #ifdef _X360
  915. if ( !m_pXlspBatch )
  916. return NULL;
  917. if ( !m_pXlspBatch->GetResults().IsValidIndex( idx ) )
  918. return NULL;
  919. return m_pXlspBatch->GetResults()[idx];
  920. #else
  921. return NULL;
  922. #endif
  923. }
  924. void CDatacenterCmdBatchImpl::Destroy()
  925. {
  926. if ( m_pParent )
  927. m_pParent->OnCmdBatchReleased( this );
  928. for ( int k = 0; k < m_arrCommands.Count(); ++ k )
  929. {
  930. m_arrCommands[k]->deleteThis();
  931. }
  932. m_arrCommands.Purge();
  933. #ifdef _X360
  934. if ( m_pXlspBatch )
  935. {
  936. m_pXlspBatch->Destroy();
  937. m_pXlspBatch = NULL;
  938. }
  939. if ( m_pXlspConnection )
  940. {
  941. m_pXlspConnection->Destroy();
  942. m_pXlspConnection = NULL;
  943. }
  944. #endif
  945. delete this;
  946. }
  947. void CDatacenterCmdBatchImpl::SetDestroyWhenFinished( bool bDestroyWhenFinished )
  948. {
  949. m_bDestroyWhenFinished = bDestroyWhenFinished;
  950. }
  951. void CDatacenterCmdBatchImpl::SetNumRetriesAllowedPerCmd( int numRetriesAllowed )
  952. {
  953. m_numRetriesAllowedPerCmd = numRetriesAllowed;
  954. }
  955. void CDatacenterCmdBatchImpl::SetRetryCmdTimeout( float flRetryCmdTimeout )
  956. {
  957. m_flRetryCmdTimeout = flRetryCmdTimeout;
  958. }
  959. void CDatacenterCmdBatchImpl::Update()
  960. {
  961. #ifdef _X360
  962. if ( !m_pXlspBatch && m_arrCommands.Count() )
  963. {
  964. // Need to kick off XLSP batch processing
  965. m_pXlspConnection = new CXlspConnection( m_bMustSupportPII );
  966. m_pXlspBatch = new CXlspConnectionCmdBatch( m_pXlspConnection, m_arrCommands, m_numRetriesAllowedPerCmd, m_flRetryCmdTimeout );
  967. return;
  968. }
  969. if ( m_pXlspBatch )
  970. {
  971. m_pXlspBatch->Update();
  972. if ( m_pXlspBatch->IsFinished() )
  973. {
  974. // Detach this cmd batch processor from the frame updates
  975. if ( m_pParent )
  976. m_pParent->OnCmdBatchReleased( this );
  977. // Notify listeners
  978. // Signal that we are finished with cmd batch
  979. KeyValues *pNotify = new KeyValues( "OnDatacenterCmdBatchUpdate", "update", "finished" );
  980. pNotify->SetPtr( "cmdbatch", this );
  981. pNotify->SetInt( "results", m_pXlspBatch->GetResults().Count() );
  982. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( pNotify );
  983. // Destroy ourselves since we are finished
  984. if ( m_bDestroyWhenFinished )
  985. this->Destroy();
  986. }
  987. }
  988. #else
  989. // Destroy ourselves since we cannot do any work anyway
  990. if ( m_bDestroyWhenFinished )
  991. this->Destroy();
  992. #endif
  993. }