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.
456 lines
15 KiB
456 lines
15 KiB
//========= 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
|
|
|