bscly.
[code]//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include “cbase.h”
#include “gamemovement.h”
#include “in_buttons.h”
#include <stdarg.h>
#include “movevars_shared.h”
#include “engine/IEngineTrace.h”
#include “SoundEmitterSystem/isoundemittersystembase.h”
#include “decals.h”
#include “coordsize.h”
#include “rumble_shared.h”
#if defined(HL2_DLL) || defined(HL2_CLIENT_DLL)
#include “hl_movedata.h”
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include “tier0/memdbgon.h”
#define STOP_EPSILON 0.1
#define MAX_CLIP_PLANES 5
#include “filesystem.h”
#include <stdarg.h>
extern IFileSystem *filesystem;
#ifndef CLIENT_DLL
#include “env_player_surface_trigger.h”
static ConVar dispcoll_drawplane( “dispcoll_drawplane”, “0” );
#endif
// tickcount currently isn’t set during prediction, although gpGlobals->curtime and
// gpGlobals->frametime are. We should probably set tickcount (to player->m_nTickBase),
// but we’re REALLY close to shipping, so we can change that later and people can use
// player->CurrentCommandNumber() in the meantime.
#define tickcount USE_PLAYER_CURRENT_COMMAND_NUMBER__INSTEAD_OF_TICKCOUNT
#if defined( HL2_DLL )
ConVar xc_uncrouch_on_jump( “xc_uncrouch_on_jump”, “1”, FCVAR_ARCHIVE, “Uncrouch when jump occurs” );
#endif
#if defined( HL2_DLL ) || defined( HL2_CLIENT_DLL )
ConVar player_limit_jump_speed( “player_limit_jump_speed”, “1”, FCVAR_REPLICATED );
#endif
ConVar cl_enable_bunnyhop( “cl_enable_bunnyhop”, “0”, FCVAR_ARCHIVE );
// option_duck_method is a carrier convar. Its sole purpose is to serve an easy-to-flip
// convar which is ONLY set by the X360 controller menu to tell us which way to bind the
// duck controls. Its value is meaningless anytime we don’t have the options window open.
ConVar option_duck_method(“option_duck_method”, “1”, FCVAR_REPLICATED|FCVAR_ARCHIVE );// 0 = HOLD to duck, 1 = Duck is a toggle
// [MD] I’ll remove this eventually. For now, I want the ability to A/B the optimizations.
bool g_bMovementOptimizations = true;
// Roughly how often we want to update the info about the ground surface we’re on.
// We don’t need to do this very often.
#define CATEGORIZE_GROUND_SURFACE_INTERVAL 0.3f
#define CATEGORIZE_GROUND_SURFACE_TICK_INTERVAL ( (int)( CATEGORIZE_GROUND_SURFACE_INTERVAL / TICK_INTERVAL ) )
#define CHECK_STUCK_INTERVAL 1.0f
#define CHECK_STUCK_TICK_INTERVAL ( (int)( CHECK_STUCK_INTERVAL / TICK_INTERVAL ) )
#define CHECK_STUCK_INTERVAL_SP 0.2f
#define CHECK_STUCK_TICK_INTERVAL_SP ( (int)( CHECK_STUCK_INTERVAL_SP / TICK_INTERVAL ) )
#define CHECK_LADDER_INTERVAL 0.2f
#define CHECK_LADDER_TICK_INTERVAL ( (int)( CHECK_LADDER_INTERVAL / TICK_INTERVAL ) )
#define NUM_CROUCH_HINTS 3
extern IGameMovement *g_pGameMovement;
#if defined( PLAYER_GETTING_STUCK_TESTING )
// If you ever get stuck walking around, then you can run this code to find the code which would leave the player in a bad spot
void CMoveData::SetAbsOrigin( const Vector &vec )
{
CGameMovement *gm = dynamic_cast< CGameMovement * >( g_pGameMovement );
if ( gm && gm->GetMoveData() &&
gm->player &&
gm->player->entindex() == 1 &&
gm->player->GetMoveType() == MOVETYPE_WALK )
{
trace_t pm;
gm->TracePlayerBBox( vec, vec, gm->PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
if ( pm.startsolid || pm.allsolid || pm.fraction != 1.0f )
{
Msg( “Player will become stuck at %f %f %f\n”, VectorExpand( vec ) );
}
}
m_vecAbsOrigin = vec;
}
#endif
// See shareddefs.h
#if PREDICTION_ERROR_CHECK_LEVEL > 0
static ConVar diffcheck( “diffcheck”, “0”, FCVAR_REPLICATED );
class IDiffMgr
{
public:
virtual void StartCommand( bool bServer, int nCommandNumber ) = 0;
virtual void AddToDiff( bool bServer, int nCommandNumber, char const *string ) = 0;
virtual void Validate( bool bServer, int nCommandNumber ) = 0;
};
static IDiffMgr *g_pDiffMgr = NULL;
class CDiffStr
{
public:
CDiffStr()
{
m_str[ 0 ] = 0;
}
CDiffStr( char const *str )
{
Q_strncpy( m_str, str, sizeof( m_str ) );
}
CDiffStr( const CDiffStr &src )
{
Q_strncpy( m_str, src.m_str, sizeof( m_str ) );
}
char const *String()
{
return m_str;
}
private:
char m_str[ 128 ];
};
// Per tick data
class CDiffInfo
{
public:
CDiffInfo() : m_nCommandNumber( 0 ) {}
CDiffInfo( const CDiffInfo& src )
{
m_nCommandNumber = src.m_nCommandNumber;
for ( int i = 0; i < src.m_Lines.Count(); ++i )
{
m_Lines.AddToTail( src.m_Lines[ i ] );
}
}
static bool Less( const CDiffInfo& lhs, const CDiffInfo& rhs )
{
return lhs.m_nCommandNumber < rhs.m_nCommandNumber;
}
int m_nCommandNumber;
CUtlVector< CDiffStr > m_Lines;
bool m_bChecked;
};
class CDiffManager : public IDiffMgr
{
public:
CDiffManager() :
m_Client( 0, 0, CDiffInfo::Less ),
m_Server( 0, 0, CDiffInfo::Less ),
m_flLastSpew( -1.0f )
{
g_pDiffMgr = this;
}
virtual void StartCommand( bool bServer, int nCommandNumber )
{
#if defined( CLIENT_DLL )
if ( !diffcheck.GetInt() )
return;
g_pDiffMgr = reinterpret_cast< IDiffMgr * >( diffcheck.GetInt() );
g_pDiffMgr->StartCommand( bServer, nCommandNumber );
return;
#endif
// Msg( "%s Startcommand %d\n", bServer ? "sv" : "cl", nCommandNumber );
diffcheck.SetValue( reinterpret_cast< int >( this ) );
Assert( CBaseEntity::IsServer() );
CUtlRBTree< CDiffInfo, int >& rb = bServer ? m_Server : m_Client;
CDiffInfo search;
search.m_nCommandNumber = nCommandNumber;
int idx = rb.Find( search );
if ( idx == rb.InvalidIndex() )
{
idx = rb.Insert( search );
}
CDiffInfo *slot = &rb[ idx ];
slot->m_Lines.RemoveAll();
}
virtual void AddToDiff( bool bServer, int nCommandNumber, char const *string )
{
#if defined( CLIENT_DLL )
if ( !diffcheck.GetInt() )
return;
g_pDiffMgr = reinterpret_cast< IDiffMgr * >( diffcheck.GetInt() );
g_pDiffMgr->AddToDiff( bServer, nCommandNumber, string );
return;
#endif
Assert( CBaseEntity::IsServer() );
// Msg( "%s Add %d %s\n", bServer ? "sv" : "cl", nCommandNumber, string );
CUtlRBTree< CDiffInfo, int >& rb = bServer ? m_Server : m_Client;
CDiffInfo search;
search.m_nCommandNumber = nCommandNumber;
int idx = rb.Find( search );
if ( idx == rb.InvalidIndex() )
{
Assert( 0 );
idx = rb.Insert( search );
}
CDiffInfo *slot = &rb[ idx ];
CDiffStr line( string );
slot->m_Lines.AddToTail( line );
}
enum EMismatched
{
DIFFCHECK_NOTREADY = 0,
DIFFCHECK_MATCHED,
DIFFCHECK_DIFFERS
};
bool ClientRecordExists( int cmd )
{
CDiffInfo clsearch;
clsearch.m_nCommandNumber = cmd;
int clidx = m_Client.Find( clsearch );
return m_Client.IsValidIndex( clidx );
}
EMismatched IsMismatched( int svidx )
{
CDiffInfo *serverslot = &m_Server[ svidx ];
// Now find the client version of this one
CDiffInfo clsearch;
clsearch.m_nCommandNumber = serverslot->m_nCommandNumber;
int clidx = m_Client.Find( clsearch );
if ( clidx == m_Client.InvalidIndex() )
return DIFFCHECK_NOTREADY;
// Now compare them
CDiffInfo *clientslot = &m_Client[ clidx ];
bool bSpew = false;
if ( serverslot->m_Lines.Count() !=
clientslot->m_Lines.Count() )
{
return DIFFCHECK_DIFFERS;
}
int maxSlot = max( serverslot->m_Lines.Count(), clientslot->m_Lines.Count() );
if ( !bSpew )
{
for ( int i = 0; i < maxSlot; ++i )
{
CDiffStr *sv = NULL;
CDiffStr *cl = NULL;
if ( i < serverslot->m_Lines.Count() )
{
sv = &serverslot->m_Lines[ i ];
}
if ( i < clientslot->m_Lines.Count() )
{
cl = &clientslot->m_Lines[ i ];
}
if ( Q_stricmp( sv ? sv->String() : "(missing)", cl ? cl->String() : "(missing)" ) )
{
return DIFFCHECK_DIFFERS;
}
}
}
return DIFFCHECK_MATCHED;
}
virtual void Validate( bool bServer, int nCommandNumber )
{
#if defined( CLIENT_DLL )
if ( !diffcheck.GetInt() )
return;
g_pDiffMgr = reinterpret_cast< IDiffMgr * >( diffcheck.GetInt() );
g_pDiffMgr->Validate( bServer, nCommandNumber );
return;
#endif
Assert( CBaseEntity::IsServer() );
// Only do this on the client
if ( !bServer )
return;
// Find the last server command number
if ( m_Server.Count() <= 0 )
return;
int svidx = m_Server.LastInorder();
EMismatched eMisMatched = IsMismatched( svidx );
if ( eMisMatched == DIFFCHECK_NOTREADY )
{
return;
}
if ( eMisMatched == DIFFCHECK_DIFFERS )
{
CUtlVector< int > vecPrev;
int nCur = svidx;
do
{
int prev = m_Server.PrevInorder( nCur );
if ( m_Server.IsValidIndex( prev ) &&
ClientRecordExists( m_Server[ prev ].m_nCommandNumber ) )
{
//SpewRecords( "prev", prev );
vecPrev.AddToHead( prev );
}
else
{
break;
}
nCur = prev;
} while ( vecPrev.Count() < 10 );
Msg( "-----\n" );
for ( int p = 0; p < vecPrev.Count(); ++p )
{
SpewRecords( "prev", vecPrev[ p ] );
}
SpewRecords( "bad ", svidx );
}
}
void SpewRecords( char const *prefix, int svidx )
{
CDiffInfo *serverslot = &m_Server[ svidx ];
// Now find the client version of this one
CDiffInfo clsearch;
clsearch.m_nCommandNumber = serverslot->m_nCommandNumber;
int clidx = m_Client.Find( clsearch );
if ( clidx == m_Client.InvalidIndex() )
return;
// Now compare them
CDiffInfo *clientslot = &m_Client[ clidx ];
int maxSlot = max( serverslot->m_Lines.Count(), clientslot->m_Lines.Count() );
for ( int i = 0; i < maxSlot; ++i )
{
char const *sv = "(missing)";
char const *cl = "(missing)";
if ( i < serverslot->m_Lines.Count() )
{
sv = serverslot->m_Lines[ i ].String();
}
if ( i < clientslot->m_Lines.Count() )
{
cl = clientslot->m_Lines[ i ].String();
}
bool bDiffers = Q_stricmp( sv, cl ) ? true : false;
Msg( "%s%s%d: sv[%50.50s] cl[%50.50s]\n",
prefix,
bDiffers ? "+++" : " ",
serverslot->m_nCommandNumber,
sv,
cl );
}
}
private:
CUtlRBTree< CDiffInfo, int > m_Server;
CUtlRBTree< CDiffInfo, int > m_Client;
float m_flLastSpew;
};
static CDiffManager g_DiffMgr;
void DiffPrint( bool bServer, int nCommandNumber, char const *fmt, … )
{
// Only track stuff for local player
CBasePlayer *pPlayer = CBaseEntity::GetPredictionPlayer();
if ( pPlayer && pPlayer->entindex() != 1 )
{
return;
}
va_list argptr;
char string[1024];
va_start (argptr,fmt);
int len = Q_vsnprintf(string, sizeof( string ), fmt,argptr);
va_end (argptr);
if ( g_pDiffMgr )
{
// Strip any \n at the end that the user accidently put int
if ( len > 0 && string[ len -1 ] == '\n' )
{
string[ len - 1 ] = 0;
}
g_pDiffMgr->AddToDiff( bServer, nCommandNumber, string );
}
}
void _CheckV( int tick, char const *ctx, const Vector &vel )
{
DiffPrint( CBaseEntity::IsServer(), tick, “%20.20s %f %f %f”, ctx, vel.x, vel.y, vel.z );
}
#define CheckV( tick, ctx, vel ) _CheckV( tick, ctx, vel );
static void StartCommand( bool bServer, int nCommandNumber )
{
// Only track stuff for local player
CBasePlayer *pPlayer = CBaseEntity::GetPredictionPlayer();
if ( pPlayer && pPlayer->entindex() != 1 )
{
return;
}
if ( g_pDiffMgr )
{
g_pDiffMgr->StartCommand( bServer, nCommandNumber );
}
}
static void Validate( bool bServer, int nCommandNumber )
{
// Only track stuff for local player
CBasePlayer *pPlayer = CBaseEntity::GetPredictionPlayer();
if ( pPlayer && pPlayer->entindex() != 1 )
{
return;
}
if ( g_pDiffMgr )
{
g_pDiffMgr->Validate( bServer, nCommandNumber );
}
}
void CGameMovement: iffPrint( char const *fmt, … )
{
if ( !player )
return;
va_list argptr;
char string[1024];
va_start (argptr,fmt);
Q_vsnprintf(string, sizeof( string ), fmt,argptr);
va_end (argptr);
: :D iffPrint( CBaseEntity::IsServer(), player->CurrentCommandNumber(), "%s", string );
}
#else
static void DiffPrint( bool bServer, int nCommandNumber, char const *fmt, … )
{
// Nothing
}
static void StartCommand( bool bServer, int nCommandNumber )
{
}
static void Validate( bool bServer, int nCommandNumber )
{
}
#define CheckV( tick, ctx, vel )
void CGameMovement: iffPrint( char const *fmt, … )
{
}
#endif // !PREDICTION_ERROR_CHECK_LEVEL
#ifndef _XBOX
void COM_Log( char *pszFile, char *fmt, …)
{
va_list argptr;
char string[1024];
FileHandle_t fp;
char *pfilename;
if ( !pszFile )
{
pfilename = "hllog.txt";
}
else
{
pfilename = pszFile;
}
va_start (argptr,fmt);
Q_vsnprintf(string, sizeof( string ), fmt,argptr);
va_end (argptr);
fp = filesystem->Open( pfilename, "a+t");
if (fp)
{
filesystem->FPrintf(fp, "%s", string);
filesystem->Close(fp);
}
}
#endif
#ifndef CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose: Debug - draw the displacement collision plane.
//-----------------------------------------------------------------------------
void DrawDispCollPlane( CBaseTrace *pTrace )
{
float flLength = 30.0f;
// Create a basis, based on the impact normal.
int nMajorAxis = 0;
Vector vecBasisU, vecBasisV, vecNormal;
vecNormal = pTrace->plane.normal;
float flAxisValue = vecNormal[0];
if ( fabs( vecNormal[1] ) > fabs( flAxisValue ) ) { nMajorAxis = 1; flAxisValue = vecNormal[1]; }
if ( fabs( vecNormal[2] ) > fabs( flAxisValue ) ) { nMajorAxis = 2; }
if ( ( nMajorAxis == 1 ) || ( nMajorAxis == 2 ) )
{
vecBasisU.Init( 1.0f, 0.0f, 0.0f );
}
else
{
vecBasisU.Init( 0.0f, 1.0f, 0.0f );
}
vecBasisV = vecNormal.Cross( vecBasisU );
VectorNormalize( vecBasisV );
vecBasisU = vecBasisV.Cross( vecNormal );
VectorNormalize( vecBasisU );
// Create the impact point. Push off the surface a bit.
Vector vecImpactPoint = pTrace->startpos + pTrace->fraction * ( pTrace->endpos - pTrace->startpos );
vecImpactPoint += vecNormal;
// Generate a quad to represent the plane.
Vector vecPlanePoints[4];
vecPlanePoints[0] = vecImpactPoint + ( vecBasisU * -flLength ) + ( vecBasisV * -flLength );
vecPlanePoints[1] = vecImpactPoint + ( vecBasisU * -flLength ) + ( vecBasisV * flLength );
vecPlanePoints[2] = vecImpactPoint + ( vecBasisU * flLength ) + ( vecBasisV * flLength );
vecPlanePoints[3] = vecImpactPoint + ( vecBasisU * flLength ) + ( vecBasisV * -flLength );
#if 0
// Test facing.
Vector vecEdges[2];
vecEdges[0] = vecPlanePoints[1] - vecPlanePoints[0];
vecEdges[1] = vecPlanePoints[2] - vecPlanePoints[0];
Vector vecCross = vecEdges[0].Cross( vecEdges[1] );
if ( vecCross.Dot( vecNormal ) < 0.0f )
{
// Reverse winding.
}
#endif
// Draw the plane.
NDebugOverlay::Triangle( vecPlanePoints[0], vecPlanePoints[1], vecPlanePoints[2], 125, 125, 125, 125, false, 5.0f );
NDebugOverlay::Triangle( vecPlanePoints[0], vecPlanePoints[2], vecPlanePoints[3], 125, 125, 125, 125, false, 5.0f );
NDebugOverlay::Line( vecPlanePoints[0], vecPlanePoints[1], 255, 255, 255, false, 5.0f );
NDebugOverlay::Line( vecPlanePoints[1], vecPlanePoints[2], 255, 255, 255, false, 5.0f );
NDebugOverlay::Line( vecPlanePoints[2], vecPlanePoints[3], 255, 255, 255, false, 5.0f );
NDebugOverlay::Line( vecPlanePoints[3], vecPlanePoints[0], 255, 255, 255, false, 5.0f );
// Draw the normal.
NDebugOverlay::Line( vecImpactPoint, vecImpactPoint + ( vecNormal * flLength ), 255, 0, 0, false, 5.0f );
}
#endif
//-----------------------------------------------------------------------------
// Purpose: Constructs GameMovement interface
//-----------------------------------------------------------------------------
CGameMovement::CGameMovement( void )
{
m_nOldWaterLevel = WL_NotInWater;
m_flWaterEntryTime = 0;
m_nOnLadder = 0;
mv = NULL;
memset( m_flStuckCheckTime, 0, sizeof(m_flStuckCheckTime) );
}
WHY DID YOU READ ALL THIS!?!?!?!?!?!
Anyhow, its part of the HL2 movement engine.[/code]