diff options
Diffstat (limited to 'game/shared/tf/tf_wardata.cpp')
| -rw-r--r-- | game/shared/tf/tf_wardata.cpp | 559 |
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 ) |