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
721 lines
26 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: Holds the CAccountDetails class. |
|
// |
|
//============================================================================= |
|
|
|
#include "stdafx.h" |
|
#include "accountdetails.h" |
|
#include "rtime.h" |
|
#include "gcsdk_gcmessages.pb.h" |
|
|
|
#include "memdbgon.h" // needs to be the last include in the file |
|
|
|
namespace GCSDK |
|
{ |
|
GCConVar cv_account_details_cache_time( "account_details_cache_time", "600" ); |
|
GCConVar cv_account_details_failure_cache_time( "account_details_failure_cache_time", "10" ); |
|
GCConVar account_details_timeout( "account_details_timeout", "10" ); |
|
GCConVar cv_persona_name_cache_time( "persona_name_cache_time", "60" ); |
|
GCConVar cv_persona_name_failure_cache_time( "persona_name_failure_cache_time", "10" ); |
|
GCConVar cv_persona_name_batch_size( "persona_name_batch_size", "100" ); |
|
GCConVar persona_name_timeout( "persona_name_timeout", "10" ); |
|
|
|
const char *kszAccountDetailsKey = "AccountDetails-v001"; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor |
|
//----------------------------------------------------------------------------- |
|
CAccountDetails::CAccountDetails() |
|
: m_rtimeCached( CRTime::RTime32TimeCur() ), |
|
m_bValid( false ), |
|
m_bPublicProfile( false ), |
|
m_bVacBanned( false ), |
|
m_bCyberCafe( false ), |
|
m_bSchoolAccount( false ), |
|
m_bFreeTrialAccount( false ), |
|
m_bSubscribed( false ), |
|
m_bLowViolence( false ), |
|
m_bLimited( false ), |
|
m_bAccountLocked( false ), |
|
m_bCommunityBanned( false ), |
|
m_bTradeBanned( false ), |
|
m_bIsSteamGuardEnabled( false ), |
|
m_bIsPhoneVerified( false ), |
|
m_bIsTwoFactorAuthEnabled( false ), |
|
m_bIsPhoneIdentifying( false ), |
|
m_unPackage( 0 ), |
|
m_rtimeVACBanEnd( 0 ), |
|
m_unSteamLevel( 0 ), |
|
m_unFriendCount( 0 ), |
|
m_rtimeAccountCreated( 0 ), |
|
m_rtimeTwoFactorEnabled( 0 ), |
|
m_rtimePhoneVerified( 0 ), |
|
m_unPhoneID( 0 ) |
|
{ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initialize a fresh CAccountDetails with data from Steam |
|
//----------------------------------------------------------------------------- |
|
void CAccountDetails::Init( CGCSystemMsg_GetAccountDetails_Response &msgResponse ) |
|
{ |
|
m_bValid = true; |
|
m_sAccountName = msgResponse.account_name().c_str(); |
|
m_bPublicProfile = msgResponse.is_profile_public(); |
|
m_bPublicInventory = msgResponse.is_inventory_public(); |
|
m_bVacBanned = msgResponse.is_vac_banned(); |
|
m_bCyberCafe = msgResponse.is_cyber_cafe(); |
|
m_bSchoolAccount = msgResponse.is_school_account(); |
|
m_bFreeTrialAccount = msgResponse.is_free_trial_account(); |
|
m_bSubscribed = msgResponse.is_subscribed(); |
|
m_bLowViolence = msgResponse.is_low_violence(); |
|
m_bLimited = msgResponse.is_limited(); |
|
m_bAccountLocked = msgResponse.is_account_locked_down(); |
|
m_bCommunityBanned = msgResponse.is_community_banned(); |
|
m_bTradeBanned = msgResponse.is_trade_banned(); |
|
m_unPackage = msgResponse.package(); |
|
m_rtimeVACBanEnd = msgResponse.suspension_end_time(); |
|
m_sCurrency = msgResponse.currency().c_str(); |
|
m_unSteamLevel = msgResponse.steam_level(); |
|
m_unFriendCount = msgResponse.friend_count(); |
|
m_rtimeAccountCreated = msgResponse.account_creation_time(); |
|
m_bIsSteamGuardEnabled = msgResponse.is_steamguard_enabled(); |
|
m_bIsPhoneVerified = msgResponse.is_phone_verified(); |
|
m_bIsTwoFactorAuthEnabled = msgResponse.is_two_factor_auth_enabled(); |
|
m_bIsPhoneIdentifying = msgResponse.is_phone_identifying(); |
|
m_rtimeTwoFactorEnabled = msgResponse.two_factor_enabled_time(); |
|
m_rtimePhoneVerified = msgResponse.phone_verification_time(); |
|
m_unPhoneID = msgResponse.phone_id(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns true if it's time to remove this entry from the cache |
|
//----------------------------------------------------------------------------- |
|
bool CAccountDetails::BIsExpired() const |
|
{ |
|
int nCacheSeconds = BIsValid() ? cv_account_details_cache_time.GetInt() : cv_account_details_failure_cache_time.GetInt(); |
|
|
|
return m_rtimeCached + nCacheSeconds < CRTime::RTime32TimeCur(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Reverts this to an invalid record |
|
//----------------------------------------------------------------------------- |
|
void CAccountDetails::Reset() |
|
{ |
|
m_bValid = false; |
|
m_rtimeCached = CRTime::RTime32TimeCur(); |
|
} |
|
|
|
|
|
#ifdef DBGFLAG_VALIDATE |
|
//----------------------------------------------------------------------------- |
|
// Purpose: Claims all the memory for the AccountDetails object |
|
//----------------------------------------------------------------------------- |
|
void CAccountDetails::Validate( CValidator &validator, const char *pchName ) |
|
{ |
|
VALIDATE_SCOPE(); |
|
ValidateObj( m_sAccountName ); |
|
} |
|
#endif // DBGFLAG_VALIDATE |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Sends a message to Steam to get a CAccountDetails object |
|
//----------------------------------------------------------------------------- |
|
class CGCJobSendGetAccountDetailsRequest : public CGCJob |
|
{ |
|
CAccountDetailsManager *m_pManager; |
|
CSteamID m_SteamID; |
|
|
|
public: |
|
CGCJobSendGetAccountDetailsRequest( CGCBase *pGC, CAccountDetailsManager *pManager, const CSteamID &steamID ) |
|
: CGCJob( pGC ), m_pManager( pManager ), m_SteamID( steamID ) {} |
|
virtual bool BYieldingRunGCJob() |
|
{ |
|
// Yield immediately to be sure that the calling job gets in the wakeup list |
|
BYield(); |
|
|
|
// These requests should come back very quickly, so if they don't we shouldn't wait very long |
|
// jamming up the system |
|
SetJobTimeout( account_details_timeout.GetInt() ); |
|
|
|
// Get an empty account details object |
|
CAccountDetails *pAccount = m_pManager->m_hashAccountDetailsCache.PvRecordFind( m_SteamID.GetAccountID() ); |
|
if ( NULL == pAccount ) |
|
{ |
|
pAccount = m_pManager->m_hashAccountDetailsCache.PvRecordInsert( m_SteamID.GetAccountID() ); |
|
} |
|
else |
|
{ |
|
// If the record isn't expired, why is it there? |
|
Assert( pAccount->BIsExpired() ); |
|
pAccount->Reset(); |
|
} |
|
|
|
CProtoBufMsg< CGCSystemMsg_GetAccountDetails > msgReqeust( k_EGCMsgGetAccountDetails ); |
|
CProtoBufMsg< CGCSystemMsg_GetAccountDetails_Response > msgReply; |
|
msgReqeust.Body().set_steamid( m_SteamID.ConvertToUint64() ); |
|
msgReqeust.Body().set_appid( GGCBase()->GetAppID() ); |
|
msgReqeust.ExpectingReply( GJobCur().GetJobID() ); |
|
|
|
// try to get the account details at most 2 times |
|
const int kMaxTries = 2; |
|
for ( int iTries = 0; iTries < kMaxTries; iTries++ ) |
|
{ |
|
if( !m_pGC->BSendSystemMessage( msgReqeust ) ) |
|
{ |
|
EmitWarning( SPEW_GC, 2, "Unable to send GetAccountDetails system message\n" ); |
|
continue; |
|
} |
|
|
|
// All of our request messages are identical, so if we get our replies |
|
// mixed up, it's OK. Bypass the system used to protect us against |
|
// mismatched replies. |
|
ClearFailedToReceivedMsgType( k_EGCMsgGetAccountDetailsResponse ); |
|
|
|
// Wait for the reply |
|
if( !BYieldingWaitForMsg( &msgReply, k_EGCMsgGetAccountDetailsResponse ) ) |
|
{ |
|
EmitWarning( SPEW_GC, 2, "Timeout waiting for GetAccountDetails reply for SteamID %s\n", m_SteamID.Render() ); |
|
continue; |
|
} |
|
|
|
if ( k_EResultOK != msgReply.GetEResult() ) |
|
{ |
|
EmitInfo( SPEW_GC, 4, 4, "GetAccountDetails request failed with result %d for SteamID %s\n", msgReply.GetEResult(), m_SteamID.Render() ); |
|
break; |
|
} |
|
|
|
Assert( msgReply.Body().eresult_deprecated() == k_EResultOK ); |
|
|
|
// Sanity check the response |
|
if ( msgReply.Body().has_accountid() && msgReply.Body().accountid() != m_SteamID.GetAccountID() ) |
|
{ |
|
static bool bHasAlerted = false; |
|
if ( !bHasAlerted ) |
|
{ |
|
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() ) ); |
|
bHasAlerted = true; |
|
} |
|
|
|
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() ); |
|
break; |
|
} |
|
|
|
// All responses should have this |
|
if ( !msgReply.Body().has_account_name() ) |
|
{ |
|
EmitError( SPEW_GC, "GetAccountDetails got a response with missing fields for SteamID %s\n", m_SteamID.Render() ); |
|
break; |
|
} |
|
|
|
pAccount->Init( msgReply.Body() ); |
|
m_pManager->CachePersonaName( CSteamID( m_SteamID ), msgReply.Body().persona_name().c_str() ); |
|
|
|
// We got a response, so we shouldn't try again |
|
break; |
|
} |
|
|
|
m_pManager->WakeWaitingAccountDetailsJobs( m_SteamID ); |
|
return true; |
|
} |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor |
|
//----------------------------------------------------------------------------- |
|
CCachedPersonaName::CCachedPersonaName() |
|
: m_rtimeCached( 0 ) // This initialization value is important because it makes it expired on creation, |
|
// and the rest of the code will only ask Steam for a name if the cached entry is expired |
|
, m_nLoading( 0 ) |
|
, m_bPreloading( false ) |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Destructor |
|
//----------------------------------------------------------------------------- |
|
CCachedPersonaName::~CCachedPersonaName() |
|
{ |
|
Assert( !BIsLoading() ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Sets the newly retrieved persona name |
|
//----------------------------------------------------------------------------- |
|
void CCachedPersonaName::Init( const char *pchPersonaName ) |
|
{ |
|
m_sPersonaName = pchPersonaName; |
|
m_rtimeCached = CRTime::RTime32TimeCur(); |
|
m_bPreloading = false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns true if it's time to remove this entry from the cache |
|
//----------------------------------------------------------------------------- |
|
bool CCachedPersonaName::BIsExpired() const |
|
{ |
|
int nCacheSeconds = BIsValid() ? cv_persona_name_cache_time.GetInt() : cv_persona_name_failure_cache_time.GetInt(); |
|
|
|
return m_rtimeCached + nCacheSeconds < CRTime::RTime32TimeCur(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns true if there is an actual cached name |
|
//----------------------------------------------------------------------------- |
|
bool CCachedPersonaName::BIsValid() const |
|
{ |
|
return !m_sPersonaName.IsEmpty(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Reverts this to an invalid record |
|
//----------------------------------------------------------------------------- |
|
void CCachedPersonaName::Reset() |
|
{ |
|
m_sPersonaName.Clear(); |
|
m_rtimeCached = CRTime::RTime32TimeCur(); |
|
m_bPreloading = false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Gets the cached string |
|
//----------------------------------------------------------------------------- |
|
const char *CCachedPersonaName::GetPersonaName() const |
|
{ |
|
return BIsValid() ? m_sPersonaName.Get() : "[unknown]"; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns if this name is loading |
|
//----------------------------------------------------------------------------- |
|
bool CCachedPersonaName::BIsLoading() const |
|
{ |
|
return m_nLoading > 0 || m_bPreloading; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Sets that this name has been preloaded |
|
//----------------------------------------------------------------------------- |
|
void CCachedPersonaName::SetPreloading() |
|
{ |
|
m_bPreloading = true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Sets that a job is waiting for this name to be loaded |
|
//----------------------------------------------------------------------------- |
|
void CCachedPersonaName::AddLoadingRef() |
|
{ |
|
m_nLoading++; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Releases the loading ref |
|
//----------------------------------------------------------------------------- |
|
void CCachedPersonaName::ReleaseLoadingRef() |
|
{ |
|
DbgVerify( --m_nLoading >= 0 ); |
|
} |
|
|
|
|
|
#ifdef DBGFLAG_VALIDATE |
|
//----------------------------------------------------------------------------- |
|
// Purpose: Claims all the memory for the CCachedPersonaName object |
|
//----------------------------------------------------------------------------- |
|
void CCachedPersonaName::Validate( CValidator &validator, const char *pchName ) |
|
{ |
|
VALIDATE_SCOPE(); |
|
ValidateObj( m_sPersonaName ); |
|
} |
|
#endif // DBGFLAG_VALIDATE |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Sends a message to Steam to get a CAccountDetails object |
|
//----------------------------------------------------------------------------- |
|
class CGCJobSendGetPersonaNamesRequest : public CGCJob |
|
{ |
|
CAccountDetailsManager *m_pManager; |
|
CUtlVector<CSteamID> m_vecSteamIDs; |
|
|
|
public: |
|
CGCJobSendGetPersonaNamesRequest( CGCBase *pGC, CAccountDetailsManager *pManager, CUtlVector<CSteamID> &vecSteamIDs ) |
|
: CGCJob( pGC ), m_pManager( pManager ) |
|
{ |
|
m_vecSteamIDs.Swap( vecSteamIDs ); |
|
} |
|
|
|
virtual bool BYieldingRunGCJob() |
|
{ |
|
// Yield immediately to be sure that the calling job gets in the wakeup list |
|
BYield(); |
|
|
|
// These requests should come back very quickly, so if they don't we shouldn't wait very long |
|
// jamming up the system |
|
SetJobTimeout( persona_name_timeout.GetInt() ); |
|
|
|
CProtoBufMsg< CMsgGCGetPersonaNames > msgReqeust( k_EGCMsgGetPersonaNames ); |
|
msgReqeust.ExpectingReply( GJobCur().GetJobID() ); |
|
|
|
FOR_EACH_VEC( m_vecSteamIDs, i ) |
|
{ |
|
msgReqeust.Body().add_steamids( m_vecSteamIDs[i].ConvertToUint64() ); |
|
} |
|
|
|
CProtoBufMsg< CMsgGCGetPersonaNames_Response > msgReply; |
|
if( !m_pGC->BSendSystemMessage( msgReqeust ) || |
|
!BYieldingWaitForMsg( &msgReply, k_EGCMsgGetPersonaNamesResponse ) ) |
|
{ |
|
FOR_EACH_VEC( m_vecSteamIDs, i ) |
|
{ |
|
m_pManager->CachePersonaNameFailure( m_vecSteamIDs[i] ); |
|
} |
|
|
|
//if we are shutting down, don't bother reporting this issue, we know we won't be able to get persona names |
|
if( !GGCBase()->GetIsShuttingDown() ) |
|
{ |
|
EmitWarning( SPEW_GC, 2, "GetPersonaNames request failed for %d IDs:", m_vecSteamIDs.Count() ); |
|
FOR_EACH_VEC( m_vecSteamIDs, nID ) |
|
{ |
|
EmitWarning( SPEW_GC, 2, " %s", m_vecSteamIDs[ nID ].Render() ); |
|
} |
|
EmitWarning( SPEW_GC, 2, "\n" ); |
|
} |
|
} |
|
else |
|
{ |
|
for ( int i = 0; i < msgReply.Body().succeeded_lookups_size(); i++ ) |
|
{ |
|
const CMsgGCGetPersonaNames_Response_PersonaName &result = msgReply.Body().succeeded_lookups( i ); |
|
m_pManager->CachePersonaName( CSteamID( result.steamid() ), result.persona_name().c_str() ); |
|
} |
|
for ( int i = 0; i < msgReply.Body().failed_lookup_steamids_size(); i++ ) |
|
{ |
|
m_pManager->CachePersonaNameFailure( CSteamID( msgReply.Body().failed_lookup_steamids( i ) ) ); |
|
} |
|
} |
|
|
|
FOR_EACH_VEC( m_vecSteamIDs, i ) |
|
{ |
|
m_pManager->WakeWaitingPersonaNameJobs( m_vecSteamIDs[i] ); |
|
} |
|
return true; |
|
} |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor |
|
//----------------------------------------------------------------------------- |
|
CAccountDetailsManager::CAccountDetailsManager() |
|
: m_hashAccountDetailsCache( k_nAccountDetailsRunInterval / k_cMicroSecPerShellFrame ) |
|
, m_hashPersonaNameCache( k_nAccountDetailsRunInterval / k_cMicroSecPerShellFrame ) |
|
{ |
|
m_hashAccountDetailsCache.Init( k_cAccountDetailsInit, k_cBucketAccountDetails ); |
|
m_hashPersonaNameCache.Init( k_cAccountDetailsInit, k_cBucketAccountDetails ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Work to be done once per frame |
|
//----------------------------------------------------------------------------- |
|
void CAccountDetailsManager::MarkFrame() |
|
{ |
|
m_hashAccountDetailsCache.StartFrameSchedule( true ); |
|
m_hashPersonaNameCache.StartFrameSchedule( true ); |
|
SendBatchedPersonaNamesRequest(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Gets a CAccountDetails object |
|
//----------------------------------------------------------------------------- |
|
CAccountDetails *CAccountDetailsManager::YieldingGetAccountDetails( const CSteamID &steamID, bool bForceReload ) |
|
{ |
|
AssertRunningJob(); |
|
if( !steamID.IsValid() || !steamID.BIndividualAccount() ) |
|
return NULL; |
|
|
|
// Check the local cache |
|
CAccountDetails *pAccountDetails = NULL; |
|
if ( BFindAccountDetailsInLocalCache( steamID, &pAccountDetails ) ) |
|
{ |
|
if ( pAccountDetails && bForceReload ) |
|
{ |
|
// Clear it, continue with fresh load |
|
m_hashAccountDetailsCache.Remove( pAccountDetails ); |
|
} |
|
else |
|
{ |
|
return pAccountDetails; |
|
} |
|
} |
|
|
|
// Not in the local cache, ask Steam |
|
int iMapIndex = m_mapQueuedAccountDetailsRequests.Find( steamID ); |
|
if ( !m_mapQueuedAccountDetailsRequests.IsValidIndex( iMapIndex ) ) |
|
{ |
|
iMapIndex = m_mapQueuedAccountDetailsRequests.Insert( steamID ); |
|
CGCJobSendGetAccountDetailsRequest *pJob = new CGCJobSendGetAccountDetailsRequest( GGCBase(), this, steamID ); |
|
pJob->StartJob( NULL ); |
|
} |
|
|
|
m_mapQueuedAccountDetailsRequests[iMapIndex].AddToTail( GJobCur().GetJobID() ); |
|
GJobCur().BYieldingWaitForWorkItem(); |
|
|
|
// Check again, if it's not there then it's not there |
|
BFindAccountDetailsInLocalCache( steamID, &pAccountDetails ); |
|
return pAccountDetails; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Finds an AccountDetails object in the local cache. Returns true |
|
// if it was found, false if it should be checked remotely |
|
//----------------------------------------------------------------------------- |
|
bool CAccountDetailsManager::BFindAccountDetailsInLocalCache( const CSteamID &steamID, CAccountDetails **ppAccount ) |
|
{ |
|
CAccountDetails *pAccountLocal = m_hashAccountDetailsCache.PvRecordFind( steamID.GetAccountID() ); |
|
if( NULL == pAccountLocal || pAccountLocal->BIsExpired() ) |
|
return false; |
|
|
|
*ppAccount = pAccountLocal->BIsValid() ? pAccountLocal : NULL; |
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Wakes any jobs waiting on this result |
|
//----------------------------------------------------------------------------- |
|
void CAccountDetailsManager::WakeWaitingAccountDetailsJobs( const CSteamID &steamID ) |
|
{ |
|
int iMapIndex = m_mapQueuedAccountDetailsRequests.Find( steamID ); |
|
if ( !m_mapQueuedAccountDetailsRequests.IsValidIndex( iMapIndex ) ) |
|
return; |
|
|
|
CCopyableUtlVector<JobID_t> &vecJobsWaiting = m_mapQueuedAccountDetailsRequests[iMapIndex]; |
|
FOR_EACH_VEC( vecJobsWaiting, i ) |
|
{ |
|
GGCBase()->GetJobMgr().BRouteWorkItemCompletedDelayed( vecJobsWaiting[i], false ); |
|
} |
|
m_mapQueuedAccountDetailsRequests.RemoveAt( iMapIndex ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Gets a persona name for a user |
|
//----------------------------------------------------------------------------- |
|
const char *CAccountDetailsManager::YieldingGetPersonaName( const CSteamID &steamID ) |
|
{ |
|
VPROF_BUDGET( "CAccountDetailsManager::YieldingGetPersonaName", VPROF_BUDGETGROUP_STEAM ); |
|
|
|
AssertRunningJob(); |
|
|
|
if( !steamID.IsValid() || !steamID.BIndividualAccount() ) |
|
return "[unknown]"; |
|
|
|
// Check the local cache |
|
CCachedPersonaName *pPersonaName = FindOrCreateCachedPersonaName( steamID ); |
|
if ( !pPersonaName->BIsExpired() ) |
|
return pPersonaName->GetPersonaName(); |
|
|
|
// Not in the local cache, ask Steam |
|
pPersonaName->AddLoadingRef(); |
|
|
|
// Queue the request and start a lookup job if we have enough pending |
|
int iMapIndex = m_mapQueuedPersonaNameRequests.Find( steamID ); |
|
if ( !m_mapQueuedPersonaNameRequests.IsValidIndex( iMapIndex ) ) |
|
{ |
|
iMapIndex = m_mapQueuedPersonaNameRequests.Insert( steamID ); |
|
m_vecPendingPersonaNameLookups.AddToTail( steamID ); |
|
if ( m_vecPendingPersonaNameLookups.Count() >= cv_persona_name_batch_size.GetInt() ) |
|
{ |
|
SendBatchedPersonaNamesRequest(); |
|
} |
|
} |
|
|
|
m_mapQueuedPersonaNameRequests[iMapIndex].AddToTail( GJobCur().GetJobID() ); |
|
GJobCur().BYieldingWaitForWorkItem(); |
|
|
|
// At this point we'll either have a persona name or we won't |
|
pPersonaName->ReleaseLoadingRef(); |
|
return pPersonaName->GetPersonaName(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Let's the system know that we should load the persona name for this |
|
// user, but does not block on it |
|
//----------------------------------------------------------------------------- |
|
void CAccountDetailsManager::PreloadPersonaName( const CSteamID &steamID ) |
|
{ |
|
if( !steamID.IsValid() || !steamID.BIndividualAccount() ) |
|
return; |
|
|
|
// See if we already have it |
|
CCachedPersonaName *pCachedName = FindOrCreateCachedPersonaName( steamID ); |
|
if ( !pCachedName->BIsExpired() || pCachedName->BIsLoading() ) |
|
return; |
|
|
|
// Queue the request and start a lookup job if we have enough pending |
|
pCachedName->SetPreloading(); |
|
m_mapQueuedPersonaNameRequests.Insert( steamID ); |
|
m_vecPendingPersonaNameLookups.AddToTail( steamID ); |
|
if ( m_vecPendingPersonaNameLookups.Count() >= cv_persona_name_batch_size.GetInt() ) |
|
{ |
|
SendBatchedPersonaNamesRequest(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Sends a batch of persona name requests |
|
//----------------------------------------------------------------------------- |
|
void CAccountDetailsManager::SendBatchedPersonaNamesRequest() |
|
{ |
|
if ( 0 == m_vecPendingPersonaNameLookups.Count() ) |
|
return; |
|
|
|
// Start the job. This swaps out our buffer with an empty one |
|
CGCJobSendGetPersonaNamesRequest *pJob = new CGCJobSendGetPersonaNamesRequest( GGCBase(), this, m_vecPendingPersonaNameLookups ); |
|
pJob->StartJob( NULL ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Caches a persona name |
|
//----------------------------------------------------------------------------- |
|
void CAccountDetailsManager::CachePersonaName( const CSteamID &steamID, const char *pchPersonaName ) |
|
{ |
|
CCachedPersonaName *pCachedPersonaName = FindOrCreateCachedPersonaName( steamID ); |
|
pCachedPersonaName->Init( pchPersonaName ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Remembers that we failed to cache a persona name |
|
//----------------------------------------------------------------------------- |
|
void CAccountDetailsManager::CachePersonaNameFailure( const CSteamID &steamID ) |
|
{ |
|
CCachedPersonaName *pCachedPersonaName = FindOrCreateCachedPersonaName( steamID ); |
|
pCachedPersonaName->Reset(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Purges a specific persona name from the cache |
|
//----------------------------------------------------------------------------- |
|
void CAccountDetailsManager::ClearCachedPersonaName( const CSteamID &steamID ) |
|
{ |
|
CCachedPersonaName *pPersonaNameLocal = m_hashPersonaNameCache.PvRecordFind( steamID.GetAccountID() ); |
|
if( NULL != pPersonaNameLocal && !pPersonaNameLocal->BIsLoading() ) |
|
{ |
|
m_hashPersonaNameCache.Remove( pPersonaNameLocal ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Gets a CCachedPersonaName record |
|
//----------------------------------------------------------------------------- |
|
CCachedPersonaName *CAccountDetailsManager::FindOrCreateCachedPersonaName( const CSteamID &steamID ) |
|
{ |
|
CCachedPersonaName *pPersonaName = m_hashPersonaNameCache.PvRecordFind( steamID.GetAccountID() ); |
|
if ( NULL != pPersonaName ) |
|
return pPersonaName; |
|
else |
|
return m_hashPersonaNameCache.PvRecordInsert( steamID.GetAccountID() ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Wakes any jobs waiting on this result |
|
//----------------------------------------------------------------------------- |
|
void CAccountDetailsManager::WakeWaitingPersonaNameJobs( const CSteamID &steamID ) |
|
{ |
|
int iMapIndex = m_mapQueuedPersonaNameRequests.Find( steamID ); |
|
if ( !m_mapQueuedPersonaNameRequests.IsValidIndex( iMapIndex ) ) |
|
return; |
|
|
|
CCopyableUtlVector<JobID_t> &vecJobsWaiting = m_mapQueuedPersonaNameRequests[iMapIndex]; |
|
FOR_EACH_VEC( vecJobsWaiting, i ) |
|
{ |
|
GGCBase()->GetJobMgr().BRouteWorkItemCompletedDelayed( vecJobsWaiting[i], false ); |
|
} |
|
m_mapQueuedPersonaNameRequests.RemoveAt( iMapIndex ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Purges old data from the cache. Returns true if there is more |
|
// work to do |
|
//----------------------------------------------------------------------------- |
|
bool CAccountDetailsManager::BExpireRecords( CLimitTimer &limitTimer ) |
|
{ |
|
VPROF_BUDGET( "Expire account details", VPROF_BUDGETGROUP_STEAM ); |
|
|
|
for ( CAccountDetails *pDetails = m_hashAccountDetailsCache.PvRecordRun(); NULL != pDetails; pDetails = m_hashAccountDetailsCache.PvRecordRun() ) |
|
{ |
|
if ( pDetails->BIsExpired() ) |
|
{ |
|
m_hashAccountDetailsCache.Remove( pDetails ); |
|
} |
|
|
|
if ( limitTimer.BLimitReached() ) |
|
return true; |
|
} |
|
|
|
for ( CCachedPersonaName *pPersonaName = m_hashPersonaNameCache.PvRecordRun(); NULL != pPersonaName; pPersonaName = m_hashPersonaNameCache.PvRecordRun() ) |
|
{ |
|
if ( pPersonaName->BIsExpired() && !pPersonaName->BIsLoading() ) |
|
{ |
|
m_hashPersonaNameCache.Remove( pPersonaName ); |
|
} |
|
|
|
if ( limitTimer.BLimitReached() ) |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Prints status |
|
//----------------------------------------------------------------------------- |
|
void CAccountDetailsManager::Dump() const |
|
{ |
|
int nJobsWaiting = 0; |
|
FOR_EACH_MAP_FAST( m_mapQueuedAccountDetailsRequests, iMap ) |
|
{ |
|
nJobsWaiting += m_mapQueuedAccountDetailsRequests[iMap].Count(); |
|
} |
|
|
|
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 ); |
|
|
|
nJobsWaiting = 0; |
|
FOR_EACH_MAP_FAST( m_mapQueuedPersonaNameRequests, iMap ) |
|
{ |
|
nJobsWaiting += m_mapQueuedPersonaNameRequests[iMap].Count(); |
|
} |
|
|
|
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 ); |
|
} |
|
|
|
} // namespace GCSDK
|
|
|