Improved lag compensation

This commit is contained in:
Kamay Xutax 2024-01-25 13:27:03 +01:00
parent 1b8162a4b6
commit ca6cdb51e6
20 changed files with 179 additions and 241 deletions

View file

@ -175,7 +175,7 @@ void CL_ClanIdChanged( IConVar *pConVar, const char *pOldString, float flOldValu
ConVar cl_resend ( "cl_resend","6", FCVAR_NONE, "Delay in seconds before the client will resend the 'connect' attempt", true, CL_MIN_RESEND_TIME, true, CL_MAX_RESEND_TIME );
ConVar cl_name ( "name","unnamed", FCVAR_ARCHIVE | FCVAR_USERINFO | FCVAR_PRINTABLEONLY | FCVAR_SERVER_CAN_EXECUTE, "Current user name", CL_NameCvarChanged );
ConVar password ( "password", "", FCVAR_ARCHIVE | FCVAR_SERVER_CANNOT_QUERY | FCVAR_DONTRECORD, "Current server access password" );
ConVar cl_interpolate( "cl_interpolate", "1.0", FCVAR_USERINFO | FCVAR_DEVELOPMENTONLY | FCVAR_NOT_CONNECTED, "Interpolate entities on the client." );
ConVar cl_interpolate( "cl_interpolate", "1", FCVAR_USERINFO, "Interpolate entities on the client." );
ConVar cl_clanid( "cl_clanid", "0", FCVAR_ARCHIVE | FCVAR_USERINFO | FCVAR_HIDDEN, "Current clan ID for name decoration", CL_ClanIdChanged );
ConVar cl_show_connectionless_packet_warnings( "cl_show_connectionless_packet_warnings", "0", FCVAR_NONE, "Show console messages about ignored connectionless packets on the client." );

View file

@ -47,6 +47,9 @@
#include "matchmaking.h"
#include "server.h"
#include "eiface.h"
#include "globalvars_base.h"
extern CGlobalVarsBase* gpGlobals;
#include "tier0/platform.h"
#include "tier0/systeminformation.h"
@ -623,6 +626,13 @@ float CClientState::GetFrameTime() const
float CClientState::GetClientInterpAmount()
{
static const ConVar *cl_interpolate = g_pCVar->FindVar("cl_interpolate");
if (!cl_interpolate->GetBool())
{
return 0.0f;
}
// we need client cvar cl_interp_ratio
static const ConVar *s_cl_interp_ratio = NULL;
if ( !s_cl_interp_ratio )

View file

@ -3233,11 +3233,14 @@ void _Host_RunFrame (float time)
// Make sure state is correct
CL_CheckClientState();
#endif
//-------------------
// input processing
//-------------------
//---------------------------------------------------------
// Run prediction, useful when fps is lower than tickrate.
//---------------------------------------------------------
CL_RunPrediction( PREDICTION_NORMAL );
_Host_RunFrame_Input( prevremainder, bFinalTick );
prevremainder = 0;
//-------------------
//
// server operations
@ -3248,6 +3251,7 @@ void _Host_RunFrame (float time)
// Additional networking ops for SPLITPACKET stuff (99.9% of the time this will be an empty list of work)
NET_SendQueuedPackets();
//-------------------
//
// client operations
@ -3438,6 +3442,8 @@ void _Host_RunFrame (float time)
++host_currentframetick;
g_ClientGlobalVariables.tickcount = host_tickcount;
bool bFinalTick = tick==(serverticks-1) ? true : false;
// Run prediction before inputs if fps is lower than tickrate
CL_RunPrediction( PREDICTION_NORMAL );
_Host_RunFrame_Input( prevremainder, bFinalTick );
prevremainder = 0;
// process any asynchronous network traffic (TCP), set net_time

View file

@ -6,11 +6,13 @@
//===========================================================================//
#include "cbase.h"
#include "c_baseentity.h"
#include "interpolatedvar.h"
#include "prediction.h"
#include "model_types.h"
#include "iviewrender_beams.h"
#include "dlight.h"
#include "iviewrender.h"
#include "shareddefs.h"
#include "view.h"
#include "iefx.h"
#include "c_team.h"
@ -73,7 +75,7 @@ void cc_cl_interp_all_changed( IConVar *pConVar, const char *pOldString, float f
}
static ConVar cl_extrapolate( "cl_extrapolate", "1", FCVAR_CHEAT, "Enable/disable extrapolation if interpolation history runs out." );
static ConVar cl_extrapolate( "cl_extrapolate", "0", FCVAR_CHEAT, "Enable/disable extrapolation if interpolation history runs out." );
static ConVar cl_interp_npcs( "cl_interp_npcs", "0.0", FCVAR_USERINFO, "Interpolate NPC positions starting this many seconds in past (or cl_interp, if greater)" );
static ConVar cl_interp_all( "cl_interp_all", "0", 0, "Disable interpolation list optimizations.", 0, 0, 0, 0, cc_cl_interp_all_changed );
ConVar r_drawmodeldecals( "r_drawmodeldecals", "1" );
@ -301,70 +303,6 @@ int CRecordingList::Count()
// Should these be somewhere else?
#define PITCH 0
//-----------------------------------------------------------------------------
// Purpose: Decodes animtime and notes when it changes
// Input : *pStruct - ( C_BaseEntity * ) used to flag animtime is changine
// *pVarData -
// *pIn -
// objectID -
//-----------------------------------------------------------------------------
void RecvProxy_AnimTime( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
C_BaseEntity *pEntity = ( C_BaseEntity * )pStruct;
Assert( pOut == &pEntity->m_flAnimTime );
int t;
int tickbase;
int addt;
// Unpack the data.
addt = pData->m_Value.m_Int;
// Note, this needs to be encoded relative to packet timestamp, not raw client clock
tickbase = gpGlobals->GetNetworkBase( gpGlobals->tickcount, pEntity->entindex() );
t = tickbase;
// and then go back to floating point time.
t += addt; // Add in an additional up to 256 100ths from the server
// center m_flAnimTime around current time.
while (t < gpGlobals->tickcount - 127)
t += 256;
while (t > gpGlobals->tickcount + 127)
t -= 256;
pEntity->m_flAnimTime = ( t * TICK_INTERVAL );
}
void RecvProxy_SimulationTime( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
C_BaseEntity *pEntity = ( C_BaseEntity * )pStruct;
Assert( pOut == &pEntity->m_flSimulationTime );
int t;
int tickbase;
int addt;
// Unpack the data.
addt = pData->m_Value.m_Int;
// Note, this needs to be encoded relative to packet timestamp, not raw client clock
tickbase = gpGlobals->GetNetworkBase( gpGlobals->tickcount, pEntity->entindex() );
t = tickbase;
// and then go back to floating point time.
t += addt; // Add in an additional up to 256 100ths from the server
// center m_flSimulationTime around current time.
while (t < gpGlobals->tickcount - 127)
t += 256;
while (t > gpGlobals->tickcount + 127)
t -= 256;
pEntity->m_flSimulationTime = ( t * TICK_INTERVAL );
}
void RecvProxy_LocalVelocity( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
CBaseEntity *pEnt = (CBaseEntity *)pStruct;
@ -417,7 +355,7 @@ void RecvProxy_EffectFlags( const CRecvProxyData *pData, void *pStruct, void *pO
BEGIN_RECV_TABLE_NOBASE( C_BaseEntity, DT_AnimTimeMustBeFirst )
RecvPropInt( RECVINFO(m_flAnimTime), 0, RecvProxy_AnimTime ),
RecvPropFloat( RECVINFO(m_flAnimTime) ),
END_RECV_TABLE()
@ -431,7 +369,7 @@ END_RECV_TABLE()
BEGIN_RECV_TABLE_NOBASE(C_BaseEntity, DT_BaseEntity)
RecvPropDataTable( "AnimTimeMustBeFirst", 0, 0, &REFERENCE_RECV_TABLE(DT_AnimTimeMustBeFirst) ),
RecvPropInt( RECVINFO(m_flSimulationTime), 0, RecvProxy_SimulationTime ),
RecvPropFloat( RECVINFO(m_flSimulationTime) ),
RecvPropInt( RECVINFO( m_ubInterpolationFrame ) ),
RecvPropVector( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ),
@ -890,10 +828,13 @@ inline int C_BaseEntity::Interp_Interpolate( VarMapping_t *map, float currentTim
C_BaseEntity::C_BaseEntity() :
m_iv_vecOrigin( "C_BaseEntity::m_iv_vecOrigin" ),
m_iv_angRotation( "C_BaseEntity::m_iv_angRotation" ),
m_iv_vecVelocity( "C_BaseEntity::m_iv_vecVelocity" )
m_iv_vecVelocity( "C_BaseEntity::m_iv_vecVelocity" ),
m_iv_flSimulationTime( "C_BaseEntity::m_iv_flSimulationTime" )
{
AddVar( &m_vecOrigin, &m_iv_vecOrigin, LATCH_SIMULATION_VAR );
AddVar( &m_angRotation, &m_iv_angRotation, LATCH_SIMULATION_VAR );
// Needed for lag compensation
AddVar( &m_flInterpolatedSimulationTime, &m_iv_flSimulationTime, LATCH_SIMULATION_VAR );
// Removing this until we figure out why velocity introduces view hitching.
// One possible fix is removing the player->ResetLatched() call in CGameMovement::FinishDuck(),
// but that re-introduces a third-person hitching bug. One possible cause is the abrupt change
@ -1010,6 +951,7 @@ void C_BaseEntity::Clear( void )
m_nModelIndex = 0;
m_flAnimTime = 0;
m_flSimulationTime = 0;
m_flInterpolatedSimulationTime = 0;
SetSolid( SOLID_NONE );
SetSolidFlags( 0 );
SetMoveCollide( MOVECOLLIDE_DEFAULT );
@ -2549,6 +2491,10 @@ void C_BaseEntity::PostDataUpdate( DataUpdateType_t updateType )
bool anglesChanged = ( m_vecOldAngRotation != GetLocalAngles() ) ? true : false;
bool simTimeChanged = ( m_flSimulationTime != m_flOldSimulationTime ) ? true : false;
// Store simulation time for lag compensation.
if (simTimeChanged)
m_flInterpolatedSimulationTime = m_flSimulationTime;
// Detect simulation changes
bool simulationChanged = originChanged || anglesChanged || simTimeChanged;
@ -3173,7 +3119,7 @@ void C_BaseEntity::Simulate()
}
// Defined in engine
static ConVar cl_interpolate( "cl_interpolate", "1.0f", FCVAR_USERINFO | FCVAR_DEVELOPMENTONLY );
static ConVar cl_interpolate( "cl_interpolate", "1", FCVAR_USERINFO );
// (static function)
void C_BaseEntity::InterpolateServerEntities()

View file

@ -1287,6 +1287,8 @@ public:
float m_flSimulationTime;
float m_flOldSimulationTime;
float m_flInterpolatedSimulationTime;
CInterpolatedVar<float> m_iv_flSimulationTime;
float m_flCreateTime;

View file

@ -31,7 +31,7 @@ IMPLEMENT_CLIENTCLASS_DT( C_FuncRotating, DT_FuncRotating, CFuncRotating )
RecvPropFloat( RECVINFO_NAME( m_angNetworkAngles[0], m_angRotation[0] ) ),
RecvPropFloat( RECVINFO_NAME( m_angNetworkAngles[1], m_angRotation[1] ) ),
RecvPropFloat( RECVINFO_NAME( m_angNetworkAngles[2], m_angRotation[2] ) ),
RecvPropInt( RECVINFO(m_flSimulationTime), 0, RecvProxy_SimulationTime ),
RecvPropFloat( RECVINFO(m_flSimulationTime) ),
END_RECV_TABLE()

View file

@ -8,6 +8,7 @@
#include "cbase.h"
#include "cdll_bounded_cvars.h"
#include "convar_serverbounded.h"
#include "icvar.h"
#include "tier0/icommandline.h"
@ -22,7 +23,7 @@ class CBoundedCvar_Predict : public ConVar_ServerBounded
public:
CBoundedCvar_Predict() :
ConVar_ServerBounded( "cl_predict",
"1.0",
"1.0",
#if defined(DOD_DLL) || defined(CSTRIKE_DLL)
FCVAR_USERINFO | FCVAR_CHEAT,
#else
@ -65,7 +66,7 @@ class CBoundedCvar_InterpRatio : public ConVar_ServerBounded
public:
CBoundedCvar_InterpRatio() :
ConVar_ServerBounded( "cl_interp_ratio",
"2.0",
"1.0",
FCVAR_USERINFO | FCVAR_NOT_CONNECTED,
"Sets the interpolation amount (final amount is cl_interp_ratio / cl_updaterate)." )
{
@ -99,7 +100,7 @@ class CBoundedCvar_Interp : public ConVar_ServerBounded
public:
CBoundedCvar_Interp() :
ConVar_ServerBounded( "cl_interp",
"0.1",
"0.0",
FCVAR_USERINFO | FCVAR_NOT_CONNECTED,
"Sets the interpolation amount (bounded on low side by server interp ratio settings).", true, 0.0f, true, 0.5f )
{
@ -125,7 +126,14 @@ ConVar_ServerBounded *cl_interp = &cl_interp_var;
float GetClientInterpAmount()
{
static const ConVar *cl_interpolate = g_pCVar->FindVar("cl_interpolate");
static const ConVar *pUpdateRate = g_pCVar->FindVar( "cl_updaterate" );
if (!cl_interpolate->GetBool())
{
return 0.0f;
}
if ( pUpdateRate )
{
// #define FIXME_INTERP_RATIO
@ -138,7 +146,7 @@ float GetClientInterpAmount()
AssertMsgOnce( false, "GetInterpolationAmount: can't get cl_updaterate cvar." );
}
return 0.1;
return 0.1f;
}
}

View file

@ -9,7 +9,12 @@
#include "cbase.h"
#include "cdll_bounded_cvars.h"
#include "cdll_client_int.h"
#include "cdll_util.h"
#include "dbg.h"
#include "kbutton.h"
#include "shareddefs.h"
#include "usercmd.h"
#include "in_buttons.h"
#include "input.h"
@ -20,6 +25,7 @@
#include "checksum_md5.h"
#include "touch.h"
#include "hltvcamera.h"
#include "util_shared.h"
#if defined( REPLAY_ENABLED )
#include "replay/replaycamera.h"
#endif
@ -1283,6 +1289,42 @@ void CInput::CreateMove ( int sequence_number, float input_sample_frametime, boo
m_EntityGroundContact.RemoveAll();
#endif
// static bool firstTime = false;
// if (cmd->buttons & IN_ATTACK)
// {
// if (!firstTime)
// {
// for (int i = 0; i <= MAX_PLAYERS; i++)
// {
// auto pPlayer = UTIL_PlayerByIndex(i);
// if (pPlayer)
// {
// const auto &origin = pPlayer->GetAbsOrigin();
// DevMsg("Movement: %s => %f %f %f => %f\n", pPlayer->GetPlayerName(), origin.x, origin.y, origin.z, pPlayer->m_flInterpolatedSimulationTime);
// }
// }
// firstTime = true;
// }
// }
// else
// {
// firstTime = false;
// }
// Send interpolated simulation time for lag compensation
for (int i = 0; i <= MAX_PLAYERS; i++)
{
auto pPlayer = UTIL_PlayerByIndex(i);
if (pPlayer)
{
cmd->simulationtimes[pPlayer->index] = pPlayer->m_flInterpolatedSimulationTime;
}
}
pVerified->m_cmd = *cmd;
pVerified->m_crc = cmd->GetChecksum();
}

View file

@ -97,51 +97,6 @@ bool CBaseEntity::s_bAbsQueriesValid = true;
ConVar sv_netvisdist( "sv_netvisdist", "10000", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Test networking visibility distance" );
// This table encodes edict data.
void SendProxy_AnimTime( const SendProp *pProp, const void *pStruct, const void *pVarData, DVariant *pOut, int iElement, int objectID )
{
CBaseEntity *pEntity = (CBaseEntity *)pStruct;
#if defined( _DEBUG )
CBaseAnimating *pAnimating = pEntity->GetBaseAnimating();
Assert( pAnimating );
if ( pAnimating )
{
Assert( !pAnimating->IsUsingClientSideAnimation() );
}
#endif
int ticknumber = TIME_TO_TICKS( pEntity->m_flAnimTime );
// Tickbase is current tick rounded down to closes 100 ticks
int tickbase = gpGlobals->GetNetworkBase( gpGlobals->tickcount, pEntity->entindex() );
int addt = 0;
// If it's within the last tick interval through the current one, then we can encode it
if ( ticknumber >= ( tickbase - 100 ) )
{
addt = ( ticknumber - tickbase ) & 0xFF;
}
pOut->m_Int = addt;
}
// This table encodes edict data.
void SendProxy_SimulationTime( const SendProp *pProp, const void *pStruct, const void *pVarData, DVariant *pOut, int iElement, int objectID )
{
CBaseEntity *pEntity = (CBaseEntity *)pStruct;
int ticknumber = TIME_TO_TICKS( pEntity->m_flSimulationTime );
// tickbase is current tick rounded down to closest 100 ticks
int tickbase = gpGlobals->GetNetworkBase( gpGlobals->tickcount, pEntity->entindex() );
int addt = 0;
if ( ticknumber >= tickbase )
{
addt = ( ticknumber - tickbase ) & 0xff;
}
pOut->m_Int = addt;
}
void* SendProxy_ClientSideAnimation( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID )
{
CBaseEntity *pEntity = (CBaseEntity *)pStruct;
@ -160,7 +115,7 @@ BEGIN_SEND_TABLE_NOBASE( CBaseEntity, DT_AnimTimeMustBeFirst )
// proxy on the client that stores off the old values before writing in the new values and
// if it is sent after the new values, then it will only have the new origin and studio model, etc.
// interpolation will be busted
SendPropInt (SENDINFO(m_flAnimTime), 8, SPROP_UNSIGNED|SPROP_CHANGES_OFTEN|SPROP_ENCODED_AGAINST_TICKCOUNT, SendProxy_AnimTime),
SendPropFloat (SENDINFO(m_flAnimTime)),
END_SEND_TABLE()
#if !defined( NO_ENTITY_PREDICTION )
@ -259,7 +214,7 @@ void SendProxy_Angles( const SendProp *pProp, const void *pStruct, const void *p
// This table encodes the CBaseEntity data.
IMPLEMENT_SERVERCLASS_ST_NOBASE( CBaseEntity, DT_BaseEntity )
SendPropDataTable( "AnimTimeMustBeFirst", 0, &REFERENCE_SEND_TABLE(DT_AnimTimeMustBeFirst), SendProxy_ClientSideAnimation ),
SendPropInt (SENDINFO(m_flSimulationTime), SIMULATION_TIME_WINDOW_BITS, SPROP_UNSIGNED|SPROP_CHANGES_OFTEN|SPROP_ENCODED_AGAINST_TICKCOUNT, SendProxy_SimulationTime),
SendPropFloat (SENDINFO(m_flSimulationTime)),
#if PREDICTION_ERROR_CHECK_LEVEL > 1
SendPropVector (SENDINFO(m_vecOrigin), -1, SPROP_NOSCALE|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_Origin ),

View file

@ -560,24 +560,6 @@ void SendProxy_FuncRotatingAngle( const SendProp *pProp, const void *pStruct, co
Assert( IsFinite( pOut->m_Float ) );
}
extern void SendProxy_SimulationTime( const SendProp *pProp, const void *pStruct, const void *pVarData, DVariant *pOut, int iElement, int objectID );
void SendProxy_FuncRotatingSimulationTime( const SendProp *pProp, const void *pStruct, const void *pVarData, DVariant *pOut, int iElement, int objectID )
{
#ifdef TF_DLL
CFuncRotating *entity = (CFuncRotating*)pStruct;
Assert( entity );
if ( entity->HasSpawnFlags(SF_BRUSH_ROTATE_CLIENTSIDE) )
{
pOut->m_Int = 0;
return;
}
#endif
SendProxy_SimulationTime( pProp, pStruct, pVarData, pOut, iElement, objectID );
}
IMPLEMENT_SERVERCLASS_ST(CFuncRotating, DT_FuncRotating)
SendPropExclude( "DT_BaseEntity", "m_angRotation" ),
SendPropExclude( "DT_BaseEntity", "m_vecOrigin" ),
@ -588,7 +570,7 @@ IMPLEMENT_SERVERCLASS_ST(CFuncRotating, DT_FuncRotating)
SendPropAngle( SENDINFO_VECTORELEM(m_angRotation, 1), 13, SPROP_CHANGES_OFTEN, SendProxy_FuncRotatingAngle ),
SendPropAngle( SENDINFO_VECTORELEM(m_angRotation, 2), 13, SPROP_CHANGES_OFTEN, SendProxy_FuncRotatingAngle ),
SendPropInt(SENDINFO(m_flSimulationTime), SIMULATION_TIME_WINDOW_BITS, SPROP_UNSIGNED|SPROP_CHANGES_OFTEN|SPROP_ENCODED_AGAINST_TICKCOUNT, SendProxy_FuncRotatingSimulationTime),
SendPropFloat(SENDINFO(m_flSimulationTime))
END_SEND_TABLE()

View file

@ -4181,23 +4181,9 @@ void CCSPlayer::NoteWeaponFired()
}
bool CCSPlayer::WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec<MAX_EDICTS> *pEntityTransmitBits ) const
bool CCSPlayer::WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec<MAX_EDICTS> *pEntityTransmitBits )
{
// No need to lag compensate at all if we're not attacking in this command and
// we haven't attacked recently.
if ( !( pCmd->buttons & IN_ATTACK ) && (pCmd->command_number - m_iLastWeaponFireUsercmd > 5) )
{
if ( ( pCmd->buttons & IN_ATTACK2 ) == 0 )
return false;
CWeaponCSBase *weapon = GetActiveCSWeapon();
if ( !weapon )
return false;
if ( weapon->GetWeaponID() != WEAPON_KNIFE )
return false; // IN_ATTACK2 with WEAPON_KNIFE should do lag compensation
}
// No need to check for IN_ATTACK etc since it is called in firebullet
return BaseClass::WantsLagCompensationOnEntity( pPlayer, pCmd, pEntityTransmitBits );
}

View file

@ -508,7 +508,7 @@ public:
// Called whenever this player fires a shot.
void NoteWeaponFired();
virtual bool WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec<MAX_EDICTS> *pEntityTransmitBits ) const;
virtual bool WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec<MAX_EDICTS> *pEntityTransmitBits );
// ------------------------------------------------------------------------------------------------ //
// Player state management.

View file

@ -2831,33 +2831,6 @@ void CServerGameClients::ClientSettingsChanged( edict_t *pEdict )
static const ConVar *pMaxUpdateRate = g_pCVar->FindVar( "sv_maxupdaterate" );
if ( pMinUpdateRate && pMaxUpdateRate )
player->m_nUpdateRate = clamp( player->m_nUpdateRate, (int) pMinUpdateRate->GetFloat(), (int) pMaxUpdateRate->GetFloat() );
bool useInterpolation = Q_atoi( QUICKGETCVARVALUE("cl_interpolate") ) != 0;
if ( useInterpolation )
{
float flLerpRatio = Q_atof( QUICKGETCVARVALUE("cl_interp_ratio") );
if ( flLerpRatio == 0 )
flLerpRatio = 1.0f;
float flLerpAmount = Q_atof( QUICKGETCVARVALUE("cl_interp") );
static const ConVar *pMin = g_pCVar->FindVar( "sv_client_min_interp_ratio" );
static const ConVar *pMax = g_pCVar->FindVar( "sv_client_max_interp_ratio" );
if ( pMin && pMax && pMin->GetFloat() != -1 )
{
flLerpRatio = clamp( flLerpRatio, pMin->GetFloat(), pMax->GetFloat() );
}
else
{
if ( flLerpRatio == 0 )
flLerpRatio = 1.0f;
}
// #define FIXME_INTERP_RATIO
player->m_fLerpTime = MAX( flLerpAmount, flLerpRatio / player->m_nUpdateRate );
}
else
{
player->m_fLerpTime = 0.0f;
}
#if !defined( NO_ENTITY_PREDICTION )
bool usePrediction = Q_atoi( QUICKGETCVARVALUE("cl_predict")) != 0;

View file

@ -283,7 +283,6 @@ BEGIN_DATADESC( CBasePlayer )
// DEFINE_CUSTOM_FIELD( m_Activity, ActivityDataOps() ),
DEFINE_FIELD( m_nUpdateRate, FIELD_INTEGER ),
DEFINE_FIELD( m_fLerpTime, FIELD_FLOAT ),
DEFINE_FIELD( m_bLagCompensation, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bPredictWeapons, FIELD_BOOLEAN ),
@ -592,7 +591,6 @@ CBasePlayer::CBasePlayer( )
m_hZoomOwner = NULL;
m_nUpdateRate = 20; // cl_updaterate defualt
m_fLerpTime = 0.1f; // cl_interp default
m_bPredictWeapons = true;
m_bLagCompensation = false;
m_flLaggedMovementValue = 1.0f;
@ -724,37 +722,46 @@ int CBasePlayer::ShouldTransmit( const CCheckTransmitInfo *pInfo )
return BaseClass::ShouldTransmit( pInfo );
}
static void NormalizeAngles( QAngle& angles )
{
int i;
bool CBasePlayer::WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec<MAX_EDICTS> *pEntityTransmitBits ) const
// Normalize angles to -180 to 180 range
for ( i = 0; i < 3; i++ )
{
if ( angles[i] > 180.0 )
{
angles[i] -= 360.0;
}
else if ( angles[i] < -180.0 )
{
angles[i] += 360.0;
}
}
}
bool CBasePlayer::WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec<MAX_EDICTS> *pEntityTransmitBits )
{
// Team members shouldn't be adjusted unless friendly fire is on.
if ( !friendlyfire.GetInt() && pPlayer->GetTeamNumber() == GetTeamNumber() )
return false;
// If this entity hasn't been transmitted to us and acked, then don't bother lag compensating it.
if ( pEntityTransmitBits && !pEntityTransmitBits->Get( pPlayer->entindex() ) )
return false;
const Vector &vMyOrigin = GetAbsOrigin();
Vector vShootPosition = Weapon_ShootPosition();
const Vector &vHisOrigin = pPlayer->GetAbsOrigin();
// get max distance player could have moved within max lag compensation time,
// multiply by 1.5 to to avoid "dead zones" (sqrt(2) would be the exact value)
float maxDistance = 1.5 * pPlayer->MaxSpeed() * sv_maxunlag.GetFloat();
// If the player is within this distance, lag compensate them in case they're running past us.
if ( vHisOrigin.DistTo( vMyOrigin ) < maxDistance )
return true;
// If their origin is not within a 45 degree cone in front of us, no need to lag compensate.
Vector vForward;
AngleVectors( pCmd->viewangles, &vForward );
Vector vDiff = vHisOrigin - vMyOrigin;
Vector vDiff = vHisOrigin - vShootPosition;
VectorNormalize( vDiff );
float flCosAngle = 0.707107f; // 45 degree angle
if ( vForward.Dot( vDiff ) < flCosAngle )
QAngle aDiff;
VectorAngles(vDiff, aDiff);
aDiff = pCmd->viewangles - aDiff;
NormalizeAngles(aDiff);
// 90 degree angle, just to be sure;
static constexpr auto flMaxAngle = 90.f;
if ( abs( aDiff.x ) > flMaxAngle && abs( aDiff.y ) > flMaxAngle )
return false;
return true;

View file

@ -282,7 +282,7 @@ public:
// Returns true if this player wants pPlayer to be moved back in time when this player runs usercmds.
// Saves a lot of overhead on the server if we can cull out entities that don't need to lag compensate
// (like team members, entities out of our PVS, etc).
virtual bool WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec<MAX_EDICTS> *pEntityTransmitBits ) const;
virtual bool WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec<MAX_EDICTS> *pEntityTransmitBits );
virtual void Spawn( void );
virtual void Activate( void );
@ -873,7 +873,6 @@ public:
char m_szAnimExtension[32];
int m_nUpdateRate; // user snapshot rate cl_updaterate
float m_fLerpTime; // users cl_interp
bool m_bLagCompensation; // user wants lag compenstation
bool m_bPredictWeapons; // user has client side predicted weapons

View file

@ -26,7 +26,6 @@
#define LC_ANIMATION_CHANGED (1<<11)
#define LC_POSE_PARAMS_CHANGED (1<<12)
static ConVar sv_lagcompensation_teleport_dist( "sv_lagcompensation_teleport_dist", "64", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "How far a player got moved by game code before we can't lag compensate their position back" );
#define LAG_COMPENSATION_EPS_SQR ( 0.1f * 0.1f )
// Allow 4 units of error ( about 1 / 8 bbox width )
#define LAG_COMPENSATION_ERROR_EPS_SQR ( 4.0f * 4.0f )
@ -173,7 +172,7 @@ static void RestorePlayerTo( CBasePlayer *pPlayer, const Vector &vWantedPos )
class CLagCompensationManager : public CAutoGameSystemPerFrame, public ILagCompensationManager
{
public:
CLagCompensationManager( char const *name ) : CAutoGameSystemPerFrame( name ), m_flTeleportDistanceSqr( 64 *64 )
CLagCompensationManager( char const *name ) : CAutoGameSystemPerFrame( name )
{
}
@ -217,8 +216,6 @@ private:
LagRecord m_ChangeData[ MAX_PLAYERS ]; // player data where we moved him back
CBasePlayer *m_pCurrentPlayer; // The player we are doing lag compensation for
float m_flTeleportDistanceSqr;
};
static CLagCompensationManager g_LagCompensationManager( "CLagCompensationManager" );
@ -235,8 +232,6 @@ void CLagCompensationManager::FrameUpdatePostEntityThink()
ClearHistory();
return;
}
m_flTeleportDistanceSqr = sv_lagcompensation_teleport_dist.GetFloat() * sv_lagcompensation_teleport_dist.GetFloat();
VPROF_BUDGET( "FrameUpdatePostEntityThink", "CLagCompensationManager" );
@ -362,12 +357,6 @@ void CLagCompensationManager::StartLagCompensation( CBasePlayer *player, CUserCm
Q_memset( m_RestoreData, 0, sizeof( m_RestoreData ) );
Q_memset( m_ChangeData, 0, sizeof( m_ChangeData ) );
// calc number of view interpolation ticks - 1
int lerpTicks = TIME_TO_TICKS( player->m_fLerpTime );
// correct tick send by player
int targettick = cmd->tick_count - lerpTicks;
// Iterate all active players
const CBitVec<MAX_EDICTS> *pEntityTransmitBits = engine->GetEntityTransmitBitsForClient( player->entindex() - 1 );
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
@ -390,7 +379,7 @@ void CLagCompensationManager::StartLagCompensation( CBasePlayer *player, CUserCm
continue;
// Move other player back in time
BacktrackPlayer( pPlayer, TICKS_TO_TIME( targettick ) );
BacktrackPlayer( pPlayer, cmd->simulationtimes[i] );
}
}
@ -433,12 +422,7 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer *pPlayer, float flTar
return;
}
Vector delta = record->m_vecOrigin - prevOrg;
if ( delta.Length2DSqr() > m_flTeleportDistanceSqr )
{
// lost track, too much difference
return;
}
// TODO: do proper teleportation checks.
// did we find a context smaller than target time ?
if ( record->m_flSimulationTime <= flTargetTime )
@ -728,6 +712,9 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer *pPlayer, float flTar
{
pPlayer->DrawServerHitboxes(4, true);
}
// DevMsg("Server: %s => %f %f %f => %f (frac: %f)\n", pPlayer->GetPlayerName(), change->m_vecOrigin.x, change->m_vecOrigin.y, change->m_vecOrigin.z, flTargetTime, frac);
}
@ -799,11 +786,7 @@ void CLagCompensationManager::FinishLagCompensation( CBasePlayer *player )
// Okay, let's see if we can do something reasonable with the change
Vector delta = pPlayer->GetLocalOrigin() - change->m_vecOrigin;
// If it moved really far, just leave the player in the new spot!!!
if ( delta.Length2DSqr() < m_flTeleportDistanceSqr )
{
RestorePlayerTo( pPlayer, restore->m_vecOrigin + delta );
}
RestorePlayerTo( pPlayer, restore->m_vecOrigin + delta );
}
if( restore->m_fFlags & LC_ANIMATION_CHANGED )

