722 lines
26 KiB
C++
722 lines
26 KiB
C++
//========= 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
|