diff options
Diffstat (limited to 'game/shared/gamestats.h')
| -rw-r--r-- | game/shared/gamestats.h | 456 |
1 files changed, 456 insertions, 0 deletions
diff --git a/game/shared/gamestats.h b/game/shared/gamestats.h new file mode 100644 index 0000000..fae1e41 --- /dev/null +++ b/game/shared/gamestats.h @@ -0,0 +1,456 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#ifndef GAMESTATS_H +#define GAMESTATS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utldict.h" +#include "tier1/utlbuffer.h" +#include "igamesystem.h" +//#include "steamworks_gamestats.h" + +const int GAMESTATS_VERSION = 1; + +enum StatSendType_t +{ + STATSEND_LEVELSHUTDOWN, + STATSEND_APPSHUTDOWN, + STATSEND_NOTENOUGHPLAYERS, +}; + +struct StatsBufferRecord_t +{ + float m_flFrameRate; // fps + float m_flServerPing; // client ping to server + +}; + +#define STATS_WINDOW_SIZE ( 60 * 10 ) // # of records to hold +#define STATS_RECORD_INTERVAL 1 // # of seconds between data grabs. 2 * 300 = every 10 minutes + +class CGameStats; + +void UpdatePerfStats( void ); +void SetGameStatsHandler( CGameStats *pGameStats ); + +class CBasePlayer; +class CPropVehicleDriveable; +class CTakeDamageInfo; + +#ifdef GAME_DLL + +#define GAMESTATS_STANDARD_NOT_SAVED 0xFEEDBEEF + +enum GameStatsVersions_t +{ + GAMESTATS_FILE_VERSION_OLD = 001, + GAMESTATS_FILE_VERSION_OLD2, + GAMESTATS_FILE_VERSION_OLD3, + GAMESTATS_FILE_VERSION_OLD4, + GAMESTATS_FILE_VERSION_OLD5, + GAMESTATS_FILE_VERSION +}; + +struct BasicGameStatsRecord_t +{ +public: + BasicGameStatsRecord_t() : + m_nCount( 0 ), + m_nSeconds( 0 ), + m_nCommentary( 0 ), + m_nHDR( 0 ), + m_nCaptions( 0 ), + m_bSteam( true ), + m_bCyberCafe( false ), + m_nDeaths( 0 ) + { + Q_memset( m_nSkill, 0, sizeof( m_nSkill ) ); + } + + void Clear(); + + void SaveToBuffer( CUtlBuffer& buf ); + bool ParseFromBuffer( CUtlBuffer& buf, int iBufferStatsVersion ); + + // Data +public: + int m_nCount; + int m_nSeconds; + + int m_nCommentary; + int m_nHDR; + int m_nCaptions; + int m_nSkill[ 3 ]; + bool m_bSteam; + bool m_bCyberCafe; + int m_nDeaths; +}; + +struct BasicGameStats_t +{ +public: + BasicGameStats_t() : + m_nSecondsToCompleteGame( 0 ), + m_nHL2ChaptureUnlocked( 0 ), + m_bSteam( true ), + m_bCyberCafe( false ), + m_nDXLevel( 0 ) + { + } + + void Clear(); + + void SaveToBuffer( CUtlBuffer& buf ); + bool ParseFromBuffer( CUtlBuffer& buf, int iBufferStatsVersion ); + + BasicGameStatsRecord_t *FindOrAddRecordForMap( char const *mapname ); + + // Data +public: + int m_nSecondsToCompleteGame; // 0 means they haven't finished playing yet + + BasicGameStatsRecord_t m_Summary; // Summary record + CUtlDict< BasicGameStatsRecord_t, unsigned short > m_MapTotals; + bool m_bSteam; + bool m_bCyberCafe; + int m_nHL2ChaptureUnlocked; + int m_nDXLevel; +}; +#endif // GAME_DLL + +class CBaseGameStats +{ +public: + CBaseGameStats(); + + // override this to declare what format you want to send. New products should use new format. + virtual bool UseOldFormat() + { +#ifdef GAME_DLL + return true; // servers by default send old format for backward compat +#else + return false; // clients never used old format so no backward compat issues, they use new format by default +#endif + } + + // Implement this if you support new format gamestats. + // Return true if you added data to KeyValues, false if you have no data to report + virtual bool AddDataForSend( KeyValues *pKV, StatSendType_t sendType ) { return false; } + + // These methods used for new format gamestats only and control when data gets sent. + virtual bool ShouldSendDataOnLevelShutdown() + { + // by default, servers send data at every level change and clients don't +#ifdef GAME_DLL + return true; +#else + return false; +#endif + } + virtual bool ShouldSendDataOnAppShutdown() + { + // by default, clients send data at app shutdown and servers don't +#ifdef GAME_DLL + return false; +#else + return true; +#endif + } + + virtual void Event_Init( void ); + virtual void Event_Shutdown( void ); + virtual void Event_MapChange( const char *szOldMapName, const char *szNewMapName ); + virtual void Event_LevelInit( void ); + virtual void Event_LevelShutdown( float flElapsed ); + virtual void Event_SaveGame( void ); + virtual void Event_LoadGame( void ); + + void CollectData( StatSendType_t sendType ); + void SendData(); + + void StatsLog( PRINTF_FORMAT_STRING char const *fmt, ... ); + + // This is the first call made, so that we can "subclass" the CBaseGameStats based on gamedir as needed (e.g., ep2 vs. episodic) + virtual CBaseGameStats *OnInit( CBaseGameStats *pCurrentGameStats, char const *gamedir ) { return pCurrentGameStats; } + + // Frees up data from gamestats and resets it to a clean state. + virtual void Clear( void ); + + virtual bool StatTrackingEnabledForMod( void ) { return false; } //Override this to turn on the system. Stat tracking is disabled by default and will always be disabled at the user's request + static bool StatTrackingAllowed( void ); //query whether stat tracking is possible and warranted by the user + virtual bool HaveValidData( void ) { return true; } // whether we currently have an interesting enough data set to upload. Called at upload time; if false, data is not uploaded. + + virtual bool ShouldTrackStandardStats( void ) { return true; } //exactly what was tracked for EP1 release + + //Get mod specific strings used for tracking, defaults should work fine for most cases + virtual const char *GetStatSaveFileName( void ); + virtual const char *GetStatUploadRegistryKeyName( void ); + const char *GetUserPseudoUniqueID( void ); + + virtual bool UserPlayedAllTheMaps( void ) { return false; } //be sure to override this to determine user completion time + +#ifdef CLIENT_DLL + virtual void Event_AchievementProgress( int achievementID, const char* achievementName ) {} +#endif + +#ifdef GAME_DLL + virtual void Event_PlayerKilled( CBasePlayer *pPlayer, const CTakeDamageInfo &info ); + virtual void Event_PlayerConnected( CBasePlayer *pBasePlayer ); + virtual void Event_PlayerDisconnected( CBasePlayer *pBasePlayer ); + virtual void Event_PlayerDamage( CBasePlayer *pBasePlayer, const CTakeDamageInfo &info ); + virtual void Event_PlayerKilledOther( CBasePlayer *pAttacker, CBaseEntity *pVictim, const CTakeDamageInfo &info ); + virtual void Event_PlayerSuicide( CBasePlayer* pPlayer ) {} + virtual void Event_Credits(); + virtual void Event_Commentary(); + virtual void Event_CrateSmashed(); + virtual void Event_Punted( CBaseEntity *pObject ); + virtual void Event_PlayerTraveled( CBasePlayer *pBasePlayer, float distanceInInches, bool bInVehicle, bool bSprinting ); + virtual void Event_WeaponFired( CBasePlayer *pShooter, bool bPrimary, char const *pchWeaponName ); + virtual void Event_WeaponHit( CBasePlayer *pShooter, bool bPrimary, char const *pchWeaponName, const CTakeDamageInfo &info ); + virtual void Event_FlippedVehicle( CBasePlayer *pDriver, CPropVehicleDriveable *pVehicle ); + virtual void Event_PreSaveGameLoaded( char const *pSaveName, bool bInGame ); + virtual void Event_PlayerEnteredGodMode( CBasePlayer *pBasePlayer ); + virtual void Event_PlayerEnteredNoClip( CBasePlayer *pBasePlayer ); + virtual void Event_DecrementPlayerEnteredNoClip( CBasePlayer *pBasePlayer ); + virtual void Event_IncrementCountedStatistic( const Vector& vecAbsOrigin, char const *pchStatisticName, float flIncrementAmount ); + + //============================================================================= + // HPE_BEGIN + // [dwenger] Functions necessary for cs-specific stats + //============================================================================= + virtual void Event_WindowShattered( CBasePlayer *pPlayer ); + //============================================================================= + // HPE_END + //============================================================================= + + //custom data to tack onto existing stats if you're not doing a complete overhaul + virtual void AppendCustomDataToSaveBuffer( CUtlBuffer &SaveBuffer ) { } //custom data you want thrown into the default save and upload path + virtual void LoadCustomDataFromBuffer( CUtlBuffer &LoadBuffer ) { }; //when loading the saved stats file, this will point to where you started saving data to the save buffer + + virtual void LoadingEvent_PlayerIDDifferentThanLoadedStats( void ); //Only called if you use the base SaveToFileNOW() and LoadFromFile() functions. Used in case you want to keep/invalidate data that was just loaded. + + virtual bool LoadFromFile( void ); //called just before Event_Init() + virtual bool SaveToFileNOW( bool bForceSyncWrite = false ); //saves buffers to their respective files now, returns success or failure + virtual bool UploadStatsFileNOW( void ); //uploads data to the CSER now, returns success or failure + + static bool AppendLump( int nMaxLumpCount, CUtlBuffer &SaveBuffer, unsigned short iLump, unsigned short iLumpCount, size_t nSize, void *pData ); + static bool GetLumpHeader( int nMaxLumpCount, CUtlBuffer &LoadBuffer, unsigned short &iLump, unsigned short &iLumpCount, bool bPermissive = false ); + static void LoadLump( CUtlBuffer &LoadBuffer, unsigned short iLumpCount, size_t nSize, void *pData ); + + //default save behavior is to save on level shutdown, and game shutdown + virtual bool AutoSave_OnInit( void ) { return false; } + virtual bool AutoSave_OnShutdown( void ) { return true; } + virtual bool AutoSave_OnMapChange( void ) { return false; } + virtual bool AutoSave_OnLevelInit( void ) { return false; } + virtual bool AutoSave_OnLevelShutdown( void ) { return true; } + + //default upload behavior is to upload on game shutdown + virtual bool AutoUpload_OnInit( void ) { return false; } + virtual bool AutoUpload_OnShutdown( void ) { return true; } + virtual bool AutoUpload_OnMapChange( void ) { return false; } + virtual bool AutoUpload_OnLevelInit( void ) { return false; } + virtual bool AutoUpload_OnLevelShutdown( void ) { return false; } + + // Helper for builtin stuff + void SetSteamStatistic( bool bUsingSteam ); + void SetCyberCafeStatistic( bool bIsCyberCafeUser ); + void SetHDRStatistic( bool bHDREnabled ); + void SetCaptionsStatistic( bool bClosedCaptionsEnabled ); + void SetSkillStatistic( int iSkillSetting ); + void SetDXLevelStatistic( int iDXLevel ); + void SetHL2UnlockedChapterStatistic( void ); +#endif // GAMEDLL +public: +#ifdef GAME_DLL + BasicGameStats_t m_BasicStats; //exposed in case you do a complete overhaul and still want to save it +#endif + bool m_bLogging : 1; + bool m_bLoggingToFile : 1; +}; + +#ifdef GAME_DLL + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &SaveBuffer - +// iLump - +// iLumpCount - +//----------------------------------------------------------------------------- +inline bool CBaseGameStats::AppendLump( int nMaxLumpCount, CUtlBuffer &SaveBuffer, unsigned short iLump, unsigned short iLumpCount, size_t nSize, void *pData ) +{ + // Verify the lump index. + Assert( ( iLump > 0 ) && ( iLump < nMaxLumpCount ) ); + + if ( !( ( iLump > 0 ) && ( iLump < nMaxLumpCount ) ) ) + return false; + + // Check to see if we have any elements to save. + if ( iLumpCount <= 0 ) + return false; + + // Write the lump id and element count. + SaveBuffer.PutUnsignedShort( iLump ); + SaveBuffer.PutUnsignedShort( iLumpCount ); + + size_t nTotalSize = iLumpCount * nSize; + SaveBuffer.Put( pData, nTotalSize ); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &LoadBuffer - +// &iLump - +// &iLumpCount - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +inline bool CBaseGameStats::GetLumpHeader( int nMaxLumpCount, CUtlBuffer &LoadBuffer, unsigned short &iLump, unsigned short &iLumpCount, bool bPermissive /*= false*/ ) +{ + // Get the lump id and element count. + iLump = LoadBuffer.GetUnsignedShort(); + if ( !LoadBuffer.IsValid() ) + { + // check for EOF + return false; + } + iLumpCount = LoadBuffer.GetUnsignedShort(); + + if ( bPermissive ) + return true; + + // Verify the lump index. + Assert( ( iLump > 0 ) && ( iLump < nMaxLumpCount ) ); + if ( !( ( iLump > 0 ) && ( iLump < nMaxLumpCount ) ) ) + { + return false; + } + + // Check to see if we have any elements to save. + if ( iLumpCount <= 0 ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Loads 1 or more lumps of raw data +// Input : &LoadBuffer - buffer to be read from +// iLumpCount - # of lumps to read +// nSize - size of each lump +// pData - where to store the data +//----------------------------------------------------------------------------- +inline void CBaseGameStats::LoadLump( CUtlBuffer &LoadBuffer, unsigned short iLumpCount, size_t nSize, void *pData ) +{ + LoadBuffer.Get( pData, iLumpCount * nSize ); +} + +#endif // GAME_DLL + +// Moving the extern out of the GAME_DLL block so that the client can access it +extern CBaseGameStats *gamestats; //starts out pointing at a singleton of the class above, overriding this in any constructor should work for replacing it + +//used to drive most of the game stat event handlers as well as track basic stats under the hood of CBaseGameStats +class CBaseGameStats_Driver : public CAutoGameSystemPerFrame +{ +public: + CBaseGameStats_Driver( void ); + + typedef CAutoGameSystemPerFrame BaseClass; + + // IGameSystem overloads + virtual bool Init(); + virtual void Shutdown(); + + // Level init, shutdown + virtual void LevelInitPreEntity(); + virtual void LevelShutdownPreEntity(); + virtual void LevelShutdownPreClearSteamAPIContext(); + virtual void LevelShutdown(); + // Called during game save + virtual void OnSave(); + // Called during game restore, after the local player has connected and entities have been fully restored + virtual void OnRestore(); + + virtual void FrameUpdatePostEntityThink(); + + void PossibleMapChange( void ); + + void CollectData( StatSendType_t sendType ); + void SendData(); + void ResetData(); + bool AddBaseDataForSend( KeyValues *pKV, StatSendType_t sendType ); + + StatsBufferRecord_t m_StatsBuffer[STATS_WINDOW_SIZE]; + bool m_bBufferFull; + int m_nWriteIndex; + float m_flLastRealTime; + float m_flLastSampleTime; + float m_flTotalTimeInLevels; + int m_iNumLevels; + bool m_bDidVoiceChat; // Did the player use voice chat at ALL this map? + + template<class T> T AverageStat( T StatsBufferRecord_t::*field ) const + { + T sum = 0; + for( int i = 0; i < STATS_WINDOW_SIZE; i++ ) + sum += m_StatsBuffer[i].*field; + return sum / STATS_WINDOW_SIZE; + } + + template<class T> T MaxStat( T StatsBufferRecord_t::*field ) const + { + T maxsofar = -16000000; + for( int i = 0; i < STATS_WINDOW_SIZE; i++ ) + maxsofar = MAX( maxsofar, m_StatsBuffer[i].*field ); + return maxsofar; + } + + template<class T> T MinStat( T StatsBufferRecord_t::*field ) const + { + T minsofar = 16000000; + for( int i = 0; i < STATS_WINDOW_SIZE; i++ ) + minsofar = MIN( minsofar, m_StatsBuffer[i].*field ); + return minsofar; + } + + inline void AdvanceIndex( void ) + { + m_nWriteIndex++; + if ( m_nWriteIndex == STATS_WINDOW_SIZE ) + { + m_nWriteIndex = 0; + m_bBufferFull = true; + } + } + + void UpdatePerfStats( void ); + + CUtlString m_PrevMapName; //used to track "OnMapChange" events + int m_iLoadedVersion; + char m_szLoadedUserID[ 17 ]; // GUID + + bool m_bEnabled; //false if incapable of uploading or the user doesn't want to enable stat tracking + bool m_bShuttingDown; + bool m_bInLevel; + bool m_bFirstLevel; + time_t m_tLastUpload; + + float m_flLevelStartTime; + + bool m_bStationary; + float m_flLastMovementTime; + CUserCmd m_LastUserCmd; + bool m_bGamePaused; + float m_flPauseStartTime; + + CGamestatsData *m_pGamestatsData; +}; + +#endif // GAMESTATS_H |