Team Fortress 2 Source Code as on 22/4/2020
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.

721 lines
26 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Holds the CAccountDetails class.
  4. //
  5. //=============================================================================
  6. #include "stdafx.h"
  7. #include "accountdetails.h"
  8. #include "rtime.h"
  9. #include "gcsdk_gcmessages.pb.h"
  10. #include "memdbgon.h" // needs to be the last include in the file
  11. namespace GCSDK
  12. {
  13. GCConVar cv_account_details_cache_time( "account_details_cache_time", "600" );
  14. GCConVar cv_account_details_failure_cache_time( "account_details_failure_cache_time", "10" );
  15. GCConVar account_details_timeout( "account_details_timeout", "10" );
  16. GCConVar cv_persona_name_cache_time( "persona_name_cache_time", "60" );
  17. GCConVar cv_persona_name_failure_cache_time( "persona_name_failure_cache_time", "10" );
  18. GCConVar cv_persona_name_batch_size( "persona_name_batch_size", "100" );
  19. GCConVar persona_name_timeout( "persona_name_timeout", "10" );
  20. const char *kszAccountDetailsKey = "AccountDetails-v001";
  21. //-----------------------------------------------------------------------------
  22. // Purpose: Constructor
  23. //-----------------------------------------------------------------------------
  24. CAccountDetails::CAccountDetails()
  25. : m_rtimeCached( CRTime::RTime32TimeCur() ),
  26. m_bValid( false ),
  27. m_bPublicProfile( false ),
  28. m_bVacBanned( false ),
  29. m_bCyberCafe( false ),
  30. m_bSchoolAccount( false ),
  31. m_bFreeTrialAccount( false ),
  32. m_bSubscribed( false ),
  33. m_bLowViolence( false ),
  34. m_bLimited( false ),
  35. m_bAccountLocked( false ),
  36. m_bCommunityBanned( false ),
  37. m_bTradeBanned( false ),
  38. m_bIsSteamGuardEnabled( false ),
  39. m_bIsPhoneVerified( false ),
  40. m_bIsTwoFactorAuthEnabled( false ),
  41. m_bIsPhoneIdentifying( false ),
  42. m_unPackage( 0 ),
  43. m_rtimeVACBanEnd( 0 ),
  44. m_unSteamLevel( 0 ),
  45. m_unFriendCount( 0 ),
  46. m_rtimeAccountCreated( 0 ),
  47. m_rtimeTwoFactorEnabled( 0 ),
  48. m_rtimePhoneVerified( 0 ),
  49. m_unPhoneID( 0 )
  50. {
  51. }
  52. //-----------------------------------------------------------------------------
  53. // Purpose: Initialize a fresh CAccountDetails with data from Steam
  54. //-----------------------------------------------------------------------------
  55. void CAccountDetails::Init( CGCSystemMsg_GetAccountDetails_Response &msgResponse )
  56. {
  57. m_bValid = true;
  58. m_sAccountName = msgResponse.account_name().c_str();
  59. m_bPublicProfile = msgResponse.is_profile_public();
  60. m_bPublicInventory = msgResponse.is_inventory_public();
  61. m_bVacBanned = msgResponse.is_vac_banned();
  62. m_bCyberCafe = msgResponse.is_cyber_cafe();
  63. m_bSchoolAccount = msgResponse.is_school_account();
  64. m_bFreeTrialAccount = msgResponse.is_free_trial_account();
  65. m_bSubscribed = msgResponse.is_subscribed();
  66. m_bLowViolence = msgResponse.is_low_violence();
  67. m_bLimited = msgResponse.is_limited();
  68. m_bAccountLocked = msgResponse.is_account_locked_down();
  69. m_bCommunityBanned = msgResponse.is_community_banned();
  70. m_bTradeBanned = msgResponse.is_trade_banned();
  71. m_unPackage = msgResponse.package();
  72. m_rtimeVACBanEnd = msgResponse.suspension_end_time();
  73. m_sCurrency = msgResponse.currency().c_str();
  74. m_unSteamLevel = msgResponse.steam_level();
  75. m_unFriendCount = msgResponse.friend_count();
  76. m_rtimeAccountCreated = msgResponse.account_creation_time();
  77. m_bIsSteamGuardEnabled = msgResponse.is_steamguard_enabled();
  78. m_bIsPhoneVerified = msgResponse.is_phone_verified();
  79. m_bIsTwoFactorAuthEnabled = msgResponse.is_two_factor_auth_enabled();
  80. m_bIsPhoneIdentifying = msgResponse.is_phone_identifying();
  81. m_rtimeTwoFactorEnabled = msgResponse.two_factor_enabled_time();
  82. m_rtimePhoneVerified = msgResponse.phone_verification_time();
  83. m_unPhoneID = msgResponse.phone_id();
  84. }
  85. //-----------------------------------------------------------------------------
  86. // Purpose: Returns true if it's time to remove this entry from the cache
  87. //-----------------------------------------------------------------------------
  88. bool CAccountDetails::BIsExpired() const
  89. {
  90. int nCacheSeconds = BIsValid() ? cv_account_details_cache_time.GetInt() : cv_account_details_failure_cache_time.GetInt();
  91. return m_rtimeCached + nCacheSeconds < CRTime::RTime32TimeCur();
  92. }
  93. //-----------------------------------------------------------------------------
  94. // Purpose: Reverts this to an invalid record
  95. //-----------------------------------------------------------------------------
  96. void CAccountDetails::Reset()
  97. {
  98. m_bValid = false;
  99. m_rtimeCached = CRTime::RTime32TimeCur();
  100. }
  101. #ifdef DBGFLAG_VALIDATE
  102. //-----------------------------------------------------------------------------
  103. // Purpose: Claims all the memory for the AccountDetails object
  104. //-----------------------------------------------------------------------------
  105. void CAccountDetails::Validate( CValidator &validator, const char *pchName )
  106. {
  107. VALIDATE_SCOPE();
  108. ValidateObj( m_sAccountName );
  109. }
  110. #endif // DBGFLAG_VALIDATE
  111. //-----------------------------------------------------------------------------
  112. // Purpose: Sends a message to Steam to get a CAccountDetails object
  113. //-----------------------------------------------------------------------------
  114. class CGCJobSendGetAccountDetailsRequest : public CGCJob
  115. {
  116. CAccountDetailsManager *m_pManager;
  117. CSteamID m_SteamID;
  118. public:
  119. CGCJobSendGetAccountDetailsRequest( CGCBase *pGC, CAccountDetailsManager *pManager, const CSteamID &steamID )
  120. : CGCJob( pGC ), m_pManager( pManager ), m_SteamID( steamID ) {}
  121. virtual bool BYieldingRunGCJob()
  122. {
  123. // Yield immediately to be sure that the calling job gets in the wakeup list
  124. BYield();
  125. // These requests should come back very quickly, so if they don't we shouldn't wait very long
  126. // jamming up the system
  127. SetJobTimeout( account_details_timeout.GetInt() );
  128. // Get an empty account details object
  129. CAccountDetails *pAccount = m_pManager->m_hashAccountDetailsCache.PvRecordFind( m_SteamID.GetAccountID() );
  130. if ( NULL == pAccount )
  131. {
  132. pAccount = m_pManager->m_hashAccountDetailsCache.PvRecordInsert( m_SteamID.GetAccountID() );
  133. }
  134. else
  135. {
  136. // If the record isn't expired, why is it there?
  137. Assert( pAccount->BIsExpired() );
  138. pAccount->Reset();
  139. }
  140. CProtoBufMsg< CGCSystemMsg_GetAccountDetails > msgReqeust( k_EGCMsgGetAccountDetails );
  141. CProtoBufMsg< CGCSystemMsg_GetAccountDetails_Response > msgReply;
  142. msgReqeust.Body().set_steamid( m_SteamID.ConvertToUint64() );
  143. msgReqeust.Body().set_appid( GGCBase()->GetAppID() );
  144. msgReqeust.ExpectingReply( GJobCur().GetJobID() );
  145. // try to get the account details at most 2 times
  146. const int kMaxTries = 2;
  147. for ( int iTries = 0; iTries < kMaxTries; iTries++ )
  148. {
  149. if( !m_pGC->BSendSystemMessage( msgReqeust ) )
  150. {
  151. EmitWarning( SPEW_GC, 2, "Unable to send GetAccountDetails system message\n" );
  152. continue;
  153. }
  154. // All of our request messages are identical, so if we get our replies
  155. // mixed up, it's OK. Bypass the system used to protect us against
  156. // mismatched replies.
  157. ClearFailedToReceivedMsgType( k_EGCMsgGetAccountDetailsResponse );
  158. // Wait for the reply
  159. if( !BYieldingWaitForMsg( &msgReply, k_EGCMsgGetAccountDetailsResponse ) )
  160. {
  161. EmitWarning( SPEW_GC, 2, "Timeout waiting for GetAccountDetails reply for SteamID %s\n", m_SteamID.Render() );
  162. continue;
  163. }
  164. if ( k_EResultOK != msgReply.GetEResult() )
  165. {
  166. EmitInfo( SPEW_GC, 4, 4, "GetAccountDetails request failed with result %d for SteamID %s\n", msgReply.GetEResult(), m_SteamID.Render() );
  167. break;
  168. }
  169. Assert( msgReply.Body().eresult_deprecated() == k_EResultOK );
  170. // Sanity check the response
  171. if ( msgReply.Body().has_accountid() && msgReply.Body().accountid() != m_SteamID.GetAccountID() )
  172. {
  173. static bool bHasAlerted = false;
  174. if ( !bHasAlerted )
  175. {
  176. GGCBase()->PostAlert( k_EAlertTypeInfo, true, CFmtStr( "GetAccountDetails got a response for account %d, but we were expecting a response for account %s\n", msgReply.Body().accountid(), m_SteamID.Render() ) );
  177. bHasAlerted = true;
  178. }
  179. EmitError( SPEW_GC, "GetAccountDetails got a response for account %d, but we were expecting a response for account %s\n", msgReply.Body().accountid(), m_SteamID.Render() );
  180. break;
  181. }
  182. // All responses should have this
  183. if ( !msgReply.Body().has_account_name() )
  184. {
  185. EmitError( SPEW_GC, "GetAccountDetails got a response with missing fields for SteamID %s\n", m_SteamID.Render() );
  186. break;
  187. }
  188. pAccount->Init( msgReply.Body() );
  189. m_pManager->CachePersonaName( CSteamID( m_SteamID ), msgReply.Body().persona_name().c_str() );
  190. // We got a response, so we shouldn't try again
  191. break;
  192. }
  193. m_pManager->WakeWaitingAccountDetailsJobs( m_SteamID );
  194. return true;
  195. }
  196. };
  197. //-----------------------------------------------------------------------------
  198. // Purpose: Constructor
  199. //-----------------------------------------------------------------------------
  200. CCachedPersonaName::CCachedPersonaName()
  201. : m_rtimeCached( 0 ) // This initialization value is important because it makes it expired on creation,
  202. // and the rest of the code will only ask Steam for a name if the cached entry is expired
  203. , m_nLoading( 0 )
  204. , m_bPreloading( false )
  205. {
  206. }
  207. //-----------------------------------------------------------------------------
  208. // Purpose: Destructor
  209. //-----------------------------------------------------------------------------
  210. CCachedPersonaName::~CCachedPersonaName()
  211. {
  212. Assert( !BIsLoading() );
  213. }
  214. //-----------------------------------------------------------------------------
  215. // Purpose: Sets the newly retrieved persona name
  216. //-----------------------------------------------------------------------------
  217. void CCachedPersonaName::Init( const char *pchPersonaName )
  218. {
  219. m_sPersonaName = pchPersonaName;
  220. m_rtimeCached = CRTime::RTime32TimeCur();
  221. m_bPreloading = false;
  222. }
  223. //-----------------------------------------------------------------------------
  224. // Purpose: Returns true if it's time to remove this entry from the cache
  225. //-----------------------------------------------------------------------------
  226. bool CCachedPersonaName::BIsExpired() const
  227. {
  228. int nCacheSeconds = BIsValid() ? cv_persona_name_cache_time.GetInt() : cv_persona_name_failure_cache_time.GetInt();
  229. return m_rtimeCached + nCacheSeconds < CRTime::RTime32TimeCur();
  230. }
  231. //-----------------------------------------------------------------------------
  232. // Purpose: Returns true if there is an actual cached name
  233. //-----------------------------------------------------------------------------
  234. bool CCachedPersonaName::BIsValid() const
  235. {
  236. return !m_sPersonaName.IsEmpty();
  237. }
  238. //-----------------------------------------------------------------------------
  239. // Purpose: Reverts this to an invalid record
  240. //-----------------------------------------------------------------------------
  241. void CCachedPersonaName::Reset()
  242. {
  243. m_sPersonaName.Clear();
  244. m_rtimeCached = CRTime::RTime32TimeCur();
  245. m_bPreloading = false;
  246. }
  247. //-----------------------------------------------------------------------------
  248. // Purpose: Gets the cached string
  249. //-----------------------------------------------------------------------------
  250. const char *CCachedPersonaName::GetPersonaName() const
  251. {
  252. return BIsValid() ? m_sPersonaName.Get() : "[unknown]";
  253. }
  254. //-----------------------------------------------------------------------------
  255. // Purpose: Returns if this name is loading
  256. //-----------------------------------------------------------------------------
  257. bool CCachedPersonaName::BIsLoading() const
  258. {
  259. return m_nLoading > 0 || m_bPreloading;
  260. }
  261. //-----------------------------------------------------------------------------
  262. // Purpose: Sets that this name has been preloaded
  263. //-----------------------------------------------------------------------------
  264. void CCachedPersonaName::SetPreloading()
  265. {
  266. m_bPreloading = true;
  267. }
  268. //-----------------------------------------------------------------------------
  269. // Purpose: Sets that a job is waiting for this name to be loaded
  270. //-----------------------------------------------------------------------------
  271. void CCachedPersonaName::AddLoadingRef()
  272. {
  273. m_nLoading++;
  274. }
  275. //-----------------------------------------------------------------------------
  276. // Purpose: Releases the loading ref
  277. //-----------------------------------------------------------------------------
  278. void CCachedPersonaName::ReleaseLoadingRef()
  279. {
  280. DbgVerify( --m_nLoading >= 0 );
  281. }
  282. #ifdef DBGFLAG_VALIDATE
  283. //-----------------------------------------------------------------------------
  284. // Purpose: Claims all the memory for the CCachedPersonaName object
  285. //-----------------------------------------------------------------------------
  286. void CCachedPersonaName::Validate( CValidator &validator, const char *pchName )
  287. {
  288. VALIDATE_SCOPE();
  289. ValidateObj( m_sPersonaName );
  290. }
  291. #endif // DBGFLAG_VALIDATE
  292. //-----------------------------------------------------------------------------
  293. // Purpose: Sends a message to Steam to get a CAccountDetails object
  294. //-----------------------------------------------------------------------------
  295. class CGCJobSendGetPersonaNamesRequest : public CGCJob
  296. {
  297. CAccountDetailsManager *m_pManager;
  298. CUtlVector<CSteamID> m_vecSteamIDs;
  299. public:
  300. CGCJobSendGetPersonaNamesRequest( CGCBase *pGC, CAccountDetailsManager *pManager, CUtlVector<CSteamID> &vecSteamIDs )
  301. : CGCJob( pGC ), m_pManager( pManager )
  302. {
  303. m_vecSteamIDs.Swap( vecSteamIDs );
  304. }
  305. virtual bool BYieldingRunGCJob()
  306. {
  307. // Yield immediately to be sure that the calling job gets in the wakeup list
  308. BYield();
  309. // These requests should come back very quickly, so if they don't we shouldn't wait very long
  310. // jamming up the system
  311. SetJobTimeout( persona_name_timeout.GetInt() );
  312. CProtoBufMsg< CMsgGCGetPersonaNames > msgReqeust( k_EGCMsgGetPersonaNames );
  313. msgReqeust.ExpectingReply( GJobCur().GetJobID() );
  314. FOR_EACH_VEC( m_vecSteamIDs, i )
  315. {
  316. msgReqeust.Body().add_steamids( m_vecSteamIDs[i].ConvertToUint64() );
  317. }
  318. CProtoBufMsg< CMsgGCGetPersonaNames_Response > msgReply;
  319. if( !m_pGC->BSendSystemMessage( msgReqeust ) ||
  320. !BYieldingWaitForMsg( &msgReply, k_EGCMsgGetPersonaNamesResponse ) )
  321. {
  322. FOR_EACH_VEC( m_vecSteamIDs, i )
  323. {
  324. m_pManager->CachePersonaNameFailure( m_vecSteamIDs[i] );
  325. }
  326. //if we are shutting down, don't bother reporting this issue, we know we won't be able to get persona names
  327. if( !GGCBase()->GetIsShuttingDown() )
  328. {
  329. EmitWarning( SPEW_GC, 2, "GetPersonaNames request failed for %d IDs:", m_vecSteamIDs.Count() );
  330. FOR_EACH_VEC( m_vecSteamIDs, nID )
  331. {
  332. EmitWarning( SPEW_GC, 2, " %s", m_vecSteamIDs[ nID ].Render() );
  333. }
  334. EmitWarning( SPEW_GC, 2, "\n" );
  335. }
  336. }
  337. else
  338. {
  339. for ( int i = 0; i < msgReply.Body().succeeded_lookups_size(); i++ )
  340. {
  341. const CMsgGCGetPersonaNames_Response_PersonaName &result = msgReply.Body().succeeded_lookups( i );
  342. m_pManager->CachePersonaName( CSteamID( result.steamid() ), result.persona_name().c_str() );
  343. }
  344. for ( int i = 0; i < msgReply.Body().failed_lookup_steamids_size(); i++ )
  345. {
  346. m_pManager->CachePersonaNameFailure( CSteamID( msgReply.Body().failed_lookup_steamids( i ) ) );
  347. }
  348. }
  349. FOR_EACH_VEC( m_vecSteamIDs, i )
  350. {
  351. m_pManager->WakeWaitingPersonaNameJobs( m_vecSteamIDs[i] );
  352. }
  353. return true;
  354. }
  355. };
  356. //-----------------------------------------------------------------------------
  357. // Purpose: Constructor
  358. //-----------------------------------------------------------------------------
  359. CAccountDetailsManager::CAccountDetailsManager()
  360. : m_hashAccountDetailsCache( k_nAccountDetailsRunInterval / k_cMicroSecPerShellFrame )
  361. , m_hashPersonaNameCache( k_nAccountDetailsRunInterval / k_cMicroSecPerShellFrame )
  362. {
  363. m_hashAccountDetailsCache.Init( k_cAccountDetailsInit, k_cBucketAccountDetails );
  364. m_hashPersonaNameCache.Init( k_cAccountDetailsInit, k_cBucketAccountDetails );
  365. }
  366. //-----------------------------------------------------------------------------
  367. // Purpose: Work to be done once per frame
  368. //-----------------------------------------------------------------------------
  369. void CAccountDetailsManager::MarkFrame()
  370. {
  371. m_hashAccountDetailsCache.StartFrameSchedule( true );
  372. m_hashPersonaNameCache.StartFrameSchedule( true );
  373. SendBatchedPersonaNamesRequest();
  374. }
  375. //-----------------------------------------------------------------------------
  376. // Purpose: Gets a CAccountDetails object
  377. //-----------------------------------------------------------------------------
  378. CAccountDetails *CAccountDetailsManager::YieldingGetAccountDetails( const CSteamID &steamID, bool bForceReload )
  379. {
  380. AssertRunningJob();
  381. if( !steamID.IsValid() || !steamID.BIndividualAccount() )
  382. return NULL;
  383. // Check the local cache
  384. CAccountDetails *pAccountDetails = NULL;
  385. if ( BFindAccountDetailsInLocalCache( steamID, &pAccountDetails ) )
  386. {
  387. if ( pAccountDetails && bForceReload )
  388. {
  389. // Clear it, continue with fresh load
  390. m_hashAccountDetailsCache.Remove( pAccountDetails );
  391. }
  392. else
  393. {
  394. return pAccountDetails;
  395. }
  396. }
  397. // Not in the local cache, ask Steam
  398. int iMapIndex = m_mapQueuedAccountDetailsRequests.Find( steamID );
  399. if ( !m_mapQueuedAccountDetailsRequests.IsValidIndex( iMapIndex ) )
  400. {
  401. iMapIndex = m_mapQueuedAccountDetailsRequests.Insert( steamID );
  402. CGCJobSendGetAccountDetailsRequest *pJob = new CGCJobSendGetAccountDetailsRequest( GGCBase(), this, steamID );
  403. pJob->StartJob( NULL );
  404. }
  405. m_mapQueuedAccountDetailsRequests[iMapIndex].AddToTail( GJobCur().GetJobID() );
  406. GJobCur().BYieldingWaitForWorkItem();
  407. // Check again, if it's not there then it's not there
  408. BFindAccountDetailsInLocalCache( steamID, &pAccountDetails );
  409. return pAccountDetails;
  410. }
  411. //-----------------------------------------------------------------------------
  412. // Purpose: Finds an AccountDetails object in the local cache. Returns true
  413. // if it was found, false if it should be checked remotely
  414. //-----------------------------------------------------------------------------
  415. bool CAccountDetailsManager::BFindAccountDetailsInLocalCache( const CSteamID &steamID, CAccountDetails **ppAccount )
  416. {
  417. CAccountDetails *pAccountLocal = m_hashAccountDetailsCache.PvRecordFind( steamID.GetAccountID() );
  418. if( NULL == pAccountLocal || pAccountLocal->BIsExpired() )
  419. return false;
  420. *ppAccount = pAccountLocal->BIsValid() ? pAccountLocal : NULL;
  421. return true;
  422. }
  423. //-----------------------------------------------------------------------------
  424. // Purpose: Wakes any jobs waiting on this result
  425. //-----------------------------------------------------------------------------
  426. void CAccountDetailsManager::WakeWaitingAccountDetailsJobs( const CSteamID &steamID )
  427. {
  428. int iMapIndex = m_mapQueuedAccountDetailsRequests.Find( steamID );
  429. if ( !m_mapQueuedAccountDetailsRequests.IsValidIndex( iMapIndex ) )
  430. return;
  431. CCopyableUtlVector<JobID_t> &vecJobsWaiting = m_mapQueuedAccountDetailsRequests[iMapIndex];
  432. FOR_EACH_VEC( vecJobsWaiting, i )
  433. {
  434. GGCBase()->GetJobMgr().BRouteWorkItemCompletedDelayed( vecJobsWaiting[i], false );
  435. }
  436. m_mapQueuedAccountDetailsRequests.RemoveAt( iMapIndex );
  437. }
  438. //-----------------------------------------------------------------------------
  439. // Purpose: Gets a persona name for a user
  440. //-----------------------------------------------------------------------------
  441. const char *CAccountDetailsManager::YieldingGetPersonaName( const CSteamID &steamID )
  442. {
  443. VPROF_BUDGET( "CAccountDetailsManager::YieldingGetPersonaName", VPROF_BUDGETGROUP_STEAM );
  444. AssertRunningJob();
  445. if( !steamID.IsValid() || !steamID.BIndividualAccount() )
  446. return "[unknown]";
  447. // Check the local cache
  448. CCachedPersonaName *pPersonaName = FindOrCreateCachedPersonaName( steamID );
  449. if ( !pPersonaName->BIsExpired() )
  450. return pPersonaName->GetPersonaName();
  451. // Not in the local cache, ask Steam
  452. pPersonaName->AddLoadingRef();
  453. // Queue the request and start a lookup job if we have enough pending
  454. int iMapIndex = m_mapQueuedPersonaNameRequests.Find( steamID );
  455. if ( !m_mapQueuedPersonaNameRequests.IsValidIndex( iMapIndex ) )
  456. {
  457. iMapIndex = m_mapQueuedPersonaNameRequests.Insert( steamID );
  458. m_vecPendingPersonaNameLookups.AddToTail( steamID );
  459. if ( m_vecPendingPersonaNameLookups.Count() >= cv_persona_name_batch_size.GetInt() )
  460. {
  461. SendBatchedPersonaNamesRequest();
  462. }
  463. }
  464. m_mapQueuedPersonaNameRequests[iMapIndex].AddToTail( GJobCur().GetJobID() );
  465. GJobCur().BYieldingWaitForWorkItem();
  466. // At this point we'll either have a persona name or we won't
  467. pPersonaName->ReleaseLoadingRef();
  468. return pPersonaName->GetPersonaName();
  469. }
  470. //-----------------------------------------------------------------------------
  471. // Purpose: Let's the system know that we should load the persona name for this
  472. // user, but does not block on it
  473. //-----------------------------------------------------------------------------
  474. void CAccountDetailsManager::PreloadPersonaName( const CSteamID &steamID )
  475. {
  476. if( !steamID.IsValid() || !steamID.BIndividualAccount() )
  477. return;
  478. // See if we already have it
  479. CCachedPersonaName *pCachedName = FindOrCreateCachedPersonaName( steamID );
  480. if ( !pCachedName->BIsExpired() || pCachedName->BIsLoading() )
  481. return;
  482. // Queue the request and start a lookup job if we have enough pending
  483. pCachedName->SetPreloading();
  484. m_mapQueuedPersonaNameRequests.Insert( steamID );
  485. m_vecPendingPersonaNameLookups.AddToTail( steamID );
  486. if ( m_vecPendingPersonaNameLookups.Count() >= cv_persona_name_batch_size.GetInt() )
  487. {
  488. SendBatchedPersonaNamesRequest();
  489. }
  490. }
  491. //-----------------------------------------------------------------------------
  492. // Purpose: Sends a batch of persona name requests
  493. //-----------------------------------------------------------------------------
  494. void CAccountDetailsManager::SendBatchedPersonaNamesRequest()
  495. {
  496. if ( 0 == m_vecPendingPersonaNameLookups.Count() )
  497. return;
  498. // Start the job. This swaps out our buffer with an empty one
  499. CGCJobSendGetPersonaNamesRequest *pJob = new CGCJobSendGetPersonaNamesRequest( GGCBase(), this, m_vecPendingPersonaNameLookups );
  500. pJob->StartJob( NULL );
  501. }
  502. //-----------------------------------------------------------------------------
  503. // Purpose: Caches a persona name
  504. //-----------------------------------------------------------------------------
  505. void CAccountDetailsManager::CachePersonaName( const CSteamID &steamID, const char *pchPersonaName )
  506. {
  507. CCachedPersonaName *pCachedPersonaName = FindOrCreateCachedPersonaName( steamID );
  508. pCachedPersonaName->Init( pchPersonaName );
  509. }
  510. //-----------------------------------------------------------------------------
  511. // Purpose: Remembers that we failed to cache a persona name
  512. //-----------------------------------------------------------------------------
  513. void CAccountDetailsManager::CachePersonaNameFailure( const CSteamID &steamID )
  514. {
  515. CCachedPersonaName *pCachedPersonaName = FindOrCreateCachedPersonaName( steamID );
  516. pCachedPersonaName->Reset();
  517. }
  518. //-----------------------------------------------------------------------------
  519. // Purpose: Purges a specific persona name from the cache
  520. //-----------------------------------------------------------------------------
  521. void CAccountDetailsManager::ClearCachedPersonaName( const CSteamID &steamID )
  522. {
  523. CCachedPersonaName *pPersonaNameLocal = m_hashPersonaNameCache.PvRecordFind( steamID.GetAccountID() );
  524. if( NULL != pPersonaNameLocal && !pPersonaNameLocal->BIsLoading() )
  525. {
  526. m_hashPersonaNameCache.Remove( pPersonaNameLocal );
  527. }
  528. }
  529. //-----------------------------------------------------------------------------
  530. // Purpose: Gets a CCachedPersonaName record
  531. //-----------------------------------------------------------------------------
  532. CCachedPersonaName *CAccountDetailsManager::FindOrCreateCachedPersonaName( const CSteamID &steamID )
  533. {
  534. CCachedPersonaName *pPersonaName = m_hashPersonaNameCache.PvRecordFind( steamID.GetAccountID() );
  535. if ( NULL != pPersonaName )
  536. return pPersonaName;
  537. else
  538. return m_hashPersonaNameCache.PvRecordInsert( steamID.GetAccountID() );
  539. }
  540. //-----------------------------------------------------------------------------
  541. // Purpose: Wakes any jobs waiting on this result
  542. //-----------------------------------------------------------------------------
  543. void CAccountDetailsManager::WakeWaitingPersonaNameJobs( const CSteamID &steamID )
  544. {
  545. int iMapIndex = m_mapQueuedPersonaNameRequests.Find( steamID );
  546. if ( !m_mapQueuedPersonaNameRequests.IsValidIndex( iMapIndex ) )
  547. return;
  548. CCopyableUtlVector<JobID_t> &vecJobsWaiting = m_mapQueuedPersonaNameRequests[iMapIndex];
  549. FOR_EACH_VEC( vecJobsWaiting, i )
  550. {
  551. GGCBase()->GetJobMgr().BRouteWorkItemCompletedDelayed( vecJobsWaiting[i], false );
  552. }
  553. m_mapQueuedPersonaNameRequests.RemoveAt( iMapIndex );
  554. }
  555. //-----------------------------------------------------------------------------
  556. // Purpose: Purges old data from the cache. Returns true if there is more
  557. // work to do
  558. //-----------------------------------------------------------------------------
  559. bool CAccountDetailsManager::BExpireRecords( CLimitTimer &limitTimer )
  560. {
  561. VPROF_BUDGET( "Expire account details", VPROF_BUDGETGROUP_STEAM );
  562. for ( CAccountDetails *pDetails = m_hashAccountDetailsCache.PvRecordRun(); NULL != pDetails; pDetails = m_hashAccountDetailsCache.PvRecordRun() )
  563. {
  564. if ( pDetails->BIsExpired() )
  565. {
  566. m_hashAccountDetailsCache.Remove( pDetails );
  567. }
  568. if ( limitTimer.BLimitReached() )
  569. return true;
  570. }
  571. for ( CCachedPersonaName *pPersonaName = m_hashPersonaNameCache.PvRecordRun(); NULL != pPersonaName; pPersonaName = m_hashPersonaNameCache.PvRecordRun() )
  572. {
  573. if ( pPersonaName->BIsExpired() && !pPersonaName->BIsLoading() )
  574. {
  575. m_hashPersonaNameCache.Remove( pPersonaName );
  576. }
  577. if ( limitTimer.BLimitReached() )
  578. return true;
  579. }
  580. return false;
  581. }
  582. //-----------------------------------------------------------------------------
  583. // Purpose: Prints status
  584. //-----------------------------------------------------------------------------
  585. void CAccountDetailsManager::Dump() const
  586. {
  587. int nJobsWaiting = 0;
  588. FOR_EACH_MAP_FAST( m_mapQueuedAccountDetailsRequests, iMap )
  589. {
  590. nJobsWaiting += m_mapQueuedAccountDetailsRequests[iMap].Count();
  591. }
  592. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "\tAccount Details: %d cached, %d lookups in flight, %d jobs waiting\n", m_hashAccountDetailsCache.Count(), m_mapQueuedAccountDetailsRequests.Count(), nJobsWaiting );
  593. nJobsWaiting = 0;
  594. FOR_EACH_MAP_FAST( m_mapQueuedPersonaNameRequests, iMap )
  595. {
  596. nJobsWaiting += m_mapQueuedPersonaNameRequests[iMap].Count();
  597. }
  598. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "\tPersona Names: %d cached, %d lookups in flight, %d jobs waiting\n", m_hashPersonaNameCache.Count(), m_mapQueuedPersonaNameRequests.Count(), nJobsWaiting );
  599. }
  600. } // namespace GCSDK