Counter Strike : Global Offensive Source Code
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.

480 lines
14 KiB

  1. //====== Copyright � 1996-2005, Valve Corporation, All rights reserved. =======
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #ifndef GAMESTATS_H
  7. #define GAMESTATS_H
  8. #ifdef _WIN32
  9. #pragma once
  10. #endif
  11. #include "tier1/utldict.h"
  12. #include "tier1/utlbuffer.h"
  13. #include "igamesystem.h"
  14. const int GAMESTATS_VERSION = 1;
  15. enum StatSendType_t
  16. {
  17. STATSEND_LEVELSHUTDOWN,
  18. STATSEND_APPSHUTDOWN
  19. };
  20. struct StatsBufferRecord_t
  21. {
  22. float m_flFrameRate; // fps
  23. float m_flServerPing; // client ping to server
  24. float m_flMainThreadTimeMS; // time in ms taken by the main thread
  25. float m_flMainThreadWaitTimeMS; // time in ms the main thread is waiting for the render thread (cf EndFrame)
  26. float m_flRenderThreadTimeMS; // time in ms taken by the render thread
  27. float m_flRenderThreadWaitTimeMS; // time in ms waiting for the gpu (time spent in ForceHardwareSync)
  28. bool operator< ( const StatsBufferRecord_t &a) const
  29. {
  30. return m_flFrameRate < a.m_flFrameRate;
  31. }
  32. };
  33. class CFpsHistory
  34. {
  35. public:
  36. enum FpsEnum_t
  37. {
  38. FPS_120,
  39. FPS_90,
  40. FPS_60,
  41. FPS_45,
  42. FPS_30,
  43. FPS_20,
  44. FPS_10,
  45. FPS_LOW,
  46. FPS_BIN_COUNT
  47. };
  48. enum HistoryBufSizeEnum_t { HISTORY_BUF_SIZE = 32 };
  49. struct Rec_t
  50. {
  51. float m_flFps;
  52. float m_flCpuWait;
  53. };
  54. CFpsHistory()
  55. {
  56. V_memset( this, 0, sizeof( *this ) );
  57. }
  58. uint8 Update( float flFps, float flCpuWait );
  59. uint8 operator []( int i ) const
  60. {
  61. return m_History[ ( m_nNext + HISTORY_BUF_SIZE - 1 - i ) % HISTORY_BUF_SIZE ];
  62. }
  63. int m_nNext;
  64. uint8 m_History[ HISTORY_BUF_SIZE ];
  65. };
  66. class CFpsHistogram
  67. {
  68. public:
  69. enum FpsEnum_t
  70. {
  71. FPS_BIN_COUNT = CFpsHistory::FPS_BIN_COUNT
  72. };
  73. CFpsHistogram()
  74. {
  75. Reset();
  76. }
  77. void Reset()
  78. {
  79. V_memset( this, 0, sizeof( *this ) );
  80. }
  81. void Update( uint8 nBin )
  82. {
  83. m_nFps[ nBin & ( FPS_BIN_COUNT - 1 ) ]++;
  84. m_nCpuWaits[ nBin & ( FPS_BIN_COUNT - 1 ) ] += ( nBin >> 7 );
  85. }
  86. int64 Encode()const;
  87. protected:
  88. uint32 m_nFps[ FPS_BIN_COUNT ];
  89. uint32 m_nCpuWaits[ FPS_BIN_COUNT ];
  90. };
  91. class CFpsSelectiveHistogram: public CFpsHistogram
  92. {
  93. public:
  94. CFpsSelectiveHistogram()
  95. {
  96. m_nLookAhead = 0;
  97. m_nLookBack = 0;
  98. }
  99. void Reset()
  100. {
  101. V_memset( this, 0, sizeof( *this ) );
  102. }
  103. void Fire( int nLookAhead, int nLookBack, const CFpsHistory &history )
  104. {
  105. // get the prerecorded stats
  106. if ( m_nLookAhead < nLookAhead )
  107. {
  108. m_nLookAhead = nLookAhead;
  109. }
  110. int nCatchUp = MIN( nLookBack, m_nLookBack );
  111. // exhaust back-stats buffer
  112. for ( int i = 0; i < nCatchUp; ++i )
  113. {
  114. CFpsHistogram::Update( history[ i ] );
  115. }
  116. m_nLookBack = 0;
  117. }
  118. void Update( uint8 nBin )
  119. {
  120. if ( m_nLookAhead > 0 )
  121. {
  122. m_nLookAhead--;
  123. CFpsHistogram::Update( nBin );
  124. }
  125. else
  126. {
  127. ++m_nLookBack;
  128. }
  129. }
  130. int GetInactiveCount() const { return m_nLookBack; }
  131. void ResetInactiveCount() { m_nLookBack = 0; }
  132. protected:
  133. int m_nLookAhead; // this is how many more frames we need to collect statistics (stay active)
  134. int m_nLookBack; // this is how many frames we skipped (did not collect statistics)
  135. };
  136. enum PerfStatsEventEnum_t
  137. {
  138. PERF_STATS_SMOKE,
  139. PERF_STATS_BULLET,
  140. PERF_STATS_PLAYER,
  141. PERF_STATS_PLAYER_SPAWN,
  142. PERF_STATS_EVENT_COUNT
  143. };
  144. #define STATS_WINDOW_SIZE ( 60 * 10 ) // # of records to hold
  145. #define STATS_TRAILING_WINDOW_SIZE ( 30 ) // # of records to hold in trailing window prior to stat recording
  146. #define STATS_RECORD_INTERVAL 1 // # of seconds between data grabs. 2 * 300 = every 10 minutes
  147. class CGameStats;
  148. void UpdatePerfStats( void );
  149. void FirePerfStatsEvent( PerfStatsEventEnum_t nEvent, int nLookAhead = CFpsHistory::HISTORY_BUF_SIZE, int nLookBack = CFpsHistory::HISTORY_BUF_SIZE );
  150. void SetGameStatsHandler( CGameStats *pGameStats );
  151. class CBasePlayer;
  152. class CPropVehicleDriveable;
  153. class CTakeDamageInfo;
  154. #ifdef GAME_DLL
  155. #define GAMESTATS_STANDARD_NOT_SAVED 0xFEEDBEEF
  156. enum GameStatsVersions_t
  157. {
  158. GAMESTATS_FILE_VERSION_OLD = 001,
  159. GAMESTATS_FILE_VERSION_OLD2,
  160. GAMESTATS_FILE_VERSION_OLD3,
  161. GAMESTATS_FILE_VERSION_OLD4,
  162. GAMESTATS_FILE_VERSION_OLD5,
  163. GAMESTATS_FILE_VERSION
  164. };
  165. struct BasicGameStatsRecord_t
  166. {
  167. public:
  168. BasicGameStatsRecord_t() :
  169. m_nCount( 0 ),
  170. m_nSeconds( 0 ),
  171. m_nCommentary( 0 ),
  172. m_nHDR( 0 ),
  173. m_nCaptions( 0 ),
  174. m_bSteam( true ),
  175. m_bCyberCafe( false ),
  176. m_nDeaths( 0 )
  177. {
  178. Q_memset( m_nSkill, 0, sizeof( m_nSkill ) );
  179. }
  180. void Clear();
  181. void SaveToBuffer( CUtlBuffer& buf );
  182. bool ParseFromBuffer( CUtlBuffer& buf, int iBufferStatsVersion );
  183. // Data
  184. public:
  185. int m_nCount;
  186. int m_nSeconds;
  187. int m_nCommentary;
  188. int m_nHDR;
  189. int m_nCaptions;
  190. int m_nSkill[ 3 ];
  191. bool m_bSteam;
  192. bool m_bCyberCafe;
  193. int m_nDeaths;
  194. };
  195. struct BasicGameStats_t
  196. {
  197. public:
  198. BasicGameStats_t() :
  199. m_nSecondsToCompleteGame( 0 ),
  200. m_bSteam( true ),
  201. m_bCyberCafe( false ),
  202. m_nDXLevel( 0 )
  203. {
  204. }
  205. void Clear();
  206. void SaveToBuffer( CUtlBuffer& buf );
  207. bool ParseFromBuffer( CUtlBuffer& buf, int iBufferStatsVersion );
  208. BasicGameStatsRecord_t *FindOrAddRecordForMap( char const *mapname );
  209. // Data
  210. public:
  211. int m_nSecondsToCompleteGame; // 0 means they haven't finished playing yet
  212. BasicGameStatsRecord_t m_Summary; // Summary record
  213. CUtlDict< BasicGameStatsRecord_t, unsigned short > m_MapTotals;
  214. bool m_bSteam;
  215. bool m_bCyberCafe;
  216. int m_nDXLevel;
  217. };
  218. #endif // GAME_DLL
  219. class CBaseGameStats
  220. {
  221. public:
  222. CBaseGameStats();
  223. // override this to declare what format you want to send. New products should use new format.
  224. virtual bool UseOldFormat()
  225. {
  226. #ifdef GAME_DLL
  227. return true; // servers by default send old format for backward compat
  228. #else
  229. return false; // clients never used old format so no backward compat issues, they use new format by default
  230. #endif
  231. }
  232. // Implement this if you support new format gamestats.
  233. // Return true if you added data to KeyValues, false if you have no data to report
  234. virtual bool AddDataForSend( KeyValues *pKV, StatSendType_t sendType ) { return false; }
  235. // These methods used for new format gamestats only and control when data gets sent.
  236. virtual bool ShouldSendDataOnLevelShutdown()
  237. {
  238. // by default, servers send data at every level change and clients don't
  239. #ifdef GAME_DLL
  240. return true;
  241. #else
  242. return false;
  243. #endif
  244. }
  245. virtual bool ShouldSendDataOnAppShutdown()
  246. {
  247. // by default, clients send data at app shutdown and servers don't
  248. #ifdef GAME_DLL
  249. return false;
  250. #else
  251. return true;
  252. #endif
  253. }
  254. virtual void Event_Init( void );
  255. virtual void Event_Shutdown( void );
  256. virtual void Event_MapChange( const char *szOldMapName, const char *szNewMapName );
  257. virtual void Event_LevelInit( void );
  258. virtual void Event_LevelShutdown( float flElapsed );
  259. virtual void Event_SaveGame( void );
  260. virtual void Event_LoadGame( void );
  261. void StatsLog( PRINTF_FORMAT_STRING char const *fmt, ... );
  262. // This is the first call made, so that we can "subclass" the CBaseGameStats based on gamedir as needed (e.g., ep2 vs. episodic)
  263. virtual CBaseGameStats *OnInit( CBaseGameStats *pCurrentGameStats, char const *gamedir ) { return pCurrentGameStats; }
  264. // Frees up data from gamestats and resets it to a clean state.
  265. virtual void Clear( void );
  266. 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
  267. static bool StatTrackingAllowed( void ); //query whether stat tracking is possible and warranted by the user
  268. 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.
  269. virtual bool ShouldTrackStandardStats( void ) { return true; } //exactly what was tracked for EP1 release
  270. //Get mod specific strings used for tracking, defaults should work fine for most cases
  271. virtual const char *GetStatSaveFileName( void );
  272. virtual const char *GetStatUploadRegistryKeyName( void );
  273. const char *GetUserPseudoUniqueID( void );
  274. virtual bool UserPlayedAllTheMaps( void ) { return false; } //be sure to override this to determine user completion time
  275. #ifdef GAME_DLL
  276. virtual void Event_PlayerKilled( CBasePlayer *pPlayer, const CTakeDamageInfo &info );
  277. virtual void Event_PlayerConnected( CBasePlayer *pBasePlayer );
  278. virtual void Event_PlayerDisconnected( CBasePlayer *pBasePlayer );
  279. virtual void Event_PlayerDamage( CBasePlayer *pBasePlayer, const CTakeDamageInfo &info );
  280. virtual void Event_PlayerKilledOther( CBasePlayer *pAttacker, CBaseEntity *pVictim, const CTakeDamageInfo &info );
  281. virtual void Event_Credits();
  282. virtual void Event_Commentary();
  283. virtual void Event_CrateSmashed();
  284. virtual void Event_Punted( CBaseEntity *pObject );
  285. virtual void Event_PlayerTraveled( CBasePlayer *pBasePlayer, float distanceInInches, bool bInVehicle, bool bSprinting );
  286. virtual void Event_WeaponFired( CBasePlayer *pShooter, bool bPrimary, char const *pchWeaponName );
  287. virtual void Event_WeaponHit( CBasePlayer *pShooter, bool bPrimary, char const *pchWeaponName, const CTakeDamageInfo &info );
  288. virtual void Event_FlippedVehicle( CBasePlayer *pDriver, CPropVehicleDriveable *pVehicle );
  289. virtual void Event_PreSaveGameLoaded( char const *pSaveName, bool bInGame );
  290. virtual void Event_PlayerEnteredGodMode( CBasePlayer *pBasePlayer );
  291. virtual void Event_PlayerEnteredNoClip( CBasePlayer *pBasePlayer );
  292. virtual void Event_DecrementPlayerEnteredNoClip( CBasePlayer *pBasePlayer );
  293. virtual void Event_IncrementCountedStatistic( const Vector& vecAbsOrigin, char const *pchStatisticName, float flIncrementAmount );
  294. virtual void Event_WindowShattered( CBasePlayer *pPlayer );
  295. //custom data to tack onto existing stats if you're not doing a complete overhaul
  296. virtual void AppendCustomDataToSaveBuffer( CUtlBuffer &SaveBuffer ) { } //custom data you want thrown into the default save and upload path
  297. virtual void LoadCustomDataFromBuffer( CUtlBuffer &LoadBuffer ) { }; //when loading the saved stats file, this will point to where you started saving data to the save buffer
  298. 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.
  299. virtual bool LoadFromFile( void ); //called just before Event_Init()
  300. virtual bool SaveToFileNOW( bool bForceSyncWrite = false ); //saves buffers to their respective files now, returns success or failure
  301. virtual bool UploadStatsFileNOW( void ); //uploads data to the CSER now, returns success or failure
  302. static bool AppendLump( int nMaxLumpCount, CUtlBuffer &SaveBuffer, unsigned short iLump, unsigned short iLumpCount, size_t nSize, void *pData );
  303. static bool GetLumpHeader( int nMaxLumpCount, CUtlBuffer &LoadBuffer, unsigned short &iLump, unsigned short &iLumpCount, bool bPermissive = false );
  304. static void LoadLump( CUtlBuffer &LoadBuffer, unsigned short iLumpCount, size_t nSize, void *pData );
  305. //default save behavior is to save on level shutdown, and game shutdown
  306. virtual bool AutoSave_OnInit( void ) { return false; }
  307. virtual bool AutoSave_OnShutdown( void ) { return true; }
  308. virtual bool AutoSave_OnMapChange( void ) { return false; }
  309. virtual bool AutoSave_OnLevelInit( void ) { return false; }
  310. virtual bool AutoSave_OnLevelShutdown( void ) { return true; }
  311. //default upload behavior is to upload on game shutdown
  312. virtual bool AutoUpload_OnInit( void ) { return false; }
  313. virtual bool AutoUpload_OnShutdown( void ) { return true; }
  314. virtual bool AutoUpload_OnMapChange( void ) { return false; }
  315. virtual bool AutoUpload_OnLevelInit( void ) { return false; }
  316. virtual bool AutoUpload_OnLevelShutdown( void ) { return false; }
  317. // Helper for builtin stuff
  318. void SetSteamStatistic( bool bUsingSteam );
  319. void SetCyberCafeStatistic( bool bIsCyberCafeUser );
  320. void SetHDRStatistic( bool bHDREnabled );
  321. void SetCaptionsStatistic( bool bClosedCaptionsEnabled );
  322. void SetSkillStatistic( int iSkillSetting );
  323. void SetDXLevelStatistic( int iDXLevel );
  324. #endif // GAMEDLL
  325. public:
  326. #ifdef GAME_DLL
  327. BasicGameStats_t m_BasicStats; //exposed in case you do a complete overhaul and still want to save it
  328. #endif
  329. bool m_bLogging : 1;
  330. bool m_bLoggingToFile : 1;
  331. };
  332. #ifdef GAME_DLL
  333. //-----------------------------------------------------------------------------
  334. // Purpose:
  335. // Input : &SaveBuffer -
  336. // iLump -
  337. // iLumpCount -
  338. //-----------------------------------------------------------------------------
  339. inline bool CBaseGameStats::AppendLump( int nMaxLumpCount, CUtlBuffer &SaveBuffer, unsigned short iLump, unsigned short iLumpCount, size_t nSize, void *pData )
  340. {
  341. // Verify the lump index.
  342. Assert( ( iLump > 0 ) && ( iLump < nMaxLumpCount ) );
  343. if ( !( ( iLump > 0 ) && ( iLump < nMaxLumpCount ) ) )
  344. return false;
  345. // Check to see if we have any elements to save.
  346. if ( iLumpCount <= 0 )
  347. return false;
  348. // Write the lump id and element count.
  349. SaveBuffer.PutUnsignedShort( iLump );
  350. SaveBuffer.PutUnsignedShort( iLumpCount );
  351. size_t nTotalSize = iLumpCount * nSize;
  352. SaveBuffer.Put( pData, nTotalSize );
  353. return true;
  354. }
  355. //-----------------------------------------------------------------------------
  356. // Purpose:
  357. // Input : &LoadBuffer -
  358. // &iLump -
  359. // &iLumpCount -
  360. // Output : Returns true on success, false on failure.
  361. //-----------------------------------------------------------------------------
  362. inline bool CBaseGameStats::GetLumpHeader( int nMaxLumpCount, CUtlBuffer &LoadBuffer, unsigned short &iLump, unsigned short &iLumpCount, bool bPermissive /*= false*/ )
  363. {
  364. // Get the lump id and element count.
  365. iLump = LoadBuffer.GetUnsignedShort();
  366. if ( !LoadBuffer.IsValid() )
  367. {
  368. // check for EOF
  369. return false;
  370. }
  371. iLumpCount = LoadBuffer.GetUnsignedShort();
  372. if ( bPermissive )
  373. return true;
  374. // Verify the lump index.
  375. Assert( ( iLump > 0 ) && ( iLump < nMaxLumpCount ) );
  376. if ( !( ( iLump > 0 ) && ( iLump < nMaxLumpCount ) ) )
  377. {
  378. return false;
  379. }
  380. // Check to see if we have any elements to save.
  381. if ( iLumpCount <= 0 )
  382. return false;
  383. return true;
  384. }
  385. //-----------------------------------------------------------------------------
  386. // Purpose: Loads 1 or more lumps of raw data
  387. // Input : &LoadBuffer - buffer to be read from
  388. // iLumpCount - # of lumps to read
  389. // nSize - size of each lump
  390. // pData - where to store the data
  391. //-----------------------------------------------------------------------------
  392. inline void CBaseGameStats::LoadLump( CUtlBuffer &LoadBuffer, unsigned short iLumpCount, size_t nSize, void *pData )
  393. {
  394. LoadBuffer.Get( pData, iLumpCount * nSize );
  395. }
  396. #endif // GAME_DLL
  397. extern CBaseGameStats *gamestats; //starts out pointing at a singleton of the class above, overriding this in any constructor should work for replacing it
  398. #endif // GAMESTATS_H