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.

559 lines
18 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "tf_wardata.h"
  9. #include "gcsdk/enumutils.h"
  10. #include "schemainitutils.h"
  11. #ifdef CLIENT_DLL
  12. #include "c_tf_player.h"
  13. #endif
  14. #ifdef GAME_DLL
  15. #include "tf_player.h"
  16. #endif
  17. using namespace GCSDK;
  18. //-----------------------------------------------------------------------------
  19. // Purpose: Get user's War Data by steamID and war ID. Returns NULL if it
  20. // doesnt exist or if the war is inactive and bLoadEvenIfWarInactive
  21. // is false. On the GC bLoadSOCacheIfNeeded will load the user's
  22. // SOCache if needed in order to try and get their war data
  23. //-----------------------------------------------------------------------------
  24. CWarData* GetPlayerWarData( const CSteamID& steamID, war_definition_index_t warDefIndex, bool bLoadEvenIfWarInactive
  25. #ifdef GC_DLL
  26. , bool bLoadSOCacheIfNeeded
  27. #endif
  28. )
  29. {
  30. const CWarDefinition* pWarDef = GetItemSchema()->GetWarDefinitionByIndex( warDefIndex );
  31. // If the war isn't active and we weren't told to ignore that, then return NULL
  32. if ( !pWarDef || ( !bLoadEvenIfWarInactive && !pWarDef->IsActive() ) )
  33. return NULL;
  34. #ifdef GC_DLL
  35. CGCSharedObjectCache *pSOCache = GGCBase()->FindSOCache( steamID );
  36. // Load their SO cache if needed
  37. if ( pSOCache == NULL && bLoadSOCacheIfNeeded )
  38. {
  39. pSOCache = GGCBase()->YieldingFindOrLoadSOCache( steamID );
  40. }
  41. #else
  42. GCSDK::CGCClientSharedObjectCache *pSOCache = GCClientSystem()->GetSOCache( steamID );
  43. #endif
  44. if ( pSOCache )
  45. {
  46. auto *pTypeCache = pSOCache->FindTypeCache( CWarData::k_nTypeID );
  47. if ( pTypeCache )
  48. {
  49. int nCount = pTypeCache->GetCount();
  50. for( int i=0; i < nCount; ++i )
  51. {
  52. CWarData* pWarData = static_cast< CWarData* >( pTypeCache->GetObject( i ) );
  53. if ( pWarData->Obj().war_id() == warDefIndex )
  54. return pWarData;
  55. }
  56. }
  57. }
  58. return NULL;
  59. }
  60. #ifdef CLIENT_DLL
  61. CWarData* GetLocalPlayerWarData( war_definition_index_t warDefIndex )
  62. {
  63. if ( !steamapicontext || !steamapicontext->SteamUser() )
  64. {
  65. return NULL;
  66. }
  67. return GetPlayerWarData( steamapicontext->SteamUser()->GetSteamID(), warDefIndex, true );
  68. }
  69. #endif
  70. CWarDefinition::CWarDefinition()
  71. : m_nDefIndex( INVALID_WAR_DEF_INDEX )
  72. , m_pszLocalizedWarname( NULL )
  73. , m_pszDefName( NULL )
  74. , m_mapSides( DefLessFunc( SidesMap_t::KeyType_t ) )
  75. , m_rtTimeEnd( 0 )
  76. , m_rtTimeStart( 0 )
  77. {}
  78. bool CWarDefinition::BInitFromKV( KeyValues *pKV, CUtlVector<CUtlString> *pVecErrors )
  79. {
  80. m_nDefIndex = atoi( pKV->GetName() );
  81. SCHEMA_INIT_CHECK( m_nDefIndex != INVALID_WAR_DEF_INDEX, "Missing 'war_id' for war def %s", pKV->GetName() );
  82. m_pszDefName = pKV->GetString( "name" );
  83. SCHEMA_INIT_CHECK( m_nDefIndex != INVALID_WAR_DEF_INDEX, "Missing 'name' for war def %s", pKV->GetName() );
  84. const char *pszTime = pKV->GetString( "start_time", NULL );
  85. SCHEMA_INIT_CHECK( pszTime != NULL, "war definition %s does not have 'start_time'", pKV->GetName() );
  86. m_rtTimeStart = ( pszTime && pszTime[0] )
  87. ? CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pszTime )
  88. : RTime32(0);
  89. pszTime = pKV->GetString( "end_time", NULL );
  90. SCHEMA_INIT_CHECK( pszTime != NULL, "war definition %s does not have 'end_time'", pKV->GetName() );
  91. m_rtTimeEnd = ( pszTime && pszTime[0] )
  92. ? CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pszTime )
  93. : RTime32(0);
  94. KeyValues* pKVSidesBlock = pKV->FindKey( "sides" );
  95. FOR_EACH_TRUE_SUBKEY( pKVSidesBlock, pKVSide )
  96. {
  97. auto& side = m_mapSides[m_mapSides.Insert( atoi( pKVSide->GetName() ) ) ];
  98. side.BInitFromKV( pKV->GetName(), pKVSide, pVecErrors );
  99. }
  100. m_pszLocalizedWarname = pKV->GetString( "localized_name", NULL );
  101. SCHEMA_INIT_CHECK( m_pszLocalizedWarname != NULL, "war definition %s does not have 'localized_name'", pKV->GetName() );
  102. return SCHEMA_INIT_SUCCESS();
  103. }
  104. const CWarDefinition::CWarSideDefinition_t* CWarDefinition::GetSide( war_side_t nSide ) const
  105. {
  106. if ( IsValidSide( nSide ) )
  107. {
  108. SidesMap_t::IndexType_t idx = m_mapSides.Find( nSide );
  109. if ( idx != m_mapSides.InvalidIndex() )
  110. {
  111. return &m_mapSides[ idx ];
  112. }
  113. }
  114. return NULL;
  115. }
  116. bool CWarDefinition::IsActive() const
  117. {
  118. Assert( m_rtTimeEnd != 0 );
  119. Assert( m_rtTimeStart != 0 );
  120. if ( m_rtTimeEnd == 0 || m_rtTimeStart == 0 )
  121. return false;
  122. return CRTime::RTime32TimeCur() > m_rtTimeStart && CRTime::RTime32TimeCur() < m_rtTimeEnd;
  123. }
  124. bool CWarDefinition::IsValidSide( war_side_t nSide ) const
  125. {
  126. FOR_EACH_MAP_FAST( m_mapSides, i )
  127. {
  128. if ( m_mapSides[i].m_nSideIndex == nSide )
  129. return true;
  130. }
  131. return false;
  132. }
  133. bool CWarDefinition::CWarSideDefinition_t::BInitFromKV( const char* pszContainingWarName, KeyValues *pKVSide, CUtlVector<CUtlString> *pVecErrors )
  134. {
  135. m_nSideIndex = (war_side_t)atoi( pKVSide->GetName() );
  136. SCHEMA_INIT_CHECK( m_nSideIndex != INVALID_WAR_SIDE, "war definition %s has invalid side index: %d", pszContainingWarName, m_nSideIndex );
  137. m_pszLocalizedName = pKVSide->GetString( "localized_name", NULL );
  138. SCHEMA_INIT_CHECK( m_pszLocalizedName != NULL, "war definition %s side %s missing side localization name", pszContainingWarName, pKVSide->GetName() );
  139. m_pszLeaderboardName = pKVSide->GetString( "leaderboard_name", NULL );
  140. SCHEMA_INIT_CHECK( m_pszLocalizedName != NULL, "war definition %s side %s missing side leaderboard name", pszContainingWarName, pKVSide->GetName() );
  141. //TODO BRETT: Grab the leaderboard now?
  142. return SCHEMA_INIT_SUCCESS();
  143. }
  144. CWarData::CWarData()
  145. {
  146. Obj().set_account_id( 0 );
  147. Obj().set_war_id( INVALID_WAR_DEF_INDEX );
  148. Obj().set_affiliation( INVALID_WAR_SIDE );
  149. Obj().set_points_scored( 0 );
  150. }
  151. #ifdef GC
  152. IMPLEMENT_CLASS_MEMPOOL( CWarData, 1000, UTLMEMORYPOOL_GROW_SLOW );
  153. // memdbgon must be the last include file in a .cpp file!!!
  154. #include "tier0/memdbgon.h"
  155. CWarData::CWarData( uint32 unAccountID, war_definition_index_t eWarID, war_side_t eSide )
  156. {
  157. Obj().set_account_id( unAccountID );
  158. Obj().set_war_id( eWarID );
  159. Obj().set_affiliation( eSide );
  160. Obj().set_points_scored( 0 );
  161. }
  162. bool CWarData::BYieldingAddInsertToTransaction( GCSDK::CSQLAccess & sqlAccess )
  163. {
  164. CSchWarData schWarData;
  165. WriteToRecord( &schWarData );
  166. return CSchemaSharedObjectHelper::BYieldingAddInsertToTransaction( sqlAccess, &schWarData );
  167. }
  168. bool CWarData::BYieldingAddWriteToTransaction( GCSDK::CSQLAccess & sqlAccess, const CUtlVector< int > &fields )
  169. {
  170. CSchWarData schWarData;
  171. WriteToRecord( &schWarData );
  172. CColumnSet csDatabaseDirty( schWarData.GetPSchema()->GetRecordInfo() );
  173. csDatabaseDirty.MakeEmpty();
  174. FOR_EACH_VEC( fields, nField )
  175. {
  176. switch ( fields[nField] )
  177. {
  178. case CSOWarData::kAccountIdFieldNumber : csDatabaseDirty.BAddColumn( CSchWarData::k_iField_unAccountID ); break;
  179. case CSOWarData::kWarIdFieldNumber : csDatabaseDirty.BAddColumn( CSchWarData::k_iField_unWarID ); break;
  180. case CSOWarData::kAffiliationFieldNumber : csDatabaseDirty.BAddColumn( CSchWarData::k_iField_unAffiliation ); break;
  181. case CSOWarData::kPointsScoredFieldNumber : csDatabaseDirty.BAddColumn( CSchWarData::k_iField_unPointsScored ); break;
  182. default:
  183. Assert( false );
  184. }
  185. }
  186. return CSchemaSharedObjectHelper::BYieldingAddWriteToTransaction( sqlAccess, &schWarData, csDatabaseDirty );
  187. }
  188. bool CWarData::BYieldingAddRemoveToTransaction( GCSDK::CSQLAccess & sqlAccess )
  189. {
  190. CSchWarData schDuelSummary;
  191. WriteToRecord( &schDuelSummary );
  192. return CSchemaSharedObjectHelper::BYieldingAddRemoveToTransaction( sqlAccess, &schDuelSummary );
  193. }
  194. void CWarData::WriteToRecord( CSchWarData *pWarData ) const
  195. {
  196. pWarData->m_unAccountID = Obj().account_id();
  197. pWarData->m_unWarID = Obj().war_id();
  198. pWarData->m_unAffiliation = Obj().affiliation();
  199. pWarData->m_unPointsScored = Obj().points_scored();
  200. }
  201. void CWarData::ReadFromRecord( const CSchWarData & warData )
  202. {
  203. Obj().set_account_id( warData.m_unAccountID );
  204. Obj().set_war_id( warData.m_unWarID );
  205. Obj().set_affiliation( warData.m_unAffiliation );
  206. Obj().set_points_scored( warData.m_unPointsScored );
  207. }
  208. #endif
  209. #if defined( CLIENT_DLL ) || defined( GC )
  210. CTFWarGlobalDataHelper::CTFWarGlobalDataHelper()
  211. : m_bInitialized( false )
  212. , m_mapWarStats( DefLessFunc( WarStatsMap_t::KeyType_t ) )
  213. #ifdef CLIENT_DLL
  214. , m_flLastUpdateRequest( 0.f )
  215. , m_flLastUpdated( 0.f )
  216. #endif
  217. {
  218. #ifdef CLIENT_DLL
  219. Init();
  220. #endif
  221. }
  222. CGCMsgGC_War_GlobalStatsResponse* CTFWarGlobalDataHelper::FindOrCreateWarData( war_definition_index_t nWarDef, bool bCreateIfDoesntExist )
  223. {
  224. const CWarDefinition* pWarDef = GetItemSchema()->GetWarDefinitionByIndex( nWarDef );
  225. if ( !pWarDef )
  226. return NULL;
  227. if ( nWarDef == INVALID_WAR_DEF_INDEX )
  228. return NULL;
  229. CGCMsgGC_War_GlobalStatsResponse* pGlobalData = NULL;
  230. auto waridx = m_mapWarStats.Find( (war_definition_index_t)nWarDef );
  231. if ( waridx == m_mapWarStats.InvalidIndex() && bCreateIfDoesntExist )
  232. {
  233. waridx = m_mapWarStats.Insert( nWarDef );
  234. m_mapWarStats[ waridx ].set_war_id( nWarDef );
  235. }
  236. if ( waridx != m_mapWarStats.InvalidIndex() )
  237. {
  238. pGlobalData = &m_mapWarStats[ waridx ];
  239. }
  240. return pGlobalData;
  241. }
  242. CGCMsgGC_War_GlobalStatsResponse_SideScore* CTFWarGlobalDataHelper::FindOrCreateWarDataSide( war_side_t nWarSide, war_definition_index_t nWarDef, bool bCreateIfDoesntExist )
  243. {
  244. // Make sure it's a valid war
  245. const CWarDefinition* pWarDef = GetItemSchema()->GetWarDefinitionByIndex( nWarDef );
  246. if ( !pWarDef )
  247. return NULL;
  248. // Valid side on a valid awr
  249. if ( !pWarDef->IsValidSide( nWarSide ) )
  250. return NULL;
  251. // Find or create the war stats
  252. CGCMsgGC_War_GlobalStatsResponse* pWarStats = FindOrCreateWarData( nWarDef, bCreateIfDoesntExist );
  253. if ( !pWarStats )
  254. return NULL;
  255. // Find or create the side for the war
  256. CGCMsgGC_War_GlobalStatsResponse_SideScore* pSide = NULL;
  257. for( int i=0; i < pWarStats->side_scores_size(); ++i )
  258. {
  259. if ( pWarStats->side_scores( i ).side() == nWarSide )
  260. {
  261. pSide = pWarStats->mutable_side_scores( i );
  262. break;
  263. }
  264. }
  265. // Didn't already exist. Create and initialize
  266. if ( pSide == NULL && bCreateIfDoesntExist )
  267. {
  268. pSide = pWarStats->add_side_scores();
  269. pSide->set_score( 0 );
  270. pSide->set_side( nWarSide );
  271. }
  272. return pSide;
  273. }
  274. //-----------------------------------------------------------------------------
  275. // Purpose: On the GC, grab all records for the war and tally up the global stats
  276. // On the client, send a request for what the GC has
  277. //-----------------------------------------------------------------------------
  278. void CTFWarGlobalDataHelper::Init()
  279. {
  280. #ifdef GC
  281. TSQLCmdStr sStatement;
  282. const CColumnSet csCountsCount = CSET_FULL( CSchWarData );
  283. BuildSelectStatementText( &sStatement, csCountsCount );
  284. TSQLCmdStr sLoadQuery;
  285. CSQLAccess sqlAccess;
  286. if( !sqlAccess.BYieldingExecute( "CTFWarGlobalDataHelper::Init", sLoadQuery ) )
  287. {
  288. EmitError( SPEW_GC, __FUNCTION__": Failed to run load query!\n" );
  289. return;
  290. }
  291. CUtlVector< CSchWarData > vecRecords;
  292. if ( !sqlAccess.BYieldingReadRecordsWithQuery< CSchWarData >( &vecRecords, sStatement, csCountsCount ) )
  293. {
  294. EmitError( SPEW_GC, __FUNCTION__": Failed to read spy vs engy points!\n" );
  295. return;
  296. }
  297. // Tally up points for each side
  298. FOR_EACH_VEC( vecRecords, i )
  299. {
  300. const CSchWarData& record = vecRecords[i];
  301. AddToSideScore( record.m_unWarID, record.m_unAffiliation, record.m_unPointsScored );
  302. }
  303. m_bInitialized = true;
  304. #else
  305. RequestUpdateGlobalStats();
  306. RequestLeaderboard();
  307. #endif
  308. }
  309. //-----------------------------------------------------------------------------
  310. // Purpose: Set the stats. We're initialized once we do this.
  311. //-----------------------------------------------------------------------------
  312. void CTFWarGlobalDataHelper::SetGlobalStats( const CGCMsgGC_War_GlobalStatsResponse& newData )
  313. {
  314. #ifdef CLIENT_DLL
  315. m_flLastUpdated = Plat_FloatTime();
  316. #endif
  317. m_bInitialized = true;
  318. CGCMsgGC_War_GlobalStatsResponse* pExistingData = FindOrCreateWarData( newData.war_id(), true );
  319. if ( pExistingData )
  320. {
  321. pExistingData->CopyFrom( newData );
  322. }
  323. #ifdef CLIENT_DLL
  324. IGameEvent *event = gameeventmanager->CreateEvent( "global_war_data_updated" );
  325. if ( event )
  326. {
  327. gameeventmanager->FireEventClientSide( event );
  328. }
  329. #endif
  330. }
  331. void CTFWarGlobalDataHelper::AddToSideScore( war_definition_index_t nWar, war_side_t nSide, uint32 nValue )
  332. {
  333. Assert( nWar != INVALID_WAR_DEF_INDEX );
  334. Assert( nSide != INVALID_WAR_SIDE );
  335. CGCMsgGC_War_GlobalStatsResponse_SideScore* pSide = FindOrCreateWarDataSide( nSide, nWar, true );
  336. if ( pSide )
  337. {
  338. pSide->set_score( pSide->score() + nValue );
  339. }
  340. }
  341. //-----------------------------------------------------------------------------
  342. // Purpose: Grab the Spy stats. Request an update if we're on the client and stale
  343. //-----------------------------------------------------------------------------
  344. uint64 CTFWarGlobalDataHelper::GetGlobalSideScore( war_definition_index_t nWar, war_side_t nSide )
  345. {
  346. Assert( nWar != INVALID_WAR_DEF_INDEX );
  347. Assert( nSide != INVALID_WAR_SIDE );
  348. #ifdef CLIENT_DLL
  349. CheckGlobalStatsStaleness();
  350. #endif
  351. CGCMsgGC_War_GlobalStatsResponse_SideScore* pSide = FindOrCreateWarDataSide( nSide, nWar, true );
  352. if ( pSide )
  353. {
  354. return pSide->score();
  355. }
  356. Assert( false );
  357. return 0;
  358. }
  359. #ifdef CLIENT_DLL
  360. //-----------------------------------------------------------------------------
  361. // Purpose: Ask the GC for the global stats.
  362. //-----------------------------------------------------------------------------
  363. void CTFWarGlobalDataHelper::RequestUpdateGlobalStats()
  364. {
  365. // Ask for new war stats
  366. GCSDK::CProtoBufMsg<CGCMsgGC_War_RequestGlobalStats> msg( k_EMsgGC_War_RequestGlobalStats );
  367. msg.Body().set_war_id( PYRO_VS_HEAVY_WAR_DEF_INDEX ); // TODO Brett: Get all the war data
  368. GCClientSystem()->BSendMessage( msg );
  369. m_flLastUpdateRequest = Plat_FloatTime();
  370. }
  371. //-----------------------------------------------------------------------------
  372. // Purpose: Checks if we're "stale". If so, request new stats from the GC.
  373. //-----------------------------------------------------------------------------
  374. void CTFWarGlobalDataHelper::CheckGlobalStatsStaleness()
  375. {
  376. float flTimeSinceLastRequest = Plat_FloatTime() - m_flLastUpdateRequest;
  377. if ( flTimeSinceLastRequest > 30.f )
  378. {
  379. RequestUpdateGlobalStats();
  380. }
  381. }
  382. //-----------------------------------------------------------------------------
  383. // Purpose: Requests the war leaderboard
  384. //-----------------------------------------------------------------------------
  385. void CTFWarGlobalDataHelper::RequestLeaderboard()
  386. {
  387. //TODO BRETT loop all the wars and grab each one
  388. /*if ( steamapicontext && steamapicontext->SteamUserStats() )
  389. {
  390. CSteamID steamID = steamapicontext->SteamUser()->GetSteamID();
  391. GCSDK::CGCClientSharedObjectCache *pSOCache = GCClientSystem()->GetSOCache( steamID );
  392. if ( pSOCache )
  393. {
  394. GCSDK::CGCClientSharedObjectTypeCache *pTypeCache = pSOCache->FindTypeCache( CWarData::k_nTypeID );
  395. if ( pTypeCache )
  396. {
  397. CWarData *pWarData = (CWarData*)pTypeCache->GetObject( 0 );
  398. if ( pWarData )
  399. {
  400. eSide = (EWarSides)pWarData->Obj().affiliation();
  401. }
  402. }
  403. }
  404. if ( eSide != k_EInvalidSide )
  405. {
  406. SteamAPICall_t apicall = steamapicontext->SteamUserStats()->FindLeaderboard( eSide == k_ESpy ? k_pszSpyVsEngyLeaderboard_Spy : k_pszSpyVsEngyLeaderboard_Engy );
  407. m_findLeaderboardCallback.Set( apicall, this, &CTFWarGlobalDataHelper::OnFindLeaderboard );
  408. }
  409. }*/
  410. }
  411. //-----------------------------------------------------------------------------
  412. // Purpose: Requests the war leaderboard
  413. //-----------------------------------------------------------------------------
  414. void CTFWarGlobalDataHelper::OnFindLeaderboard( LeaderboardFindResult_t *pResult, bool bIOFailure )
  415. {
  416. m_findLeaderboardResults = *pResult;
  417. DownloadLeaderboard();
  418. }
  419. void CTFWarGlobalDataHelper::DownloadLeaderboard()
  420. {
  421. if ( m_findLeaderboardResults.m_bLeaderboardFound )
  422. {
  423. // friends
  424. SteamAPICall_t apicall = steamapicontext->SteamUserStats()->DownloadLeaderboardEntries( m_findLeaderboardResults.m_hSteamLeaderboard, k_ELeaderboardDataRequestFriends, 1, 10 );
  425. downloadLeaderboardCallbackFriends.Set( apicall, this, &CTFWarGlobalDataHelper::OnLeaderboardScoresDownloaded_Friends );
  426. // global around user
  427. apicall = steamapicontext->SteamUserStats()->DownloadLeaderboardEntries( m_findLeaderboardResults.m_hSteamLeaderboard, k_ELeaderboardDataRequestGlobal, 1, 10 );
  428. downloadLeaderboardCallbackGlobal.Set( apicall, this, &CTFWarGlobalDataHelper::OnLeaderboardScoresDownloaded_Global );
  429. }
  430. }
  431. static void RetrieveLeaderboardEntries( LeaderboardScoresDownloaded_t &scores, CTFWarGlobalDataHelper::LeaderBoardEntries_t &entries )
  432. {
  433. entries.m_bInitialized = true;
  434. entries.m_vecEntries.PurgeAndDeleteElements();
  435. entries.m_vecEntries.EnsureCapacity( scores.m_cEntryCount );
  436. for ( int i = 0; i < scores.m_cEntryCount; ++i )
  437. {
  438. LeaderboardEntry_t *leaderboardEntry = new LeaderboardEntry_t;
  439. if ( steamapicontext->SteamUserStats()->GetDownloadedLeaderboardEntry( scores.m_hSteamLeaderboardEntries, i, leaderboardEntry, NULL, 0 ) )
  440. {
  441. entries.m_vecEntries.AddToTail( leaderboardEntry );
  442. }
  443. }
  444. }
  445. void CTFWarGlobalDataHelper::OnLeaderboardScoresDownloaded_Global( LeaderboardScoresDownloaded_t *pResult, bool bIOFailure )
  446. {
  447. RetrieveLeaderboardEntries( *pResult, downloadedLeaderboardScoresGlobal );
  448. }
  449. void CTFWarGlobalDataHelper::OnLeaderboardScoresDownloaded_Friends( LeaderboardScoresDownloaded_t *pResult, bool bIOFailure )
  450. {
  451. RetrieveLeaderboardEntries( *pResult, downloadedLeaderboardScoresFriends );
  452. }
  453. class CGC_War_GlobalStatsResponse : public GCSDK::CGCClientJob
  454. {
  455. public:
  456. CGC_War_GlobalStatsResponse( GCSDK::CGCClient *pGCClient ) : GCSDK::CGCClientJob( pGCClient ) { }
  457. virtual bool BYieldingRunJobFromMsg( GCSDK::IMsgNetPacket *pNetPacket )
  458. {
  459. GCSDK::CProtoBufMsg< CGCMsgGC_War_GlobalStatsResponse > msg( pNetPacket );
  460. GetWarData().SetGlobalStats( msg.Body() );
  461. return true;
  462. }
  463. };
  464. GC_REG_JOB( GCSDK::CGCClient, CGC_War_GlobalStatsResponse, "CGC_War_GlobalStatsResponse", k_EMsgGC_War_GlobalStatsResponse, GCSDK::k_EServerTypeGCClient );
  465. #endif
  466. CTFWarGlobalDataHelper& GetWarData()
  467. {
  468. #ifdef GC
  469. return GGCTF()->GetGlobalWarData();
  470. #else
  471. static CTFWarGlobalDataHelper s_WarData;
  472. return s_WarData;
  473. #endif
  474. }
  475. #endif // defined( CLIENT_DLL ) || defined( GC )