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.

540 lines
14 KiB

  1. //========= Copyright � 1996-2009, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=====================================================================================//
  6. #include "mm_framework.h"
  7. #include "leaderboards.h"
  8. #include "fmtstr.h"
  9. // NOTE: This has to be the last file included!
  10. #include "tier0/memdbgon.h"
  11. //
  12. // Definition of leaderboard request queue class
  13. //
  14. class CLeaderboardRequestQueue : public ILeaderboardRequestQueue
  15. {
  16. public:
  17. CLeaderboardRequestQueue();
  18. ~CLeaderboardRequestQueue();
  19. // ILeaderboardRequestQueue
  20. public:
  21. virtual void Request( KeyValues *pRequest );
  22. virtual void Update();
  23. public:
  24. KeyValues * GetFinishedRequest();
  25. protected:
  26. CUtlVector< KeyValues * > m_arrRequests;
  27. bool m_bQueryRunning;
  28. KeyValues *m_pFinishedRequest;
  29. void OnStartNewQuery();
  30. void OnSubmitQuery();
  31. void OnQueryFinished();
  32. void Cleanup();
  33. protected:
  34. #ifdef _X360
  35. // Leaderboard query data
  36. CUtlVector< XUID > m_arrXuids;
  37. CUtlVector< XUSER_STATS_SPEC > m_arrSpecs;
  38. CUtlBuffer m_bufResults;
  39. XOVERLAPPED m_xOverlapped;
  40. void ProcessResults( XUSER_STATS_READ_RESULTS const *pResults );
  41. // Leaderboard description data
  42. CUtlVector< KeyValues * > m_arrViewDescriptions;
  43. KeyValues * FindViewDescription( DWORD dwViewId );
  44. #elif !defined( NO_STEAM )
  45. KeyValues *m_pViewDescription;
  46. CCallResult< CLeaderboardRequestQueue, LeaderboardFindResult_t > m_CallbackOnLeaderboardFindResult;
  47. void Steam_OnLeaderboardFindResult( LeaderboardFindResult_t *p, bool bError );
  48. CCallResult< CLeaderboardRequestQueue, LeaderboardScoresDownloaded_t > m_CallbackOnLeaderboardScoresDownloaded;
  49. void Steam_OnLeaderboardScoresDownloaded( LeaderboardScoresDownloaded_t *p, bool bError );
  50. void ProcessResults( LeaderboardEntry_t const &lbe );
  51. #endif
  52. };
  53. CLeaderboardRequestQueue g_LeaderboardRequestQueue;
  54. ILeaderboardRequestQueue *g_pLeaderboardRequestQueue = &g_LeaderboardRequestQueue;
  55. //
  56. // Implementation of leaderboard request queue class
  57. //
  58. CLeaderboardRequestQueue::CLeaderboardRequestQueue() :
  59. m_bQueryRunning( false ),
  60. #if !defined( _X360 ) && !defined( NO_STEAM )
  61. m_pViewDescription( NULL ),
  62. #endif
  63. m_pFinishedRequest( NULL )
  64. {
  65. }
  66. CLeaderboardRequestQueue::~CLeaderboardRequestQueue()
  67. {
  68. Cleanup();
  69. }
  70. void CLeaderboardRequestQueue::Request( KeyValues *pRequest )
  71. {
  72. if ( !pRequest )
  73. return;
  74. DevMsg( "CLeaderboardRequestQueue::Request\n" );
  75. KeyValuesDumpAsDevMsg( pRequest, 1 );
  76. m_arrRequests.AddToTail( pRequest->MakeCopy() );
  77. }
  78. void CLeaderboardRequestQueue::Update()
  79. {
  80. if ( m_bQueryRunning )
  81. {
  82. #ifdef _X360
  83. if ( XHasOverlappedIoCompleted( &m_xOverlapped ) )
  84. {
  85. OnQueryFinished();
  86. }
  87. #endif
  88. }
  89. else if ( m_arrRequests.Count() )
  90. {
  91. OnStartNewQuery();
  92. }
  93. else if ( m_arrRequests.NumAllocated() )
  94. {
  95. Cleanup();
  96. }
  97. }
  98. KeyValues * CLeaderboardRequestQueue::GetFinishedRequest()
  99. {
  100. return m_pFinishedRequest;
  101. }
  102. void CLeaderboardRequestQueue::OnQueryFinished()
  103. {
  104. #ifdef _X360
  105. // Submit results for processing
  106. XUSER_STATS_READ_RESULTS const *pResults = ( XUSER_STATS_READ_RESULTS const * ) m_bufResults.Base();
  107. if ( pResults && pResults->dwNumViews > 0 && pResults->pViews )
  108. ProcessResults( pResults );
  109. #endif
  110. // Query is no longer running
  111. m_bQueryRunning = false;
  112. DevMsg( "CLeaderboardRequestQueue::OnQueryFinished\n" );
  113. KeyValuesDumpAsDevMsg( m_pFinishedRequest, 1 );
  114. // Stuff the data into the players
  115. #ifdef _X360
  116. for ( int iCtrlr = 0; iCtrlr < XUSER_MAX_COUNT; ++ iCtrlr )
  117. {
  118. XUID xuid = 0;
  119. XUSER_SIGNIN_INFO xsi;
  120. if ( XUserGetSigninState( iCtrlr ) != eXUserSigninState_NotSignedIn &&
  121. ERROR_SUCCESS == XUserGetSigninInfo( iCtrlr, XUSER_GET_SIGNIN_INFO_ONLINE_XUID_ONLY, &xsi ) &&
  122. !(xsi.dwInfoFlags & XUSER_INFO_FLAG_GUEST) )
  123. xuid = xsi.xuid;
  124. #else
  125. int iCtrlr = XBX_GetPrimaryUserId();
  126. {
  127. XUID xuid = g_pPlayerManager->GetLocalPlayer( iCtrlr )->GetXUID();
  128. #endif
  129. KeyValues *pUserViews = NULL;
  130. if ( xuid )
  131. pUserViews = m_pFinishedRequest->FindKey( CFmtStr( "%llx", xuid ) );
  132. if ( pUserViews )
  133. {
  134. IPlayerLocal *pPlayer = g_pPlayerManager->GetLocalPlayer( iCtrlr );
  135. if ( pPlayer )
  136. (( PlayerLocal * ) pPlayer)->OnLeaderboardRequestFinished( pUserViews );
  137. }
  138. }
  139. }
  140. void CLeaderboardRequestQueue::Cleanup()
  141. {
  142. #ifdef _X360
  143. // Clear view descriptions
  144. while ( m_arrViewDescriptions.Count() )
  145. {
  146. m_arrViewDescriptions.Head()->deleteThis();
  147. m_arrViewDescriptions.FastRemove( 0 );
  148. }
  149. m_arrXuids.Purge();
  150. m_arrSpecs.Purge();
  151. m_bufResults.Purge();
  152. m_arrViewDescriptions.Purge();
  153. #elif !defined( NO_STEAM )
  154. if ( m_pViewDescription )
  155. m_pViewDescription->deleteThis();
  156. m_pViewDescription = NULL;
  157. #endif
  158. // Clear requests
  159. while ( m_arrRequests.Count() )
  160. {
  161. m_arrRequests.Head()->deleteThis();
  162. m_arrRequests.FastRemove( 0 );
  163. }
  164. m_arrRequests.Purge();
  165. // Clear finished result
  166. if ( m_pFinishedRequest )
  167. m_pFinishedRequest->deleteThis();
  168. m_pFinishedRequest = NULL;
  169. }
  170. void CLeaderboardRequestQueue::OnStartNewQuery()
  171. {
  172. // When we are starting a new query we need to get rid of the old finished result
  173. if ( m_pFinishedRequest )
  174. m_pFinishedRequest->deleteThis();
  175. m_pFinishedRequest = NULL;
  176. #if !defined( NO_STEAM ) && !defined( SWDS )
  177. extern CInterlockedInt g_numSteamLeaderboardWriters;
  178. if ( g_numSteamLeaderboardWriters )
  179. return; // yield to writers that can alter the leaderboard
  180. #endif
  181. DevMsg( "CLeaderboardRequestQueue::OnStartNewQuery preparing request...\n" );
  182. #ifdef _X360
  183. // Prepare the XUIDs first
  184. m_arrXuids.RemoveAll();
  185. for ( DWORD k = 0; k < XBX_GetNumGameUsers(); ++ k )
  186. {
  187. int iCtrlr = XBX_GetUserId( k );
  188. XUSER_SIGNIN_INFO xsi;
  189. if ( XUserGetSigninState( iCtrlr ) != eXUserSigninState_NotSignedIn &&
  190. ERROR_SUCCESS == XUserGetSigninInfo( iCtrlr, XUSER_GET_SIGNIN_INFO_ONLINE_XUID_ONLY, &xsi ) &&
  191. !(xsi.dwInfoFlags & XUSER_INFO_FLAG_GUEST) )
  192. {
  193. m_arrXuids.AddToTail( xsi.xuid );
  194. DevMsg( " XUID: %s (%llx)\n", xsi.szUserName, xsi.xuid );
  195. }
  196. }
  197. // Clear view descriptions
  198. while ( m_arrViewDescriptions.Count() )
  199. {
  200. m_arrViewDescriptions.Head()->deleteThis();
  201. m_arrViewDescriptions.FastRemove( 0 );
  202. }
  203. // Prepare the spec
  204. m_arrSpecs.RemoveAll();
  205. for ( int q = 0; q < m_arrRequests.Count(); ++ q )
  206. {
  207. KeyValues *pRequest = m_arrRequests[q];
  208. KeyValues::AutoDelete autodelete_pRequest( pRequest );
  209. m_arrRequests.Remove( q -- );
  210. char const *szViewName = pRequest->GetName();
  211. KeyValues *pDescription = g_pMMF->GetMatchTitle()->DescribeTitleLeaderboard( szViewName );
  212. if ( !pDescription )
  213. {
  214. DevWarning( " View %s failed to allocate description!\n", szViewName );
  215. }
  216. KeyValues::AutoDelete autodelete_pDescription( pDescription );
  217. // See if we already have a request for this view
  218. DWORD dwViewId = pDescription->GetInt( ":id" );
  219. for ( int k = 0; k < m_arrSpecs.Count(); ++ k )
  220. {
  221. if ( m_arrSpecs[k].dwViewId == dwViewId )
  222. {
  223. dwViewId = 0;
  224. break;
  225. }
  226. }
  227. // If we already have a request for this view, then continue
  228. if ( !dwViewId )
  229. continue;
  230. // Otherwise add this view to the spec
  231. XUSER_STATS_SPEC xss = {0};
  232. xss.dwViewId = dwViewId;
  233. Assert( !xss.dwNumColumnIds );
  234. m_arrSpecs.AddToTail( xss );
  235. pDescription->SetString( ":name", szViewName );
  236. m_arrViewDescriptions.AddToTail( pDescription );
  237. autodelete_pDescription.Assign( NULL ); // don't autodelete now
  238. DevMsg( " View: %s (%d)\n", szViewName, dwViewId );
  239. }
  240. #elif !defined( NO_STEAM )
  241. // Clear view descriptions
  242. if ( m_pViewDescription )
  243. m_pViewDescription->deleteThis();
  244. m_pViewDescription = NULL;
  245. for ( int q = 0; q < m_arrRequests.Count(); ++ q )
  246. {
  247. KeyValues *pRequest = m_arrRequests[q];
  248. KeyValues::AutoDelete autodelete_pRequest( pRequest );
  249. m_arrRequests.Remove( q -- );
  250. char const *szViewName = pRequest->GetName();
  251. m_pViewDescription = g_pMMF->GetMatchTitle()->DescribeTitleLeaderboard( szViewName );
  252. if ( !m_pViewDescription )
  253. {
  254. DevWarning( " View %s failed to allocate description!\n", szViewName );
  255. continue;
  256. }
  257. m_pViewDescription->SetString( ":name", szViewName );
  258. SteamAPICall_t hCall = steamapicontext->SteamUserStats()->FindLeaderboard( szViewName );
  259. m_CallbackOnLeaderboardFindResult.Set( hCall, this, &CLeaderboardRequestQueue::Steam_OnLeaderboardFindResult );
  260. m_bQueryRunning = true;
  261. break;
  262. }
  263. #endif
  264. // Clean up all the requests in the queue
  265. DevMsg( "CLeaderboardRequestQueue::OnStartNewQuery - request prepared.\n" );
  266. // Run the query
  267. OnSubmitQuery();
  268. }
  269. void CLeaderboardRequestQueue::OnSubmitQuery()
  270. {
  271. #ifdef _X360
  272. if ( m_arrXuids.Count() && m_arrSpecs.Count() )
  273. {
  274. DWORD dwBytes = 0;
  275. DWORD ret = XUserReadStats( 0,
  276. m_arrXuids.Count(), m_arrXuids.Base(),
  277. m_arrSpecs.Count(), m_arrSpecs.Base(),
  278. &dwBytes, NULL, NULL );
  279. if ( ret == ERROR_INSUFFICIENT_BUFFER )
  280. {
  281. ZeroMemory( &m_xOverlapped, sizeof( m_xOverlapped ) );
  282. m_bufResults.EnsureCapacity( dwBytes );
  283. ret = XUserReadStats( 0,
  284. m_arrXuids.Count(), m_arrXuids.Base(),
  285. m_arrSpecs.Count(), m_arrSpecs.Base(),
  286. &dwBytes, ( PXUSER_STATS_READ_RESULTS ) m_bufResults.Base(),
  287. &m_xOverlapped );
  288. }
  289. if ( ret == ERROR_IO_PENDING )
  290. {
  291. DevMsg( "CLeaderboardRequestQueue::OnSubmitQuery - query submitted...\n" );
  292. m_bQueryRunning = true;
  293. }
  294. else
  295. {
  296. DevWarning( "CLeaderboardRequestQueue::OnSubmitQuery - failed [code = %d, bytes = %d]\n", ret, dwBytes );
  297. }
  298. }
  299. #endif
  300. }
  301. #ifdef _X360
  302. void CLeaderboardRequestQueue::ProcessResults( XUSER_STATS_READ_RESULTS const *pResults )
  303. {
  304. /*
  305. 901D41D61DC61 // XUID %llx
  306. {
  307. survival_c5m2_park
  308. {
  309. :rank = 923 // uint64
  310. :rows = 999 // uint64
  311. :rating = 600 // uint64
  312. besttime = 600 // uint64
  313. }
  314. ... more views ...
  315. }
  316. ... more users ...
  317. */
  318. DevMsg( "LeaderboardRequestQueue: ProcessResults ( %d views )\n", pResults->dwNumViews );
  319. for ( DWORD k = 0; k < pResults->dwNumViews; ++ k )
  320. {
  321. XUSER_STATS_VIEW const *pView = &pResults->pViews[k];
  322. Assert( pView );
  323. KeyValues *pViewDesc = FindViewDescription( pView->dwViewId );
  324. Assert( pViewDesc );
  325. if ( !pViewDesc )
  326. {
  327. Warning( "LeaderboardRequestQueue: ProcessResults has no view description for view %d!\n", pView->dwViewId );
  328. continue;
  329. }
  330. char const *szViewName = pViewDesc->GetString( ":name" );
  331. DevMsg( " Processing view %d ( %s ), total rows = %d\n", pView->dwViewId, szViewName, pView->dwTotalViewRows );
  332. for ( DWORD r = 0; r < pView->dwNumRows; ++ r )
  333. {
  334. XUSER_STATS_ROW const *pRow = &pView->pRows[r];
  335. if ( !pRow->dwRank && !pRow->i64Rating )
  336. {
  337. DevMsg( " Gamer %s (%llx) not in view\n", pRow->szGamertag, pRow->xuid );
  338. continue; // gamer is not present in the leaderboard
  339. }
  340. DevMsg( " Gamer %s (%llx) data loaded: rank=%d, rating=%lld\n",
  341. pRow->szGamertag, pRow->xuid, pRow->dwRank, pRow->i64Rating );
  342. // Gamer is present in the leaderboard and should be included in the results
  343. if ( !m_pFinishedRequest )
  344. m_pFinishedRequest = new KeyValues( "Leaderboard" );
  345. // Find or create the view for this gamer
  346. KeyValues *pUserInfo = m_pFinishedRequest->FindKey( CFmtStr( "%llx/%s", pRow->xuid, szViewName ), true );
  347. // Set his rank and rating
  348. pUserInfo->SetUint64( ":rank", pRow->dwRank );
  349. pUserInfo->SetUint64( ":rows", pView->dwTotalViewRows );
  350. pUserInfo->SetUint64( ":rating", pRow->i64Rating );
  351. if ( char const *szName = pViewDesc->GetString( ":rating/name", NULL ) )
  352. {
  353. if ( szName[0] )
  354. pUserInfo->SetUint64( szName, pRow->i64Rating );
  355. }
  356. // Process additional columns that were retrieved
  357. Assert( !pRow->dwNumColumns );
  358. // for ( DWORD c = 0; c < pRow->dwNumColumns; ++ c )
  359. // {
  360. // XUSER_STATS_COLUMN const *pCol = pRow->pColumns[ c ];
  361. // KeyValues *pColDesc = FindViewColumnDesc( pViewDesc, pCol->wColumnId )
  362. // }
  363. }
  364. }
  365. DevMsg( "LeaderboardRequestQueue: ProcessResults finished.\n" );
  366. }
  367. #elif !defined( NO_STEAM )
  368. void CLeaderboardRequestQueue::ProcessResults( LeaderboardEntry_t const &lbe )
  369. {
  370. /*
  371. 901D41D61DC61 // XUID %llx
  372. {
  373. survival_c5m2_park
  374. {
  375. :rank = 923 // uint64
  376. :rows = 999 // uint64
  377. :rating = 600 // uint64
  378. besttime = 600 // uint64
  379. }
  380. ... more views ...
  381. }
  382. ... more users ...
  383. */
  384. KeyValues *pViewDesc = m_pViewDescription;
  385. Assert( pViewDesc );
  386. if ( !pViewDesc )
  387. {
  388. Warning( "LeaderboardRequestQueue: ProcessResults has no view description for view!\n" );
  389. return;
  390. }
  391. char const *szViewName = pViewDesc->GetString( ":name" );
  392. DevMsg( " Processing view %s\n", szViewName );
  393. DevMsg( " Gamer data loaded: rank=%d, score=%d\n",
  394. lbe.m_nGlobalRank, lbe.m_nScore );
  395. // Gamer is present in the leaderboard and should be included in the results
  396. if ( !m_pFinishedRequest )
  397. m_pFinishedRequest = new KeyValues( "Leaderboard" );
  398. // Find or create the view for this gamer
  399. KeyValues *pUserInfo = m_pFinishedRequest->FindKey( CFmtStr( "%llx/%s",
  400. g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() )->GetXUID(), szViewName ), true );
  401. // Set user score
  402. pUserInfo->SetUint64( pViewDesc->GetString( ":score" ), lbe.m_nScore );
  403. DevMsg( "LeaderboardRequestQueue: ProcessResults finished.\n" );
  404. }
  405. #endif
  406. #ifdef _X360
  407. KeyValues * CLeaderboardRequestQueue::FindViewDescription( DWORD dwViewId )
  408. {
  409. if ( !dwViewId )
  410. return NULL;
  411. for ( int k = 0; k < m_arrViewDescriptions.Count(); ++ k )
  412. {
  413. KeyValues *pDesc = m_arrViewDescriptions[k];
  414. if ( pDesc->GetInt( ":id" ) == ( int ) dwViewId )
  415. return pDesc;
  416. }
  417. return NULL;
  418. }
  419. #elif !defined( NO_STEAM )
  420. void CLeaderboardRequestQueue::Steam_OnLeaderboardFindResult( LeaderboardFindResult_t *p, bool bError )
  421. {
  422. if ( bError || !p->m_bLeaderboardFound )
  423. {
  424. DevMsg( "Steam leaderboard was not found.\n" );
  425. OnQueryFinished();
  426. return;
  427. }
  428. // Download the data
  429. SteamAPICall_t hCall = steamapicontext->SteamUserStats()->DownloadLeaderboardEntries( p->m_hSteamLeaderboard,
  430. k_ELeaderboardDataRequestGlobalAroundUser, 0, 0 );
  431. m_CallbackOnLeaderboardScoresDownloaded.Set( hCall, this, &CLeaderboardRequestQueue::Steam_OnLeaderboardScoresDownloaded );
  432. }
  433. void CLeaderboardRequestQueue::Steam_OnLeaderboardScoresDownloaded( LeaderboardScoresDownloaded_t *p, bool bError )
  434. {
  435. // Fetch the data if found and no error
  436. LeaderboardEntry_t lbe;
  437. if ( !bError &&
  438. p->m_cEntryCount == 1 &&
  439. steamapicontext->SteamUserStats()->GetDownloadedLeaderboardEntry( p->m_hSteamLeaderboardEntries, 0, &lbe, NULL, 0 ) )
  440. {
  441. ProcessResults( lbe );
  442. }
  443. OnQueryFinished();
  444. }
  445. #endif