2013-12-03 07:31:46 +04:00
//========= 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