//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
# include "cbase.h"
# include "player.h"
# include "usercmd.h"
# include "igamemovement.h"
# include "mathlib/mathlib.h"
# include "client.h"
# include "player_command.h"
# include "movehelper_server.h"
# include "iservervehicle.h"
# include "tier0/vprof.h"
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
extern IGameMovement * g_pGameMovement ;
extern CMoveData * g_pMoveData ; // This is a global because it is subclassed by each game.
extern ConVar sv_noclipduringpause ;
ConVar sv_maxusrcmdprocessticks_warning ( " sv_maxusrcmdprocessticks_warning " , " -1 " , FCVAR_NONE , " Print a warning when user commands get dropped due to insufficient usrcmd ticks allocated, number of seconds to throttle, negative disabled " ) ;
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CPlayerMove : : CPlayerMove ( void )
{
}
//-----------------------------------------------------------------------------
// Purpose: We're about to run this usercmd for the specified player. We can set up groupinfo and masking here, etc.
// This is the time to examine the usercmd for anything extra. This call happens even if think does not.
// Input : *player -
// *cmd -
//-----------------------------------------------------------------------------
void CPlayerMove : : StartCommand ( CBasePlayer * player , CUserCmd * cmd )
{
VPROF ( " CPlayerMove::StartCommand " ) ;
# if !defined( NO_ENTITY_PREDICTION )
CPredictableId : : ResetInstanceCounters ( ) ;
# endif
player - > m_pCurrentCommand = cmd ;
CBaseEntity : : SetPredictionRandomSeed ( cmd ) ;
CBaseEntity : : SetPredictionPlayer ( player ) ;
# if defined (HL2_DLL)
// pull out backchannel data and move this out
int i ;
for ( i = 0 ; i < cmd - > entitygroundcontact . Count ( ) ; i + + )
{
int entindex = cmd - > entitygroundcontact [ i ] . entindex ;
CBaseEntity * pEntity = CBaseEntity : : Instance ( engine - > PEntityOfEntIndex ( entindex ) ) ;
if ( pEntity )
{
CBaseAnimating * pAnimating = pEntity - > GetBaseAnimating ( ) ;
if ( pAnimating )
{
pAnimating - > SetIKGroundContactInfo ( cmd - > entitygroundcontact [ i ] . minheight , cmd - > entitygroundcontact [ i ] . maxheight ) ;
}
}
}
# endif
}
//-----------------------------------------------------------------------------
// Purpose: We've finished running a user's command
// Input : *player -
//-----------------------------------------------------------------------------
void CPlayerMove : : FinishCommand ( CBasePlayer * player )
{
VPROF ( " CPlayerMove::FinishCommand " ) ;
player - > m_pCurrentCommand = NULL ;
CBaseEntity : : SetPredictionRandomSeed ( NULL ) ;
CBaseEntity : : SetPredictionPlayer ( NULL ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Checks if the player is standing on a moving entity and adjusts velocity and
// basevelocity appropriately
// Input : *player -
// frametime -
//-----------------------------------------------------------------------------
void CPlayerMove : : CheckMovingGround ( CBasePlayer * player , double frametime )
{
VPROF ( " CPlayerMove::CheckMovingGround() " ) ;
CBaseEntity * groundentity ;
if ( player - > GetFlags ( ) & FL_ONGROUND )
{
groundentity = player - > GetGroundEntity ( ) ;
if ( groundentity & & ( groundentity - > GetFlags ( ) & FL_CONVEYOR ) )
{
Vector vecNewVelocity ;
groundentity - > GetGroundVelocityToApply ( vecNewVelocity ) ;
if ( player - > GetFlags ( ) & FL_BASEVELOCITY )
{
vecNewVelocity + = player - > GetBaseVelocity ( ) ;
}
player - > SetBaseVelocity ( vecNewVelocity ) ;
player - > AddFlag ( FL_BASEVELOCITY ) ;
}
}
if ( ! ( player - > GetFlags ( ) & FL_BASEVELOCITY ) )
{
// Apply momentum (add in half of the previous frame of velocity first)
player - > ApplyAbsVelocityImpulse ( ( 1.0 + ( frametime * 0.5 ) ) * player - > GetBaseVelocity ( ) ) ;
player - > SetBaseVelocity ( vec3_origin ) ;
}
player - > RemoveFlag ( FL_BASEVELOCITY ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Prepares for running movement
// Input : *player -
// *ucmd -
// *pHelper -
// *move -
// time -
//-----------------------------------------------------------------------------
void CPlayerMove : : SetupMove ( CBasePlayer * player , CUserCmd * ucmd , IMoveHelper * pHelper , CMoveData * move )
{
VPROF ( " CPlayerMove::SetupMove " ) ;
// Allow sound, etc. to be created by movement code
move - > m_bFirstRunOfFunctions = true ;
move - > m_bGameCodeMovedPlayer = false ;
if ( player - > GetPreviouslyPredictedOrigin ( ) ! = player - > GetAbsOrigin ( ) )
{
move - > m_bGameCodeMovedPlayer = true ;
}
// Prepare the usercmd fields
move - > m_nImpulseCommand = ucmd - > impulse ;
move - > m_vecViewAngles = ucmd - > viewangles ;
CBaseEntity * pMoveParent = player - > GetMoveParent ( ) ;
if ( ! pMoveParent )
{
move - > m_vecAbsViewAngles = move - > m_vecViewAngles ;
}
else
{
matrix3x4_t viewToParent , viewToWorld ;
AngleMatrix ( move - > m_vecViewAngles , viewToParent ) ;
ConcatTransforms ( pMoveParent - > EntityToWorldTransform ( ) , viewToParent , viewToWorld ) ;
MatrixAngles ( viewToWorld , move - > m_vecAbsViewAngles ) ;
}
move - > m_nButtons = ucmd - > buttons ;
// Ingore buttons for movement if at controls
if ( player - > GetFlags ( ) & FL_ATCONTROLS )
{
move - > m_flForwardMove = 0 ;
move - > m_flSideMove = 0 ;
move - > m_flUpMove = 0 ;
}
else
{
move - > m_flForwardMove = ucmd - > forwardmove ;
move - > m_flSideMove = ucmd - > sidemove ;
move - > m_flUpMove = ucmd - > upmove ;
}
// Prepare remaining fields
move - > m_flClientMaxSpeed = player - > m_flMaxspeed ;
move - > m_nOldButtons = player - > m_Local . m_nOldButtons ;
move - > m_vecAngles = player - > pl . v_angle ;
move - > m_vecVelocity = player - > GetAbsVelocity ( ) ;
move - > m_nPlayerHandle = player ;
move - > SetAbsOrigin ( player - > GetAbsOrigin ( ) ) ;
// Copy constraint information
if ( player - > m_hConstraintEntity . Get ( ) )
move - > m_vecConstraintCenter = player - > m_hConstraintEntity . Get ( ) - > GetAbsOrigin ( ) ;
else
move - > m_vecConstraintCenter = player - > m_vecConstraintCenter ;
move - > m_flConstraintRadius = player - > m_flConstraintRadius ;
move - > m_flConstraintWidth = player - > m_flConstraintWidth ;
move - > m_flConstraintSpeedFactor = player - > m_flConstraintSpeedFactor ;
}
//-----------------------------------------------------------------------------
// Purpose: Finishes running movement
// Input : *player -
// *move -
// *ucmd -
// time -
//-----------------------------------------------------------------------------
void CPlayerMove : : FinishMove ( CBasePlayer * player , CUserCmd * ucmd , CMoveData * move )
{
VPROF ( " CPlayerMove::FinishMove " ) ;
// NOTE: Don't copy this. the movement code modifies its local copy but is not expecting to be authoritative
//player->m_flMaxspeed = move->m_flClientMaxSpeed;
player - > SetAbsOrigin ( move - > GetAbsOrigin ( ) ) ;
player - > SetAbsVelocity ( move - > m_vecVelocity ) ;
player - > SetPreviouslyPredictedOrigin ( move - > GetAbsOrigin ( ) ) ;
player - > m_Local . m_nOldButtons = move - > m_nButtons ;
// Convert final pitch to body pitch
float pitch = move - > m_vecAngles [ PITCH ] ;
if ( pitch > 180.0f )
{
pitch - = 360.0f ;
}
pitch = clamp ( pitch , - 90.f , 90.f ) ;
move - > m_vecAngles [ PITCH ] = pitch ;
player - > SetBodyPitch ( pitch ) ;
player - > SetLocalAngles ( move - > m_vecAngles ) ;
// The class had better not have changed during the move!!
if ( player - > m_hConstraintEntity )
Assert ( move - > m_vecConstraintCenter = = player - > m_hConstraintEntity . Get ( ) - > GetAbsOrigin ( ) ) ;
else
Assert ( move - > m_vecConstraintCenter = = player - > m_vecConstraintCenter ) ;
Assert ( move - > m_flConstraintRadius = = player - > m_flConstraintRadius ) ;
Assert ( move - > m_flConstraintWidth = = player - > m_flConstraintWidth ) ;
Assert ( move - > m_flConstraintSpeedFactor = = player - > m_flConstraintSpeedFactor ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Called before player thinks
// Input : *player -
// thinktime -
//-----------------------------------------------------------------------------
void CPlayerMove : : RunPreThink ( CBasePlayer * player )
{
VPROF ( " CPlayerMove::RunPreThink " ) ;
// Run think functions on the player
VPROF_SCOPE_BEGIN ( " player->PhysicsRunThink() " ) ;
if ( ! player - > PhysicsRunThink ( ) )
return ;
VPROF_SCOPE_END ( ) ;
VPROF_SCOPE_BEGIN ( " g_pGameRules->PlayerThink( player ) " ) ;
// Called every frame to let game rules do any specific think logic for the player
g_pGameRules - > PlayerThink ( player ) ;
VPROF_SCOPE_END ( ) ;
VPROF_SCOPE_BEGIN ( " player->PreThink() " ) ;
player - > PreThink ( ) ;
VPROF_SCOPE_END ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Runs the PLAYER's thinking code if time. There is some play in the exact time the think
// function will be called, because it is called before any movement is done
// in a frame. Not used for pushmove objects, because they must be exact.
// Returns false if the entity removed itself.
// Input : *ent -
// frametime -
// clienttimebase -
// Output : void CPlayerMove::RunThink
//-----------------------------------------------------------------------------
void CPlayerMove : : RunThink ( CBasePlayer * player , double frametime )
{
VPROF ( " CPlayerMove::RunThink " ) ;
int thinktick = player - > GetNextThinkTick ( ) ;
if ( thinktick < = 0 | | thinktick > player - > m_nTickBase )
return ;
//gpGlobals->curtime = thinktime;
player - > SetNextThink ( TICK_NEVER_THINK ) ;
// Think
player - > Think ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Called after player movement
// Input : *player -
// thinktime -
// frametime -
//-----------------------------------------------------------------------------
void CPlayerMove : : RunPostThink ( CBasePlayer * player )
{
VPROF ( " CPlayerMove::RunPostThink " ) ;
// Run post-think
player - > PostThink ( ) ;
}
void CommentarySystem_PePlayerRunCommand ( CBasePlayer * player , CUserCmd * ucmd ) ;
//-----------------------------------------------------------------------------
// Purpose: Runs movement commands for the player
// Input : *player -
// *ucmd -
// *moveHelper -
// Output : void CPlayerMove::RunCommand
//-----------------------------------------------------------------------------
void CPlayerMove : : RunCommand ( CBasePlayer * player , CUserCmd * ucmd , IMoveHelper * moveHelper )
{
const float playerCurTime = player - > m_nTickBase * TICK_INTERVAL ;
const float playerFrameTime = player - > m_bGamePaused ? 0 : TICK_INTERVAL ;
const float flTimeAllowedForProcessing = player - > ConsumeMovementTimeForUserCmdProcessing ( playerFrameTime ) ;
if ( ! player - > IsBot ( ) & & ( flTimeAllowedForProcessing < playerFrameTime ) )
{
// Make sure that the activity in command is erased because player cheated or dropped too many packets
double dblWarningFrequencyThrottle = sv_maxusrcmdprocessticks_warning . GetFloat ( ) ;
if ( dblWarningFrequencyThrottle > = 0 )
{
static double s_dblLastWarningTime = 0 ;
double dblTimeNow = Plat_FloatTime ( ) ;
if ( ! s_dblLastWarningTime | | ( dblTimeNow - s_dblLastWarningTime > = dblWarningFrequencyThrottle ) )
{
s_dblLastWarningTime = dblTimeNow ;
Warning ( " sv_maxusrcmdprocessticks_warning at server tick %u: Ignored client %s usrcmd (%.6f < %.6f)! \n " , gpGlobals - > tickcount , player - > GetPlayerName ( ) , flTimeAllowedForProcessing , playerFrameTime ) ;
}
}
return ; // Don't process this command
}
StartCommand ( player , ucmd ) ;
// Set globals appropriately
gpGlobals - > curtime = playerCurTime ;
gpGlobals - > frametime = playerFrameTime ;
// Prevent hacked clients from sending us invalid view angles to try to get leaf server code to crash
if ( ! ucmd - > viewangles . IsValid ( ) | | ! IsEntityQAngleReasonable ( ucmd - > viewangles ) )
{
ucmd - > viewangles = vec3_angle ;
}
// Add and subtract buttons we're forcing on the player
ucmd - > buttons | = player - > m_afButtonForced ;
ucmd - > buttons & = ~ player - > m_afButtonDisabled ;
if ( player - > m_bGamePaused )
{
// If no clipping and cheats enabled and noclipduring game enabled, then leave
// forwardmove and angles stuff in usercmd
if ( player - > GetMoveType ( ) = = MOVETYPE_NOCLIP & &
sv_cheats - > GetBool ( ) & &
sv_noclipduringpause . GetBool ( ) )
{
gpGlobals - > frametime = TICK_INTERVAL ;
}
}
/*
// TODO: We can check whether the player is sending more commands than elapsed real time
cmdtimeremaining - = ucmd - > msec ;
if ( cmdtimeremaining < 0 )
{
// return;
}
*/
g_pGameMovement - > StartTrackPredictionErrors ( player ) ;
CommentarySystem_PePlayerRunCommand ( player , ucmd ) ;
// Do weapon selection
if ( ucmd - > weaponselect ! = 0 )
{
CBaseCombatWeapon * weapon = dynamic_cast < CBaseCombatWeapon * > ( CBaseEntity : : Instance ( ucmd - > weaponselect ) ) ;
if ( weapon )
{
VPROF ( " player->SelectItem() " ) ;
player - > SelectItem ( weapon - > GetName ( ) , ucmd - > weaponsubtype ) ;
}
}
IServerVehicle * pVehicle = player - > GetVehicle ( ) ;
// Latch in impulse.
if ( ucmd - > impulse )
{
// Discard impulse commands unless the vehicle allows them.
// FIXME: UsingStandardWeapons seems like a bad filter for this. The flashlight is an impulse command, for example.
if ( ! pVehicle | | player - > UsingStandardWeaponsInVehicle ( ) )
{
player - > m_nImpulse = ucmd - > impulse ;
}
}
// Update player input button states
VPROF_SCOPE_BEGIN ( " player->UpdateButtonState " ) ;
player - > UpdateButtonState ( ucmd - > buttons ) ;
VPROF_SCOPE_END ( ) ;
CheckMovingGround ( player , TICK_INTERVAL ) ;
g_pMoveData - > m_vecOldAngles = player - > pl . v_angle ;
// Copy from command to player unless game .dll has set angle using fixangle
if ( player - > pl . fixangle = = FIXANGLE_NONE )
{
player - > pl . v_angle = ucmd - > viewangles ;
}
else if ( player - > pl . fixangle = = FIXANGLE_RELATIVE )
{
player - > pl . v_angle = ucmd - > viewangles + player - > pl . anglechange ;
}
// Let server invoke any needed impact functions
VPROF_SCOPE_BEGIN ( " moveHelper->ProcessImpacts " ) ;
moveHelper - > ProcessImpacts ( ) ;
VPROF_SCOPE_END ( ) ;
// Call standard client pre-think
RunPreThink ( player ) ;
// Call Think if one is set
RunThink ( player , TICK_INTERVAL ) ;
// Setup input.
SetupMove ( player , ucmd , moveHelper , g_pMoveData ) ;
// Let the game do the movement.
if ( ! pVehicle )
{
VPROF ( " g_pGameMovement->ProcessMovement() " ) ;
Assert ( g_pGameMovement ) ;
g_pGameMovement - > ProcessMovement ( player , g_pMoveData ) ;
}
else
{
VPROF ( " pVehicle->ProcessMovement() " ) ;
pVehicle - > ProcessMovement ( player , g_pMoveData ) ;
}
// Copy output
FinishMove ( player , ucmd , g_pMoveData ) ;
RunPostThink ( player ) ;
g_pGameMovement - > FinishTrackPredictionErrors ( player ) ;
FinishCommand ( player ) ;
// Let time pass
if ( gpGlobals - > frametime > 0 )
{
player - > m_nTickBase + + ;
}
}