diff --git a/engine/baseclientstate.cpp b/engine/baseclientstate.cpp index ed001afd8d..282938e2a4 100644 --- a/engine/baseclientstate.cpp +++ b/engine/baseclientstate.cpp @@ -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." ); diff --git a/engine/client.cpp b/engine/client.cpp index ee64d78694..5c0d99875c 100644 --- a/engine/client.cpp +++ b/engine/client.cpp @@ -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 ) diff --git a/engine/host.cpp b/engine/host.cpp index dbd43c65b2..02b0752bf8 100644 --- a/engine/host.cpp +++ b/engine/host.cpp @@ -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 diff --git a/game/client/c_baseentity.cpp b/game/client/c_baseentity.cpp index 9e65f38843..bedeba6684 100644 --- a/game/client/c_baseentity.cpp +++ b/game/client/c_baseentity.cpp @@ -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() diff --git a/game/client/c_baseentity.h b/game/client/c_baseentity.h index c62b732f07..c7265fc79f 100644 --- a/game/client/c_baseentity.h +++ b/game/client/c_baseentity.h @@ -1287,6 +1287,8 @@ public: float m_flSimulationTime; float m_flOldSimulationTime; + float m_flInterpolatedSimulationTime; + CInterpolatedVar m_iv_flSimulationTime; float m_flCreateTime; diff --git a/game/client/c_func_rotating.cpp b/game/client/c_func_rotating.cpp index 9d28bc268e..4066fd6240 100644 --- a/game/client/c_func_rotating.cpp +++ b/game/client/c_func_rotating.cpp @@ -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() diff --git a/game/client/cdll_bounded_cvars.cpp b/game/client/cdll_bounded_cvars.cpp index 0928b77481..01c98360bb 100644 --- a/game/client/cdll_bounded_cvars.cpp +++ b/game/client/cdll_bounded_cvars.cpp @@ -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; } } diff --git a/game/client/in_main.cpp b/game/client/in_main.cpp index 5275907850..df4451057b 100644 --- a/game/client/in_main.cpp +++ b/game/client/in_main.cpp @@ -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(); } diff --git a/game/server/baseentity.cpp b/game/server/baseentity.cpp index 4ca5381076..7f72a30032 100644 --- a/game/server/baseentity.cpp +++ b/game/server/baseentity.cpp @@ -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 ), diff --git a/game/server/bmodels.cpp b/game/server/bmodels.cpp index 71be7d397f..da69ca03a9 100644 --- a/game/server/bmodels.cpp +++ b/game/server/bmodels.cpp @@ -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() diff --git a/game/server/cstrike/cs_player.cpp b/game/server/cstrike/cs_player.cpp index 79ed9c1497..210812699e 100644 --- a/game/server/cstrike/cs_player.cpp +++ b/game/server/cstrike/cs_player.cpp @@ -4181,23 +4181,9 @@ void CCSPlayer::NoteWeaponFired() } -bool CCSPlayer::WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec *pEntityTransmitBits ) const +bool CCSPlayer::WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec *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 ); } diff --git a/game/server/cstrike/cs_player.h b/game/server/cstrike/cs_player.h index 9abf96a67d..9448131d4d 100644 --- a/game/server/cstrike/cs_player.h +++ b/game/server/cstrike/cs_player.h @@ -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 *pEntityTransmitBits ) const; + virtual bool WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec *pEntityTransmitBits ); // ------------------------------------------------------------------------------------------------ // // Player state management. diff --git a/game/server/gameinterface.cpp b/game/server/gameinterface.cpp index e434f9d401..364b63d179 100644 --- a/game/server/gameinterface.cpp +++ b/game/server/gameinterface.cpp @@ -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; diff --git a/game/server/player.cpp b/game/server/player.cpp index 18aededa7d..c65daa5e75 100644 --- a/game/server/player.cpp +++ b/game/server/player.cpp @@ -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 *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 *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; diff --git a/game/server/player.h b/game/server/player.h index ddbd441621..898d4bfc0d 100644 --- a/game/server/player.h +++ b/game/server/player.h @@ -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 *pEntityTransmitBits ) const; + virtual bool WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec *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 diff --git a/game/server/player_lagcompensation.cpp b/game/server/player_lagcompensation.cpp index 3a7f41237f..1c107d1362 100644 --- a/game/server/player_lagcompensation.cpp +++ b/game/server/player_lagcompensation.cpp @@ -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 *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 ) diff --git a/game/shared/cstrike/cs_player_shared.cpp b/game/shared/cstrike/cs_player_shared.cpp index c35a4da46e..b46003f87c 100644 --- a/game/shared/cstrike/cs_player_shared.cpp +++ b/game/shared/cstrike/cs_player_shared.cpp @@ -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 diff --git a/game/shared/shareddefs.h b/game/shared/shareddefs.h index d3b23263d0..8e35691e1b 100644 --- a/game/shared/shareddefs.h +++ b/game/shared/shareddefs.h @@ -873,11 +873,6 @@ enum RT_STATE_NORMAL, // Timer is in normal mode }; -enum -{ - SIMULATION_TIME_WINDOW_BITS = 8, -}; - //----------------------------------------------------------------------------- // Commentary Mode //----------------------------------------------------------------------------- diff --git a/game/shared/usercmd.cpp b/game/shared/usercmd.cpp index 84ffa24314..436840de8b 100644 --- a/game/shared/usercmd.cpp +++ b/game/shared/usercmd.cpp @@ -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() ) { diff --git a/game/shared/usercmd.h b/game/shared/usercmd.h index 20b4034594..6982e6c373 100644 --- a/game/shared/usercmd.h +++ b/game/shared/usercmd.h @@ -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;