Skip to content
Open
2 changes: 2 additions & 0 deletions src/game/client/c_baseplayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ END_RECV_TABLE()
RecvPropFloat ( RECVINFO(m_vecVelocity[2]), 0, RecvProxy_LocalVelocityZ ),

RecvPropVector ( RECVINFO( m_vecBaseVelocity ) ),
RecvPropVector(RECVINFO(m_vecPreviouslyPreviouslyPredictedEyePosition)),

RecvPropEHandle ( RECVINFO( m_hConstraintEntity)),
RecvPropVector ( RECVINFO( m_vecConstraintCenter) ),
Expand Down Expand Up @@ -399,6 +400,7 @@ BEGIN_PREDICTION_DATA( C_BasePlayer )
// DEFINE_FIELD( m_pEnvironmentLight, dlight_t* ),
// DEFINE_FIELD( m_pBrightLight, dlight_t* ),
DEFINE_PRED_FIELD( m_hLastWeapon, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD(m_vecPreviouslyPreviouslyPredictedEyePosition, FIELD_POSITION_VECTOR, FTYPEDESC_INSENDTABLE),

DEFINE_PRED_FIELD( m_nTickBase, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),

Expand Down
19 changes: 17 additions & 2 deletions src/game/client/c_baseplayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,10 @@ class C_BasePlayer : public C_BaseCombatCharacter, public CGameEventListener
float GetDeathTime( void ) { return m_flDeathTime; }

void SetPreviouslyPredictedOrigin( const Vector &vecAbsOrigin );
void SetPreviouslyPreviouslyPredictedEyePosition(const Vector& vecAbsOrigin);

const Vector &GetPreviouslyPredictedOrigin() const;
const Vector& GetPreviouslyPreviouslyPredictedEyePosition() const;

// CS wants to allow small FOVs for zoomed-in AWPs.
virtual float GetMinFOV() const;
Expand Down Expand Up @@ -399,7 +402,13 @@ class C_BasePlayer : public C_BaseCombatCharacter, public CGameEventListener
void SetFiredWeapon( bool bFlag ) { m_bFiredWeapon = bFlag; }

virtual bool CanUseFirstPersonCommand( void ){ return true; }

void SetAttackInterpolationData(const QAngle& viewAngles, float interpolationAmount);
void GetAttackInterpolationData(QAngle& viewAngles, float& lerpTime);
bool HasAttackInterpolationData() const;
void ClearAttackInterpolationData();
void SetInPostThink(bool inPostThink);
bool IsInPostThink() const;
Vector GetInterpolatedEyePosition();
protected:
fogparams_t m_CurrentFog;
EHANDLE m_hOldFogController;
Expand Down Expand Up @@ -600,7 +609,8 @@ class C_BasePlayer : public C_BaseCombatCharacter, public CGameEventListener
float m_flPredictionErrorTime;

Vector m_vecPreviouslyPredictedOrigin; // Used to determine if non-gamemovement game code has teleported, or tweaked the player's origin

Vector m_vecPreviouslyPreviouslyPredictedEyePosition; // Used for attack interpolation

char m_szLastPlaceName[MAX_PLACE_NAME_LENGTH]; // received from the server

// Texture names and surface data, used by CGameMovement
Expand All @@ -623,6 +633,11 @@ class C_BasePlayer : public C_BaseCombatCharacter, public CGameEventListener
#endif

private:
QAngle m_angAttackViewAngles; // Stored view angles during attack
float m_flAttackInterpolationAmount = 1.0f; // Interpolation amount when attack was issued
bool m_bHasAttackInterpolationData = false; // Whether we have valid data
float m_flAttackLerpTime = 1.0f; // Calculated lerp time for the attack
bool m_bInPostThink; // Flag for post-think state

struct StepSoundCache_t
{
Expand Down
62 changes: 62 additions & 0 deletions src/game/client/in_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1061,6 +1061,31 @@ void CInput::ExtraMouseSample( float frametime, bool active )
cmd->buttons = GetButtonBits( 0 );
#endif

// Check if this is an attack frame
bool bIsAttackFrame = false;

C_BaseCombatWeapon* pWeapon = NULL;
C_BasePlayer* pPlayer = CBasePlayer::GetLocalPlayer();
if (pPlayer)
pWeapon = pPlayer->GetActiveWeapon();

if (pWeapon) {
// Check primary attack
if ((cmd->buttons & IN_ATTACK) && pWeapon->m_flNextPrimaryAttack <= (gpGlobals->curtime + (gpGlobals->interpolation_amount * gpGlobals->interval_per_tick))) {
bIsAttackFrame = true;
}
// Check secondary attack
else if ((cmd->buttons & IN_ATTACK2) && pWeapon->m_flNextSecondaryAttack <= (gpGlobals->curtime + (gpGlobals->interpolation_amount * gpGlobals->interval_per_tick))) {
bIsAttackFrame = true;
}
}

if (bIsAttackFrame && !pPlayer->HasAttackInterpolationData()) {
// Store attack data
pPlayer->SetAttackInterpolationData(viewangles, gpGlobals->interpolation_amount);
}


// Use new view angles if alive, otherwise user last angles we stored off.
if ( g_iAlive )
{
Expand Down Expand Up @@ -1161,6 +1186,7 @@ void CInput::CreateMove ( int sequence_number, float input_sample_frametime, boo
ResetMouse();
}
}

// Retreive view angles from engine ( could have been set in IN_AdjustAngles above )
engine->GetViewAngles( viewangles );

Expand Down Expand Up @@ -1271,6 +1297,42 @@ void CInput::CreateMove ( int sequence_number, float input_sample_frametime, boo

cmd->random_seed = MD5_PseudoRandom( sequence_number ) & 0x7fffffff;

C_BasePlayer* pPlayer = C_BasePlayer::GetLocalPlayer();
bool bUseAttackData = pPlayer && pPlayer->HasAttackInterpolationData();

// If we have attack data, use it
if (bUseAttackData) {
QAngle attackAngles;
float lerpTime;

pPlayer->GetAttackInterpolationData(attackAngles, lerpTime);

// Store original angles before applying stored attack angles
QAngle currentAngles = viewangles;

// Apply attack data to command
cmd->viewangles = attackAngles;
cmd->lerp_time = lerpTime;

// Fix movement commands for new viewangles
float deltaYaw = DEG2RAD(currentAngles[YAW] - cmd->viewangles[YAW]);
float s = sin(deltaYaw);
float c = cos(deltaYaw);

// Store original movement values
float forwardmove = cmd->forwardmove;
float sidemove = cmd->sidemove;

// Adjust movement values based on angle change
cmd->forwardmove = (c * forwardmove) - (s * sidemove);
cmd->sidemove = (s * forwardmove) + (c * sidemove);

pPlayer->ClearAttackInterpolationData();
}
else {
cmd->lerp_time = 1.0f;
}
//Msg("CreateMove lerp_time was: %f\n", cmd->lerp_time);
HLTVCamera()->CreateMove( cmd );
#if defined( REPLAY_ENABLED )
ReplayCamera()->CreateMove( cmd );
Expand Down
8 changes: 7 additions & 1 deletion src/game/client/prediction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,7 @@ void CPrediction::SetupMove( C_BasePlayer *player, CUserCmd *ucmd, IMoveHelper *
{
move->m_bGameCodeMovedPlayer = true;
}

player->SetPreviouslyPreviouslyPredictedEyePosition(player->GetAbsOrigin() + player->GetViewOffset());
move->m_nPlayerHandle = player->GetClientHandle();
move->m_vecVelocity = player->GetAbsVelocity();
move->SetAbsOrigin( player->GetNetworkOrigin() );
Expand Down Expand Up @@ -830,8 +830,14 @@ void CPrediction::RunPostThink( C_BasePlayer *player )
#if !defined( NO_ENTITY_PREDICTION )
VPROF( "CPrediction::RunPostThink" );

// Mark that we're in post-think
player->SetInPostThink(true);

// Run post-think
player->PostThink();

// Clear post-think flag
player->SetInPostThink(false);
#endif
}

Expand Down
2 changes: 2 additions & 0 deletions src/game/server/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ BEGIN_DATADESC( CBasePlayer )
DEFINE_FIELD( m_flForwardMove, FIELD_FLOAT ),
DEFINE_FIELD( m_flSideMove, FIELD_FLOAT ),
DEFINE_FIELD( m_vecPreviouslyPredictedOrigin, FIELD_POSITION_VECTOR ),
DEFINE_FIELD(m_vecPreviouslyPreviouslyPredictedEyePosition, FIELD_POSITION_VECTOR),

DEFINE_FIELD( m_nNumCrateHudHints, FIELD_INTEGER ),

Expand Down Expand Up @@ -8159,6 +8160,7 @@ void SendProxy_CropFlagsToPlayerFlagBitsLength( const SendProp *pProp, const voi
SendPropFloat ( SENDINFO_VECTORELEM(m_vecVelocity, 2), 32, SPROP_NOSCALE|SPROP_CHANGES_OFTEN ),

SendPropVector ( SENDINFO( m_vecBaseVelocity ), 32, SPROP_NOSCALE ),
SendPropVector(SENDINFO(m_vecPreviouslyPreviouslyPredictedEyePosition), 32, SPROP_NOSCALE | SPROP_CHANGES_OFTEN),

SendPropEHandle ( SENDINFO( m_hConstraintEntity)),
SendPropVector ( SENDINFO( m_vecConstraintCenter), 0, SPROP_NOSCALE ),
Expand Down
18 changes: 17 additions & 1 deletion src/game/server/player.h
Original file line number Diff line number Diff line change
Expand Up @@ -844,7 +844,13 @@ class CBasePlayer : public CBaseCombatCharacter
// How long since this player last interacted with something the game considers an objective/target/goal
float GetTimeSinceLastObjective( void ) const { return ( m_flLastObjectiveTime == -1.f ) ? 999.f : gpGlobals->curtime - m_flLastObjectiveTime; }
void SetLastObjectiveTime( float flTime ) { m_flLastObjectiveTime = flTime; }

void SetAttackInterpolationData(const QAngle& viewAngles, float interpolationAmount);
void GetAttackInterpolationData(QAngle& viewAngles, float& lerpTime);
bool HasAttackInterpolationData() const;
void ClearAttackInterpolationData();
void SetInPostThink(bool inPostThink);
bool IsInPostThink() const;
Vector GetInterpolatedEyePosition();
// Used by gamemovement to check if the entity is stuck.
int m_StuckLast;

Expand Down Expand Up @@ -908,7 +914,10 @@ class CBasePlayer : public CBaseCombatCharacter
void ClearZoomOwner( void );

void SetPreviouslyPredictedOrigin( const Vector &vecAbsOrigin );
void SetPreviouslyPreviouslyPredictedEyePosition(const Vector& vecAbsOrigin);
const Vector &GetPreviouslyPredictedOrigin() const;
const Vector& GetPreviouslyPreviouslyPredictedEyePosition() const;

float GetFOVTime( void ){ return m_flFOVTime; }

void AdjustDrownDmg( int nAmount );
Expand Down Expand Up @@ -1191,8 +1200,10 @@ class CBasePlayer : public CBaseCombatCharacter
int m_nVehicleViewSavedFrame; // Used to mark which frame was the last one the view was calculated for

Vector m_vecPreviouslyPredictedOrigin; // Used to determine if non-gamemovement game code has teleported, or tweaked the player's origin
CNetworkVar( Vector, m_vecPreviouslyPreviouslyPredictedEyePosition); // Used for attack interpolation
int m_nBodyPitchPoseParam;


CNetworkString( m_szLastPlaceName, MAX_PLACE_NAME_LENGTH );

char m_szNetworkIDString[MAX_NETWORKID_LENGTH];
Expand Down Expand Up @@ -1262,6 +1273,11 @@ class CBasePlayer : public CBaseCombatCharacter

// used to prevent achievement announcement spam
CUtlVector< float > m_flAchievementTimes;
QAngle m_angAttackViewAngles; // Stored view angles during attack
float m_flAttackInterpolationAmount = 1.0f; // Interpolation amount when attack was issued
bool m_bHasAttackInterpolationData = false; // Whether we have valid data
float m_flAttackLerpTime = 1.0f; // Calculated lerp time for the attack
bool m_bInPostThink; // Flag for post-think state

public:
virtual unsigned int PlayerSolidMask( bool brushOnly = false ) const; // returns the solid mask for the given player, so bots can have a more-restrictive set
Expand Down
11 changes: 9 additions & 2 deletions src/game/server/player_command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ void CPlayerMove::SetupMove( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper *p
{
move->m_bGameCodeMovedPlayer = true;
}

player->SetPreviouslyPreviouslyPredictedEyePosition(player->GetAbsOrigin() + player->GetViewOffset());
// Prepare the usercmd fields
move->m_nImpulseCommand = ucmd->impulse;
move->m_vecViewAngles = ucmd->viewangles;
Expand Down Expand Up @@ -300,8 +300,14 @@ void CPlayerMove::RunPostThink( CBasePlayer *player )
{
VPROF( "CPlayerMove::RunPostThink" );

// Mark that we're in post-think
player->SetInPostThink(true);
// Run post-think
player->PostThink();

// Clear post-think flag
player->SetInPostThink(false);
player->ClearAttackInterpolationData();
}

void CommentarySystem_PePlayerRunCommand( CBasePlayer *player, CUserCmd *ucmd );
Expand Down Expand Up @@ -446,7 +452,8 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper
VPROF_SCOPE_BEGIN( "moveHelper->ProcessImpacts" );
moveHelper->ProcessImpacts();
VPROF_SCOPE_END();

//Msg("CPlayerMove::RunCommand lerp_time was: %f\n", ucmd->lerp_time);
player->SetAttackInterpolationData(ucmd->viewangles, ucmd->lerp_time);
RunPostThink( player );

g_pGameMovement->FinishTrackPredictionErrors( player );
Expand Down
29 changes: 16 additions & 13 deletions src/game/server/player_lagcompensation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -389,18 +389,20 @@ void CLagCompensationManager::StartLagCompensation( CBasePlayer *player, CUserCm
correct+= nci->GetLatency( FLOW_OUTGOING );
}

// calc number of view interpolation ticks - 1
int lerpTicks = TIME_TO_TICKS( player->m_fLerpTime );

// add view interpolation latency see C_BaseEntity::GetInterpolationAmount()
correct += TICKS_TO_TIME( lerpTicks );

// check bouns [0,sv_maxunlag]
correct = clamp( correct, 0.0f, sv_maxunlag.GetFloat() );

// add view interpolation latency directly using player's lerp time
correct += player->m_fLerpTime;

// check bounds [0,sv_maxunlag]
correct = clamp(correct, 0.0f, sv_maxunlag.GetFloat());
// adjust attack timing if we have valid interpolation data
float extraTime = 0.0f;
if (cmd->lerp_time > 0.0f && cmd->lerp_time <= 1.0f)
{
// Add fraction of a tick based on lerp_time
extraTime = cmd->lerp_time * TICK_INTERVAL;
}
// correct tick send by player
int targettick = cmd->tick_count - lerpTicks;

int targettick = cmd->tick_count - TIME_TO_TICKS(player->m_fLerpTime);
// calc difference between tick send by player and our latency based tick
float deltaTime = correct - TICKS_TO_TIME(gpGlobals->tickcount - targettick);

Expand All @@ -410,7 +412,8 @@ void CLagCompensationManager::StartLagCompensation( CBasePlayer *player, CUserCm
// DevMsg("StartLagCompensation: delta too big (%.3f)\n", deltaTime );
targettick = gpGlobals->tickcount - TIME_TO_TICKS( correct );
}

// Add the interpolation amount
float targettime = TICKS_TO_TIME(targettick) + extraTime;
// Iterate all active players
const CBitVec<MAX_EDICTS> *pEntityTransmitBits = engine->GetEntityTransmitBitsForClient( player->entindex() - 1 );
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
Expand All @@ -433,7 +436,7 @@ void CLagCompensationManager::StartLagCompensation( CBasePlayer *player, CUserCm
continue;

// Move other player back in time
BacktrackPlayer( pPlayer, TICKS_TO_TIME( targettick ) );
BacktrackPlayer(pPlayer, targettime);
}
}

Expand Down
Loading