2023-10-03 14:23:56 +00:00
//========= Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ============//
2020-04-22 16:56:21 +00:00
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
# include "cbase.h"
2023-10-03 14:23:56 +00:00
# include "player_lagcompensation.h"
2020-04-22 16:56:21 +00:00
# include "usercmd.h"
# include "inetchannelinfo.h"
# include "utllinkedlist.h"
# include "BaseAnimatingOverlay.h"
# include "tier0/vprof.h"
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
# define LC_NONE 0
# define LC_ALIVE (1<<0)
# define LC_ORIGIN_CHANGED (1<<8)
# define LC_ANGLES_CHANGED (1<<9)
# define LC_SIZE_CHANGED (1<<10)
# define LC_ANIMATION_CHANGED (1<<11)
2023-10-03 14:23:56 +00:00
# define LAG_COMPENSATION_TELEPORTED_DISTANCE_SQR ( 64.0f * 64.0f )
2020-04-22 16:56:21 +00:00
# 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 )
ConVar sv_unlag ( " sv_unlag " , " 1 " , FCVAR_DEVELOPMENTONLY , " Enables player lag compensation " ) ;
ConVar sv_maxunlag ( " sv_maxunlag " , " 1.0 " , FCVAR_DEVELOPMENTONLY , " Maximum lag compensation in seconds " , true , 0.0f , true , 1.0f ) ;
ConVar sv_lagflushbonecache ( " sv_lagflushbonecache " , " 1 " , FCVAR_DEVELOPMENTONLY , " Flushes entity bone cache on lag compensation " ) ;
ConVar sv_showlagcompensation ( " sv_showlagcompensation " , " 0 " , FCVAR_CHEAT , " Show lag compensated hitboxes whenever a player is lag compensated. " ) ;
2023-10-03 14:23:56 +00:00
ConVar sv_lagcompensationforcerestore ( " sv_lagcompensationforcerestore " , " 1 " , FCVAR_CHEAT , " Don't test validity of a lag comp restore, just do it. " ) ;
2020-04-22 16:56:21 +00:00
ConVar sv_unlag_fixstuck ( " sv_unlag_fixstuck " , " 0 " , FCVAR_DEVELOPMENTONLY , " Disallow backtracking a player for lag compensation if it will cause them to become stuck " ) ;
2023-10-03 14:23:56 +00:00
ConVar sv_lagpushticks ( " sv_lagpushticks " , " 0 " , FCVAR_DEVELOPMENTONLY , " Push computed lag compensation amount by this many ticks. " ) ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
static CLagCompensationManager g_LagCompensationManager ( " CLagCompensationManager " ) ;
ILagCompensationManager * lagcompensation = & g_LagCompensationManager ;
2020-04-22 16:56:21 +00:00
//
// Try to take the player from his current origin to vWantedPos.
// If it can't get there, leave the player where he is.
//
ConVar sv_unlag_debug ( " sv_unlag_debug " , " 0 " , FCVAR_GAMEDLL | FCVAR_DEVELOPMENTONLY ) ;
float g_flFractionScale = 0.95 ;
2023-10-03 14:23:56 +00:00
static void LC_SetAbsOrigin ( CBaseEntity * entity , const Vector & vecAbsOrigin , bool bFireTriggers )
{
entity - > SetAbsOrigin ( vecAbsOrigin ) ;
if ( bFireTriggers )
{
entity - > PhysicsTouchTriggers ( ) ;
}
}
static void RestoreEntityTo ( CBaseEntity * pEntity , const Vector & vWantedPos )
2020-04-22 16:56:21 +00:00
{
// Try to move to the wanted position from our current position.
trace_t tr ;
2023-10-03 14:23:56 +00:00
VPROF_BUDGET ( " RestoreEntityTo " , " CLagCompensationManager " ) ;
if ( sv_lagcompensationforcerestore . GetBool ( ) )
{
// We don't have to test for validity. Just put them back where you found them.
LC_SetAbsOrigin ( pEntity , vWantedPos , true ) ;
}
unsigned int mask = MASK_PLAYERSOLID ;
UTIL_TraceEntity ( pEntity , vWantedPos , vWantedPos , mask , pEntity , COLLISION_GROUP_PLAYER_MOVEMENT , & tr ) ;
2020-04-22 16:56:21 +00:00
if ( tr . startsolid | | tr . allsolid )
{
if ( sv_unlag_debug . GetBool ( ) )
{
2023-10-03 14:23:56 +00:00
DevMsg ( " RestoreEntityTo could not restore player position for client \" %d \" ( %.1f %.1f %.1f ) \n " ,
pEntity - > entindex ( ) , vWantedPos . x , vWantedPos . y , vWantedPos . z ) ;
2020-04-22 16:56:21 +00:00
}
2023-10-03 14:23:56 +00:00
UTIL_TraceEntity ( pEntity , pEntity - > GetAbsOrigin ( ) , vWantedPos , mask , pEntity , COLLISION_GROUP_PLAYER_MOVEMENT , & tr ) ;
2020-04-22 16:56:21 +00:00
if ( tr . startsolid | | tr . allsolid )
{
// In this case, the guy got stuck back wherever we lag compensated him to. Nasty.
if ( sv_unlag_debug . GetBool ( ) )
DevMsg ( " restore failed entirely \n " ) ;
}
else
{
// We can get to a valid place, but not all the way back to where we were.
Vector vPos ;
2023-10-03 14:23:56 +00:00
VectorLerp ( pEntity - > GetAbsOrigin ( ) , vWantedPos , tr . fraction * g_flFractionScale , vPos ) ;
LC_SetAbsOrigin ( pEntity , vPos , true ) ;
2020-04-22 16:56:21 +00:00
if ( sv_unlag_debug . GetBool ( ) )
DevMsg ( " restore got most of the way \n " ) ;
}
}
else
{
// Cool, the player can go back to whence he came.
2023-10-03 14:23:56 +00:00
LC_SetAbsOrigin ( pEntity , tr . endpos , true ) ;
2020-04-22 16:56:21 +00:00
}
}
2023-10-03 14:23:56 +00:00
// Mappers can flag certain additional entities to lag compensate, this handles them
void CLagCompensationManager : : AddAdditionalEntity ( CBaseEntity * pEntity )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
EHANDLE eh ;
eh = pEntity ;
if ( m_AdditionalEntities . Find ( eh ) = = m_AdditionalEntities . InvalidIndex ( ) )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
m_AdditionalEntities . Insert ( eh ) ;
2020-04-22 16:56:21 +00:00
}
2023-10-03 14:23:56 +00:00
}
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
void CLagCompensationManager : : RemoveAdditionalEntity ( CBaseEntity * pEntity )
{
EHANDLE eh ;
eh = pEntity ;
m_AdditionalEntities . Remove ( eh ) ;
}
2020-04-22 16:56:21 +00:00
//-----------------------------------------------------------------------------
// Purpose: Called once per frame after all entities have had a chance to think
//-----------------------------------------------------------------------------
void CLagCompensationManager : : FrameUpdatePostEntityThink ( )
{
if ( ( gpGlobals - > maxClients < = 1 ) | | ! sv_unlag . GetBool ( ) )
{
ClearHistory ( ) ;
return ;
}
VPROF_BUDGET ( " FrameUpdatePostEntityThink " , " CLagCompensationManager " ) ;
2023-10-03 14:23:56 +00:00
CUtlRBTree < CBaseEntity * > rbEntityList ( 0 , 0 , DefLessFunc ( CBaseEntity * ) ) ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
// Add active players
2020-04-22 16:56:21 +00:00
for ( int i = 1 ; i < = gpGlobals - > maxClients ; i + + )
{
CBasePlayer * pPlayer = UTIL_PlayerByIndex ( i ) ;
if ( ! pPlayer )
{
continue ;
}
2023-10-03 14:23:56 +00:00
if ( rbEntityList . Find ( pPlayer ) = = rbEntityList . InvalidIndex ( ) )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
rbEntityList . Insert ( pPlayer ) ;
2020-04-22 16:56:21 +00:00
}
2023-10-03 14:23:56 +00:00
}
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
// Add any additional entities
for ( int i = m_AdditionalEntities . FirstInorder ( ) ; i ! = m_AdditionalEntities . InvalidIndex ( ) ; i = m_AdditionalEntities . NextInorder ( i ) )
{
CBaseEntity * pAddEntity = m_AdditionalEntities [ i ] . Get ( ) ;
if ( ! pAddEntity )
continue ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
if ( rbEntityList . Find ( pAddEntity ) = = rbEntityList . InvalidIndex ( ) )
{
rbEntityList . Insert ( pAddEntity ) ;
2020-04-22 16:56:21 +00:00
}
2023-10-03 14:23:56 +00:00
}
// Now record the actual history information
for ( int i = rbEntityList . FirstInorder ( ) ; i ! = rbEntityList . InvalidIndex ( ) ; i = rbEntityList . NextInorder ( i ) )
{
CBaseEntity * pEntity = rbEntityList [ i ] ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
EHANDLE eh ;
eh = pEntity ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
int slot = m_CompensatedEntities . Find ( eh ) ;
if ( slot = = m_CompensatedEntities . InvalidIndex ( ) )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
EntityLagData * pNewEntry = new EntityLagData ( ) ;
slot = m_CompensatedEntities . Insert ( eh , pNewEntry ) ;
2020-04-22 16:56:21 +00:00
}
2023-10-03 14:23:56 +00:00
EntityLagData * ld = m_CompensatedEntities [ slot ] ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
RecordDataIntoTrack ( pEntity , & ld - > m_LagRecords , true ) ;
2020-04-22 16:56:21 +00:00
}
}
2023-10-03 14:23:56 +00:00
//-----------------------------------------------------------------------------
// Purpose: Called during gamemovment weapon firing to set up/restore after lag compensation around the bullet traces
//-----------------------------------------------------------------------------
void CLagCompensationManager : : StartLagCompensation ( CBasePlayer * player , LagCompensationType lagCompensationType , const Vector & weaponPos , const QAngle & weaponAngles , float weaponRange )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
Assert ( ! m_isCurrentlyDoingCompensation ) ;
// Assume no entities need to be restored
CUtlVector < EHANDLE > invalidList ;
FOR_EACH_MAP ( m_CompensatedEntities , i )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
EntityLagData * ld = m_CompensatedEntities [ i ] ;
EHANDLE key ;
key = m_CompensatedEntities . Key ( i ) ;
if ( ! key . Get ( ) )
{
// Note that the EHANDLE is NULL now
invalidList . AddToTail ( key ) ;
continue ;
}
// Clear state
ld - > m_bRestoreEntity = false ;
ld - > m_RestoreData . Clear ( ) ;
ld - > m_ChangeData . Clear ( ) ;
}
// Wipe any deleted entities from the list
for ( int i = 0 ; i < invalidList . Count ( ) ; + + i )
{
int slot = m_CompensatedEntities . Find ( invalidList [ i ] ) ;
Assert ( slot ! = m_CompensatedEntities . InvalidIndex ( ) ) ;
if ( slot = = m_CompensatedEntities . InvalidIndex ( ) )
continue ;
EntityLagData * ld = m_CompensatedEntities [ slot ] ;
delete ld ;
m_CompensatedEntities . RemoveAt ( slot ) ;
2020-04-22 16:56:21 +00:00
}
m_bNeedToRestore = false ;
m_pCurrentPlayer = player ;
if ( ! player - > m_bLagCompensation // Player not wanting lag compensation
| | ( gpGlobals - > maxClients < = 1 ) // no lag compensation in single player
| | ! sv_unlag . GetBool ( ) // disabled by server admin
| | player - > IsBot ( ) // not for bots
| | player - > IsObserver ( ) // not for spectators
)
return ;
2023-10-03 14:23:56 +00:00
const CUserCmd * cmd = player - > GetCurrentUserCommand ( ) ;
if ( ! cmd )
{
// This can hit if m_hActiveWeapon incorrectly gets set to a weapon not actually owned by the local player
// (since lag comp asks for the GetPlayerOwner() which will have m_pCurrentCommand == NULL,
// not the player currently executing a CUserCmd).
Error ( " CLagCompensationManager::StartLagCompensation with NULL CUserCmd!!! \n " ) ;
}
2020-04-22 16:56:21 +00:00
// NOTE: Put this here so that it won't show up in single player mode.
VPROF_BUDGET ( " StartLagCompensation " , VPROF_BUDGETGROUP_OTHER_NETWORKING ) ;
2023-10-03 14:23:56 +00:00
m_isCurrentlyDoingCompensation = true ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
// correct is the amount of time we have to correct game time
2020-04-22 16:56:21 +00:00
float correct = 0.0f ;
2023-10-03 14:23:56 +00:00
// Get true latency
2020-04-22 16:56:21 +00:00
INetChannelInfo * nci = engine - > GetPlayerNetInfo ( player - > entindex ( ) ) ;
if ( nci )
{
// add network latency
correct + = nci - > GetLatency ( FLOW_OUTGOING ) ;
}
2023-10-03 14:23:56 +00:00
// NOTE: do these computations in float time, not ticks, to avoid big roundoff error accumulations in the math
2020-04-22 16:56:21 +00:00
// add view interpolation latency see C_BaseEntity::GetInterpolationAmount()
2023-10-03 14:23:56 +00:00
correct + = player - > m_fLerpTime ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
// check bounds [0,sv_maxunlag]
2020-04-22 16:56:21 +00:00
correct = clamp ( correct , 0.0f , sv_maxunlag . GetFloat ( ) ) ;
// correct tick send by player
2023-10-03 14:23:56 +00:00
float flTargetTime = TICKS_TO_TIME ( cmd - > tick_count ) - player - > m_fLerpTime ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
// calculate difference between tick sent by player and our latency based tick
float deltaTime = correct - ( gpGlobals - > curtime - flTargetTime ) ;
2020-04-22 16:56:21 +00:00
if ( fabs ( deltaTime ) > 0.2f )
{
// difference between cmd time and latency is too big > 200ms, use time correction based on latency
// DevMsg("StartLagCompensation: delta too big (%.3f)\n", deltaTime );
2023-10-03 14:23:56 +00:00
flTargetTime = gpGlobals - > curtime - correct ;
2020-04-22 16:56:21 +00:00
}
2023-10-03 14:23:56 +00:00
flTargetTime + = TICKS_TO_TIME ( sv_lagpushticks . GetInt ( ) ) ;
m_lagCompensationType = lagCompensationType ;
m_weaponPos = weaponPos ;
m_weaponAngles = weaponAngles ;
m_weaponRange = weaponRange ;
// Iterate all lag compensatable entities
2020-04-22 16:56:21 +00:00
const CBitVec < MAX_EDICTS > * pEntityTransmitBits = engine - > GetEntityTransmitBitsForClient ( player - > entindex ( ) - 1 ) ;
2023-10-03 14:23:56 +00:00
FOR_EACH_MAP ( m_CompensatedEntities , i )
{
EntityLagData * ld = m_CompensatedEntities [ i ] ;
EHANDLE key = m_CompensatedEntities . Key ( i ) ;
CBaseEntity * pEntity = key . Get ( ) ;
if ( ! pEntity )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
// Stale entity in list, fixed up on next time through loop
2020-04-22 16:56:21 +00:00
continue ;
}
2023-10-03 14:23:56 +00:00
// Don't lag compensate self
if ( player = = pEntity )
2020-04-22 16:56:21 +00:00
{
continue ;
}
2023-10-03 14:23:56 +00:00
// Custom checks for if things should lag compensate (based on things like what team the entity is associated with).
if ( ! player - > WantsLagCompensationOnEntity ( pEntity , cmd , pEntityTransmitBits ) )
2020-04-22 16:56:21 +00:00
continue ;
2023-10-03 14:23:56 +00:00
// Move entity back in time and remember that fact
ld - > m_bRestoreEntity = BacktrackEntity ( pEntity , flTargetTime , & ld - > m_LagRecords , & ld - > m_RestoreData , & ld - > m_ChangeData , true ) ;
2020-04-22 16:56:21 +00:00
}
}
2023-10-03 14:23:56 +00:00
bool CLagCompensationManager : : BacktrackEntity ( CBaseEntity * entity , float flTargetTime , LagRecordList * track , LagRecord * restore , LagRecord * change , bool wantsAnims )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
Vector org , mins , maxs ;
2020-04-22 16:56:21 +00:00
QAngle ang ;
2023-10-03 14:23:56 +00:00
VPROF_BUDGET ( " BacktrackEntity " , " CLagCompensationManager " ) ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
// check if we have at least one entry
2020-04-22 16:56:21 +00:00
if ( track - > Count ( ) < = 0 )
2023-10-03 14:23:56 +00:00
return false ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
int curr = track - > Head ( ) ;
2020-04-22 16:56:21 +00:00
LagRecord * prevRecord = NULL ;
LagRecord * record = NULL ;
2023-10-03 14:23:56 +00:00
Vector prevOrg = entity - > GetAbsOrigin ( ) ;
2020-04-22 16:56:21 +00:00
// Walk context looking for any invalidating event
while ( track - > IsValidIndex ( curr ) )
{
// remember last record
prevRecord = record ;
// get next record
record = & track - > Element ( curr ) ;
if ( ! ( record - > m_fFlags & LC_ALIVE ) )
{
2023-10-03 14:23:56 +00:00
// entity must be alive, lost track
return false ;
2020-04-22 16:56:21 +00:00
}
Vector delta = record - > m_vecOrigin - prevOrg ;
2023-10-03 14:23:56 +00:00
if ( delta . LengthSqr ( ) > LAG_COMPENSATION_TELEPORTED_DISTANCE_SQR )
2020-04-22 16:56:21 +00:00
{
// lost track, too much difference
2023-10-03 14:23:56 +00:00
return false ;
2020-04-22 16:56:21 +00:00
}
// did we find a context smaller than target time ?
if ( record - > m_flSimulationTime < = flTargetTime )
break ; // hurra, stop
prevOrg = record - > m_vecOrigin ;
// go one step back
curr = track - > Next ( curr ) ;
}
Assert ( record ) ;
if ( ! record )
{
if ( sv_unlag_debug . GetBool ( ) )
{
2023-10-03 14:23:56 +00:00
DevMsg ( " No valid positions in history for BacktrackPlayer client ( %d ) \n " , entity - > entindex ( ) ) ;
2020-04-22 16:56:21 +00:00
}
2023-10-03 14:23:56 +00:00
return false ;
2020-04-22 16:56:21 +00:00
}
float frac = 0.0f ;
if ( prevRecord & &
( record - > m_flSimulationTime < flTargetTime ) & &
( record - > m_flSimulationTime < prevRecord - > m_flSimulationTime ) )
{
// we didn't find the exact time but have a valid previous record
// so interpolate between these two records;
Assert ( prevRecord - > m_flSimulationTime > record - > m_flSimulationTime ) ;
Assert ( flTargetTime < prevRecord - > m_flSimulationTime ) ;
// calc fraction between both records
frac = ( flTargetTime - record - > m_flSimulationTime ) /
( prevRecord - > m_flSimulationTime - record - > m_flSimulationTime ) ;
Assert ( frac > 0 & & frac < 1 ) ; // should never extrapolate
2023-10-03 14:23:56 +00:00
ang = Lerp ( frac , record - > m_vecAngles , prevRecord - > m_vecAngles ) ;
org = Lerp ( frac , record - > m_vecOrigin , prevRecord - > m_vecOrigin ) ;
mins = Lerp ( frac , record - > m_vecMins , prevRecord - > m_vecMins ) ;
maxs = Lerp ( frac , record - > m_vecMaxs , prevRecord - > m_vecMaxs ) ;
2020-04-22 16:56:21 +00:00
}
else
{
// we found the exact record or no other record to interpolate with
// just copy these values since they are the best we have
2023-10-03 14:23:56 +00:00
ang = record - > m_vecAngles ;
org = record - > m_vecOrigin ;
mins = record - > m_vecMins ;
maxs = record - > m_vecMaxs ;
2020-04-22 16:56:21 +00:00
}
// See if this is still a valid position for us to teleport to
if ( sv_unlag_fixstuck . GetBool ( ) )
{
// Try to move to the wanted position from our current position.
trace_t tr ;
2023-10-03 14:23:56 +00:00
UTIL_TraceEntity ( entity , org , org , MASK_PLAYERSOLID , & tr ) ;
2020-04-22 16:56:21 +00:00
if ( tr . startsolid | | tr . allsolid )
{
if ( sv_unlag_debug . GetBool ( ) )
2023-10-03 14:23:56 +00:00
DevMsg ( " WARNING: BackupPlayer trying to back player into a bad position - client %d \n " , entity - > entindex ( ) ) ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
CBaseEntity * pHitEntity = tr . m_pEnt ;
2020-04-22 16:56:21 +00:00
// don't lag compensate the current player
2023-10-03 14:23:56 +00:00
if ( pHitEntity & & ( pHitEntity ! = m_pCurrentPlayer ) )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
// Find it
EHANDLE eh ;
eh = pHitEntity ;
int slot = m_CompensatedEntities . Find ( eh ) ;
if ( slot ! = m_CompensatedEntities . InvalidIndex ( ) )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
EntityLagData * ld = m_CompensatedEntities [ slot ] ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
// If we haven't backtracked this player, do it now
// this deliberately ignores WantsLagCompensationOnEntity.
if ( ! ld - > m_bRestoreEntity )
{
// Temp turn this flag on
ld - > m_bRestoreEntity = true ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
BacktrackEntity ( pHitEntity , flTargetTime , & ld - > m_LagRecords , & ld - > m_RestoreData , & ld - > m_ChangeData , true ) ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
// Remove the temp flag
ld - > m_bRestoreEntity = false ;
}
}
2020-04-22 16:56:21 +00:00
}
// now trace us back as far as we can go
2023-10-03 14:23:56 +00:00
unsigned int mask = MASK_PLAYERSOLID ;
UTIL_TraceEntity ( entity , entity - > GetAbsOrigin ( ) , org , mask , & tr ) ;
2020-04-22 16:56:21 +00:00
if ( tr . startsolid | | tr . allsolid )
{
// Our starting position is bogus
if ( sv_unlag_debug . GetBool ( ) )
DevMsg ( " Backtrack failed completely, bad starting position \n " ) ;
}
else
{
// We can get to a valid place, but not all the way to the target
Vector vPos ;
2023-10-03 14:23:56 +00:00
VectorLerp ( entity - > GetAbsOrigin ( ) , org , tr . fraction * g_flFractionScale , vPos ) ;
2020-04-22 16:56:21 +00:00
// This is as close as we're going to get
org = vPos ;
if ( sv_unlag_debug . GetBool ( ) )
DevMsg ( " Backtrack got most of the way \n " ) ;
}
}
}
2023-10-03 14:23:56 +00:00
// See if this represents a change for the entity
2020-04-22 16:56:21 +00:00
int flags = 0 ;
2023-10-03 14:23:56 +00:00
QAngle angdiff = entity - > GetAbsAngles ( ) - ang ;
Vector orgdiff = entity - > GetAbsOrigin ( ) - org ;
2020-04-22 16:56:21 +00:00
// Always remember the pristine simulation time in case we need to restore it.
2023-10-03 14:23:56 +00:00
restore - > m_flSimulationTime = entity - > GetSimulationTime ( ) ;
2020-04-22 16:56:21 +00:00
if ( angdiff . LengthSqr ( ) > LAG_COMPENSATION_EPS_SQR )
{
flags | = LC_ANGLES_CHANGED ;
2023-10-03 14:23:56 +00:00
restore - > m_vecAngles = entity - > GetAbsAngles ( ) ;
entity - > SetAbsAngles ( ang ) ;
2020-04-22 16:56:21 +00:00
change - > m_vecAngles = ang ;
}
// Use absolute equality here
2023-10-03 14:23:56 +00:00
if ( ( mins ! = entity - > WorldAlignMins ( ) ) | |
( maxs ! = entity - > WorldAlignMaxs ( ) ) )
2020-04-22 16:56:21 +00:00
{
flags | = LC_SIZE_CHANGED ;
2023-10-03 14:23:56 +00:00
restore - > m_vecMins = entity - > WorldAlignMins ( ) ;
restore - > m_vecMaxs = entity - > WorldAlignMaxs ( ) ;
entity - > SetSize ( mins , maxs ) ;
change - > m_vecMins = mins ;
change - > m_vecMaxs = maxs ;
2020-04-22 16:56:21 +00:00
}
// Note, do origin at end since it causes a relink into the k/d tree
if ( orgdiff . LengthSqr ( ) > LAG_COMPENSATION_EPS_SQR )
{
flags | = LC_ORIGIN_CHANGED ;
2023-10-03 14:23:56 +00:00
restore - > m_vecOrigin = entity - > GetAbsOrigin ( ) ;
entity - > SetAbsOrigin ( org ) ;
2020-04-22 16:56:21 +00:00
change - > m_vecOrigin = org ;
}
2023-10-03 14:23:56 +00:00
bool skipAnims = false ;
switch ( m_lagCompensationType )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
case LAG_COMPENSATE_BOUNDS :
skipAnims = true ;
break ;
case LAG_COMPENSATE_HITBOXES :
skipAnims = false ;
break ;
case LAG_COMPENSATE_HITBOXES_ALONG_RAY :
skipAnims = false ;
{
Vector forward ;
AngleVectors ( m_weaponAngles , & forward ) ;
const float maxRange = 100.0f ;
float range = DistanceToRay ( entity - > WorldSpaceCenter ( ) , m_weaponPos , m_weaponPos + forward * m_weaponRange ) ;
if ( range < 0.0f | | range > maxRange )
{
skipAnims = true ;
}
}
break ;
2020-04-22 16:56:21 +00:00
}
2023-10-03 14:23:56 +00:00
if ( ! skipAnims & & wantsAnims & & entity - > GetBaseAnimating ( ) )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
CBaseAnimating * pAnimating = entity - > GetBaseAnimating ( ) ;
// Sorry for the loss of the optimization for the case of people
// standing still, but you breathe even on the server.
// This is quicker than actually comparing all bazillion floats.
flags | = LC_ANIMATION_CHANGED ;
restore - > m_masterSequence = pAnimating - > GetSequence ( ) ;
restore - > m_masterCycle = pAnimating - > GetCycle ( ) ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
bool interpolationAllowed = false ;
if ( prevRecord & & ( record - > m_masterSequence = = prevRecord - > m_masterSequence ) )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
// If the master state changes, all layers will be invalid too, so don't interp (ya know, interp barely ever happens anyway)
interpolationAllowed = true ;
2020-04-22 16:56:21 +00:00
}
2023-10-03 14:23:56 +00:00
////////////////////////
// First do the master settings
bool interpolatedMasters = false ;
if ( frac > 0.0f & & interpolationAllowed )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
interpolatedMasters = true ;
pAnimating - > SetSequence ( Lerp ( frac , record - > m_masterSequence , prevRecord - > m_masterSequence ) ) ;
pAnimating - > SetCycle ( Lerp ( frac , record - > m_masterCycle , prevRecord - > m_masterCycle ) ) ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
if ( record - > m_masterCycle > prevRecord - > m_masterCycle )
{
// the older record is higher in frame than the newer, it must have wrapped around from 1 back to 0
// add one to the newer so it is lerping from .9 to 1.1 instead of .9 to .1, for example.
float newCycle = Lerp ( frac , record - > m_masterCycle , prevRecord - > m_masterCycle + 1 ) ;
pAnimating - > SetCycle ( newCycle < 1 ? newCycle : newCycle - 1 ) ; // and make sure .9 to 1.2 does not end up 1.05
}
else
{
pAnimating - > SetCycle ( Lerp ( frac , record - > m_masterCycle , prevRecord - > m_masterCycle ) ) ;
}
}
if ( ! interpolatedMasters )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
pAnimating - > SetSequence ( record - > m_masterSequence ) ;
pAnimating - > SetCycle ( record - > m_masterCycle ) ;
}
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
////////////////////////
// Now do all the layers
//
CBaseAnimatingOverlay * pAnimatingOverlay = entity - > GetBaseAnimatingOverlay ( ) ;
if ( pAnimatingOverlay )
{
int layerCount = pAnimatingOverlay - > GetNumAnimOverlays ( ) ;
for ( int layerIndex = 0 ; layerIndex < layerCount ; + + layerIndex )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
CAnimationLayer * currentLayer = pAnimatingOverlay - > GetAnimOverlay ( layerIndex ) ;
if ( currentLayer )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
restore - > m_layerRecords [ layerIndex ] . m_cycle = currentLayer - > m_flCycle ;
restore - > m_layerRecords [ layerIndex ] . m_order = currentLayer - > m_nOrder ;
restore - > m_layerRecords [ layerIndex ] . m_sequence = currentLayer - > m_nSequence ;
restore - > m_layerRecords [ layerIndex ] . m_weight = currentLayer - > m_flWeight ;
bool interpolated = false ;
if ( ( frac > 0.0f ) & & interpolationAllowed )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
LayerRecord & recordsLayerRecord = record - > m_layerRecords [ layerIndex ] ;
LayerRecord & prevRecordsLayerRecord = prevRecord - > m_layerRecords [ layerIndex ] ;
if ( ( recordsLayerRecord . m_order = = prevRecordsLayerRecord . m_order )
& & ( recordsLayerRecord . m_sequence = = prevRecordsLayerRecord . m_sequence )
)
{
// We can't interpolate across a sequence or order change
interpolated = true ;
if ( recordsLayerRecord . m_cycle > prevRecordsLayerRecord . m_cycle )
{
// the older record is higher in frame than the newer, it must have wrapped around from 1 back to 0
// add one to the newer so it is lerping from .9 to 1.1 instead of .9 to .1, for example.
float newCycle = Lerp ( frac , recordsLayerRecord . m_cycle , prevRecordsLayerRecord . m_cycle + 1 ) ;
currentLayer - > m_flCycle = newCycle < 1 ? newCycle : newCycle - 1 ; // and make sure .9 to 1.2 does not end up 1.05
}
else
{
currentLayer - > m_flCycle = Lerp ( frac , recordsLayerRecord . m_cycle , prevRecordsLayerRecord . m_cycle ) ;
}
currentLayer - > m_nOrder = recordsLayerRecord . m_order ;
currentLayer - > m_nSequence = recordsLayerRecord . m_sequence ;
currentLayer - > m_flWeight = Lerp ( frac , recordsLayerRecord . m_weight , prevRecordsLayerRecord . m_weight ) ;
}
2020-04-22 16:56:21 +00:00
}
2023-10-03 14:23:56 +00:00
if ( ! interpolated )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
//Either no interp, or interp failed. Just use record.
currentLayer - > m_flCycle = record - > m_layerRecords [ layerIndex ] . m_cycle ;
currentLayer - > m_nOrder = record - > m_layerRecords [ layerIndex ] . m_order ;
currentLayer - > m_nSequence = record - > m_layerRecords [ layerIndex ] . m_sequence ;
currentLayer - > m_flWeight = record - > m_layerRecords [ layerIndex ] . m_weight ;
2020-04-22 16:56:21 +00:00
}
}
}
}
}
if ( ! flags )
2023-10-03 14:23:56 +00:00
return false ; // we didn't change anything
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
if ( sv_lagflushbonecache . GetBool ( ) & & ( flags & LC_ANIMATION_CHANGED ) & & entity - > GetBaseAnimating ( ) )
entity - > GetBaseAnimating ( ) - > InvalidateBoneCache ( ) ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
/*
char text [ 256 ] ; Q_snprintf ( text , sizeof ( text ) , " time %.2f " , flTargetTime ) ;
2020-04-22 16:56:21 +00:00
pPlayer - > DrawServerHitboxes ( 10 ) ;
NDebugOverlay : : Text ( org , text , false , 10 ) ;
2023-10-03 14:23:56 +00:00
if ( skipAnims )
{
NDebugOverlay : : EntityBounds ( pPlayer , 0 , 255 , 0 , 32 , 10 ) ;
}
else
{
NDebugOverlay : : EntityBounds ( pPlayer , 255 , 0 , 0 , 32 , 10 ) ;
}
*/
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
m_bNeedToRestore = true ; // we changed at least one entity
2020-04-22 16:56:21 +00:00
restore - > m_fFlags = flags ; // we need to restore these flags
change - > m_fFlags = flags ; // we have changed these flags
2023-10-03 14:23:56 +00:00
if ( sv_showlagcompensation . GetInt ( ) = = 1 & & entity - > GetBaseAnimating ( ) )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
entity - > GetBaseAnimating ( ) - > DrawServerHitboxes ( 4 , true ) ;
2020-04-22 16:56:21 +00:00
}
2023-10-03 14:23:56 +00:00
return true ; // Yes, this guy has been backtracked
2020-04-22 16:56:21 +00:00
}
void CLagCompensationManager : : FinishLagCompensation ( CBasePlayer * player )
{
VPROF_BUDGET_FLAGS ( " FinishLagCompensation " , VPROF_BUDGETGROUP_OTHER_NETWORKING , BUDGETFLAG_CLIENT | BUDGETFLAG_SERVER ) ;
2023-10-03 14:23:56 +00:00
m_isCurrentlyDoingCompensation = false ;
2020-04-22 16:56:21 +00:00
if ( ! m_bNeedToRestore )
2023-10-03 14:23:56 +00:00
return ; // no entity was changed at all
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
// Iterate all active entities
FOR_EACH_MAP ( m_CompensatedEntities , i )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
EntityLagData * ld = m_CompensatedEntities [ i ] ;
// entity wasn't changed by lag compensation
if ( ! ld - > m_bRestoreEntity )
2020-04-22 16:56:21 +00:00
continue ;
2023-10-03 14:23:56 +00:00
CBaseEntity * pEntity = m_CompensatedEntities . Key ( i ) . Get ( ) ;
if ( ! pEntity )
2020-04-22 16:56:21 +00:00
continue ;
2023-10-03 14:23:56 +00:00
LagRecord * restore = & ld - > m_RestoreData ;
LagRecord * change = & ld - > m_ChangeData ;
RestoreEntityFromRecords ( pEntity , restore , change , true ) ;
}
}
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
void CLagCompensationManager : : RecordDataIntoTrack ( CBaseEntity * entity , LagRecordList * track , bool wantsAnims )
{
Assert ( track - > Count ( ) < 1000 ) ; // insanity check
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
// remove all records before that time:
int flDeadtime = gpGlobals - > curtime - sv_maxunlag . GetFloat ( ) ;
// remove tail records that are too old
int tailIndex = track - > Tail ( ) ;
while ( track - > IsValidIndex ( tailIndex ) )
{
LagRecord & tail = track - > Element ( tailIndex ) ;
// if tail is within limits, stop
if ( tail . m_flSimulationTime > = flDeadtime )
break ;
// remove tail, get new tail
track - > Remove ( tailIndex ) ;
tailIndex = track - > Tail ( ) ;
}
// check if head has same simulation time
if ( track - > Count ( ) > 0 )
{
LagRecord & head = track - > Element ( track - > Head ( ) ) ;
// check if player changed simulation time since last time updated
if ( head . m_flSimulationTime > = entity - > GetSimulationTime ( ) )
return ; // don't add new entry for same or older time
}
// add new record to entity track
LagRecord & record = track - > Element ( track - > AddToHead ( ) ) ;
record . m_fFlags = 0 ;
if ( entity - > IsAlive ( ) )
{
record . m_fFlags | = LC_ALIVE ;
}
record . m_flSimulationTime = entity - > GetSimulationTime ( ) ;
record . m_vecAngles = entity - > GetAbsAngles ( ) ;
record . m_vecOrigin = entity - > GetAbsOrigin ( ) ;
record . m_vecMaxs = entity - > WorldAlignMaxs ( ) ;
record . m_vecMins = entity - > WorldAlignMins ( ) ;
CBaseAnimating * pAnimating = entity - > GetBaseAnimating ( ) ;
if ( wantsAnims & & pAnimating )
{
CBaseAnimatingOverlay * pAnimatingOverlay = entity - > GetBaseAnimatingOverlay ( ) ;
if ( pAnimatingOverlay )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
int layerCount = pAnimatingOverlay - > GetNumAnimOverlays ( ) ;
for ( int layerIndex = 0 ; layerIndex < layerCount ; + + layerIndex )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
CAnimationLayer * currentLayer = pAnimatingOverlay - > GetAnimOverlay ( layerIndex ) ;
if ( currentLayer )
{
record . m_layerRecords [ layerIndex ] . m_cycle = currentLayer - > m_flCycle ;
record . m_layerRecords [ layerIndex ] . m_order = currentLayer - > m_nOrder ;
record . m_layerRecords [ layerIndex ] . m_sequence = currentLayer - > m_nSequence ;
record . m_layerRecords [ layerIndex ] . m_weight = currentLayer - > m_flWeight ;
}
2020-04-22 16:56:21 +00:00
}
}
2023-10-03 14:23:56 +00:00
record . m_masterSequence = pAnimating - > GetSequence ( ) ;
record . m_masterCycle = pAnimating - > GetCycle ( ) ;
}
}
void CLagCompensationManager : : RestoreEntityFromRecords ( CBaseEntity * entity , LagRecord * restore , LagRecord * change , bool wantsAnims )
{
bool restoreSimulationTime = false ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
if ( restore - > m_fFlags & LC_SIZE_CHANGED )
{
restoreSimulationTime = true ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
// see if simulation made any changes, if no, then do the restore, otherwise,
// leave new values in
if ( entity - > WorldAlignMins ( ) = = change - > m_vecMins & &
entity - > WorldAlignMaxs ( ) = = change - > m_vecMaxs )
{
// Restore it
entity - > SetSize ( restore - > m_vecMins , restore - > m_vecMaxs ) ;
2020-04-22 16:56:21 +00:00
}
2023-10-03 14:23:56 +00:00
}
if ( restore - > m_fFlags & LC_ANGLES_CHANGED )
{
restoreSimulationTime = true ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
if ( entity - > GetAbsAngles ( ) = = change - > m_vecAngles )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
entity - > SetAbsAngles ( restore - > m_vecAngles ) ;
}
}
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
if ( restore - > m_fFlags & LC_ORIGIN_CHANGED )
{
restoreSimulationTime = true ;
// Okay, let's see if we can do something reasonable with the change
Vector delta = entity - > GetAbsOrigin ( ) - change - > m_vecOrigin ;
// If it moved really far, just leave the entity in the new spot!!!
if ( delta . LengthSqr ( ) < LAG_COMPENSATION_TELEPORTED_DISTANCE_SQR )
{
RestoreEntityTo ( entity , restore - > m_vecOrigin + delta ) ;
2020-04-22 16:56:21 +00:00
}
2023-10-03 14:23:56 +00:00
}
CBaseAnimating * pAnimating = entity - > GetBaseAnimating ( ) ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
if ( wantsAnims & & pAnimating )
{
2020-04-22 16:56:21 +00:00
if ( restore - > m_fFlags & LC_ANIMATION_CHANGED )
{
restoreSimulationTime = true ;
2023-10-03 14:23:56 +00:00
pAnimating - > SetSequence ( restore - > m_masterSequence ) ;
pAnimating - > SetCycle ( restore - > m_masterCycle ) ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
CBaseAnimatingOverlay * pAnimatingOverlay = entity - > GetBaseAnimatingOverlay ( ) ;
if ( pAnimatingOverlay )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
int layerCount = pAnimatingOverlay - > GetNumAnimOverlays ( ) ;
for ( int layerIndex = 0 ; layerIndex < layerCount ; + + layerIndex )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
CAnimationLayer * currentLayer = pAnimatingOverlay - > GetAnimOverlay ( layerIndex ) ;
if ( currentLayer )
{
currentLayer - > m_flCycle = restore - > m_layerRecords [ layerIndex ] . m_cycle ;
currentLayer - > m_nOrder = restore - > m_layerRecords [ layerIndex ] . m_order ;
currentLayer - > m_nSequence = restore - > m_layerRecords [ layerIndex ] . m_sequence ;
currentLayer - > m_flWeight = restore - > m_layerRecords [ layerIndex ] . m_weight ;
}
2020-04-22 16:56:21 +00:00
}
}
}
2023-10-03 14:23:56 +00:00
}
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
if ( restoreSimulationTime )
{
entity - > SetSimulationTime ( restore - > m_flSimulationTime ) ;
2020-04-22 16:56:21 +00:00
}
}