Team Fortress 2 Source Code as on 22/4/2020
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

  1. //========= Copyright 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. //#include "steamworks_gamestats.h"
  15. const int GAMESTATS_VERSION = 1;
  16. enum StatSendType_t
  17. {
  18. STATSEND_LEVELSHUTDOWN,
  19. STATSEND_APPSHUTDOWN,
  20. STATSEND_NOTENOUGHPLAYERS,
  21. };
  22. struct StatsBufferRecord_t
  23. {
  24. float m_flFrameRate; // fps
  25. float m_flServerPing; // client ping to server
  26. };
  27. #define STATS_WINDOW_SIZE ( 60 * 10 ) // # of records to hold
  28. #define STATS_RECORD_INTERVAL 1 // # of seconds between data grabs. 2 * 300 = every 10 minutes
  29. class CGameStats;
  30. void UpdatePerfStats( void );
  31. void SetGameStatsHandler( CGameStats *pGameStats );
  32. class CBasePlayer;
  33. class CPropVehicleDriveable;
  34. class CTakeDamageInfo;
  35. #ifdef GAME_DLL
  36. #define GAMESTATS_STANDARD_NOT_SAVED 0xFEEDBEEF
  37. enum GameStatsVersions_t
  38. {
  39. GAMESTATS_FILE_VERSION_OLD = 001,
  40. GAMESTATS_FILE_VERSION_OLD2,
  41. GAMESTATS_FILE_VERSION_OLD3,
  42. GAMESTATS_FILE_VERSION_OLD4,
  43. GAMESTATS_FILE_VERSION_OLD5,
  44. GAMESTATS_FILE_VERSION
  45. };
  46. struct BasicGameStatsRecord_t
  47. {
  48. public:
  49. BasicGameStatsRecord_t() :
  50. m_nCount( 0 ),
  51. m_nSeconds( 0 ),
  52. m_nCommentary( 0 ),
  53. m_nHDR( 0 ),
  54. m_nCaptions( 0 ),
  55. m_bSteam( true ),
  56. m_bCyberCafe( false ),
  57. m_nDeaths( 0 )
  58. {
  59. Q_memset( m_nSkill, 0, sizeof( m_nSkill ) );
  60. }
  61. void Clear();
  62. void SaveToBuffer( CUtlBuffer& buf );
  63. bool ParseFromBuffer( CUtlBuffer& buf, int iBufferStatsVersion );
  64. // Data
  65. public:
  66. int m_nCount;
  67. int m_nSeconds;
  68. int m_nCommentary;
  69. int m_nHDR;
  70. int m_nCaptions;
  71. int m_nSkill[ 3 ];
  72. bool m_bSteam;
  73. bool m_bCyberCafe;
  74. int m_nDeaths;
  75. };
  76. struct BasicGameStats_t
  77. {
  78. public:
  79. BasicGameStats_t() :
  80. m_nSecondsToCompleteGame( 0 ),
  81. m_nHL2ChaptureUnlocked( 0 ),
  82. m_bSteam( true ),
  83. m_bCyberCafe( false ),
  84. m_nDXLevel( 0 )
  85. {
  86. }
  87. void Clear();
  88. void SaveToBuffer( CUtlBuffer& buf );
  89. bool ParseFromBuffer( CUtlBuffer& buf, int iBufferStatsVersion );
  90. BasicGameStatsRecord_t *FindOrAddRecordForMap( char const *mapname );
  91. // Data
  92. public:
  93. int m_nSecondsToCompleteGame; // 0 means they haven't finished playing yet
  94. BasicGameStatsRecord_t m_Summary; // Summary record
  95. CUtlDict< BasicGameStatsRecord_t, unsigned short > m_MapTotals;
  96. bool m_bSteam;
  97. bool m_bCyberCafe;
  98. int m_nHL2ChaptureUnlocked;
  99. int m_nDXLevel;
  100. };
  101. #endif // GAME_DLL
  102. class CBaseGameStats
  103. {
  104. public:
  105. CBaseGameStats();
  106. // override this to declare what format you want to send. New products should use new format.
  107. virtual bool UseOldFormat()
  108. {
  109. #ifdef GAME_DLL
  110. return true; // servers by default send old format for backward compat
  111. #else
  112. return false; // clients never used old format so no backward compat issues, they use new format by default
  113. #endif
  114. }
  115. // Implement this if you support new format gamestats.
  116. // Return true if you added data to KeyValues, false if you have no data to report
  117. virtual bool AddDataForSend( KeyValues *pKV, StatSendType_t sendType ) { return false; }
  118. // These methods used for new format gamestats only and control when data gets sent.
  119. virtual bool ShouldSendDataOnLevelShutdown()
  120. {
  121. // by default, servers send data at every level change and clients don't
  122. #ifdef GAME_DLL
  123. return true;
  124. #else
  125. return false;
  126. #endif
  127. }
  128. virtual bool ShouldSendDataOnAppShutdown()
  129. {
  130. // by default, clients send data at app shutdown and servers don't
  131. #ifdef GAME_DLL
  132. return false;
  133. #else
  134. return true;
  135. #endif
  136. }
  137. virtual void Event_Init( void );
  138. virtual void Event_Shutdown( void );
  139. virtual void Event_MapChange( const char *szOldMapName, const char *szNewMapName );
  140. virtual void Event_LevelInit( void );
  141. virtual void Event_LevelShutdown( float flElapsed );
  142. virtual void Event_SaveGame( void );
  143. virtual void Event_LoadGame( void );
  144. void CollectData( StatSendType_t sendType );
  145. void SendData();
  146. void StatsLog( PRINTF_FORMAT_STRING char const *fmt, ... );
  147. // This is the first call made, so that we can "subclass" the CBaseGameStats based on gamedir as needed (e.g., ep2 vs. episodic)
  148. virtual CBaseGameStats *OnInit( CBaseGameStats *pCurrentGameStats, char const *gamedir ) { return pCurrentGameStats; }
  149. // Frees up data from gamestats and resets it to a clean state.
  150. virtual void Clear( void );
  151. 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
  152. static bool StatTrackingAllowed( void ); //query whether stat tracking is possible and warranted by the user
  153. 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.
  154. virtual bool ShouldTrackStandardStats( void ) { return true; } //exactly what was tracked for EP1 release
  155. //Get mod specific strings used for tracking, defaults should work fine for most cases
  156. virtual const char *GetStatSaveFileName( void );
  157. virtual const char *GetStatUploadRegistryKeyName( void );
  158. const char *GetUserPseudoUniqueID( void );
  159. virtual bool UserPlayedAllTheMaps( void ) { return false; } //be sure to override this to determine user completion time
  160. #ifdef CLIENT_DLL
  161. virtual void Event_AchievementProgress( int achievementID, const char* achievementName ) {}
  162. #endif
  163. #ifdef GAME_DLL
  164. virtual void Event_PlayerKilled( CBasePlayer *pPlayer, const CTakeDamageInfo &info );
  165. virtual void Event_PlayerConnected( CBasePlayer *pBasePlayer );
  166. virtual void Event_PlayerDisconnected( CBasePlayer *pBasePlayer );
  167. virtual void Event_PlayerDamage( CBasePlayer *pBasePlayer, const CTakeDamageInfo &info );
  168. virtual void Event_PlayerKilledOther( CBasePlayer *pAttacker, CBaseEntity *pVictim, const CTakeDamageInfo &info );
  169. virtual void Event_PlayerSuicide( CBasePlayer* pPlayer ) {}
  170. virtual void Event_Credits();
  171. virtual void Event_Commentary();
  172. virtual void Event_CrateSmashed();
  173. virtual void Event_Punted( CBaseEntity *pObject );
  174. virtual void Event_PlayerTraveled( CBasePlayer *pBasePlayer, float distanceInInches, bool bInVehicle, bool bSprinting );
  175. virtual void Event_WeaponFired( CBasePlayer *pShooter, bool bPrimary, char const *pchWeaponName );
  176. virtual void Event_WeaponHit( CBasePlayer *pShooter, bool bPrimary, char const *pchWeaponName, const CTakeDamageInfo &info );
  177. virtual void Event_FlippedVehicle( CBasePlayer *pDriver, CPropVehicleDriveable *pVehicle );
  178. virtual void Event_PreSaveGameLoaded( char const *pSaveName, bool bInGame );
  179. virtual void Event_PlayerEnteredGodMode( CBasePlayer *pBasePlayer );
  180. virtual void Event_PlayerEnteredNoClip( CBasePlayer *pBasePlayer );
  181. virtual void Event_DecrementPlayerEnteredNoClip( CBasePlayer *pBasePlayer );
  182. virtual void Event_IncrementCountedStatistic( const Vector& vecAbsOrigin, char const *pchStatisticName, float flIncrementAmount );
  183. //=============================================================================
  184. // HPE_BEGIN
  185. // [dwenger] Functions necessary for cs-specific stats
  186. //=============================================================================
  187. virtual void Event_WindowShattered( CBasePlayer *pPlayer );
  188. //=============================================================================
  189. // HPE_END
  190. //=============================================================================
  191. //custom data to tack onto existing stats if you're not doing a complete overhaul
  192. virtual void AppendCustomDataToSaveBuffer( CUtlBuffer &SaveBuffer ) { } //custom data you want thrown into the default save and upload path
  193. virtual void LoadCustomDataFromBuffer( CUtlBuffer &LoadBuffer ) { }; //when loading the saved stats file, this will point to where you started saving data to the save buffer
  194. 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.
  195. virtual bool LoadFromFile( void ); //called just before Event_Init()
  196. virtual bool SaveToFileNOW( bool bForceSyncWrite = false ); //saves buffers to their respective files now, returns success or failure
  197. virtual bool UploadStatsFileNOW( void ); //uploads data to the CSER now, returns success or failure
  198. static bool AppendLump( int nMaxLumpCount, CUtlBuffer &SaveBuffer, unsigned short iLump, unsigned short iLumpCount, size_t nSize, void *pData );
  199. static bool GetLumpHeader( int nMaxLumpCount, CUtlBuffer &LoadBuffer, unsigned short &iLump, unsigned short &iLumpCount, bool bPermissive = false );
  200. static void LoadLump( CUtlBuffer &LoadBuffer, unsigned short iLumpCount, size_t nSize, void *pData );
  201. //default save behavior is to save on level shutdown, and game shutdown
  202. virtual bool AutoSave_OnInit( void ) { return false; }
  203. virtual bool AutoSave_OnShutdown( void ) { return true; }
  204. virtual bool AutoSave_OnMapChange( void ) { return false; }
  205. virtual bool AutoSave_OnLevelInit( void ) { return false; }
  206. virtual bool AutoSave_OnLevelShutdown( void ) { return true; }
  207. //default upload behavior is to upload on game shutdown
  208. virtual bool AutoUpload_OnInit( void ) { return false; }
  209. virtual bool AutoUpload_OnShutdown( void ) { return true; }
  210. virtual bool AutoUpload_OnMapChange( void ) { return false; }
  211. virtual bool AutoUpload_OnLevelInit( void ) { return false; }
  212. virtual bool AutoUpload_OnLevelShutdown( void ) { return false; }
  213. // Helper for builtin stuff
  214. void SetSteamStatistic( bool bUsingSteam );
  215. void SetCyberCafeStatistic( bool bIsCyberCafeUser );
  216. void SetHDRStatistic( bool bHDREnabled );
  217. void SetCaptionsStatistic( bool bClosedCaptionsEnabled );
  218. void SetSkillStatistic( int iSkillSetting );
  219. void SetDXLevelStatistic( int iDXLevel );
  220. void SetHL2UnlockedChapterStatistic( void );
  221. #endif // GAMEDLL
  222. public:
  223. #ifdef GAME_DLL
  224. BasicGameStats_t m_BasicStats; //exposed in case you do a complete overhaul and still want to save it
  225. #endif
  226. bool m_bLogging : 1;
  227. bool m_bLoggingToFile : 1;
  228. };
  229. #ifdef GAME_DLL
  230. //-----------------------------------------------------------------------------
  231. // Purpose:
  232. // Input : &SaveBuffer -
  233. // iLump -
  234. // iLumpCount -
  235. //-----------------------------------------------------------------------------
  236. inline bool CBaseGameStats::AppendLump( int nMaxLumpCount, CUtlBuffer &SaveBuffer, unsigned short iLump, unsigned short iLumpCount, size_t nSize, void *pData )
  237. {
  238. // Verify the lump index.
  239. Assert( ( iLump > 0 ) && ( iLump < nMaxLumpCount ) );
  240. if ( !( ( iLump > 0 ) && ( iLump < nMaxLumpCount ) ) )
  241. return false;
  242. // Check to see if we have any elements to save.
  243. if ( iLumpCount <= 0 )
  244. return false;
  245. // Write the lump id and element count.
  246. SaveBuffer.PutUnsignedShort( iLump );
  247. SaveBuffer.PutUnsignedShort( iLumpCount );
  248. size_t nTotalSize = iLumpCount * nSize;
  249. SaveBuffer.Put( pData, nTotalSize );
  250. return true;
  251. }
  252. //-----------------------------------------------------------------------------
  253. // Purpose:
  254. // Input : &LoadBuffer -
  255. // &iLump -
  256. // &iLumpCount -
  257. // Output : Returns true on success, false on failure.
  258. //-----------------------------------------------------------------------------
  259. inline bool CBaseGameStats::GetLumpHeader( int nMaxLumpCount, CUtlBuffer &LoadBuffer, unsigned short &iLump, unsigned short &iLumpCount, bool bPermissive /*= false*/ )
  260. {
  261. // Get the lump id and element count.
  262. iLump = LoadBuffer.GetUnsignedShort();
  263. if ( !LoadBuffer.IsValid() )
  264. {
  265. // check for EOF
  266. return false;
  267. }
  268. iLumpCount = LoadBuffer.GetUnsignedShort();
  269. if ( bPermissive )
  270. return true;
  271. // Verify the lump index.
  272. Assert( ( iLump > 0 ) && ( iLump < nMaxLumpCount ) );
  273. if ( !( ( iLump > 0 ) && ( iLump < nMaxLumpCount ) ) )
  274. {
  275. return false;
  276. }
  277. // Check to see if we have any elements to save.
  278. if ( iLumpCount <= 0 )
  279. return false;
  280. return true;
  281. }
  282. //-----------------------------------------------------------------------------
  283. // Purpose: Loads 1 or more lumps of raw data
  284. // Input : &LoadBuffer - buffer to be read from
  285. // iLumpCount - # of lumps to read
  286. // nSize - size of each lump
  287. // pData - where to store the data
  288. //-----------------------------------------------------------------------------
  289. inline void CBaseGameStats::LoadLump( CUtlBuffer &LoadBuffer, unsigned short iLumpCount, size_t nSize, void *pData )
  290. {
  291. LoadBuffer.Get( pData, iLumpCount * nSize );
  292. }
  293. #endif // GAME_DLL
  294. // Moving the extern out of the GAME_DLL block so that the client can access it
  295. extern CBaseGameStats *gamestats; //starts out pointing at a singleton of the class above, overriding this in any constructor should work for replacing it
  296. //used to drive most of the game stat event handlers as well as track basic stats under the hood of CBaseGameStats
  297. class CBaseGameStats_Driver : public CAutoGameSystemPerFrame
  298. {
  299. public:
  300. CBaseGameStats_Driver( void );
  301. typedef CAutoGameSystemPerFrame BaseClass;
  302. // IGameSystem overloads
  303. virtual bool Init();
  304. virtual void Shutdown();
  305. // Level init, shutdown
  306. virtual void LevelInitPreEntity();
  307. virtual void LevelShutdownPreEntity();
  308. virtual void LevelShutdownPreClearSteamAPIContext();
  309. virtual void LevelShutdown();
  310. // Called during game save
  311. virtual void OnSave();
  312. // Called during game restore, after the local player has connected and entities have been fully restored
  313. virtual void OnRestore();
  314. virtual void FrameUpdatePostEntityThink();
  315. void PossibleMapChange( void );
  316. void CollectData( StatSendType_t sendType );
  317. void SendData();
  318. void ResetData();
  319. bool AddBaseDataForSend( KeyValues *pKV, StatSendType_t sendType );
  320. StatsBufferRecord_t m_StatsBuffer[STATS_WINDOW_SIZE];
  321. bool m_bBufferFull;
  322. int m_nWriteIndex;
  323. float m_flLastRealTime;
  324. float m_flLastSampleTime;
  325. float m_flTotalTimeInLevels;
  326. int m_iNumLevels;
  327. bool m_bDidVoiceChat; // Did the player use voice chat at ALL this map?
  328. template<class T> T AverageStat( T StatsBufferRecord_t::*field ) const
  329. {
  330. T sum = 0;
  331. for( int i = 0; i < STATS_WINDOW_SIZE; i++ )
  332. sum += m_StatsBuffer[i].*field;
  333. return sum / STATS_WINDOW_SIZE;
  334. }
  335. template<class T> T MaxStat( T StatsBufferRecord_t::*field ) const
  336. {
  337. T maxsofar = -16000000;
  338. for( int i = 0; i < STATS_WINDOW_SIZE; i++ )
  339. maxsofar = MAX( maxsofar, m_StatsBuffer[i].*field );
  340. return maxsofar;
  341. }
  342. template<class T> T MinStat( T StatsBufferRecord_t::*field ) const
  343. {
  344. T minsofar = 16000000;
  345. for( int i = 0; i < STATS_WINDOW_SIZE; i++ )
  346. minsofar = MIN( minsofar, m_StatsBuffer[i].*field );
  347. return minsofar;
  348. }
  349. inline void AdvanceIndex( void )
  350. {
  351. m_nWriteIndex++;
  352. if ( m_nWriteIndex == STATS_WINDOW_SIZE )
  353. {
  354. m_nWriteIndex = 0;
  355. m_bBufferFull = true;
  356. }
  357. }
  358. void UpdatePerfStats( void );
  359. CUtlString m_PrevMapName; //used to track "OnMapChange" events
  360. int m_iLoadedVersion;
  361. char m_szLoadedUserID[ 17 ]; // GUID
  362. bool m_bEnabled; //false if incapable of uploading or the user doesn't want to enable stat tracking
  363. bool m_bShuttingDown;
  364. bool m_bInLevel;
  365. bool m_bFirstLevel;
  366. time_t m_tLastUpload;
  367. float m_flLevelStartTime;
  368. bool m_bStationary;
  369. float m_flLastMovementTime;
  370. CUserCmd m_LastUserCmd;
  371. bool m_bGamePaused;
  372. float m_flPauseStartTime;
  373. CGamestatsData *m_pGamestatsData;
  374. };
  375. #endif // GAMESTATS_H