501 lines
17 KiB
C++
501 lines
17 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
//=============================================================================
|
|
|
|
#ifndef GAMESTATS_H
|
|
#define GAMESTATS_H
|
|
#ifdef _WIN32
|
|
#pragma once
|
|
#endif
|
|
|
|
#include "igamesystem.h"
|
|
#include "tier1/utlbuffer.h"
|
|
#include "tier1/utldict.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
|