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.

509 lines
16 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: The CS game stats header
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #ifndef CS_GAMESTATS_H
  8. #define CS_GAMESTATS_H
  9. #ifdef _WIN32
  10. #pragma once
  11. #endif
  12. #include "cs_blackmarket.h"
  13. #include "GameStats.h"
  14. #include "cs_gamestats_shared.h"
  15. #include "GameEventListener.h"
  16. #include "weapon_csbase.h"
  17. #include "steamworks_gamestats_server.h"
  18. // forward declares
  19. class CBreakableProp;
  20. const float cDisseminationTimeHigh = 0.25f; // Time interval for high priority stats sent to the player
  21. const float cDisseminationTimeLow = 2.5f; // Time interval for medium priority stats sent to the player
  22. #define BULLET_SUB_GROUP_MASK 0xF0000000
  23. #define BULLET_ID_GROUP_MASK 0x000FFFFF
  24. #define BULLET_RECOIL_MASK 0x0FF00000
  25. #define RECOIL_BIT_SHIFT(val) (val<<20)
  26. #define SUB_BULLET_BIT_SHIFT(val) (val << 28 )
  27. //Helper enum table and conversion function to simplify bomb data recording. Update whenever new bomb-related events become relevant
  28. enum CSBombEventName
  29. {
  30. BOMB_EVENT_NAME_NONE = 0, // 0 is an unknown event
  31. BOMB_EVENT_NAME_FIRST,
  32. BOMB_EVENT_NAME_PLANTED = BOMB_EVENT_NAME_FIRST,
  33. BOMB_EVENT_NAME_DEFUSED,
  34. BOMB_EVENT_NAME_MAX = BOMB_EVENT_NAME_DEFUSED, // number of BOMB_EVENT_NAMES
  35. };
  36. CSBombEventName BombEventNameFromString( const char* pEventName );
  37. int GetCSLevelIndex( const char *pLevelName );
  38. #if !defined( NO_STEAM )
  39. uint32 GetPlayerID( CCSPlayer *pPlayer );
  40. #endif
  41. typedef struct
  42. {
  43. char szGameName[8];
  44. byte iVersion;
  45. char szMapName[32];
  46. char ipAddr[4];
  47. short port;
  48. int serverid;
  49. } gamestats_header_t;
  50. typedef struct
  51. {
  52. gamestats_header_t header;
  53. short iMinutesPlayed;
  54. short iTerroristVictories[CS_NUM_LEVELS];
  55. short iCounterTVictories[CS_NUM_LEVELS];
  56. short iBlackMarketPurchases[WEAPON_MAX];
  57. short iAutoBuyPurchases;
  58. short iReBuyPurchases;
  59. short iAutoBuyM4A1Purchases;
  60. short iAutoBuyAK47Purchases;
  61. short iAutoBuyFamasPurchases;
  62. short iAutoBuyGalilPurchases;
  63. short iAutoBuyGalilARPurchases;
  64. short iAutoBuyVestHelmPurchases;
  65. short iAutoBuyVestPurchases;
  66. } cs_gamestats_t;
  67. struct WeaponStats
  68. {
  69. int shots;
  70. int hits;
  71. int kills;
  72. int damage;
  73. };
  74. static byte PackVelocityComponent( float f )
  75. {
  76. // Gets velocity components and sticks fits them into a byte by scaling them
  77. // and clamping them
  78. f = f / 2.0f;
  79. if ( f > 127 ) f = 127;
  80. if ( f < -128 ) f = -128;
  81. return (byte) f;
  82. }
  83. static byte PackMovementComponent( bool bDucking, bool bInAir)
  84. {
  85. return (((byte)bDucking) << 1) | (byte)bInAir;
  86. }
  87. static uint32 PackMovementStatInternal( Vector vVelocity, bool bDucking, bool bInAir )
  88. {
  89. return ( (uint32)PackVelocityComponent( vVelocity.x ) << 24 ) |
  90. ( (uint32)PackVelocityComponent( vVelocity.y ) << 16 ) |
  91. ( (uint32)PackVelocityComponent( vVelocity.z ) << 8 ) |
  92. ( (uint32)PackMovementComponent( bDucking, bInAir ) );
  93. }
  94. static uint32 PackPlayerMovementStat( CCSPlayer *pPlayer )
  95. {
  96. Vector vAttackerVelocity;
  97. pPlayer->GetVelocity( &vAttackerVelocity, NULL );
  98. bool bInAir = FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) ? false : true;
  99. bool bDucking = FBitSet( pPlayer->GetFlags(), FL_DUCKING ) ? true : false;
  100. return PackMovementStatInternal( vAttackerVelocity, bDucking, bInAir );
  101. }
  102. // Pack and convert Target Aim Angle component into 32bits
  103. // OGS
  104. static uint8 PackAimAngleComponent( float f )
  105. {
  106. // Pack component of QAngle into Byte. Angle is -180 -> 180, so there's no space for the full range.
  107. // Make sure we're not getting weird values, rather have dumb data than broken game
  108. if ( f > 180 ) f = 180;
  109. if ( f < -180 ) f = -180;
  110. // The greatest granularity comes from packing angle into ( 2 * Pi )/255 units.
  111. // Effectively that's just a shift and stretch of the range: ( ( f + 180 )/ 360 ) * 255;
  112. return (uint8) ( ( ( f + 180 ) / 360 ) * 255 );
  113. }
  114. static uint32 PackAimAngleStat( CCSPlayer *pPlayer )
  115. {
  116. QAngle vPlayerAimAngle = pPlayer->GetFinalAimAngle();
  117. return ( (uint32)PackAimAngleComponent( (float)vPlayerAimAngle.z ) << 16 |
  118. (uint32)PackAimAngleComponent( (float)vPlayerAimAngle.y ) << 8 |
  119. (uint32)PackAimAngleComponent( (float)vPlayerAimAngle.x )
  120. );
  121. }
  122. //=============================================================================
  123. //
  124. // OGS Gamestats
  125. //
  126. #if !defined( _GAMECONSOLE ) && !defined( NO_STEAM )
  127. struct SWeaponShotData : public BaseStatData
  128. {
  129. SWeaponShotData( CCSPlayer *pPlayer, CWeaponCSBase* pWeapon, uint8 subBullet, uint8 round, uint8 iRecoilIndex )
  130. {
  131. Clear();
  132. if ( pWeapon )
  133. {
  134. m_ui8WeaponID = (uint8)pWeapon->GetEconItemView()->GetItemIndex();
  135. }
  136. if ( pPlayer )
  137. {
  138. m_iUserID = GetPlayerID( pPlayer );
  139. m_uiBulletID = pPlayer->GetBulletGroup();
  140. m_vAttackerPos = pPlayer->GetAbsOrigin();
  141. m_uAttackerMovement = PackPlayerMovementStat( pPlayer );
  142. }
  143. m_uiSubBulletID = subBullet;
  144. m_RoundID = round;
  145. m_uiRecoilIndex = iRecoilIndex;
  146. }
  147. void Clear()
  148. {
  149. m_iUserID = 0;
  150. m_WeaponID = WEAPON_NONE;
  151. m_ui8WeaponID = 0;
  152. m_uiBulletID = 0;
  153. m_uiSubBulletID = 0;
  154. m_vAttackerPos.Init();
  155. m_uAttackerMovement = 0;
  156. m_RoundID = 0;
  157. m_uiRecoilIndex = 0;
  158. }
  159. int m_iUserID;
  160. CSWeaponID m_WeaponID;
  161. uint8 m_ui8WeaponID;
  162. uint32 m_uiBulletID;
  163. uint8 m_uiSubBulletID;
  164. uint8 m_uiRecoilIndex;
  165. Vector m_vAttackerPos;
  166. uint32 m_uAttackerMovement;
  167. uint8 m_RoundID;
  168. };
  169. struct SWeaponHitData : public BaseStatData
  170. {
  171. SWeaponHitData( CCSPlayer *pCSTarget, const CTakeDamageInfo &info, uint8 subBullet, uint8 round, uint8 iRecoilIndex );
  172. SWeaponHitData()
  173. {
  174. Clear();
  175. }
  176. // When any grenade explodes-- this is separate from any damage it may deal, which are also recorded as hits.
  177. // Can fail! check the return!
  178. bool InitAsGrenadeDetonation( class CBaseCSGrenadeProjectile *pGrenade, uint32 unBulletGroup );
  179. // When a bomb is planted or defused, we want to collect data from each alive player, reporting their locations
  180. bool InitAsBombEvent( CCSPlayer *pCSPlayer, class CPlantedC4 *pPlantedC4, uint32 unBulletGroup, uint8 nBombsite, CSBombEventName nBombEventName );
  181. void Clear()
  182. {
  183. m_uiBulletID = 0;
  184. m_uiSubBulletID = 0;
  185. m_vAttackerPos.Init();
  186. m_vTargetPos.Init();
  187. m_uiDamage = 0;
  188. m_HitRegion = 0;
  189. m_RoundID = 0;
  190. m_ui8WeaponID = 0;
  191. m_ui64TargertID = 0;
  192. m_ui64AttackerID = 0;
  193. m_ui8Health = 0;
  194. m_uAttackerMovement = 0;
  195. m_uiRecoilIndex = 0;
  196. }
  197. void CompactBulletID()
  198. {
  199. m_uiBulletID = (m_uiBulletID & BULLET_ID_GROUP_MASK) | (SUB_BULLET_BIT_SHIFT(m_uiSubBulletID) & BULLET_SUB_GROUP_MASK) | (RECOIL_BIT_SHIFT(MIN(m_uiRecoilIndex, 255)) & BULLET_RECOIL_MASK);
  200. }
  201. // Data that gets sent to OGS
  202. uint32 m_uiBulletID;
  203. uint8 m_uiSubBulletID;
  204. uint8 m_uiRecoilIndex;
  205. Vector m_vAttackerPos;
  206. Vector m_vTargetPos;
  207. uint16 m_uiDamage;
  208. uint8 m_HitRegion;
  209. uint8 m_RoundID;
  210. uint8 m_ui8Health;
  211. uint8 m_ui8WeaponID;
  212. uint32 m_ui64AttackerID;
  213. uint32 m_ui64TargertID;
  214. uint32 m_uAttackerMovement;
  215. BEGIN_STAT_TABLE( "CSGOWeaponHitData" )
  216. REGISTER_STAT_NAMED( m_ui8WeaponID, "WeaponID" )
  217. REGISTER_STAT_NAMED( m_uiBulletID, "BulletID" )
  218. REGISTER_STAT_NAMED( m_ui64AttackerID, "AttackerID" )
  219. REGISTER_STAT_NAMED( (int)m_vAttackerPos.x, "AttackerX" )
  220. REGISTER_STAT_NAMED( (int)m_vAttackerPos.y, "AttackerY" )
  221. REGISTER_STAT_NAMED( (int)m_vAttackerPos.z, "AttackerZ" )
  222. REGISTER_STAT_NAMED( (int)m_uAttackerMovement, "AttackerMovement" )
  223. REGISTER_STAT_NAMED( m_ui64TargertID, "TargetID" )
  224. REGISTER_STAT_NAMED( (int)m_vTargetPos.x, "TargetX" )
  225. REGISTER_STAT_NAMED( (int)m_vTargetPos.y, "TargetY" )
  226. REGISTER_STAT_NAMED( (int)m_vTargetPos.z, "TargetZ" )
  227. REGISTER_STAT_NAMED( m_ui8Health, "Health" )
  228. REGISTER_STAT_NAMED( m_uiDamage, "Damage" )
  229. REGISTER_STAT_NAMED( m_HitRegion, "HitRegion" )
  230. REGISTER_STAT_NAMED( m_RoundID, "Round" )
  231. END_STAT_TABLE()
  232. };
  233. struct SWeaponMissData : public BaseStatData
  234. {
  235. SWeaponMissData( SWeaponShotData *data )
  236. {
  237. Clear();
  238. if ( data )
  239. {
  240. m_ui64AttackerID = data->m_iUserID;
  241. m_ui8WeaponID = data->m_ui8WeaponID;//data->m_WeaponID;
  242. m_uiBulletID = data->m_uiBulletID;
  243. m_uiRecoilIndex = data->m_uiRecoilIndex;
  244. m_uiSubBulletID = data->m_uiSubBulletID;
  245. m_vAttackerPos = data->m_vAttackerPos;
  246. m_uAttackerMovement = data->m_uAttackerMovement;
  247. m_RoundID = data->m_RoundID;
  248. TimeSubmitted = data->TimeSubmitted;
  249. }
  250. }
  251. void Clear()
  252. {
  253. m_ui8WeaponID = 0;
  254. m_uiBulletID = 0;
  255. m_uiSubBulletID = 0;
  256. m_uiRecoilIndex = 0;
  257. m_ui64AttackerID = 0;
  258. m_vAttackerPos.Init();
  259. m_uAttackerMovement = 0;
  260. m_RoundID = 0;
  261. }
  262. void CompactBulletID()
  263. {
  264. m_uiBulletID = (m_uiBulletID & BULLET_ID_GROUP_MASK) | (SUB_BULLET_BIT_SHIFT(m_uiSubBulletID) & BULLET_SUB_GROUP_MASK) | (RECOIL_BIT_SHIFT(MIN(m_uiRecoilIndex, 255)) & BULLET_RECOIL_MASK);
  265. }
  266. uint8 m_ui8WeaponID;
  267. uint32 m_uiBulletID;
  268. uint8 m_uiSubBulletID;
  269. uint8 m_uiRecoilIndex;
  270. uint32 m_ui64AttackerID;
  271. Vector m_vAttackerPos;
  272. uint32 m_uAttackerMovement;
  273. uint8 m_RoundID;
  274. BEGIN_STAT_TABLE( "CSGOWeaponMissData" )
  275. REGISTER_STAT_NAMED( m_ui8WeaponID, "WeaponID" )
  276. REGISTER_STAT_NAMED( m_uiBulletID, "BulletID" )
  277. REGISTER_STAT_NAMED( m_ui64AttackerID, "AttackerID" )
  278. REGISTER_STAT_NAMED( (int)m_vAttackerPos.x, "AttackerX" )
  279. REGISTER_STAT_NAMED( (int)m_vAttackerPos.y, "AttackerY" )
  280. REGISTER_STAT_NAMED( (int)m_vAttackerPos.z, "AttackerZ" )
  281. REGISTER_STAT_NAMED( (int)m_uAttackerMovement, "AttackerMovement" )
  282. REGISTER_STAT_NAMED( m_RoundID, "Round" )
  283. END_STAT_TABLE()
  284. };
  285. struct SMarketPurchases : public BaseStatData
  286. {
  287. SMarketPurchases( uint64 ulPlayerID, int iPrice, const char *pName, int round ) : ItemCost(iPrice)
  288. {
  289. m_nPlayerID = ulPlayerID;
  290. if ( pName )
  291. {
  292. //Can we find a valid Item Definition?
  293. if ( GetItemSchema()->GetItemDefinitionByName( pName ) )
  294. {
  295. ItemID = (uint)GetItemSchema()->GetItemDefinitionByName( pName )->GetDefinitionIndex();
  296. }
  297. else
  298. {
  299. //We don't know about you, you're probably equipment (armor, defuse kit)
  300. //Use CSWeaponID instead (works for all equipment)
  301. ItemID = WeaponIdFromString( pName ); //Returns WEAPON_NONE on failure to find string pName
  302. }
  303. }
  304. else
  305. {
  306. ItemID = WEAPON_NONE;
  307. }
  308. // Can't buy 'none'. Investigate why we can't get a weapon ID from the given string.
  309. Assert( ItemID != WEAPON_NONE );
  310. m_iPurchaseCnt = 1;
  311. m_niRound = round;
  312. }
  313. uint32 m_nPlayerID;
  314. int ItemCost;
  315. uint ItemID;
  316. char m_iPurchaseCnt;
  317. int m_niRound;
  318. BEGIN_STAT_TABLE( "CSGOMarketPurchase" )
  319. REGISTER_STAT_NAMED( m_nPlayerID, "AccountID" )
  320. REGISTER_STAT( ItemCost )
  321. REGISTER_STAT_NAMED( m_iPurchaseCnt, "PurchaseCount" )
  322. REGISTER_STAT_NAMED( ItemID, "WeaponID" )
  323. REGISTER_STAT_NAMED( m_niRound, "Round" )
  324. END_STAT_TABLE()
  325. };
  326. typedef CUtlVector< SWeaponHitData* > CSGOWeaponHitData;
  327. typedef CUtlVector< SWeaponMissData* > CSGOWeaponMissData;
  328. typedef CUtlVector< SWeaponShotData* > CSGOWeaponShotsData;
  329. typedef CUtlVector< SMarketPurchases* > CSGOMarketPurchaseData;
  330. #endif // OGS Data
  331. //=============================================================================
  332. //
  333. // CS Game Stats Class
  334. //
  335. class CCSGameStats : public CBaseGameStats, public CGameEventListener, public CAutoGameSystemPerFrame
  336. #if !defined( _GAMECONSOLE )
  337. , public IGameStatTracker
  338. #endif
  339. {
  340. public:
  341. // Constructor/Destructor.
  342. CCSGameStats( void );
  343. ~CCSGameStats( void );
  344. virtual void Clear( void );
  345. virtual bool Init();
  346. virtual void PreClientUpdate();
  347. // Overridden events
  348. virtual void Event_LevelInit( void );
  349. virtual void Event_LevelShutdown( float flElapsed );
  350. virtual void Event_ShotFired( CBasePlayer *pPlayer, CBaseCombatWeapon* pWeapon );
  351. virtual void Event_ShotHit( CBasePlayer *pPlayer, const CTakeDamageInfo &info );
  352. virtual void Event_PlayerKilled( CBasePlayer *pPlayer, const CTakeDamageInfo &info );
  353. virtual void Event_PlayerKilled_PreWeaponDrop( CBasePlayer *pPlayer, const CTakeDamageInfo &info );
  354. virtual void Event_PlayerConnected( CBasePlayer *pPlayer );
  355. virtual void Event_PlayerDisconnected( CBasePlayer *pPlayer );
  356. virtual void Event_WindowShattered( CBasePlayer *pPlayer );
  357. virtual void Event_PlayerDamage( CBasePlayer *pBasePlayer, const CTakeDamageInfo &info );
  358. virtual void Event_PlayerKilledOther( CBasePlayer *pAttacker, CBaseEntity *pVictim, const CTakeDamageInfo &info );
  359. // CSS specific events
  360. void Event_BombPlanted( CCSPlayer *pPlayer );
  361. void Event_BombDefused( CCSPlayer *pPlayer );
  362. void Event_BombExploded( CCSPlayer *pPlayer );
  363. void Event_MoneyEarned( CCSPlayer *pPlayer, int moneyEarned );
  364. void Event_MoneySpent( CCSPlayer* pPlayer, int moneySpent, const char *pItemName );
  365. void Event_HostageRescued( CCSPlayer *pPlayer );
  366. void Event_PlayerSprayedDecal( CCSPlayer*pPlayer );
  367. void Event_AllHostagesRescued();
  368. void Event_BreakProp( CCSPlayer *pPlayer, CBreakableProp *pProp );
  369. void Event_PlayerDonatedWeapon (CCSPlayer* pPlayer);
  370. void Event_PlayerDominatedOther( CCSPlayer* pAttacker, CCSPlayer* pVictim);
  371. void Event_PlayerRevenge( CCSPlayer* pAttacker );
  372. void Event_PlayerAvengedTeammate( CCSPlayer* pAttacker, CCSPlayer* pAvengedPlayer );
  373. void Event_MVPEarned( CCSPlayer* pPlayer );
  374. void Event_KnifeUse( CCSPlayer* pPlayer, bool bStab, int iDamage );
  375. void RecordWeaponHit( SWeaponHitData* pHitData );
  376. // Steamworks Gamestats
  377. #if !defined( _GAMECONSOLE )
  378. void UploadRoundStats( void );
  379. virtual void SubmitGameStats( KeyValues *pKV );
  380. virtual StatContainerList_t* GetStatContainerList( void );
  381. bool AnyOGSDataToSubmit( void );
  382. #endif
  383. virtual void FireGameEvent( IGameEvent *event );
  384. void UpdatePlayerRoundStats(int winner);
  385. void DumpMatchWeaponMetrics();
  386. const PlayerStats_t& FindPlayerStats( CBasePlayer *pPlayer ) const;
  387. void ResetPlayerStats( CBasePlayer *pPlayer );
  388. void ResetKillHistory( CBasePlayer *pPlayer );
  389. void ResetRoundStats();
  390. void ResetPlayerClassMatchStats();
  391. void ClearOGSRoundStats();
  392. const StatsCollection_t& GetTeamStats( int iTeamIndex ) const;
  393. void ResetAllTeamStats();
  394. void ResetAllStats();
  395. void ResetWeaponStats();
  396. void IncrementTeamStat( int iTeamIndex, int iStatIndex, int iAmount );
  397. void CalcDominationAndRevenge( CCSPlayer *pAttacker, CCSPlayer *pVictim, int *piDeathFlags );
  398. void CalculateOverkill(CCSPlayer* pAttacker, CCSPlayer* pVictim);
  399. void PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info );
  400. void IncrementStat( CCSPlayer* pPlayer, CSStatType_t statId, int iValue, bool bPlayerOnly = false, bool bIncludeBotController = false );
  401. void SendStatsToPlayer( CCSPlayer * pPlayer, int iMinStatPriority );
  402. void CreateNewGameStatsSession( void );
  403. protected:
  404. void SetStat( CCSPlayer *pPlayer, CSStatType_t statId, int iValue );
  405. void TrackKillStats( CCSPlayer *pAttacker, CCSPlayer *pVictim );
  406. private:
  407. PlayerStats_t m_aPlayerStats[MAX_PLAYERS+1]; // List of stats for each player for current life - reset after each death
  408. StatsCollection_t m_aTeamStats[TEAM_MAXCOUNT - FIRST_GAME_TEAM];
  409. float m_fDisseminationTimerLow; // how long since last medium priority stat update
  410. float m_fDisseminationTimerHigh; // how long since last high priority stat update
  411. int m_numberOfRoundsForDirectAverages;
  412. int m_numberOfTerroristEntriesForDirectAverages;
  413. int m_numberOfCounterTerroristEntriesForDirectAverages;
  414. CUtlDict< CSStatType_t, short > m_PropStatTable;
  415. WeaponStats m_weaponStats[WEAPON_MAX][WeaponMode_MAX];
  416. // Steamworks Gamestats
  417. #if !defined( _GAMECONSOLE ) && !defined( NO_STEAM )
  418. CSGOWeaponHitData m_WeaponHitData;
  419. CSGOWeaponMissData m_WeaponMissData;
  420. CSGOWeaponShotsData m_WeaponShotData;
  421. CSGOMarketPurchaseData m_MarketPurchases;
  422. // A static list of all the stat containers, one for each data structure being tracked
  423. static StatContainerList_t * s_StatLists;
  424. #endif
  425. };
  426. extern CCSGameStats CCS_GameStats;
  427. #endif // CS_GAMESTATS_H