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.

614 lines
15 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "client_pch.h"
  8. #include "ivideomode.h"
  9. #include "characterset.h"
  10. #include <ctype.h>
  11. // memdbgon must be the last include file in a .cpp file!!!
  12. #include "tier0/memdbgon.h"
  13. ConVar cl_entityreport( "cl_entityreport", "0", FCVAR_CHEAT, "For debugging, draw entity states to console" );
  14. static ConVar er_colwidth( "er_colwidth", "100", 0 );
  15. static ConVar er_maxname( "er_maxname", "14", 0 );
  16. static ConVar er_graphwidthfrac( "er_graphwidthfrac", "0.2", 0 );
  17. // How quickly to move rolling average for entityreport
  18. #define BITCOUNT_AVERAGE 0.95f
  19. // How long to flush item when something important happens
  20. #define EFFECT_TIME 1.5f
  21. // How long to latch peak bit count for item
  22. #define PEAK_LATCH_TIME 2.0f;
  23. //-----------------------------------------------------------------------------
  24. // Purpose: Entity report event types
  25. //-----------------------------------------------------------------------------
  26. enum
  27. {
  28. FENTITYBITS_ZERO = 0,
  29. FENTITYBITS_ADD = 0x01,
  30. FENTITYBITS_LEAVEPVS = 0x02,
  31. FENTITYBITS_DELETE = 0x04,
  32. };
  33. //-----------------------------------------------------------------------------
  34. // Purpose: Data about an entity
  35. //-----------------------------------------------------------------------------
  36. class CEntityBits
  37. {
  38. public:
  39. CEntityBits() :
  40. bits( 0 ),
  41. average( 0.0f ),
  42. peak( 0 ),
  43. peaktime( 0.0f ),
  44. flags( 0 ),
  45. effectfinishtime( 0.0f ),
  46. deletedclientclass( NULL )
  47. {
  48. }
  49. // Bits used for last message
  50. int bits;
  51. // Rolling average of bits used
  52. float average;
  53. // Last bit peak
  54. int peak;
  55. // Time at which peak was last reset
  56. float peaktime;
  57. // Event info
  58. int flags;
  59. // If doing effect, when it will finish
  60. float effectfinishtime;
  61. // If event was deletion, remember client class for a little bit
  62. ClientClass *deletedclientclass;
  63. };
  64. class CEntityReportManager
  65. {
  66. public:
  67. void Reset();
  68. void Record( int entnum, int bitcount );
  69. void Add( int entnum );
  70. void LeavePVS( int entnum );
  71. void DeleteEntity( int entnum, ClientClass *pclass );
  72. int Count();
  73. CEntityBits *Base();
  74. private:
  75. CUtlVector< CEntityBits > m_EntityBits;
  76. };
  77. static CEntityReportManager g_EntityReportMgr;
  78. void CL_ResetEntityBits( void )
  79. {
  80. g_EntityReportMgr.Reset();
  81. }
  82. void CL_RecordAddEntity( int entnum )
  83. {
  84. g_EntityReportMgr.Add( entnum );
  85. }
  86. void CL_RecordEntityBits( int entnum, int bitcount )
  87. {
  88. g_EntityReportMgr.Record( entnum, bitcount );
  89. }
  90. void CL_RecordLeavePVS( int entnum )
  91. {
  92. g_EntityReportMgr.LeavePVS( entnum );
  93. }
  94. void CL_RecordDeleteEntity( int entnum, ClientClass *pclass )
  95. {
  96. g_EntityReportMgr.DeleteEntity( entnum, pclass );
  97. }
  98. //-----------------------------------------------------------------------------
  99. // Purpose: Wipe structure ( level transition/startup )
  100. //-----------------------------------------------------------------------------
  101. void CEntityReportManager::Reset()
  102. {
  103. m_EntityBits.RemoveAll();
  104. }
  105. //-----------------------------------------------------------------------------
  106. // Purpose: Record activity
  107. // Input : entnum -
  108. // bitcount -
  109. //-----------------------------------------------------------------------------
  110. void CEntityReportManager::Record( int entnum, int bitcount )
  111. {
  112. if ( entnum < 0 || entnum >= MAX_EDICTS )
  113. {
  114. return;
  115. }
  116. m_EntityBits.EnsureCount( entnum + 1 );
  117. CEntityBits *slot = &m_EntityBits[ entnum ];
  118. slot->bits = bitcount;
  119. // Update average
  120. slot->average = ( BITCOUNT_AVERAGE ) * slot->average + ( 1.f - BITCOUNT_AVERAGE ) * bitcount;
  121. // Recompute peak
  122. if ( realtime >= slot->peaktime )
  123. {
  124. slot->peak = 0.0f;
  125. slot->peaktime = realtime + PEAK_LATCH_TIME;
  126. }
  127. // Store off peak
  128. if ( bitcount > slot->peak )
  129. {
  130. slot->peak = bitcount;
  131. }
  132. }
  133. //-----------------------------------------------------------------------------
  134. // Purpose: Record entity add event
  135. // Input : entnum -
  136. //-----------------------------------------------------------------------------
  137. void CEntityReportManager::Add( int entnum )
  138. {
  139. if ( !cl_entityreport.GetBool() || entnum < 0 || entnum >= MAX_EDICTS )
  140. {
  141. return;
  142. }
  143. m_EntityBits.EnsureCount( entnum + 1 );
  144. CEntityBits *slot = &m_EntityBits[ entnum ];
  145. slot->flags = FENTITYBITS_ADD;
  146. slot->effectfinishtime = realtime + EFFECT_TIME;
  147. }
  148. //-----------------------------------------------------------------------------
  149. // Purpose: record entity leave event
  150. // Input : entnum -
  151. //-----------------------------------------------------------------------------
  152. void CEntityReportManager::LeavePVS( int entnum )
  153. {
  154. if ( !cl_entityreport.GetBool() || entnum < 0 || entnum >= MAX_EDICTS )
  155. {
  156. return;
  157. }
  158. m_EntityBits.EnsureCount( entnum + 1 );
  159. CEntityBits *slot = &m_EntityBits[ entnum ];
  160. slot->flags = FENTITYBITS_LEAVEPVS;
  161. slot->effectfinishtime = realtime + EFFECT_TIME;
  162. }
  163. //-----------------------------------------------------------------------------
  164. // Purpose: record entity deletion event
  165. // Input : entnum -
  166. // *pclass -
  167. //-----------------------------------------------------------------------------
  168. void CEntityReportManager::DeleteEntity( int entnum, ClientClass *pclass )
  169. {
  170. if ( !cl_entityreport.GetBool() || entnum < 0 || entnum >= MAX_EDICTS )
  171. {
  172. return;
  173. }
  174. m_EntityBits.EnsureCount( entnum + 1 );
  175. CEntityBits *slot = &m_EntityBits[ entnum ];
  176. slot->flags = FENTITYBITS_DELETE;
  177. slot->effectfinishtime = realtime + EFFECT_TIME;
  178. slot->deletedclientclass = pclass;
  179. }
  180. int CEntityReportManager::Count()
  181. {
  182. return m_EntityBits.Count();
  183. }
  184. CEntityBits *CEntityReportManager::Base()
  185. {
  186. return m_EntityBits.Base();
  187. }
  188. //-----------------------------------------------------------------------------
  189. // Purpose: Shows entity status report if cl_entityreport cvar is set
  190. //-----------------------------------------------------------------------------
  191. class CEntityReportPanel : public CBasePanel
  192. {
  193. typedef CBasePanel BaseClass;
  194. public:
  195. // Construction
  196. CEntityReportPanel( vgui::Panel *parent );
  197. virtual ~CEntityReportPanel( void );
  198. // Refresh
  199. virtual void Paint();
  200. virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
  201. virtual bool ShouldDraw( void );
  202. // Helpers
  203. virtual void ApplyEffect( CEntityBits *entry, int& r, int& g, int& b );
  204. private:
  205. char const *MaybeTruncateName( int maxname, char const *pchName );
  206. // Font to use for drawing
  207. vgui::HFont m_hFont;
  208. characterset_t m_BreakSetVowels;
  209. };
  210. static CEntityReportPanel *g_pEntityReportPanel = NULL;
  211. //-----------------------------------------------------------------------------
  212. // Purpose: Creates the CEntityReportPanel VGUI panel
  213. // Input : *parent -
  214. //-----------------------------------------------------------------------------
  215. void CL_CreateEntityReportPanel( vgui::Panel *parent )
  216. {
  217. g_pEntityReportPanel = new CEntityReportPanel( parent );
  218. }
  219. //-----------------------------------------------------------------------------
  220. // Purpose: Instances the entity report panel
  221. // Input : *parent -
  222. //-----------------------------------------------------------------------------
  223. CEntityReportPanel::CEntityReportPanel( vgui::Panel *parent ) :
  224. CBasePanel( parent, "CEntityReportPanel" )
  225. {
  226. // Need parent here, before loading up textures, so getSurfaceBase
  227. // will work on this panel ( it's 0 otherwise )
  228. int nWidth = videomode->GetModeWidth();
  229. int nHeight = videomode->GetModeHeight();
  230. if ( IsGameConsole() )
  231. {
  232. SetSize( ( int )( nWidth * 0.9f ), ( int )( nHeight * 0.9f ) );
  233. SetPos( ( int )( nWidth * 0.05f ), ( int )( nHeight * 0.05f ) );
  234. }
  235. else
  236. {
  237. SetSize( nWidth, nHeight );
  238. SetPos( 0, 0 );
  239. }
  240. SetVisible( true );
  241. SetCursor( 0 );
  242. m_hFont = vgui::INVALID_FONT;
  243. SetFgColor( Color( 0, 0, 0, 255 ) );
  244. SetPaintBackgroundEnabled( false );
  245. SetPaintBorderEnabled(false);
  246. CharacterSetBuild( &m_BreakSetVowels, "aeiou" );
  247. }
  248. //-----------------------------------------------------------------------------
  249. // Purpose:
  250. //-----------------------------------------------------------------------------
  251. CEntityReportPanel::~CEntityReportPanel( void )
  252. {
  253. }
  254. void CEntityReportPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
  255. {
  256. BaseClass::ApplySchemeSettings( pScheme );
  257. // If you change this font, be sure to mark it with
  258. // $use_in_fillrate_mode in its .vmt file
  259. if ( IsGameConsole() )
  260. {
  261. // This is one of the few fonts we have loaded in shipping console builds
  262. m_hFont = pScheme->GetFont( "DebugFixed", false );
  263. }
  264. else
  265. {
  266. m_hFont = pScheme->GetFont( "DefaultVerySmall", false );
  267. }
  268. Assert( m_hFont );
  269. }
  270. //-----------------------------------------------------------------------------
  271. // Purpose:
  272. // Output : Returns true on success, false on failure.
  273. //-----------------------------------------------------------------------------
  274. bool CEntityReportPanel::ShouldDraw( void )
  275. {
  276. if ( !cl_entityreport.GetInt() )
  277. {
  278. return false;
  279. }
  280. return true;
  281. }
  282. //-----------------------------------------------------------------------------
  283. // Purpose: Helper to flash colors
  284. // Input : cycle -
  285. // value -
  286. // Output : static int
  287. //-----------------------------------------------------------------------------
  288. static int MungeColorValue( float cycle, int& value )
  289. {
  290. int midpoint;
  291. int remaining;
  292. bool invert = false;
  293. if ( value < 128 )
  294. {
  295. invert = true;
  296. value = 255 - value;
  297. }
  298. midpoint = value / 2;
  299. remaining = value - midpoint;
  300. midpoint = midpoint + remaining / 2;
  301. value = midpoint + ( remaining / 2 ) * cycle;
  302. if ( invert )
  303. {
  304. value = 255 - value;
  305. }
  306. value = MAX( 0, value );
  307. value = MIN( 255, value );
  308. return value;
  309. }
  310. //-----------------------------------------------------------------------------
  311. // Purpose:
  312. // Input : frac -
  313. // r -
  314. // g -
  315. // b -
  316. //-----------------------------------------------------------------------------
  317. void CEntityReportPanel::ApplyEffect( CEntityBits *entry, int& r, int& g, int& b )
  318. {
  319. bool effectactive = ( realtime <= entry->effectfinishtime ) ? true : false;
  320. if ( !effectactive )
  321. return;
  322. float frequency = 3.0f;
  323. float frac = ( EFFECT_TIME - ( entry->effectfinishtime - realtime ) ) / EFFECT_TIME;
  324. frac = MIN( 1.0, frac );
  325. frac = MAX( 0.0, frac );
  326. frac *= 2.0 * M_PI;
  327. frac = sin( frequency * frac );
  328. if ( entry->flags & FENTITYBITS_LEAVEPVS )
  329. {
  330. r = MungeColorValue( frac, r );
  331. }
  332. else if ( entry->flags & FENTITYBITS_ADD )
  333. {
  334. g = MungeColorValue( frac, g );
  335. }
  336. else if ( entry->flags & FENTITYBITS_DELETE )
  337. {
  338. r = MungeColorValue( frac, r );
  339. g = MungeColorValue( frac, g );
  340. b = MungeColorValue( frac, b );
  341. }
  342. }
  343. char const *CEntityReportPanel::MaybeTruncateName( int maxname, char const *pchName )
  344. {
  345. static char truncated[ 64 ];
  346. int len = Q_strlen( pchName );
  347. if ( *pchName == 'C' )
  348. {
  349. --len;
  350. ++pchName;
  351. }
  352. int toRemove = len - maxname;
  353. char const *in = pchName;
  354. char *out = truncated;
  355. int outlen = 1;
  356. // Strip the vowels and lower case the rest
  357. while ( *in && outlen < sizeof( truncated ) )
  358. {
  359. char check = tolower( *in );
  360. if ( toRemove >= 0 &&
  361. IN_CHARACTERSET( m_BreakSetVowels, check ) )
  362. {
  363. ++in;
  364. --toRemove;
  365. continue;
  366. }
  367. ++outlen;
  368. *out++ = check;
  369. ++in;
  370. }
  371. *out = 0;
  372. return truncated;
  373. }
  374. //-----------------------------------------------------------------------------
  375. // Purpose:
  376. //-----------------------------------------------------------------------------
  377. void CEntityReportPanel::Paint()
  378. {
  379. VPROF( "CEntityReportPanel::Paint" );
  380. if ( !m_hFont )
  381. return;
  382. if ( !GetBaseLocalClient().IsActive() )
  383. return;
  384. if ( !entitylist )
  385. return;
  386. int top = 5;
  387. int left = 5;
  388. int row = 0;
  389. int col = 0;
  390. int colwidth = er_colwidth.GetInt();
  391. int maxname = er_maxname.GetInt();
  392. int rowheight = vgui::surface()->GetFontTall( m_hFont );
  393. int screenw = videomode->GetModeWidth();
  394. int screenh = videomode->GetModeHeight();
  395. if ( IsGameConsole() )
  396. {
  397. screenw = ( int )( screenw * 0.9f );
  398. screenh = ( int )( screenh * 0.9f );
  399. }
  400. float graphfrac = clamp( er_graphwidthfrac.GetFloat(), 0.1f, 1.0f );
  401. IClientNetworkable *pNet;
  402. ClientClass *pClientClass;
  403. bool inpvs;
  404. int r, g, b, a;
  405. bool effectactive;
  406. CEntityBits *entry;
  407. int lastused = g_EntityReportMgr.Count()-1;
  408. CEntityBits *list = g_EntityReportMgr.Base();
  409. while ( lastused > 0 )
  410. {
  411. pNet = entitylist->GetClientNetworkable( lastused );
  412. entry = &list[ lastused ];
  413. effectactive = ( realtime <= entry->effectfinishtime ) ? true : false;
  414. if ( pNet && pNet->GetClientClass() )
  415. {
  416. break;
  417. }
  418. if ( effectactive )
  419. break;
  420. lastused--;
  421. }
  422. int start = 0;
  423. if ( cl_entityreport.GetInt() > 1 )
  424. {
  425. start = cl_entityreport.GetInt();
  426. }
  427. for ( int i = start; i <= lastused; i++ )
  428. {
  429. pNet = entitylist->GetClientNetworkable( i );
  430. entry = &list[ i ];
  431. effectactive = ( realtime <= entry->effectfinishtime ) ? true : false;
  432. if ( pNet && ((pClientClass = pNet->GetClientClass())) != NULL )
  433. {
  434. inpvs = !pNet->IsDormant();
  435. if ( inpvs )
  436. {
  437. if ( entry->average >= 5 )
  438. {
  439. r = 200; g = 200; b = 250;
  440. a = 255;
  441. }
  442. else
  443. {
  444. r = 200; g = 255; b = 100;
  445. a = 255;
  446. }
  447. }
  448. else
  449. {
  450. r = 255; g = 150; b = 100;
  451. a = 255;
  452. }
  453. ApplyEffect( entry, r, g, b );
  454. char text[256];
  455. wchar_t unicode[ 256 ];
  456. Q_snprintf( text, sizeof(text), "%i %s", i, MaybeTruncateName( maxname, pClientClass->m_pNetworkName ) );
  457. g_pVGuiLocalize->ConvertANSIToUnicode( text, unicode, sizeof( unicode ) );
  458. DrawColoredText( m_hFont, left + col * colwidth, top + row * rowheight, r, g, b, a, unicode );
  459. if ( inpvs )
  460. {
  461. float fracs[ 3 ];
  462. fracs[ 0 ] = (float)( entry->bits >> 3 ) / 100.0f;
  463. fracs[ 1 ] = (float)( entry->peak >> 3 ) / 100.0f;
  464. fracs[ 2 ] = (float)( (int)entry->average >> 3 ) / 100.0f;
  465. for ( int j = 0; j < 3; j++ )
  466. {
  467. fracs[ j ] = MAX( 0.0f, fracs[ j ] );
  468. fracs[ j ] = MIN( 1.0f, fracs[ j ] );
  469. }
  470. int rcright = left + col * colwidth + colwidth-2;
  471. int wide = MAX( 1, colwidth * graphfrac );
  472. int rcleft = rcright - wide;
  473. int rctop = top + row * rowheight;
  474. int rcbottom = rctop + rowheight - 1;
  475. vgui::surface()->DrawSetColor( 63, 63, 63, 127 );
  476. vgui::surface()->DrawFilledRect( rcleft, rctop, rcright, rcbottom );
  477. // draw a box around it
  478. vgui::surface()->DrawSetColor( 200, 200, 200, 127 );
  479. vgui::surface()->DrawOutlinedRect( rcleft, rctop, rcright, rcbottom );
  480. // Draw current as a filled rect
  481. vgui::surface()->DrawSetColor( 200, 255, 100, 192 );
  482. vgui::surface()->DrawFilledRect( rcleft, rctop + rowheight / 2, rcleft + wide * fracs[ 0 ], rcbottom - 1 );
  483. // Draw average a vertical bar
  484. vgui::surface()->DrawSetColor( 192, 192, 192, 255 );
  485. vgui::surface()->DrawFilledRect( rcleft + wide * fracs[ 2 ], rctop + rowheight / 2, rcleft + wide * fracs[ 2 ] + 1, rcbottom - 1 );
  486. // Draw peak as a vertical red tick
  487. vgui::surface()->DrawSetColor( 192, 0, 0, 255 );
  488. vgui::surface()->DrawFilledRect( rcleft + wide * fracs[ 1 ], rctop + 1, rcleft + wide * fracs[ 1 ] + 1, rctop + rowheight / 2 );
  489. }
  490. }
  491. row++;
  492. if ( top + row * rowheight > screenh - rowheight )
  493. {
  494. row = 0;
  495. col++;
  496. // No more space anyway, give up
  497. if ( left + ( col + 1 ) * colwidth > screenw )
  498. return;
  499. }
  500. }
  501. }