summaryrefslogtreecommitdiff
path: root/game/shared/tf/tf_wardata.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/shared/tf/tf_wardata.cpp')
-rw-r--r--game/shared/tf/tf_wardata.cpp559
1 files changed, 559 insertions, 0 deletions
diff --git a/game/shared/tf/tf_wardata.cpp b/game/shared/tf/tf_wardata.cpp
new file mode 100644
index 0000000..8af8014
--- /dev/null
+++ b/game/shared/tf/tf_wardata.cpp
@@ -0,0 +1,559 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+
+#include "tf_wardata.h"
+#include "gcsdk/enumutils.h"
+#include "schemainitutils.h"
+#ifdef CLIENT_DLL
+ #include "c_tf_player.h"
+#endif
+#ifdef GAME_DLL
+ #include "tf_player.h"
+#endif
+
+using namespace GCSDK;
+
+//-----------------------------------------------------------------------------
+// Purpose: Get user's War Data by steamID and war ID. Returns NULL if it
+// doesnt exist or if the war is inactive and bLoadEvenIfWarInactive
+// is false. On the GC bLoadSOCacheIfNeeded will load the user's
+// SOCache if needed in order to try and get their war data
+//-----------------------------------------------------------------------------
+CWarData* GetPlayerWarData( const CSteamID& steamID, war_definition_index_t warDefIndex, bool bLoadEvenIfWarInactive
+#ifdef GC_DLL
+ , bool bLoadSOCacheIfNeeded
+#endif
+ )
+{
+ const CWarDefinition* pWarDef = GetItemSchema()->GetWarDefinitionByIndex( warDefIndex );
+ // If the war isn't active and we weren't told to ignore that, then return NULL
+ if ( !pWarDef || ( !bLoadEvenIfWarInactive && !pWarDef->IsActive() ) )
+ return NULL;
+
+#ifdef GC_DLL
+ CGCSharedObjectCache *pSOCache = GGCBase()->FindSOCache( steamID );
+
+ // Load their SO cache if needed
+ if ( pSOCache == NULL && bLoadSOCacheIfNeeded )
+ {
+ pSOCache = GGCBase()->YieldingFindOrLoadSOCache( steamID );
+ }
+#else
+ GCSDK::CGCClientSharedObjectCache *pSOCache = GCClientSystem()->GetSOCache( steamID );
+#endif
+
+ if ( pSOCache )
+ {
+ auto *pTypeCache = pSOCache->FindTypeCache( CWarData::k_nTypeID );
+ if ( pTypeCache )
+ {
+ int nCount = pTypeCache->GetCount();
+ for( int i=0; i < nCount; ++i )
+ {
+ CWarData* pWarData = static_cast< CWarData* >( pTypeCache->GetObject( i ) );
+ if ( pWarData->Obj().war_id() == warDefIndex )
+ return pWarData;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+#ifdef CLIENT_DLL
+CWarData* GetLocalPlayerWarData( war_definition_index_t warDefIndex )
+{
+ if ( !steamapicontext || !steamapicontext->SteamUser() )
+ {
+ return NULL;
+ }
+
+ return GetPlayerWarData( steamapicontext->SteamUser()->GetSteamID(), warDefIndex, true );
+}
+#endif
+
+
+CWarDefinition::CWarDefinition()
+ : m_nDefIndex( INVALID_WAR_DEF_INDEX )
+ , m_pszLocalizedWarname( NULL )
+ , m_pszDefName( NULL )
+ , m_mapSides( DefLessFunc( SidesMap_t::KeyType_t ) )
+ , m_rtTimeEnd( 0 )
+ , m_rtTimeStart( 0 )
+{}
+
+bool CWarDefinition::BInitFromKV( KeyValues *pKV, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_nDefIndex = atoi( pKV->GetName() );
+ SCHEMA_INIT_CHECK( m_nDefIndex != INVALID_WAR_DEF_INDEX, "Missing 'war_id' for war def %s", pKV->GetName() );
+
+ m_pszDefName = pKV->GetString( "name" );
+ SCHEMA_INIT_CHECK( m_nDefIndex != INVALID_WAR_DEF_INDEX, "Missing 'name' for war def %s", pKV->GetName() );
+
+ const char *pszTime = pKV->GetString( "start_time", NULL );
+ SCHEMA_INIT_CHECK( pszTime != NULL, "war definition %s does not have 'start_time'", pKV->GetName() );
+ m_rtTimeStart = ( pszTime && pszTime[0] )
+ ? CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pszTime )
+ : RTime32(0);
+
+ pszTime = pKV->GetString( "end_time", NULL );
+ SCHEMA_INIT_CHECK( pszTime != NULL, "war definition %s does not have 'end_time'", pKV->GetName() );
+ m_rtTimeEnd = ( pszTime && pszTime[0] )
+ ? CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pszTime )
+ : RTime32(0);
+
+ KeyValues* pKVSidesBlock = pKV->FindKey( "sides" );
+ FOR_EACH_TRUE_SUBKEY( pKVSidesBlock, pKVSide )
+ {
+ auto& side = m_mapSides[m_mapSides.Insert( atoi( pKVSide->GetName() ) ) ];
+ side.BInitFromKV( pKV->GetName(), pKVSide, pVecErrors );
+ }
+
+ m_pszLocalizedWarname = pKV->GetString( "localized_name", NULL );
+ SCHEMA_INIT_CHECK( m_pszLocalizedWarname != NULL, "war definition %s does not have 'localized_name'", pKV->GetName() );
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+const CWarDefinition::CWarSideDefinition_t* CWarDefinition::GetSide( war_side_t nSide ) const
+{
+ if ( IsValidSide( nSide ) )
+ {
+ SidesMap_t::IndexType_t idx = m_mapSides.Find( nSide );
+ if ( idx != m_mapSides.InvalidIndex() )
+ {
+ return &m_mapSides[ idx ];
+ }
+ }
+
+ return NULL;
+}
+
+bool CWarDefinition::IsActive() const
+{
+ Assert( m_rtTimeEnd != 0 );
+ Assert( m_rtTimeStart != 0 );
+ if ( m_rtTimeEnd == 0 || m_rtTimeStart == 0 )
+ return false;
+
+ return CRTime::RTime32TimeCur() > m_rtTimeStart && CRTime::RTime32TimeCur() < m_rtTimeEnd;
+}
+
+bool CWarDefinition::IsValidSide( war_side_t nSide ) const
+{
+ FOR_EACH_MAP_FAST( m_mapSides, i )
+ {
+ if ( m_mapSides[i].m_nSideIndex == nSide )
+ return true;
+ }
+
+ return false;
+}
+
+bool CWarDefinition::CWarSideDefinition_t::BInitFromKV( const char* pszContainingWarName, KeyValues *pKVSide, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_nSideIndex = (war_side_t)atoi( pKVSide->GetName() );
+ SCHEMA_INIT_CHECK( m_nSideIndex != INVALID_WAR_SIDE, "war definition %s has invalid side index: %d", pszContainingWarName, m_nSideIndex );
+
+ m_pszLocalizedName = pKVSide->GetString( "localized_name", NULL );
+ SCHEMA_INIT_CHECK( m_pszLocalizedName != NULL, "war definition %s side %s missing side localization name", pszContainingWarName, pKVSide->GetName() );
+
+ m_pszLeaderboardName = pKVSide->GetString( "leaderboard_name", NULL );
+ SCHEMA_INIT_CHECK( m_pszLocalizedName != NULL, "war definition %s side %s missing side leaderboard name", pszContainingWarName, pKVSide->GetName() );
+
+ //TODO BRETT: Grab the leaderboard now?
+ return SCHEMA_INIT_SUCCESS();
+}
+
+CWarData::CWarData()
+{
+ Obj().set_account_id( 0 );
+ Obj().set_war_id( INVALID_WAR_DEF_INDEX );
+ Obj().set_affiliation( INVALID_WAR_SIDE );
+ Obj().set_points_scored( 0 );
+}
+#ifdef GC
+
+IMPLEMENT_CLASS_MEMPOOL( CWarData, 1000, UTLMEMORYPOOL_GROW_SLOW );
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+CWarData::CWarData( uint32 unAccountID, war_definition_index_t eWarID, war_side_t eSide )
+{
+ Obj().set_account_id( unAccountID );
+ Obj().set_war_id( eWarID );
+ Obj().set_affiliation( eSide );
+ Obj().set_points_scored( 0 );
+}
+
+bool CWarData::BYieldingAddInsertToTransaction( GCSDK::CSQLAccess & sqlAccess )
+{
+ CSchWarData schWarData;
+ WriteToRecord( &schWarData );
+ return CSchemaSharedObjectHelper::BYieldingAddInsertToTransaction( sqlAccess, &schWarData );
+}
+
+bool CWarData::BYieldingAddWriteToTransaction( GCSDK::CSQLAccess & sqlAccess, const CUtlVector< int > &fields )
+{
+ CSchWarData schWarData;
+ WriteToRecord( &schWarData );
+ CColumnSet csDatabaseDirty( schWarData.GetPSchema()->GetRecordInfo() );
+ csDatabaseDirty.MakeEmpty();
+ FOR_EACH_VEC( fields, nField )
+ {
+ switch ( fields[nField] )
+ {
+ case CSOWarData::kAccountIdFieldNumber : csDatabaseDirty.BAddColumn( CSchWarData::k_iField_unAccountID ); break;
+ case CSOWarData::kWarIdFieldNumber : csDatabaseDirty.BAddColumn( CSchWarData::k_iField_unWarID ); break;
+ case CSOWarData::kAffiliationFieldNumber : csDatabaseDirty.BAddColumn( CSchWarData::k_iField_unAffiliation ); break;
+ case CSOWarData::kPointsScoredFieldNumber : csDatabaseDirty.BAddColumn( CSchWarData::k_iField_unPointsScored ); break;
+ default:
+ Assert( false );
+ }
+ }
+ return CSchemaSharedObjectHelper::BYieldingAddWriteToTransaction( sqlAccess, &schWarData, csDatabaseDirty );
+}
+
+bool CWarData::BYieldingAddRemoveToTransaction( GCSDK::CSQLAccess & sqlAccess )
+{
+ CSchWarData schDuelSummary;
+ WriteToRecord( &schDuelSummary );
+ return CSchemaSharedObjectHelper::BYieldingAddRemoveToTransaction( sqlAccess, &schDuelSummary );
+}
+
+void CWarData::WriteToRecord( CSchWarData *pWarData ) const
+{
+ pWarData->m_unAccountID = Obj().account_id();
+ pWarData->m_unWarID = Obj().war_id();
+ pWarData->m_unAffiliation = Obj().affiliation();
+ pWarData->m_unPointsScored = Obj().points_scored();
+}
+
+void CWarData::ReadFromRecord( const CSchWarData & warData )
+{
+ Obj().set_account_id( warData.m_unAccountID );
+ Obj().set_war_id( warData.m_unWarID );
+ Obj().set_affiliation( warData.m_unAffiliation );
+ Obj().set_points_scored( warData.m_unPointsScored );
+}
+#endif
+
+#if defined( CLIENT_DLL ) || defined( GC )
+CTFWarGlobalDataHelper::CTFWarGlobalDataHelper()
+ : m_bInitialized( false )
+ , m_mapWarStats( DefLessFunc( WarStatsMap_t::KeyType_t ) )
+#ifdef CLIENT_DLL
+ , m_flLastUpdateRequest( 0.f )
+ , m_flLastUpdated( 0.f )
+#endif
+{
+#ifdef CLIENT_DLL
+ Init();
+#endif
+}
+
+CGCMsgGC_War_GlobalStatsResponse* CTFWarGlobalDataHelper::FindOrCreateWarData( war_definition_index_t nWarDef, bool bCreateIfDoesntExist )
+{
+ const CWarDefinition* pWarDef = GetItemSchema()->GetWarDefinitionByIndex( nWarDef );
+ if ( !pWarDef )
+ return NULL;
+
+ if ( nWarDef == INVALID_WAR_DEF_INDEX )
+ return NULL;
+
+ CGCMsgGC_War_GlobalStatsResponse* pGlobalData = NULL;
+
+ auto waridx = m_mapWarStats.Find( (war_definition_index_t)nWarDef );
+ if ( waridx == m_mapWarStats.InvalidIndex() && bCreateIfDoesntExist )
+ {
+ waridx = m_mapWarStats.Insert( nWarDef );
+ m_mapWarStats[ waridx ].set_war_id( nWarDef );
+ }
+
+ if ( waridx != m_mapWarStats.InvalidIndex() )
+ {
+ pGlobalData = &m_mapWarStats[ waridx ];
+ }
+
+ return pGlobalData;
+}
+
+CGCMsgGC_War_GlobalStatsResponse_SideScore* CTFWarGlobalDataHelper::FindOrCreateWarDataSide( war_side_t nWarSide, war_definition_index_t nWarDef, bool bCreateIfDoesntExist )
+{
+ // Make sure it's a valid war
+ const CWarDefinition* pWarDef = GetItemSchema()->GetWarDefinitionByIndex( nWarDef );
+ if ( !pWarDef )
+ return NULL;
+
+ // Valid side on a valid awr
+ if ( !pWarDef->IsValidSide( nWarSide ) )
+ return NULL;
+
+ // Find or create the war stats
+ CGCMsgGC_War_GlobalStatsResponse* pWarStats = FindOrCreateWarData( nWarDef, bCreateIfDoesntExist );
+ if ( !pWarStats )
+ return NULL;
+
+ // Find or create the side for the war
+ CGCMsgGC_War_GlobalStatsResponse_SideScore* pSide = NULL;
+ for( int i=0; i < pWarStats->side_scores_size(); ++i )
+ {
+ if ( pWarStats->side_scores( i ).side() == nWarSide )
+ {
+ pSide = pWarStats->mutable_side_scores( i );
+ break;
+ }
+ }
+
+ // Didn't already exist. Create and initialize
+ if ( pSide == NULL && bCreateIfDoesntExist )
+ {
+ pSide = pWarStats->add_side_scores();
+ pSide->set_score( 0 );
+ pSide->set_side( nWarSide );
+ }
+
+ return pSide;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: On the GC, grab all records for the war and tally up the global stats
+// On the client, send a request for what the GC has
+//-----------------------------------------------------------------------------
+void CTFWarGlobalDataHelper::Init()
+{
+#ifdef GC
+ TSQLCmdStr sStatement;
+ const CColumnSet csCountsCount = CSET_FULL( CSchWarData );
+ BuildSelectStatementText( &sStatement, csCountsCount );
+ TSQLCmdStr sLoadQuery;
+
+ CSQLAccess sqlAccess;
+ if( !sqlAccess.BYieldingExecute( "CTFWarGlobalDataHelper::Init", sLoadQuery ) )
+ {
+ EmitError( SPEW_GC, __FUNCTION__": Failed to run load query!\n" );
+ return;
+ }
+
+ CUtlVector< CSchWarData > vecRecords;
+ if ( !sqlAccess.BYieldingReadRecordsWithQuery< CSchWarData >( &vecRecords, sStatement, csCountsCount ) )
+ {
+ EmitError( SPEW_GC, __FUNCTION__": Failed to read spy vs engy points!\n" );
+ return;
+ }
+
+ // Tally up points for each side
+ FOR_EACH_VEC( vecRecords, i )
+ {
+ const CSchWarData& record = vecRecords[i];
+
+ AddToSideScore( record.m_unWarID, record.m_unAffiliation, record.m_unPointsScored );
+ }
+
+ m_bInitialized = true;
+#else
+
+ RequestUpdateGlobalStats();
+ RequestLeaderboard();
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the stats. We're initialized once we do this.
+//-----------------------------------------------------------------------------
+void CTFWarGlobalDataHelper::SetGlobalStats( const CGCMsgGC_War_GlobalStatsResponse& newData )
+{
+#ifdef CLIENT_DLL
+ m_flLastUpdated = Plat_FloatTime();
+#endif
+ m_bInitialized = true;
+
+ CGCMsgGC_War_GlobalStatsResponse* pExistingData = FindOrCreateWarData( newData.war_id(), true );
+ if ( pExistingData )
+ {
+ pExistingData->CopyFrom( newData );
+ }
+
+#ifdef CLIENT_DLL
+ IGameEvent *event = gameeventmanager->CreateEvent( "global_war_data_updated" );
+ if ( event )
+ {
+ gameeventmanager->FireEventClientSide( event );
+ }
+#endif
+}
+
+void CTFWarGlobalDataHelper::AddToSideScore( war_definition_index_t nWar, war_side_t nSide, uint32 nValue )
+{
+ Assert( nWar != INVALID_WAR_DEF_INDEX );
+ Assert( nSide != INVALID_WAR_SIDE );
+
+ CGCMsgGC_War_GlobalStatsResponse_SideScore* pSide = FindOrCreateWarDataSide( nSide, nWar, true );
+ if ( pSide )
+ {
+ pSide->set_score( pSide->score() + nValue );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Grab the Spy stats. Request an update if we're on the client and stale
+//-----------------------------------------------------------------------------
+uint64 CTFWarGlobalDataHelper::GetGlobalSideScore( war_definition_index_t nWar, war_side_t nSide )
+{
+ Assert( nWar != INVALID_WAR_DEF_INDEX );
+ Assert( nSide != INVALID_WAR_SIDE );
+
+#ifdef CLIENT_DLL
+ CheckGlobalStatsStaleness();
+#endif
+
+ CGCMsgGC_War_GlobalStatsResponse_SideScore* pSide = FindOrCreateWarDataSide( nSide, nWar, true );
+ if ( pSide )
+ {
+ return pSide->score();
+ }
+
+ Assert( false );
+ return 0;
+}
+
+
+#ifdef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose: Ask the GC for the global stats.
+//-----------------------------------------------------------------------------
+void CTFWarGlobalDataHelper::RequestUpdateGlobalStats()
+{
+ // Ask for new war stats
+ GCSDK::CProtoBufMsg<CGCMsgGC_War_RequestGlobalStats> msg( k_EMsgGC_War_RequestGlobalStats );
+ msg.Body().set_war_id( PYRO_VS_HEAVY_WAR_DEF_INDEX ); // TODO Brett: Get all the war data
+ GCClientSystem()->BSendMessage( msg );
+ m_flLastUpdateRequest = Plat_FloatTime();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Checks if we're "stale". If so, request new stats from the GC.
+//-----------------------------------------------------------------------------
+void CTFWarGlobalDataHelper::CheckGlobalStatsStaleness()
+{
+ float flTimeSinceLastRequest = Plat_FloatTime() - m_flLastUpdateRequest;
+ if ( flTimeSinceLastRequest > 30.f )
+ {
+ RequestUpdateGlobalStats();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Requests the war leaderboard
+//-----------------------------------------------------------------------------
+void CTFWarGlobalDataHelper::RequestLeaderboard()
+{
+ //TODO BRETT loop all the wars and grab each one
+
+ /*if ( steamapicontext && steamapicontext->SteamUserStats() )
+ {
+ CSteamID steamID = steamapicontext->SteamUser()->GetSteamID();
+ GCSDK::CGCClientSharedObjectCache *pSOCache = GCClientSystem()->GetSOCache( steamID );
+ if ( pSOCache )
+ {
+ GCSDK::CGCClientSharedObjectTypeCache *pTypeCache = pSOCache->FindTypeCache( CWarData::k_nTypeID );
+ if ( pTypeCache )
+ {
+ CWarData *pWarData = (CWarData*)pTypeCache->GetObject( 0 );
+ if ( pWarData )
+ {
+ eSide = (EWarSides)pWarData->Obj().affiliation();
+ }
+ }
+ }
+
+ if ( eSide != k_EInvalidSide )
+ {
+ SteamAPICall_t apicall = steamapicontext->SteamUserStats()->FindLeaderboard( eSide == k_ESpy ? k_pszSpyVsEngyLeaderboard_Spy : k_pszSpyVsEngyLeaderboard_Engy );
+ m_findLeaderboardCallback.Set( apicall, this, &CTFWarGlobalDataHelper::OnFindLeaderboard );
+ }
+ }*/
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Requests the war leaderboard
+//-----------------------------------------------------------------------------
+void CTFWarGlobalDataHelper::OnFindLeaderboard( LeaderboardFindResult_t *pResult, bool bIOFailure )
+{
+ m_findLeaderboardResults = *pResult;
+ DownloadLeaderboard();
+}
+
+void CTFWarGlobalDataHelper::DownloadLeaderboard()
+{
+ if ( m_findLeaderboardResults.m_bLeaderboardFound )
+ {
+ // friends
+ SteamAPICall_t apicall = steamapicontext->SteamUserStats()->DownloadLeaderboardEntries( m_findLeaderboardResults.m_hSteamLeaderboard, k_ELeaderboardDataRequestFriends, 1, 10 );
+ downloadLeaderboardCallbackFriends.Set( apicall, this, &CTFWarGlobalDataHelper::OnLeaderboardScoresDownloaded_Friends );
+ // global around user
+ apicall = steamapicontext->SteamUserStats()->DownloadLeaderboardEntries( m_findLeaderboardResults.m_hSteamLeaderboard, k_ELeaderboardDataRequestGlobal, 1, 10 );
+ downloadLeaderboardCallbackGlobal.Set( apicall, this, &CTFWarGlobalDataHelper::OnLeaderboardScoresDownloaded_Global );
+ }
+}
+
+static void RetrieveLeaderboardEntries( LeaderboardScoresDownloaded_t &scores, CTFWarGlobalDataHelper::LeaderBoardEntries_t &entries )
+{
+ entries.m_bInitialized = true;
+ entries.m_vecEntries.PurgeAndDeleteElements();
+ entries.m_vecEntries.EnsureCapacity( scores.m_cEntryCount );
+ for ( int i = 0; i < scores.m_cEntryCount; ++i )
+ {
+ LeaderboardEntry_t *leaderboardEntry = new LeaderboardEntry_t;
+ if ( steamapicontext->SteamUserStats()->GetDownloadedLeaderboardEntry( scores.m_hSteamLeaderboardEntries, i, leaderboardEntry, NULL, 0 ) )
+ {
+ entries.m_vecEntries.AddToTail( leaderboardEntry );
+ }
+ }
+}
+
+void CTFWarGlobalDataHelper::OnLeaderboardScoresDownloaded_Global( LeaderboardScoresDownloaded_t *pResult, bool bIOFailure )
+{
+ RetrieveLeaderboardEntries( *pResult, downloadedLeaderboardScoresGlobal );
+}
+
+void CTFWarGlobalDataHelper::OnLeaderboardScoresDownloaded_Friends( LeaderboardScoresDownloaded_t *pResult, bool bIOFailure )
+{
+ RetrieveLeaderboardEntries( *pResult, downloadedLeaderboardScoresFriends );
+}
+
+class CGC_War_GlobalStatsResponse : public GCSDK::CGCClientJob
+{
+public:
+ CGC_War_GlobalStatsResponse( GCSDK::CGCClient *pGCClient ) : GCSDK::CGCClientJob( pGCClient ) { }
+
+ virtual bool BYieldingRunJobFromMsg( GCSDK::IMsgNetPacket *pNetPacket )
+ {
+ GCSDK::CProtoBufMsg< CGCMsgGC_War_GlobalStatsResponse > msg( pNetPacket );
+
+ GetWarData().SetGlobalStats( msg.Body() );
+
+ return true;
+ }
+};
+GC_REG_JOB( GCSDK::CGCClient, CGC_War_GlobalStatsResponse, "CGC_War_GlobalStatsResponse", k_EMsgGC_War_GlobalStatsResponse, GCSDK::k_EServerTypeGCClient );
+#endif
+
+CTFWarGlobalDataHelper& GetWarData()
+{
+#ifdef GC
+ return GGCTF()->GetGlobalWarData();
+#else
+ static CTFWarGlobalDataHelper s_WarData;
+ return s_WarData;
+#endif
+}
+
+#endif // defined( CLIENT_DLL ) || defined( GC )