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.

601 lines
21 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Holds the CGCClient class
  4. //
  5. //=============================================================================
  6. #include "stdafx.h"
  7. #include "gcclient.h"
  8. #include "steam/isteamgamecoordinator.h"
  9. #include "gcsdk_gcmessages.pb.h"
  10. namespace GCSDK
  11. {
  12. //#define SOCDebug(...) Msg( __VA_ARGS__ )
  13. #define SOCDebug(...) ((void)0)
  14. //------------------------------------------------------------------------------
  15. // Purpose: Constructor
  16. //------------------------------------------------------------------------------
  17. CGCClient::CGCClient( ISteamGameCoordinator *pSteamGameCoordinator, bool bGameserver )
  18. : m_pSteamGameCoordinator( NULL ),
  19. m_memMsg( 0, 1024 ),
  20. #ifndef STEAM
  21. m_callbackGCMessageAvailable( NULL, NULL ),
  22. #endif
  23. m_mapSOCache( DefLessFunc(CSteamID) )
  24. {
  25. #ifndef STEAM
  26. if( bGameserver )
  27. {
  28. m_callbackGCMessageAvailable.SetGameserverFlag();
  29. }
  30. #endif
  31. if( pSteamGameCoordinator )
  32. {
  33. DbgVerify( BInit( pSteamGameCoordinator ) );
  34. }
  35. }
  36. //------------------------------------------------------------------------------
  37. // Purpose: Constructor
  38. //------------------------------------------------------------------------------
  39. CGCClient::~CGCClient( )
  40. {
  41. Uninit();
  42. FOR_EACH_MAP_FAST( m_mapSOCache, i )
  43. {
  44. delete m_mapSOCache[i];
  45. }
  46. m_mapSOCache.RemoveAll();
  47. }
  48. //------------------------------------------------------------------------------
  49. // Purpose: Performs the every-frame work required by the GC Client. Mostly that
  50. // means running yielding jobs.
  51. // Inputs: ulLimitMicroseconds - The target number of microseconds worth of
  52. // work to do this time through the loop.
  53. // Outputs: Returns true if there is still work to do that was skipped because
  54. // time ran out.
  55. //------------------------------------------------------------------------------
  56. bool CGCClient::BMainLoop( uint64 ulLimitMicroseconds, uint64 ulFrameTimeMicroseconds )
  57. {
  58. // Don't do any work if not initialized
  59. if ( !m_pSteamGameCoordinator )
  60. return false;
  61. CLimitTimer limitTimer;
  62. limitTimer.SetLimit( ulLimitMicroseconds );
  63. CJobTime::UpdateJobTime( ulFrameTimeMicroseconds ? ulFrameTimeMicroseconds : k_cMicroSecPerShellFrame );
  64. bool bWorkRemaining = m_JobMgr.BFrameFuncRunSleepingJobs( limitTimer );
  65. bWorkRemaining |= m_JobMgr.BFrameFuncRunYieldingJobs( limitTimer );
  66. return bWorkRemaining;
  67. }
  68. //------------------------------------------------------------------------------
  69. // Purpose: Sends a message to the GC
  70. // Inputs: unMsgType - the type ID of the message to send
  71. // pubData - The data for the message we're sending
  72. // cubData - The number of bytes of data in this message including any
  73. // variable-lengthed data.
  74. // Outputs: Returns false if the send failed. A return value of true doesn't
  75. // mean that the message was necessarily received by the GC just that
  76. // it didn't fail in obvious ways on the client.
  77. //------------------------------------------------------------------------------
  78. bool CGCClient::BSendMessage( uint32 unMsgType, const uint8 *pubData, uint32 cubData )
  79. {
  80. if( m_pSteamGameCoordinator )
  81. return m_pSteamGameCoordinator->SendMessage( unMsgType, pubData, cubData ) == k_EGCResultOK;
  82. else
  83. return false;
  84. }
  85. //------------------------------------------------------------------------------
  86. // Purpose: Sends a message to the GC
  87. // Inputs: msg - The message to send
  88. // Outputs: Returns false if the send failed. A return value of true doesn't
  89. // mean that the message was necessarily received by the GC just that
  90. // it didn't fail in obvious ways on the client.
  91. //------------------------------------------------------------------------------
  92. bool CGCClient::BSendMessage( const CGCMsgBase& msg )
  93. {
  94. return BSendMessage( msg.Hdr().m_eMsg, msg.PubPkt() + sizeof(GCMsgHdr_t), msg.CubPkt() - sizeof(GCMsgHdr_t) );
  95. }
  96. //-----------------------------------------------------------------------------
  97. // Purpose: Used to send protobuf messages to the GC
  98. //-----------------------------------------------------------------------------
  99. class CProtoBufGCClientSendHandler : public CProtoBufMsgBase::IProtoBufSendHandler
  100. {
  101. public:
  102. CProtoBufGCClientSendHandler( CGCClient *pGCClient )
  103. : m_pClient( pGCClient ) {}
  104. virtual bool BAsyncSend( MsgType_t eMsg, const uint8 *pubMsgBytes, uint32 cubSize )
  105. {
  106. g_theMessageList.TallySendMessage( eMsg & ~k_EMsgProtoBufFlag, cubSize );
  107. VPROF_BUDGET( "CGCClient", VPROF_BUDGETGROUP_STEAM );
  108. {
  109. VPROF_BUDGET( "CGCClient - BSendGCMsgToClient (ProtoBuf)", VPROF_BUDGETGROUP_STEAM );
  110. return m_pClient->BSendMessage( eMsg | k_EMsgProtoBufFlag, pubMsgBytes, cubSize );
  111. }
  112. }
  113. private:
  114. CGCClient *m_pClient;
  115. };
  116. //-----------------------------------------------------------------------------
  117. // Purpose: Sends a message to the given SteamID
  118. //-----------------------------------------------------------------------------
  119. bool CGCClient::BSendMessage( const CProtoBufMsgBase& msg )
  120. {
  121. CProtoBufGCClientSendHandler sender( this );
  122. return msg.BAsyncSend( sender );
  123. }
  124. //------------------------------------------------------------------------------
  125. // Purpose: Callback handler for the GCMessageAvailable_t callback. Handles
  126. // incoming messages.
  127. // Inputs: pCallback - the callback from Steam
  128. //------------------------------------------------------------------------------
  129. void CGCClient::OnGCMessageAvailable( GCMessageAvailable_t *pCallback )
  130. {
  131. uint32 cubData;
  132. uint32 unMsgType;
  133. while( m_pSteamGameCoordinator && m_pSteamGameCoordinator->IsMessageAvailable( &cubData ) )
  134. {
  135. // Get the size of the full message. sizeof( GCMsgHdr_t ) was not sent in the binary data
  136. uint32 unFullSize = cubData + sizeof( GCMsgHdr_t );
  137. m_memMsg.EnsureCapacity( unFullSize );
  138. uint8 *pFullPacket = m_memMsg.Base();
  139. uint8 *pPacketFromGC = pFullPacket+sizeof(GCMsgHdr_t);
  140. EGCResults eResult = m_pSteamGameCoordinator->RetrieveMessage( &unMsgType, pPacketFromGC, m_memMsg.Count() - sizeof( GCMsgHdr_t ), &cubData );
  141. Assert( eResult == k_EGCResultOK );
  142. if( eResult == k_EGCResultOK )
  143. {
  144. if( unMsgType & k_EMsgProtoBufFlag )
  145. {
  146. CNetPacket *pGCPacket = CNetPacketPool::AllocNetPacket();
  147. pGCPacket->Init( cubData, pPacketFromGC );
  148. CIMsgNetPacketAutoRelease pMsgNetPacket( pGCPacket );
  149. // Safety check against malformed packet
  150. if ( pMsgNetPacket.Get() != NULL )
  151. {
  152. // dispatch the packet
  153. GetJobMgr().BRouteMsgToJob( this, pMsgNetPacket.Get(), JobMsgInfo_t( pMsgNetPacket->GetEMsg(), pMsgNetPacket->GetSourceJobID(), pMsgNetPacket->GetTargetJobID(), k_EServerTypeGCClient ) );
  154. // keep track of how much we've sent/received this message
  155. g_theMessageList.TallySendMessage( pMsgNetPacket->GetEMsg(), cubData );
  156. }
  157. // release the packet
  158. pGCPacket->Release();
  159. }
  160. else
  161. {
  162. Assert( 0 == (unMsgType & k_EMsgProtoBufFlag ) );
  163. // get the header so we can fix it up
  164. GCMsgHdrEx_t *pHdr = (GCMsgHdrEx_t *)pFullPacket;
  165. pHdr->m_eMsg = unMsgType;
  166. pHdr->m_ulSteamID = CSteamID().ConvertToUint64();
  167. // make a new packet for the message so we can dispatch it
  168. // The CNetPacket takes ownership of the buffer allocated above
  169. CNetPacket *pGCPacket = CNetPacketPool::AllocNetPacket();
  170. pGCPacket->Init( unFullSize, pFullPacket );
  171. CIMsgNetPacketAutoRelease pMsgNetPacket( pGCPacket );
  172. // Safety check against malformed packet
  173. if ( pMsgNetPacket.Get() != NULL )
  174. {
  175. // dispatch the packet
  176. GetJobMgr().BRouteMsgToJob( this, pMsgNetPacket.Get(), JobMsgInfo_t( pMsgNetPacket->GetEMsg(), pMsgNetPacket->GetSourceJobID(), pMsgNetPacket->GetTargetJobID(), k_EServerTypeGCClient ) );
  177. // keep track of how much we've sent/received this message
  178. g_theMessageList.TallySendMessage( pMsgNetPacket->GetEMsg(), cubData );
  179. }
  180. // release the packet
  181. pGCPacket->Release();
  182. }
  183. }
  184. }
  185. }
  186. //------------------------------------------------------------------------------
  187. // Purpose: Performs all the initialization for the GC Client instance
  188. // Outputs: Returns false if the initialization failed
  189. //------------------------------------------------------------------------------
  190. bool CGCClient::BInit( ISteamGameCoordinator *pSteamGameCoordinator )
  191. {
  192. // Set the job pool size. Threads get lazily created so if no code
  193. // is using the thread pool, no threads will be created.
  194. m_JobMgr.SetThreadPoolSize( GetCPUInformation()->m_nLogicalProcessors - 1 );
  195. MsgRegistrationFromEnumDescriptor( EGCSystemMsg_descriptor(), GCSDK::MT_GC );
  196. m_pSteamGameCoordinator = pSteamGameCoordinator;
  197. #ifndef STEAM
  198. m_callbackGCMessageAvailable.Register( this, &CGCClient::OnGCMessageAvailable );
  199. #endif
  200. // process any messages that are already waiting
  201. if( m_pSteamGameCoordinator )
  202. {
  203. OnGCMessageAvailable( NULL );
  204. }
  205. return true;
  206. }
  207. //------------------------------------------------------------------------------
  208. // Purpose: Performs all the uninitialization for the GC Client instance
  209. //------------------------------------------------------------------------------
  210. void CGCClient::Uninit( )
  211. {
  212. #ifndef STEAM
  213. m_callbackGCMessageAvailable.Unregister();
  214. #endif
  215. m_pSteamGameCoordinator = NULL;
  216. // Clear and remove the SO caches
  217. unsigned short nMapIndex = m_mapSOCache.FirstInorder();
  218. while ( m_mapSOCache.IsValidIndex( nMapIndex ) )
  219. {
  220. unsigned short nNextMapIndex = m_mapSOCache.NextInorder( nMapIndex );
  221. CGCClientSharedObjectCache *pSOCache = m_mapSOCache[nMapIndex];
  222. Assert( pSOCache );
  223. if ( pSOCache )
  224. {
  225. // Send notifications, but only if we were actually subscribed
  226. if ( pSOCache->BIsSubscribed() )
  227. {
  228. pSOCache->NotifyUnsubscribe();
  229. }
  230. // Delete the entry
  231. delete pSOCache;
  232. m_mapSOCache.RemoveAt( nMapIndex );
  233. }
  234. nMapIndex = nNextMapIndex;
  235. }
  236. }
  237. //------------------------------------------------------------------------------
  238. // Purpose: Finds the SO cache for this steam ID. If bCreateIfMissing is false,
  239. // NULL will be returned if the cache can't be found
  240. //------------------------------------------------------------------------------
  241. CGCClientSharedObjectCache *CGCClient::FindSOCache( const CSteamID & steamID, bool bCreateIfMissing )
  242. {
  243. CUtlMap< CSteamID, CGCClientSharedObjectCache * >::IndexType_t nCache = m_mapSOCache.Find( steamID );
  244. if( m_mapSOCache.IsValidIndex( nCache ) )
  245. return m_mapSOCache[nCache];
  246. else
  247. {
  248. if( bCreateIfMissing )
  249. {
  250. Assert( steamID.IsValid() );
  251. if ( !steamID.IsValid() )
  252. return NULL;
  253. CGCClientSharedObjectCache *pCache = new CGCClientSharedObjectCache( steamID );
  254. m_mapSOCache.Insert( steamID, pCache );
  255. return pCache;
  256. }
  257. else
  258. {
  259. return NULL;
  260. }
  261. }
  262. }
  263. //------------------------------------------------------------------------------
  264. // Purpose: Add a listener to the SO cache, creating it if necessary
  265. //------------------------------------------------------------------------------
  266. void CGCClient::AddSOCacheListener( const CSteamID &ownerID, ISharedObjectListener *pListener )
  267. {
  268. Assert( ownerID.IsValid() );
  269. CGCClientSharedObjectCache *pCache = FindSOCache( ownerID, true );
  270. Assert( pCache );
  271. pCache->AddListener( pListener );
  272. }
  273. //------------------------------------------------------------------------------
  274. // Purpose: Remove listener from the SO cache, if he is listening
  275. //------------------------------------------------------------------------------
  276. bool CGCClient::RemoveSOCacheListener( const CSteamID &ownerID, ISharedObjectListener *pListener )
  277. {
  278. Assert ( this != NULL ); // Damn people - check your pointers before calling!
  279. Assert( ownerID.IsValid() );
  280. CGCClientSharedObjectCache *pCache = FindSOCache( ownerID, false );
  281. if ( pCache == NULL )
  282. return false; // cache doesn't exist, so we could't have ben listening
  283. return pCache->RemoveListener( pListener );
  284. }
  285. //------------------------------------------------------------------------------
  286. // Purpose: Notify that the given SO cache has been unsubscribed
  287. //------------------------------------------------------------------------------
  288. void CGCClient::NotifySOCacheUnsubscribed( const CSteamID & ownerID )
  289. {
  290. CUtlMap< CSteamID, CGCClientSharedObjectCache * >::IndexType_t nCache = m_mapSOCache.Find( ownerID );
  291. if( m_mapSOCache.IsValidIndex( nCache ) )
  292. {
  293. CGCClientSharedObjectCache *pSOCache = m_mapSOCache[nCache];
  294. // Ignore requests to remove caches that were never subscribed
  295. if ( pSOCache->BIsSubscribed() )
  296. {
  297. SOCDebug( "NotifySOCacheUnsubscribed(%s) [in cache, subscribed]\n", ownerID.Render() );
  298. pSOCache->NotifyUnsubscribe();
  299. }
  300. else
  301. {
  302. SOCDebug( "NotifySOCacheUnsubscribed(%s) [in cache, not subscribed]\n", ownerID.Render() );
  303. }
  304. }
  305. else
  306. {
  307. SOCDebug( "NotifySOCacheUnsubscribed(%s) [not in cache]\n", ownerID.Render() );
  308. }
  309. }
  310. //------------------------------------------------------------------------------
  311. // Purpose: Dump everything about everyone
  312. //------------------------------------------------------------------------------
  313. void CGCClient::Dump()
  314. {
  315. FOR_EACH_MAP( m_mapSOCache, idx )
  316. {
  317. m_mapSOCache[ idx ]->Dump();
  318. }
  319. }
  320. //------------------------------------------------------------------------------
  321. // Purpose: Finds the shared object for this steam ID and key object
  322. //------------------------------------------------------------------------------
  323. CSharedObject *CGCClient::FindSharedObject( const CSteamID & ownerID, const CSharedObject & soIndex )
  324. {
  325. CGCClientSharedObjectCache *pCache = FindSOCache( ownerID, false );
  326. if( pCache )
  327. return pCache->FindSharedObject( soIndex );
  328. else
  329. return NULL;
  330. }
  331. //------------------------------------------------------------------------------
  332. // Purpose: Validates all the statics in the GCSDKLib that need to be validated
  333. // when linked directly into the steam servers.
  334. //------------------------------------------------------------------------------
  335. #ifdef DBGFLAG_VALIDATE
  336. void CGCClient::ValidateStatics( CValidator &validator )
  337. {
  338. // Validate the global message list
  339. g_theMessageList.Validate( validator, "g_theMessageList" );
  340. // Validate the network global memory pool
  341. g_MemPoolMsg.Validate( validator, "g_MemPoolMsg" );
  342. CNetPacketPool::ValidateGlobals( validator );
  343. CJobMgr::ValidateStatics( validator, "CJobMgr" );
  344. CJob::ValidateStatics( validator, "CJob" );
  345. ValidateTempTextBuffers( validator );
  346. CSharedObject::ValidateStatics( validator );
  347. // validate the SQL access layer
  348. CRecordBase::ValidateStatics( validator, "CRecordBase" );
  349. GSchemaFull().Validate( validator, "GSchemaFull" );
  350. CRecordInfo::ValidateStatics( validator, "CRecordInfo" );
  351. }
  352. #endif // DBGFLAG_VALIDATE
  353. class CGCSOCreateJob : public CGCClientJob
  354. {
  355. public:
  356. CGCSOCreateJob( CGCClient *pClient ) : CGCClientJob( pClient ) {}
  357. virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
  358. {
  359. CProtoBufMsg<CMsgSOSingleObject> msg( pNetPacket );
  360. SOCDebug( "CGCSOCreateJob(owner=%s, type=%d)\n", CSteamID( msg.Body().owner() ).Render(), msg.Body().type_id() );
  361. CGCClientSharedObjectCache *pSOCache = m_pGCClient->FindSOCache( msg.Body().owner() );
  362. if ( pSOCache )
  363. {
  364. pSOCache->BCreateFromMsg( msg.Body().type_id(), msg.Body().object_data().data(), msg.Body().object_data().size() );
  365. Assert( msg.Body().has_version() );
  366. pSOCache->SetVersion( msg.Body().version() );
  367. }
  368. return true;
  369. }
  370. };
  371. GC_REG_JOB( CGCClient, CGCSOCreateJob, "CGCSOCreateJob", k_ESOMsg_Create, GCSDK::k_EServerTypeGCClient );
  372. class CGCSODestroyJob : public CGCClientJob
  373. {
  374. public:
  375. CGCSODestroyJob( CGCClient *pClient ) : CGCClientJob( pClient ) {}
  376. virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
  377. {
  378. CProtoBufMsg<CMsgSOSingleObject> msg( pNetPacket );
  379. SOCDebug( "CGCSODestroyJob(owner=%s, type=%d)\n", CSteamID( msg.Body().owner() ).Render(), msg.Body().type_id() );
  380. CGCClientSharedObjectCache *pCache = m_pGCClient->FindSOCache( msg.Body().owner(), false );
  381. if( pCache )
  382. {
  383. pCache->BDestroyFromMsg( msg.Body().type_id(), msg.Body().object_data().data(), msg.Body().object_data().size() );
  384. Assert( msg.Body().has_version() );
  385. pCache->SetVersion( msg.Body().version() );
  386. }
  387. return true;
  388. }
  389. };
  390. GC_REG_JOB( CGCClient, CGCSODestroyJob, "CGCSODestroyJob", k_ESOMsg_Destroy, GCSDK::k_EServerTypeGCClient );
  391. class CGCSOUpdateJob : public CGCClientJob
  392. {
  393. public:
  394. CGCSOUpdateJob( CGCClient *pClient ) : CGCClientJob( pClient ) {}
  395. virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
  396. {
  397. CProtoBufMsg<CMsgSOSingleObject> msg( pNetPacket );
  398. SOCDebug( "CGCSOUpdateJob(owner=%s, type=%d)\n", CSteamID( msg.Body().owner() ).Render(), msg.Body().type_id() );
  399. CGCClientSharedObjectCache *pSOCache = m_pGCClient->FindSOCache( msg.Body().owner() );
  400. if ( pSOCache )
  401. {
  402. pSOCache->BUpdateFromMsg( msg.Body().type_id(), msg.Body().object_data().data(), msg.Body().object_data().size() );
  403. Assert( msg.Body().has_version() );
  404. pSOCache->SetVersion( msg.Body().version() );
  405. }
  406. return true;
  407. }
  408. };
  409. GC_REG_JOB( CGCClient, CGCSOUpdateJob, "CGCSOUpdateJob", k_ESOMsg_Update, GCSDK::k_EServerTypeGCClient );
  410. class CGCSOUpdateMultipleJob : public CGCClientJob
  411. {
  412. public:
  413. CGCSOUpdateMultipleJob( CGCClient *pClient ) : CGCClientJob( pClient ) {}
  414. virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
  415. {
  416. CProtoBufMsg<CMsgSOMultipleObjects> msg( pNetPacket );
  417. SOCDebug( "CGCSOUpdateJob(owner=%s)\n", CSteamID( msg.Body().owner() ).Render() );
  418. CGCClientSharedObjectCache *pSOCache = m_pGCClient->FindSOCache( msg.Body().owner() );
  419. if ( pSOCache )
  420. {
  421. pSOCache->m_context.PreSOUpdate( eSOCacheEvent_Incremental );
  422. for ( int i = 0; i < msg.Body().objects_size(); ++i )
  423. {
  424. const CMsgSOMultipleObjects_SingleObject &objMessage = msg.Body().objects( i );
  425. SOCDebug( " type %d\n", objMessage.type_id() );
  426. pSOCache->BUpdateFromMsg( objMessage.type_id(), objMessage.object_data().data(), objMessage.object_data().size() );
  427. }
  428. pSOCache->m_context.PostSOUpdate( eSOCacheEvent_Incremental );
  429. pSOCache->SetVersion( msg.Body().version() );
  430. }
  431. return true;
  432. }
  433. };
  434. GC_REG_JOB( CGCClient, CGCSOUpdateMultipleJob, "CGCSOUpdateMultipleJob", k_ESOMsg_UpdateMultiple, GCSDK::k_EServerTypeGCClient );
  435. class CGCSOCacheSubscribedJob : public CGCClientJob
  436. {
  437. public:
  438. CGCSOCacheSubscribedJob( CGCClient *pClient ) : CGCClientJob( pClient ) {}
  439. virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
  440. {
  441. CProtoBufMsg< CMsgSOCacheSubscribed > msg ( pNetPacket );
  442. CGCClientSharedObjectCache *pSOCache = m_pGCClient->FindSOCache( msg.Body().owner(), true );
  443. Assert( pSOCache );
  444. if( pSOCache )
  445. {
  446. SOCDebug( "CGCSOCacheSubscribedJob(owner=%s) [in cache]\n", CSteamID( msg.Body().owner() ).Render() );
  447. DbgVerify( pSOCache->BParseCacheSubscribedMsg( msg.Body() ) );
  448. }
  449. else
  450. {
  451. SOCDebug( "CGCSOCacheSubscribedJob(owner=%s) [not in cache]\n", CSteamID( msg.Body().owner() ).Render() );
  452. }
  453. m_pGCClient->Test_CacheSubscribed( pSOCache->GetOwner() );
  454. return true;
  455. }
  456. };
  457. GC_REG_JOB( CGCClient, CGCSOCacheSubscribedJob, "CGCSOCacheSubscribedJob", k_ESOMsg_CacheSubscribed, GCSDK::k_EServerTypeGCClient );
  458. class CGCSOCacheUnsubscribedJob : public CGCClientJob
  459. {
  460. public:
  461. CGCSOCacheUnsubscribedJob( CGCClient *pClient ) : CGCClientJob( pClient ) {}
  462. virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
  463. {
  464. CProtoBufMsg< CMsgSOCacheUnsubscribed > msg( pNetPacket );
  465. SOCDebug( "CGCSOCacheUnsubscribedJob(owner=%s)\n", CSteamID( msg.Body().owner() ).Render() );
  466. m_pGCClient->NotifySOCacheUnsubscribed( msg.Body().owner() );
  467. return true;
  468. }
  469. };
  470. GC_REG_JOB( CGCClient, CGCSOCacheUnsubscribedJob, "CGCSOCacheUnsubscribedJob", k_ESOMsg_CacheUnsubscribed, GCSDK::k_EServerTypeGCClient );
  471. class CGCSOCacheSubscriptionCheck : public CGCClientJob
  472. {
  473. public:
  474. CGCSOCacheSubscriptionCheck( CGCClient *pClient ) : CGCClientJob( pClient ) {}
  475. virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
  476. {
  477. CProtoBufMsg< CMsgSOCacheSubscriptionCheck > msg ( pNetPacket );
  478. CGCClientSharedObjectCache *pSOCache = m_pGCClient->FindSOCache( msg.Body().owner(), false );
  479. // if we do not have the cache or it is out-of-date, request a refresh
  480. if ( pSOCache == NULL || !pSOCache->BIsInitialized() || pSOCache->GetVersion() != msg.Body().version() )
  481. {
  482. SOCDebug( "CGCSOCacheSubscriptionCheck(owner=%s) -- need refresh\n", CSteamID( msg.Body().owner() ).Render() );
  483. CProtoBufMsg< CMsgSOCacheSubscriptionRefresh > msg_response( k_ESOMsg_CacheSubscriptionRefresh );
  484. msg_response.Body().set_owner( msg.Body().owner() );
  485. m_pGCClient->BSendMessage( msg_response );
  486. }
  487. else
  488. {
  489. SOCDebug( "CGCSOCacheSubscriptionCheck(owner=%s) -- up-to-date, no refresh needed\n", CSteamID( msg.Body().owner() ).Render() );
  490. // This is one method by which the GC notifies us that we are subscribed.
  491. if ( !pSOCache->BIsSubscribed() )
  492. {
  493. pSOCache->NotifyResubscribedUpToDate();
  494. Assert( pSOCache->BIsSubscribed() );
  495. }
  496. }
  497. return true;
  498. }
  499. };
  500. GC_REG_JOB( CGCClient, CGCSOCacheSubscriptionCheck, "CGCSOCacheSubscriptionCheck", k_ESOMsg_CacheSubscriptionCheck, GCSDK::k_EServerTypeGCClient );
  501. } // namespace GCSDK