aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/player_command.cpp
diff options
context:
space:
mode:
authorJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
committerJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
commit39ed87570bdb2f86969d4be821c94b722dc71179 (patch)
treeabc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/server/player_command.cpp
downloadsource-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz
source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/server/player_command.cpp')
-rw-r--r--mp/src/game/server/player_command.cpp461
1 files changed, 461 insertions, 0 deletions
diff --git a/mp/src/game/server/player_command.cpp b/mp/src/game/server/player_command.cpp
new file mode 100644
index 00000000..e78c5c47
--- /dev/null
+++ b/mp/src/game/server/player_command.cpp
@@ -0,0 +1,461 @@
+//========= 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;
+ }
+
+ // 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 );
+
+ // Let server invoke any needed impact functions
+ VPROF_SCOPE_BEGIN( "moveHelper->ProcessImpacts" );
+ moveHelper->ProcessImpacts();
+ VPROF_SCOPE_END();
+
+ RunPostThink( player );
+
+ g_pGameMovement->FinishTrackPredictionErrors( player );
+
+ FinishCommand( player );
+
+ // Let time pass
+ if ( gpGlobals->frametime > 0 )
+ {
+ player->m_nTickBase++;
+ }
+}