View file

@ -417,6 +417,8 @@ void CCSPlayer::FireBullet(
{
#ifdef CLIENT_DLL
lagPlayer->DrawClientHitboxes(4, true);
//DevMsg("Client: %s => %f %f %f => %f\n", lagPlayer->GetPlayerName(), lagPlayer->GetAbsOrigin().x, lagPlayer->GetAbsOrigin().y, lagPlayer->GetAbsOrigin().z,
// lagPlayer->m_flInterpolatedSimulationTime);
#else
lagPlayer->DrawServerHitboxes(4, true);
#endif

View file

@ -873,11 +873,6 @@ enum
RT_STATE_NORMAL, // Timer is in normal mode
};
enum
{
SIMULATION_TIME_WINDOW_BITS = 8,
};
//-----------------------------------------------------------------------------
// Commentary Mode
//-----------------------------------------------------------------------------

View file

@ -11,7 +11,15 @@
#include "checksum_md5.h"
// memdbgon must be the last include file in a .cpp file!!!
#ifdef CLIENT_DLL
#include "c_baseplayer.h"
#else
#include "player.h"
#endif
#include "shareddefs.h"
#include "tier0/memdbgon.h"
#include "util_shared.h"
#include "utlvector.h"
// TF2 specific, need enough space for OBJ_LAST items from tf_shareddefs.h
#define WEAPON_SUBTYPE_BITS 6
@ -169,6 +177,19 @@ void WriteUsercmd( bf_write *buf, const CUserCmd *to, const CUserCmd *from )
buf->WriteOneBit( 0 );
}
for (int i = 0; i <= MAX_PLAYERS; i++)
{
if (to->simulationtimes[i] != from->simulationtimes[i])
{
buf->WriteOneBit( 1 );
buf->WriteFloat( to->simulationtimes[i] );
}
else
{
buf->WriteOneBit(0);
}
}
#if defined( HL2_CLIENT_DLL )
if ( to->entitygroundcontact.Count() != 0 )
{
@ -289,6 +310,15 @@ void ReadUsercmd( bf_read *buf, CUserCmd *move, CUserCmd *from )
move->mousedy = buf->ReadShort();
}
for (int i = 0; i <= MAX_PLAYERS; i++)
{
// Simulation time changed unexpectedly ?
if (buf->ReadOneBit())
{
move->simulationtimes[i] = buf->ReadFloat();
}
}
#if defined( HL2_DLL )
if ( buf->ReadOneBit() )
{

View file

@ -5,6 +5,7 @@
// $NoKeywords: $
//
//=============================================================================//
#include "shareddefs.h"
#if !defined( USERCMD_H )
#define USERCMD_H
#ifdef _WIN32
@ -55,6 +56,10 @@ public:
mousedy = 0;
hasbeenpredicted = false;
for (int i = 0; i <= MAX_PLAYERS; i++)
simulationtimes[i] = 0.0f;
#if defined( HL2_DLL ) || defined( HL2_CLIENT_DLL )
entitygroundcontact.RemoveAll();
#endif
@ -81,6 +86,8 @@ public:
hasbeenpredicted = src.hasbeenpredicted;
for (int i = 0; i <= MAX_PLAYERS; i++)
simulationtimes[i] = src.simulationtimes[i];
#if defined( HL2_DLL ) || defined( HL2_CLIENT_DLL )
entitygroundcontact = src.entitygroundcontact;
#endif
@ -111,6 +118,7 @@ public:
CRC32_ProcessBuffer( &crc, &random_seed, sizeof( random_seed ) );
CRC32_ProcessBuffer( &crc, &mousedx, sizeof( mousedx ) );
CRC32_ProcessBuffer( &crc, &mousedy, sizeof( mousedy ) );
CRC32_ProcessBuffer( &crc, &simulationtimes, sizeof( simulationtimes ) );
CRC32_Final( &crc );
return crc;
@ -158,6 +166,10 @@ public:
// Client only, tracks whether we've predicted this command at least once
bool hasbeenpredicted;
// TODO_ENHANCED: Lag compensate also other entities when needed.
// Send simulation times for each players for lag compensation.
float simulationtimes[MAX_PLAYERS+1];
// Back channel to communicate IK state
#if defined( HL2_DLL ) || defined( HL2_CLIENT_DLL )
CUtlVector< CEntityGroundContact > entitygroundcontact;