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.

607 lines
20 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Holds the CGCSession class
  4. //
  5. //=============================================================================
  6. #include "stdafx.h"
  7. #include "gcsession.h"
  8. #include "steamextra/rtime.h"
  9. #include "gcsdk_gcmessages.pb.h"
  10. #include "gcsdk/gcreportprinter.h"
  11. // memdbgon must be the last include file in a .cpp file!!!
  12. #include "tier0/memdbgon.h"
  13. // Probably this makes more sense true by default, but we're spewing a ton and Fletcher says it
  14. // isn't a big deal for TF so here we go.
  15. GCConVar gs_session_assert_valid_addr_and_port( "gs_session_assert_valid_addr_and_port", "0" );
  16. namespace GCSDK
  17. {
  18. DECLARE_GC_EMIT_GROUP( g_EGSessions, sessions );
  19. DECLARE_GC_EMIT_GROUP_DEFAULTS( g_EGRateLimit, ratelimit, 2, 3 );
  20. GCConVar max_user_messages_per_second( "max_user_messages_per_second", "20", 0, "Maximum number of messages a user can send per second. 0 disables the rate limiting" );
  21. static GCConVar user_message_rate_limit_warning_period( "user_message_rate_limit_warning_period", "30", 0, "Number of seconds between warning about rate limiting for users" );
  22. static GCConVar msg_rate_limit_report_user_bucket_1( "msg_rate_limit_report_user_bucket_1", "10", 0, "These values control where various users are bucketed in rate limiting reports to help identify how frequently users are running into rate limiting" );
  23. static GCConVar msg_rate_limit_report_user_bucket_2( "msg_rate_limit_report_user_bucket_2", "100", 0, "These values control where various users are bucketed in rate limiting reports to help identify how frequently users are running into rate limiting" );
  24. static GCConVar msg_rate_limit_list_user( "msg_rate_limit_list_user", "0", 0, "When set to a user account ID, this will report all the messages that are rate limited for that user to the console" );
  25. CMsgRateLimitTracker g_RateLimitTracker;
  26. CMsgRateLimitTracker::CMsgRateLimitTracker() :
  27. m_StartTime( CRTime::RTime32TimeCur() )
  28. {
  29. }
  30. void CMsgRateLimitTracker::TrackRateLimitedMsg( const CSteamID steamID, MsgType_t eMsgType )
  31. {
  32. //update message stat
  33. {
  34. uint32 nMsgIndex = m_MsgStats.Find( eMsgType );
  35. if( !m_MsgStats.IsValidIndex( nMsgIndex ) )
  36. {
  37. nMsgIndex = m_MsgStats.Insert( eMsgType, 0 );
  38. }
  39. m_MsgStats[ nMsgIndex ]++;
  40. }
  41. //update user stats
  42. {
  43. uint32 nUserIndex = m_UserStats.Find( steamID );
  44. if( !m_UserStats.IsValidIndex( nUserIndex ) )
  45. {
  46. nUserIndex = m_UserStats.Insert( steamID, 0 );
  47. }
  48. m_UserStats[ nUserIndex ]++;
  49. }
  50. //determine the severity to output the warning at. Assume verbose unless we are tracking a specific account ID (note that no account has 0 so 0 still effectively turns it off)
  51. CGCEmitGroup::EMsgLevel eMsgLevel = CGCEmitGroup::kMsg_Verbose;
  52. if( ( uint32 )msg_rate_limit_list_user.GetInt() == steamID.GetAccountID() )
  53. {
  54. eMsgLevel = CGCEmitGroup::kMsg_Msg;
  55. }
  56. EG_EMIT( g_EGMessages, eMsgLevel, "Dropped message %s (%d) for user %s\n", PchMsgNameFromEMsg( eMsgType ), eMsgType, steamID.Render() );
  57. }
  58. void CMsgRateLimitTracker::ReportMsgStats() const
  59. {
  60. CGCReportPrinter rp;
  61. rp.AddStringColumn( "Msg" );
  62. rp.AddIntColumn( "Count", CGCReportPrinter::eSummary_Total );
  63. FOR_EACH_MAP_FAST( m_MsgStats, nCurrMsg )
  64. {
  65. rp.StrValue( PchMsgNameFromEMsg( m_MsgStats.Key( nCurrMsg ) ) );
  66. rp.IntValue( m_MsgStats[ nCurrMsg ] );
  67. rp.CommitRow();
  68. }
  69. rp.SortReport( "Count" );
  70. rp.PrintReport( SPEW_CONSOLE );
  71. }
  72. void CMsgRateLimitTracker::ReportTopUsers( uint32 nMinMsgs, uint32 nListTop ) const
  73. {
  74. //collect a list of all messages, and sort them into order of frequency
  75. CGCReportPrinter rp;
  76. rp.AddSteamIDColumn( "User" );
  77. rp.AddIntColumn( "Count", CGCReportPrinter::eSummary_Total );
  78. FOR_EACH_MAP_FAST( m_UserStats, nCurrMsg )
  79. {
  80. rp.SteamIDValue( m_UserStats.Key( nCurrMsg ) );
  81. rp.IntValue( m_UserStats[ nCurrMsg ] );
  82. rp.CommitRow();
  83. }
  84. rp.SortReport( "Count" );
  85. rp.PrintReport( SPEW_CONSOLE, nListTop );
  86. }
  87. void CMsgRateLimitTracker::ReportUserStats() const
  88. {
  89. //run through the users and aggregate stats
  90. const uint32 nBucketLimit1 = ( uint32 )max( 0, min( msg_rate_limit_report_user_bucket_1.GetInt(), msg_rate_limit_report_user_bucket_2.GetInt() ) );
  91. const uint32 nBucketLimit2 = ( uint32 )max( 0, max( msg_rate_limit_report_user_bucket_1.GetInt(), msg_rate_limit_report_user_bucket_2.GetInt() ) );
  92. uint32 nTotalMsg = 0;
  93. uint32 nMaxUser = 0;
  94. uint32 nBucketCount1 = 0;
  95. uint32 nBucketCount2 = 0;
  96. FOR_EACH_MAP_FAST( m_UserStats, nCurrMsg )
  97. {
  98. //add user counts to the buckets
  99. const uint32 nMsgs = m_UserStats[ nCurrMsg ];
  100. if( nMsgs <= nBucketLimit1 )
  101. nBucketCount1++;
  102. else if( nMsgs <= nBucketLimit2 )
  103. nBucketCount2++;
  104. //add up our total number of offenses
  105. nTotalMsg += nMsgs;
  106. nMaxUser = max( nMaxUser, nMsgs );
  107. }
  108. EG_MSG( SPEW_CONSOLE, "Capture Duration: %ds\n", CRTime::RTime32TimeCur() - m_StartTime );
  109. EG_MSG( SPEW_CONSOLE, "Total Dropped Messages: %d\n", nTotalMsg );
  110. EG_MSG( SPEW_CONSOLE, "Message IDs: %d\n", m_MsgStats.Count() );
  111. EG_MSG( SPEW_CONSOLE, "Users: %d (peak: %d)\n", m_UserStats.Count(), nMaxUser );
  112. EG_MSG( SPEW_CONSOLE, " Below %d msgs: %d\n", nBucketLimit1, nBucketCount1 );
  113. EG_MSG( SPEW_CONSOLE, " Below %d msgs: %d\n", nBucketLimit2, nBucketCount2 );
  114. }
  115. void CMsgRateLimitTracker::ClearStats()
  116. {
  117. m_StartTime = CRTime::RTime32TimeCur();
  118. m_UserStats.RemoveAll();
  119. m_MsgStats.RemoveAll();
  120. }
  121. //console command hooks
  122. GC_CON_COMMAND( msg_rate_limit_dump, "Dumps stats about rate limiting of messages" )
  123. {
  124. g_RateLimitTracker.ReportUserStats();
  125. g_RateLimitTracker.ReportMsgStats();
  126. g_RateLimitTracker.ReportTopUsers( 0, 20 );
  127. }
  128. GC_CON_COMMAND( msg_rate_limit_dump_users, "Dumps a list of users that have been rate limited. Optional parameters can specify the number to dump or the minimum number of messages required." )
  129. {
  130. if( args.ArgC() < 3 )
  131. {
  132. EG_MSG( SPEW_CONSOLE, "Proper usage is: %s <min messages> <top users> - Specify 0 for one or both to have it be ignored\n", args[ 0 ] );
  133. return;
  134. }
  135. g_RateLimitTracker.ReportTopUsers( ( uint32 )max( 0, atoi( args[ 1 ] ) ), ( uint32 )max( 0, atoi( args[ 2 ] ) ) );
  136. }
  137. GC_CON_COMMAND( msg_rate_limit_dump_msgs, "Dumps a list of messages that have been rate limited." )
  138. {
  139. g_RateLimitTracker.ReportMsgStats();
  140. }
  141. GC_CON_COMMAND( msg_rate_limit_clear, "Clears all the accumulated msg rate limit stats" )
  142. {
  143. g_RateLimitTracker.ClearStats();
  144. }
  145. //------------------------------------------------------------------------------------------
  146. // CSteamIDRateLimit
  147. //------------------------------------------------------------------------------------------
  148. CSteamIDRateLimit::CSteamIDRateLimit( const GCConVar& cvNumPerPeriod, const GCConVar* pcvPeriodS ) :
  149. m_cvNumPerPeriod( cvNumPerPeriod ),
  150. m_pcvPeriodS( pcvPeriodS ),
  151. m_LastClear( CRTime::RTime32TimeCur() ),
  152. m_FrameFunction( "SteamIDRateLimit", CBaseFrameFunction::k_EFrameType_RunOnce )
  153. {
  154. m_FrameFunction.Register( this, &CSteamIDRateLimit::OnFrameFn );
  155. }
  156. CSteamIDRateLimit::~CSteamIDRateLimit()
  157. {
  158. }
  159. bool CSteamIDRateLimit::BIsRateLimited( CSteamID steamID, uint32 unMsgType )
  160. {
  161. int nIndex = m_Msgs.FindOrInsert( steamID, 0 );
  162. if( ++m_Msgs[ nIndex ] >= ( uint32 )m_cvNumPerPeriod.GetInt() )
  163. {
  164. g_RateLimitTracker.TrackRateLimitedMsg( steamID, unMsgType );
  165. return true;
  166. }
  167. return false;
  168. }
  169. bool CSteamIDRateLimit::OnFrameFn( const CLimitTimer& timer )
  170. {
  171. //if no period is specified, assume one second
  172. int nIntervalS = ( m_pcvPeriodS ) ? MAX( 1, m_pcvPeriodS->GetInt() ) : 1;
  173. if( CRTime::RTime32TimeCur() >= m_LastClear + nIntervalS )
  174. {
  175. m_Msgs.RemoveAll();
  176. m_LastClear = CRTime::RTime32TimeCur();
  177. }
  178. return false;
  179. }
  180. //-----------------------------------------------------------------------------
  181. // Purpose: Constructor
  182. //-----------------------------------------------------------------------------
  183. CGCSession::CGCSession( const CSteamID & steamID, CGCSharedObjectCache *pSOCache )
  184. : m_steamID( steamID ),
  185. m_pSOCache( pSOCache ),
  186. m_bIsShuttingDown( false ),
  187. m_osType( k_eOSUnknown ),
  188. m_bIsTestSession( false ),
  189. m_bIsSecure( false ),
  190. m_unIPPublic( 0 ),
  191. m_flLatitude( 0.0f ),
  192. m_flLongitude( 0.0f ) ,
  193. m_haveGeoLocation( false ),
  194. m_bInitialized( false ),
  195. m_rtLastMessageReceived( 0 )
  196. {
  197. m_jtLastMessageReceived.SetLTime( 0 );
  198. m_jtTimeSentPing.SetLTime( 0 );
  199. }
  200. //-----------------------------------------------------------------------------
  201. // Purpose: Destructor
  202. //-----------------------------------------------------------------------------
  203. CGCSession::~CGCSession()
  204. {
  205. }
  206. //-----------------------------------------------------------------------------
  207. // Purpose: Checks the message against rate limiting. Returns true if we should
  208. // drop the message. False otherwise. This default behavior is a very basic
  209. // n messages / user / second rate limiting that's only meant to stop the
  210. // worse abuses
  211. //-----------------------------------------------------------------------------
  212. bool CGCSession::BRateLimitMessage( MsgType_t unMsgType )
  213. {
  214. unMsgType &= ~k_EMsgProtoBufFlag;
  215. if ( max_user_messages_per_second.GetInt() <= 0 )
  216. return false;
  217. RTime32 rtCur = CRTime::RTime32TimeCur();
  218. m_jtLastMessageReceived.SetToJobTime();
  219. if ( m_rtLastMessageReceived != rtCur )
  220. {
  221. m_rtLastMessageReceived = rtCur;
  222. m_unMessagesRecievedThisSecond = 0;
  223. }
  224. m_unMessagesRecievedThisSecond++;
  225. if ( m_unMessagesRecievedThisSecond > (uint32)max_user_messages_per_second.GetInt() )
  226. {
  227. //log this message
  228. g_RateLimitTracker.TrackRateLimitedMsg( GetSteamID(), unMsgType );
  229. return true;
  230. }
  231. return false;
  232. }
  233. //-----------------------------------------------------------------------------
  234. // Purpose: The run function is called on each session (user and gameserver)
  235. // approximately every k_nUserSessionRunInterval microseconds (or
  236. // k_nGSSessionRunInterval for GS Sessions)
  237. //-----------------------------------------------------------------------------
  238. void CGCSession::Run()
  239. {
  240. // These cached subscription messages are very expensive and only needed for a short period of time
  241. // If we're hitting the run loop, it's been around long enough
  242. GetSOCache()->ClearCachedSubscriptionMessage();
  243. }
  244. //-----------------------------------------------------------------------------
  245. bool CGCSession::GetGeoLocation( float &latitude, float &longittude ) const
  246. {
  247. latitude = m_flLatitude;
  248. longittude = m_flLongitude;
  249. return m_haveGeoLocation;
  250. }
  251. //-----------------------------------------------------------------------------
  252. void CGCSession::SetGeoLocation( float latitude, float longittude )
  253. {
  254. m_flLatitude = latitude;
  255. m_flLongitude = longittude;
  256. m_haveGeoLocation = true;
  257. }
  258. //-----------------------------------------------------------------------------
  259. // Purpose: Claims all the memory for the session object
  260. //-----------------------------------------------------------------------------
  261. #ifdef DBGFLAG_VALIDATE
  262. void CGCSession::Validate( CValidator &validator, const char *pchName )
  263. {
  264. VALIDATE_SCOPE();
  265. }
  266. #endif // DBGFLAG_VALIDATE
  267. //-----------------------------------------------------------------------------
  268. // Purpose: Destructor
  269. //-----------------------------------------------------------------------------
  270. CGCUserSession::~CGCUserSession()
  271. {
  272. if ( m_steamIDGS.BGameServerAccount() )
  273. {
  274. EmitError( SPEW_GC, "Destroying user %s while still connected to server %s\n", GetSteamID().Render(), GetSteamIDGS().Render() );
  275. }
  276. }
  277. bool CGCUserSession::BInit()
  278. {
  279. return true;
  280. }
  281. //-----------------------------------------------------------------------------
  282. // Purpose: Sets the session's game server to the given SteamID. This will
  283. // cause the session to leave the current server it's on, it any
  284. // Returns: True if the user's session was added to the GS's session.
  285. // False if the session could not be found or if the user was already
  286. // on the server.
  287. //-----------------------------------------------------------------------------
  288. bool CGCUserSession::BSetServer( const CSteamID &steamIDGS )
  289. {
  290. if ( steamIDGS == m_steamIDGS )
  291. return false;
  292. BLeaveServer();
  293. if( steamIDGS.IsValid() )
  294. {
  295. CGCGSSession *pGSSession = GGCBase()->FindGSSession( steamIDGS );
  296. if ( !pGSSession )
  297. {
  298. EmitError( SPEW_GC, "User %s attempting to join server %s which has no session\n", GetSteamID().Render(), steamIDGS.Render() );
  299. return false;
  300. }
  301. if ( !pGSSession->BAddUser( GetSteamID() ) )
  302. {
  303. EmitWarning( SPEW_GC, SPEW_ALWAYS, "Server %s already had user %s in its user list\n", steamIDGS.Render(), GetSteamID().Render() );
  304. // Fall through
  305. }
  306. }
  307. m_steamIDGS = steamIDGS;
  308. return true;
  309. }
  310. //-----------------------------------------------------------------------------
  311. // Purpose: Removes the session from the given game server
  312. // Returns: True if the user's session was removed from the GS's session.
  313. // False if the session could not be found or if the user was not found
  314. // on the server.
  315. //-----------------------------------------------------------------------------
  316. bool CGCUserSession::BLeaveServer()
  317. {
  318. if( m_steamIDGS.IsValid() )
  319. {
  320. // Remember the last server we were connected to
  321. m_steamIDGSPrev = m_steamIDGS;
  322. CGCGSSession *pGSSession = GGCBase()->FindGSSession( m_steamIDGS );
  323. if ( pGSSession )
  324. {
  325. pGSSession->BRemoveUser( GetSteamID() );
  326. }
  327. }
  328. m_steamIDGS = CSteamID();
  329. return true;
  330. }
  331. //-----------------------------------------------------------------------------
  332. // Purpose: Dumps useful information about this session
  333. //-----------------------------------------------------------------------------
  334. void CGCUserSession::Dump( bool bFull ) const
  335. {
  336. // this is ifdef'd out in Steam because GCSDK can't depend on steamid.cpp
  337. #ifndef STEAM
  338. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "User Session %s (%s)\n", GetSteamID().Render(), BIsShuttingDown() ? "SHUTTING DOWN" : "Active" );
  339. CJob *pJob = GGCBase()->PJobHoldingLock( GetSteamID() );
  340. if( pJob )
  341. {
  342. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "\t LOCKED BY: %s\n", pJob->GetName() );
  343. }
  344. if( bFull && GetSOCache() )
  345. GetSOCache()->Dump();
  346. if( GetSteamIDGS().BGameServerAccount() )
  347. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "\tGameserver: %s\n", GetSteamIDGS().Render() );
  348. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "\tOS: %d Secure: %d\n", GetOSType(), IsSecure() ? 1 : 0 );
  349. #endif
  350. }
  351. //-----------------------------------------------------------------------------
  352. // Purpose: Constructor
  353. //-----------------------------------------------------------------------------
  354. CGCGSSession::CGCGSSession( const CSteamID & steamID, CGCSharedObjectCache *pCache, uint32 unServerAddr, uint16 usServerPort )
  355. : CGCSession( steamID, pCache ), m_unServerAddr( unServerAddr ), m_usServerPort( usServerPort )
  356. {
  357. if ( gs_session_assert_valid_addr_and_port.GetBool() )
  358. {
  359. Assert( unServerAddr );
  360. Assert( usServerPort );
  361. }
  362. // Default our public IP to be the same as our IP address
  363. m_unIPPublic = unServerAddr;
  364. }
  365. //-----------------------------------------------------------------------------
  366. // Purpose: Destructor
  367. //-----------------------------------------------------------------------------
  368. CGCGSSession::~CGCGSSession()
  369. {
  370. if ( m_vecUsers.Count() > 0 )
  371. {
  372. EmitError( SPEW_GC, "Destroying game server %s while %d users are still connected\n", GetSteamID().Render(), m_vecUsers.Count() );
  373. }
  374. }
  375. //-----------------------------------------------------------------------------
  376. // Purpose: Adds a user to the list of users active on the game server
  377. // Returns: True if the user was added, false if the user was already on this
  378. // server.
  379. //-----------------------------------------------------------------------------
  380. bool CGCGSSession::BAddUser( const CSteamID &steamIDUser )
  381. {
  382. if( m_vecUsers.HasElement( steamIDUser ) )
  383. return false;
  384. PreAddUser( steamIDUser );
  385. m_vecUsers.AddToTail( steamIDUser );
  386. PostAddUser( steamIDUser );
  387. return true;
  388. }
  389. //-----------------------------------------------------------------------------
  390. // Purpose: Called if our IP / port changes after session is started
  391. //-----------------------------------------------------------------------------
  392. void CGCGSSession::SetIPAndPort( uint32 unServerAddr, uint16 usServerPort )
  393. {
  394. // If we didn't have an override for the public IP, then also
  395. // update the public IP.
  396. //
  397. // !KLUDGE! This is gross for two reasons:
  398. // - First, do we really need two different fields?
  399. // - Second, why can the IP change after the session is created?
  400. // Shouldn't we force the session to be destroyed and recreated?
  401. // It cannot *really* be the same "session", can it?
  402. if ( m_unIPPublic == m_unServerAddr )
  403. m_unIPPublic = unServerAddr;
  404. m_unServerAddr = unServerAddr;
  405. m_usServerPort = usServerPort;
  406. }
  407. //-----------------------------------------------------------------------------
  408. // Purpose: Removes a user from the list of users active on the game server
  409. // Returns: True if the user was added, false if the user was not already on
  410. // this server.
  411. //-----------------------------------------------------------------------------
  412. bool CGCGSSession::BRemoveUser( const CSteamID &steamIDUser )
  413. {
  414. int nIndex = m_vecUsers.Find( steamIDUser );
  415. if ( !m_vecUsers.IsValidIndex( nIndex ) )
  416. return false;
  417. PreRemoveUser( steamIDUser );
  418. m_vecUsers.Remove( nIndex );
  419. PostRemoveUser( steamIDUser );
  420. return true;
  421. }
  422. //-----------------------------------------------------------------------------
  423. // Purpose: Removes all users from the list of game server users.
  424. //-----------------------------------------------------------------------------
  425. void CGCGSSession::RemoveAllUsers()
  426. {
  427. if ( 0 == m_vecUsers.Count() )
  428. return;
  429. PreRemoveAllUsers();
  430. // Iterate all the users and tell them to leave this server.
  431. // Using back because the users will remove themselves from
  432. // this list during this function
  433. FOR_EACH_VEC_BACK( m_vecUsers, i )
  434. {
  435. CGCUserSession *pUserSession = GGCBase()->FindUserSession( m_vecUsers[i] );
  436. if ( pUserSession )
  437. {
  438. pUserSession->BLeaveServer();
  439. }
  440. }
  441. // Catch anyone we don't have a session for anymore
  442. m_vecUsers.RemoveAll();
  443. PostRemoveAllUsers();
  444. }
  445. #define iptod(x) ((x)>>24&0xff), ((x)>>16&0xff), ((x)>>8&0xff), ((x)&0xff)
  446. //-----------------------------------------------------------------------------
  447. // Purpose: Dumps useful information about this session
  448. //-----------------------------------------------------------------------------
  449. void CGCGSSession::Dump( bool bFull ) const
  450. {
  451. // this is ifdef'd out in Steam because GCSDK can't depend on steamid.cpp
  452. #ifndef STEAM
  453. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "GS Session %s\n", GetSteamID().Render() );
  454. CJob *pJob = GGCBase()->PJobHoldingLock( GetSteamID() );
  455. if( pJob )
  456. {
  457. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "\t LOCKED BY: %s\n", pJob->GetName() );
  458. }
  459. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "\t%d users:\n", m_vecUsers.Count() );
  460. FOR_EACH_VEC( m_vecUsers, nUser )
  461. {
  462. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "\t\t%s\n", m_vecUsers[nUser].Render() );
  463. }
  464. if( GetSOCache() )
  465. {
  466. if ( bFull )
  467. {
  468. GetSOCache()->Dump();
  469. }
  470. else
  471. {
  472. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "\t SO Cache Version: %llu\n", GetSOCache()->GetVersion() );
  473. }
  474. }
  475. #endif
  476. }
  477. //-----------------------------------------------------------------------------
  478. // Purpose: Claims all the memory for the session object
  479. //-----------------------------------------------------------------------------
  480. #ifdef DBGFLAG_VALIDATE
  481. void CGCGSSession::Validate( CValidator &validator, const char *pchName )
  482. {
  483. CGCSession::Validate( validator, pchName);
  484. VALIDATE_SCOPE();
  485. ValidateObj( m_vecUsers );
  486. }
  487. #endif // DBGFLAG_VALIDATE
  488. //-----------------------------------------------------------------------------
  489. // Purpose: Client says it needs the SO Cache
  490. // Input : pNetPacket - received message
  491. //-----------------------------------------------------------------------------
  492. class CGCCacheSubscriptionRefresh: public CGCJob
  493. {
  494. public:
  495. CGCCacheSubscriptionRefresh( CGCBase *pGC ) : CGCJob( pGC ) { }
  496. bool BYieldingRunJobFromMsg( IMsgNetPacket *pNetPacket );
  497. };
  498. bool CGCCacheSubscriptionRefresh::BYieldingRunJobFromMsg( IMsgNetPacket *pNetPacket )
  499. {
  500. CProtoBufMsg<CMsgSOCacheSubscriptionRefresh> msg( pNetPacket );
  501. CSteamID steamID( msg.Hdr().client_steam_id() );
  502. CSteamID steamIDCacheOwner( msg.Body().owner() );
  503. CGCSharedObjectCache *pCache = m_pGC->FindSOCache( steamIDCacheOwner );
  504. if ( pCache == NULL || !pCache->BIsSubscribed( steamID ) )
  505. {
  506. return false;
  507. }
  508. pCache->SendSubscriberMessage( steamID );
  509. return true;
  510. }
  511. GC_REG_JOB( CGCBase, CGCCacheSubscriptionRefresh, "CGCCacheSubscriptionRefresh", k_ESOMsg_CacheSubscriptionRefresh, k_EServerTypeGC );
  512. } // namespace GCSDK