576 lines
14 KiB
C++
576 lines
14 KiB
C++
//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
//=============================================================================//
|
|
|
|
#include "cbase.h"
|
|
#include "hud_pdump.h"
|
|
#include "iclientmode.h"
|
|
#include "predictioncopy.h"
|
|
#include "vgui/ISurface.h"
|
|
#include "vgui/ILocalize.h"
|
|
#include "vgui_int.h"
|
|
#include "in_buttons.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
using namespace vgui;
|
|
|
|
CPDumpPanel *GetPDumpPanel()
|
|
{
|
|
return GET_FULLSCREEN_HUDELEMENT( CPDumpPanel );
|
|
}
|
|
|
|
DECLARE_HUDELEMENT_FLAGS( CPDumpPanel, HUDELEMENT_SS_FULLSCREEN_ONLY );
|
|
|
|
CPDumpPanel::CPDumpPanel( const char *pElementName ) :
|
|
CHudElement( pElementName ), BaseClass( NULL, "HudPredictionDump" ), m_nCurrentIndex( 0 )
|
|
{
|
|
vgui::Panel *pParent = GetFullscreenClientMode()->GetViewport();
|
|
SetParent( pParent );
|
|
|
|
SetProportional( false );
|
|
SetKeyBoardInputEnabled( false );
|
|
SetMouseInputEnabled( false );
|
|
}
|
|
|
|
CPDumpPanel::~CPDumpPanel()
|
|
{
|
|
}
|
|
|
|
void CPDumpPanel::ApplySettings( KeyValues *inResourceData )
|
|
{
|
|
SetProportional( false );
|
|
|
|
BaseClass::ApplySettings( inResourceData );
|
|
}
|
|
|
|
void CPDumpPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
|
|
{
|
|
SetProportional( false );
|
|
|
|
BaseClass::ApplySchemeSettings( pScheme );
|
|
|
|
SetPaintBackgroundEnabled( false );
|
|
|
|
int screenWide, screenTall;
|
|
VGui_GetTrueScreenSize(screenWide, screenTall);
|
|
SetBounds(0, 0, screenWide, screenTall);
|
|
// Make sure we sort above everyone else
|
|
SetZPos( 100 );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool CPDumpPanel::ShouldDraw()
|
|
{
|
|
if ( m_DumpEntityInfo.Count() == 0 )
|
|
return false;
|
|
|
|
return CHudElement::ShouldDraw();
|
|
}
|
|
|
|
static char const *pchButtonFields[]=
|
|
{
|
|
"m_nOldButtons",
|
|
"m_nButtons",
|
|
"m_afButtonLast",
|
|
"m_afButtonPressed",
|
|
"m_afButtonReleased",
|
|
"m_afButtonForced",
|
|
};
|
|
|
|
static bool IsButtonField( char const *fieldname )
|
|
{
|
|
for ( int i =0 ; i < ARRAYSIZE( pchButtonFields ); ++i )
|
|
{
|
|
if ( !Q_stricmp( fieldname, pchButtonFields[ i ] ) )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
struct buttonname_t
|
|
{
|
|
int nBit;
|
|
char const *pchName;
|
|
};
|
|
|
|
#define DECLARE_BUTTON_NAME( x ) { IN_##x, #x }
|
|
|
|
static buttonname_t g_ButtonNames[] =
|
|
{
|
|
DECLARE_BUTTON_NAME( ATTACK ),
|
|
DECLARE_BUTTON_NAME( JUMP ),
|
|
DECLARE_BUTTON_NAME( DUCK ),
|
|
DECLARE_BUTTON_NAME( FORWARD ),
|
|
DECLARE_BUTTON_NAME( BACK ),
|
|
DECLARE_BUTTON_NAME( USE ),
|
|
DECLARE_BUTTON_NAME( CANCEL ),
|
|
DECLARE_BUTTON_NAME( LEFT ),
|
|
DECLARE_BUTTON_NAME( RIGHT ),
|
|
DECLARE_BUTTON_NAME( MOVELEFT ),
|
|
DECLARE_BUTTON_NAME( MOVERIGHT ),
|
|
DECLARE_BUTTON_NAME( ATTACK2 ),
|
|
DECLARE_BUTTON_NAME( RUN ),
|
|
DECLARE_BUTTON_NAME( RELOAD ),
|
|
DECLARE_BUTTON_NAME( ALT1 ),
|
|
DECLARE_BUTTON_NAME( ALT2 ),
|
|
DECLARE_BUTTON_NAME( SCORE ),
|
|
DECLARE_BUTTON_NAME( SPEED),
|
|
DECLARE_BUTTON_NAME( WALK ),
|
|
DECLARE_BUTTON_NAME( ZOOM ),
|
|
DECLARE_BUTTON_NAME( WEAPON1 ),
|
|
DECLARE_BUTTON_NAME( WEAPON2 ),
|
|
DECLARE_BUTTON_NAME( BULLRUSH ),
|
|
DECLARE_BUTTON_NAME( GRENADE1 ),
|
|
DECLARE_BUTTON_NAME( GRENADE2 ),
|
|
DECLARE_BUTTON_NAME( LOOKSPIN ),
|
|
};
|
|
|
|
|
|
static char const *GetButtonFieldValue( char const *value, char *buf, size_t bufsize )
|
|
{
|
|
buf[ 0 ] = 0;
|
|
char *pchDataStart = Q_strstr( value, "(" );
|
|
if ( !pchDataStart )
|
|
return value;
|
|
|
|
int bits = Q_atoi( pchDataStart + 1 );
|
|
|
|
// Assign button bits
|
|
bool first = true;
|
|
for ( int i = 0; i < ARRAYSIZE( g_ButtonNames ); ++i )
|
|
{
|
|
int mask = (1<<i);
|
|
if ( bits & mask )
|
|
{
|
|
if ( !first )
|
|
{
|
|
Q_strncat( buf, ",", bufsize, COPY_ALL_CHARACTERS );
|
|
}
|
|
Q_strncat( buf, g_ButtonNames[ i ].pchName, bufsize, COPY_ALL_CHARACTERS );
|
|
first = false;
|
|
}
|
|
}
|
|
|
|
Q_strlower( buf );
|
|
return buf;
|
|
}
|
|
|
|
static char const *CleanupZeros( char const *value, char *buf, size_t bufsize )
|
|
{
|
|
char *out = buf;
|
|
while ( *value )
|
|
{
|
|
if ( *value != '.' )
|
|
{
|
|
*out++ = *value++;
|
|
continue;
|
|
}
|
|
|
|
// Found a . now see if next run of characters until space or ')' is all zeroes
|
|
char const *next = value + 1;
|
|
while ( *next && *next == '0' )
|
|
++next;
|
|
if ( *next == ' ' || *next == ')' )
|
|
{
|
|
// Don't write the . or the zeroes, just put value at the terminator
|
|
value = next;
|
|
}
|
|
else
|
|
{
|
|
*out++ = *value++;
|
|
}
|
|
}
|
|
|
|
*out = 0;
|
|
return buf;
|
|
}
|
|
|
|
void CPDumpPanel::DumpComparision( const char *classname, const char *fieldname, const char *fieldtype,
|
|
bool networked, bool noterrorchecked, bool differs, bool withintolerance, const char *value )
|
|
{
|
|
if ( fieldname == NULL )
|
|
return;
|
|
|
|
DumpInfo slot;
|
|
|
|
slot.index = m_nCurrentIndex++;
|
|
|
|
Q_snprintf( slot.classname, sizeof( slot.classname ), "%s", classname );
|
|
slot.networked = networked;
|
|
|
|
char bv[ DUMP_STRING_SIZE ];
|
|
|
|
if ( IsButtonField( fieldname ) )
|
|
{
|
|
value = GetButtonFieldValue( value, bv, sizeof( bv ) );
|
|
}
|
|
else
|
|
{
|
|
value = CleanupZeros( value, bv, sizeof( bv ) );
|
|
}
|
|
|
|
Q_snprintf( slot.fieldstring, sizeof( slot.fieldstring ), "%s %s",
|
|
fieldname,
|
|
value );
|
|
|
|
slot.differs = differs;
|
|
slot.withintolerance = withintolerance;
|
|
slot.noterrorchecked = noterrorchecked;
|
|
|
|
m_DumpEntityInfo.InsertNoSort( slot );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Callback function for dumping entity info to screen
|
|
// Input : *classname -
|
|
// *fieldname -
|
|
// *fieldtype -
|
|
// networked -
|
|
// noterrorchecked -
|
|
// differs -
|
|
// withintolerance -
|
|
// *value -
|
|
// Output : static void
|
|
//-----------------------------------------------------------------------------
|
|
static void DumpComparision( const char *classname, const char *fieldname, const char *fieldtype,
|
|
bool networked, bool noterrorchecked, bool differs, bool withintolerance, const char *value )
|
|
{
|
|
CPDumpPanel *pPanel = GetPDumpPanel();
|
|
if ( !pPanel )
|
|
return;
|
|
|
|
pPanel->DumpComparision( classname, fieldname, fieldtype, networked, noterrorchecked, differs, withintolerance, value );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Lookup color to use for data
|
|
// Input : networked -
|
|
// errorchecked -
|
|
// differs -
|
|
// withintolerance -
|
|
// r -
|
|
// g -
|
|
// b -
|
|
// a -
|
|
// Output : static void
|
|
//-----------------------------------------------------------------------------
|
|
void CPDumpPanel::PredictionDumpColor( bool legend, bool predictable, bool networked, bool errorchecked, bool differs, bool withintolerance,
|
|
int& r, int& g, int& b, int& a )
|
|
{
|
|
if ( !legend && !predictable )
|
|
{
|
|
r = 150;
|
|
g = 180;
|
|
b = 150;
|
|
a = 255;
|
|
return;
|
|
}
|
|
|
|
r = 255;
|
|
g = 255;
|
|
b = 255;
|
|
a = 255;
|
|
|
|
if ( networked )
|
|
{
|
|
if ( errorchecked )
|
|
{
|
|
r = 180;
|
|
g = 180;
|
|
b = 225;
|
|
}
|
|
else
|
|
{
|
|
r = 150;
|
|
g = 180;
|
|
b = 150;
|
|
}
|
|
}
|
|
|
|
if ( differs )
|
|
{
|
|
if ( withintolerance )
|
|
{
|
|
r = 255;
|
|
g = 255;
|
|
b = 0;
|
|
a = 255;
|
|
}
|
|
else
|
|
{
|
|
if ( !networked )
|
|
{
|
|
r = 180;
|
|
g = 180;
|
|
b = 100;
|
|
a = 255;
|
|
}
|
|
else
|
|
{
|
|
r = 255;
|
|
g = 0;
|
|
b = 0;
|
|
a = 255;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Dump entity data to screen
|
|
// Input : *ent -
|
|
// last_predicted -
|
|
//-----------------------------------------------------------------------------
|
|
void CPDumpPanel::DumpEntity( C_BaseEntity *ent, int commands_acknowledged )
|
|
{
|
|
#ifdef NO_ENTITY_PREDICTION
|
|
return;
|
|
#else
|
|
Assert( ent );
|
|
|
|
const byte *original_state_data = NULL;
|
|
const byte *predicted_state_data = NULL;
|
|
|
|
bool data_type_original = TD_OFFSET_PACKED;
|
|
bool data_type_predicted = TD_OFFSET_PACKED;
|
|
|
|
if ( ent->GetPredictable() )
|
|
{
|
|
original_state_data = (const byte *)ent->GetOriginalNetworkDataObject();
|
|
predicted_state_data = (const byte *)ent->GetPredictedFrame( commands_acknowledged - 1 );
|
|
}
|
|
else
|
|
{
|
|
// Compare against self so that we're just dumping data to screen
|
|
original_state_data = ( const byte * )ent;
|
|
data_type_original = TD_OFFSET_NORMAL;
|
|
predicted_state_data = original_state_data;
|
|
data_type_predicted = data_type_original;
|
|
}
|
|
|
|
Assert( original_state_data );
|
|
Assert( predicted_state_data );
|
|
|
|
Clear();
|
|
|
|
CPredictionCopy datacompare( PC_EVERYTHING,
|
|
(byte *)original_state_data, data_type_original,
|
|
predicted_state_data, data_type_predicted,
|
|
CPredictionCopy::TRANSFERDATA_ERRORCHECK_DESCRIBE,
|
|
::DumpComparision );
|
|
// Don't spew debugging info
|
|
m_nCurrentIndex = 0;
|
|
datacompare.TransferData( "", ent->entindex(), ent->GetPredDescMap() );
|
|
m_hDumpEntity = ent;
|
|
m_DumpEntityInfo.RedoSort();
|
|
#endif
|
|
}
|
|
|
|
void CPDumpPanel::Clear()
|
|
{
|
|
m_DumpEntityInfo.RemoveAll();
|
|
}
|
|
|
|
void CPDumpPanel::Paint()
|
|
{
|
|
C_BaseEntity *ent = m_hDumpEntity;
|
|
if ( !ent )
|
|
{
|
|
Clear();
|
|
return;
|
|
}
|
|
|
|
bool bPredictable = ent->GetPredictable();
|
|
|
|
// Now output the strings
|
|
int x[5];
|
|
x[0] = 20;
|
|
int columnwidth = 375;
|
|
int numcols = GetWide() / columnwidth;
|
|
int i;
|
|
|
|
numcols = clamp( numcols, 1, 5 );
|
|
|
|
for ( i = 0; i < numcols; i++ )
|
|
{
|
|
if ( i == 0 )
|
|
{
|
|
x[i] = 20;
|
|
}
|
|
else
|
|
{
|
|
x[i] = x[ i-1 ] + columnwidth - 20;
|
|
}
|
|
}
|
|
|
|
int nFontTweak = -7;
|
|
|
|
int c = m_DumpEntityInfo.Count();
|
|
int fonttall = vgui::surface()->GetFontTall( m_FontSmall ) + nFontTweak;
|
|
int fonttallMedium = vgui::surface()->GetFontTall( m_FontMedium ) + nFontTweak;
|
|
int fonttallBig = vgui::surface()->GetFontTall( m_FontBig ) + nFontTweak;
|
|
|
|
char currentclass[ 128 ];
|
|
currentclass[ 0 ] = 0;
|
|
|
|
int starty = 15;
|
|
int y = starty;
|
|
|
|
int col = 0;
|
|
|
|
int r = 255;
|
|
int g = 255;
|
|
int b = 255;
|
|
int a = 255;
|
|
|
|
char classextra[ 32 ];
|
|
classextra[ 0 ] = 0;
|
|
char classprefix[ 32 ];
|
|
Q_strncpy( classprefix, "class ", sizeof( classprefix ) );
|
|
const char *classname = ent->GetClassname();
|
|
if ( !classname[ 0 ] )
|
|
{
|
|
classname = typeid( *ent ).name();
|
|
Q_strncpy( classextra, " (classmap missing)", sizeof( classextra ) );
|
|
classprefix[ 0 ] = 0;
|
|
}
|
|
|
|
char sz[ 512 ];
|
|
wchar_t szconverted[ 1024 ];
|
|
|
|
surface()->DrawSetTextFont( m_FontBig );
|
|
surface()->DrawSetTextColor( Color( 255, 255, 255, 255 ) );
|
|
surface()->DrawSetTextPos( x[ col ] - 10, y - fonttallBig - 2 );
|
|
Q_snprintf( sz, sizeof( sz ), "entity # %i: %s%s%s", ent->entindex(), classprefix, classname, classextra );
|
|
g_pVGuiLocalize->ConvertANSIToUnicode( sz, szconverted, sizeof(szconverted) );
|
|
surface()->DrawPrintText( szconverted, wcslen( szconverted ) );
|
|
|
|
for ( i = 0; i < c; i++ )
|
|
{
|
|
DumpInfo *slot = &m_DumpEntityInfo[ i ];
|
|
|
|
if ( stricmp( slot->classname, currentclass ) )
|
|
{
|
|
y += 2;
|
|
|
|
surface()->DrawSetTextFont( m_FontMedium );
|
|
surface()->DrawSetTextColor( Color( 0, 255, 100, 255 ) );
|
|
surface()->DrawSetTextPos( x[ col ] - 10, y );
|
|
Q_snprintf( sz, sizeof( sz ), "%s", slot->classname );
|
|
g_pVGuiLocalize->ConvertANSIToUnicode( sz, szconverted, sizeof(szconverted) );
|
|
surface()->DrawPrintText( szconverted, wcslen( szconverted ) );
|
|
|
|
y += fonttallMedium;
|
|
Q_strncpy( currentclass, slot->classname, sizeof( currentclass ) );
|
|
}
|
|
|
|
|
|
PredictionDumpColor( false, bPredictable, slot->networked, !slot->noterrorchecked, slot->differs, slot->withintolerance,
|
|
r, g, b, a );
|
|
|
|
surface()->DrawSetTextFont( m_FontSmall );
|
|
surface()->DrawSetTextColor( Color( r, g, b, a ) );
|
|
surface()->DrawSetTextPos( x[ col ], y );
|
|
Q_snprintf( sz, sizeof( sz ), "%s", slot->fieldstring );
|
|
g_pVGuiLocalize->ConvertANSIToUnicode( sz, szconverted, sizeof(szconverted) );
|
|
surface()->DrawPrintText( szconverted, wcslen( szconverted ) );
|
|
|
|
y += fonttall;
|
|
|
|
if ( y >= GetTall() - fonttall - starty )
|
|
{
|
|
y = starty;
|
|
col++;
|
|
if ( col >= numcols )
|
|
break;
|
|
}
|
|
}
|
|
|
|
surface()->DrawSetTextFont( m_FontSmall );
|
|
|
|
|
|
// Figure how far over the legend needs to be.
|
|
const char *pFirstAndLongestString = "Not networked, no differences";
|
|
g_pVGuiLocalize->ConvertANSIToUnicode( pFirstAndLongestString, szconverted, sizeof(szconverted) );
|
|
int textSizeWide, textSizeTall;
|
|
surface()->GetTextSize( m_FontSmall, szconverted, textSizeWide, textSizeTall );
|
|
|
|
|
|
// Draw a legend now
|
|
int xpos = ScreenWidth() - textSizeWide - 5;
|
|
y = ScreenHeight() - 7 * fonttall - 80;
|
|
|
|
// Not networked, no differences
|
|
PredictionDumpColor( true, bPredictable, false, false, false, false, r, g, b, a );
|
|
|
|
|
|
surface()->DrawSetTextColor( Color( r, g, b, a ) );
|
|
surface()->DrawSetTextPos( xpos, y );
|
|
Q_strncpy( sz, pFirstAndLongestString, sizeof( sz ) );
|
|
g_pVGuiLocalize->ConvertANSIToUnicode( sz, szconverted, sizeof(szconverted) );
|
|
surface()->DrawPrintText( szconverted, wcslen( szconverted ) );
|
|
|
|
y += fonttall;
|
|
|
|
// Networked, no error check
|
|
PredictionDumpColor( true, bPredictable, true, false, false, false, r, g, b, a );
|
|
|
|
surface()->DrawSetTextColor( Color( r, g, b, a ) );
|
|
surface()->DrawSetTextPos( xpos, y );
|
|
Q_strncpy( sz, "Networked, not checked", sizeof( sz ) );
|
|
g_pVGuiLocalize->ConvertANSIToUnicode( sz, szconverted, sizeof(szconverted) );
|
|
surface()->DrawPrintText( szconverted, wcslen( szconverted ) );
|
|
|
|
y += fonttall;
|
|
|
|
// Networked, with error check
|
|
PredictionDumpColor( true, bPredictable, true, true, false, false, r, g, b, a );
|
|
|
|
surface()->DrawSetTextColor( Color( r, g, b, a ) );
|
|
surface()->DrawSetTextPos( xpos, y );
|
|
Q_strncpy( sz, "Networked, error checked", sizeof( sz ) );
|
|
g_pVGuiLocalize->ConvertANSIToUnicode( sz, szconverted, sizeof(szconverted) );
|
|
surface()->DrawPrintText( szconverted, wcslen( szconverted ) );
|
|
|
|
y += fonttall;
|
|
|
|
// Differs, but within tolerance
|
|
PredictionDumpColor( true, bPredictable, true, true, true, true, r, g, b, a );
|
|
|
|
surface()->DrawSetTextColor( Color( r, g, b, a ) );
|
|
surface()->DrawSetTextPos( xpos, y );
|
|
Q_strncpy( sz, "Differs, but within tolerance", sizeof( sz ) );
|
|
g_pVGuiLocalize->ConvertANSIToUnicode( sz, szconverted, sizeof(szconverted) );
|
|
surface()->DrawPrintText( szconverted, wcslen( szconverted ) );
|
|
|
|
y += fonttall;
|
|
|
|
// Differs, not within tolerance, but not networked
|
|
PredictionDumpColor( true, bPredictable, false, true, true, false, r, g, b, a );
|
|
|
|
surface()->DrawSetTextColor( Color( r, g, b, a ) );
|
|
surface()->DrawSetTextPos( xpos, y );
|
|
Q_strncpy( sz, "Differs, but not networked", sizeof( sz ) );
|
|
g_pVGuiLocalize->ConvertANSIToUnicode( sz, szconverted, sizeof(szconverted) );
|
|
surface()->DrawPrintText( szconverted, wcslen( szconverted ) );
|
|
|
|
y += fonttall;
|
|
|
|
// Differs, networked, not within tolerance
|
|
PredictionDumpColor( true, bPredictable, true, true, true, false, r, g, b, a );
|
|
|
|
surface()->DrawSetTextColor( Color( r, g, b, a ) );
|
|
surface()->DrawSetTextPos( xpos, y );
|
|
Q_strncpy( sz, "Differs, networked", sizeof( sz ) );
|
|
g_pVGuiLocalize->ConvertANSIToUnicode( sz, szconverted, sizeof(szconverted) );
|
|
surface()->DrawPrintText( szconverted, wcslen( szconverted ) );
|
|
|
|
y += fonttall;
|
|
} |