diff --git a/game/client/c_baseanimating.cpp b/game/client/c_baseanimating.cpp index c70a08ee9e..5705127184 100644 --- a/game/client/c_baseanimating.cpp +++ b/game/client/c_baseanimating.cpp @@ -1948,7 +1948,8 @@ void C_BaseAnimating::StandardBlendingRules( CStudioHdr *hdr, Vector pos[], Quat return; } - if (GetSequence() >= hdr->GetNumSeq() || GetSequence() == -1 ) + // CS weapons have this sometimes ... + if ( GetSequence() >= hdr->GetNumSeq() || GetSequence() == -1 ) { SetSequence( 0 ); } @@ -4928,7 +4929,8 @@ void C_BaseAnimating::Simulate() DelayedInitModelEffects(); } - if ( gpGlobals->frametime != 0.0f ) + // TODO_ENHANCED: check if there's other stuff like this! This can break lag compensation. + if ( gpGlobals->frametime != 0.0f && m_bClientSideAnimation ) { DoAnimationEvents( GetModelPtr() ); } diff --git a/game/client/c_baseanimating.h b/game/client/c_baseanimating.h index 7126e8d897..b5b3b9d2a1 100644 --- a/game/client/c_baseanimating.h +++ b/game/client/c_baseanimating.h @@ -534,6 +534,7 @@ protected: float m_fadeMinDist; float m_fadeMaxDist; float m_flFadeScale; + bool m_bClientSideAnimation; private: @@ -567,7 +568,6 @@ private: float m_flOldEncodedController[MAXSTUDIOBONECTRLS]; // Clientside animation - bool m_bClientSideAnimation; bool m_bLastClientSideFrameReset; int m_nNewSequenceParity; diff --git a/game/client/cstrike/c_cs_player.cpp b/game/client/cstrike/c_cs_player.cpp index c481b2c8da..bd6e420f11 100644 --- a/game/client/cstrike/c_cs_player.cpp +++ b/game/client/cstrike/c_cs_player.cpp @@ -124,40 +124,6 @@ CAddonInfo g_AddonInfo[] = { "eholster", 0, "models/weapons/w_eq_eholster_elite.mdl", "models/weapons/w_eq_eholster.mdl" }, }; -// -------------------------------------------------------------------------------- // -// Player animation event. Sent to the client when a player fires, jumps, reloads, etc.. -// -------------------------------------------------------------------------------- // - -class C_TEPlayerAnimEvent : public C_BaseTempEntity -{ -public: - DECLARE_CLASS( C_TEPlayerAnimEvent, C_BaseTempEntity ); - DECLARE_CLIENTCLASS(); - - virtual void PostDataUpdate( DataUpdateType_t updateType ) - { - // Create the effect. - C_CSPlayer *pPlayer = dynamic_cast< C_CSPlayer* >( m_hPlayer.Get() ); - if ( pPlayer && !pPlayer->IsDormant() ) - { - pPlayer->DoAnimationEvent( (PlayerAnimEvent_t)m_iEvent.Get(), m_nData ); - } - } - -public: - CNetworkHandle( CBasePlayer, m_hPlayer ); - CNetworkVar( int, m_iEvent ); - CNetworkVar( int, m_nData ); -}; - -IMPLEMENT_CLIENTCLASS_EVENT( C_TEPlayerAnimEvent, DT_TEPlayerAnimEvent, CTEPlayerAnimEvent ); - -BEGIN_RECV_TABLE_NOBASE( C_TEPlayerAnimEvent, DT_TEPlayerAnimEvent ) - RecvPropEHandle( RECVINFO( m_hPlayer ) ), - RecvPropInt( RECVINFO( m_iEvent ) ), - RecvPropInt( RECVINFO( m_nData ) ) -END_RECV_TABLE() - BEGIN_PREDICTION_DATA( C_CSPlayer ) #ifdef CS_SHIELD_ENABLED DEFINE_PRED_FIELD( m_bShieldDrawn, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), @@ -767,7 +733,7 @@ C_CSPlayer::C_CSPlayer() : { m_angEyeAngles.Init(); - AddVar( &m_angEyeAngles, &m_iv_angEyeAngles, LATCH_SIMULATION_VAR ); + // AddVar( &m_angEyeAngles, &m_iv_angEyeAngles, LATCH_SIMULATION_VAR ); m_iLastAddonBits = m_iAddonBits = 0; m_iLastPrimaryAddon = m_iLastSecondaryAddon = WEAPON_NONE; @@ -1689,6 +1655,11 @@ bool C_CSPlayer::Weapon_CanSwitchTo( CBaseCombatWeapon *pWeapon ) void C_CSPlayer::UpdateClientSideAnimation() { + if ( !m_bClientSideAnimation ) + { + return; + } + // We do this in a different order than the base class. // We need our cycle to be valid for when we call the playeranimstate update code, // or else it'll synchronize the upper body anims with the wrong cycle. @@ -2074,6 +2045,8 @@ void C_CSPlayer::PlayReloadEffect() void C_CSPlayer::DoAnimationEvent( PlayerAnimEvent_t event, int nData ) { + // do nothing ... let server doing its animations. + return; } void C_CSPlayer::FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options ) @@ -2330,11 +2303,11 @@ void C_CSPlayer::Simulate( void ) } } - BaseClass::Simulate(); + BaseClass::Simulate(); - if ((cl_showhitboxes.GetInt() == 1 || cl_showhitboxes.GetInt() == 2) && !IsLocalPlayer()) - { - DrawClientHitboxes(gpGlobals->frametime, true); + if ( ( cl_showhitboxes.GetInt() == 1 || cl_showhitboxes.GetInt() == 2 ) && !IsLocalPlayer() ) + { + DrawClientHitboxes( gpGlobals->frametime, true ); } } diff --git a/game/client/in_main.cpp b/game/client/in_main.cpp index 537612df86..5c9291ad97 100644 --- a/game/client/in_main.cpp +++ b/game/client/in_main.cpp @@ -1306,7 +1306,7 @@ void CInput::CreateMove ( int sequence_number, float input_sample_frametime, boo for ( int i = 0; i < MAX_EDICTS; i++ ) { - cmd->simulationdata[i].m_bEntityExists = false; + cmd->simulationdata[i].entityexists = false; } // Send interpolated simulation time for lag compensation, let it also auto-vectorize this. @@ -1319,9 +1319,9 @@ void CInput::CreateMove ( int sequence_number, float input_sample_frametime, boo continue; } - cmd->simulationdata[pEntity->index].m_flSimulationTime = pEntity->m_flInterpolatedSimulationTime; - cmd->simulationdata[pEntity->index].m_flAnimTime = pEntity->m_flSimulationTime; - cmd->simulationdata[pEntity->index].m_bEntityExists = true; + cmd->simulationdata[pEntity->index].lerp_time = pEntity->m_flInterpolatedSimulationTime; + cmd->simulationdata[pEntity->index].animated_sim_time = pEntity->m_flSimulationTime; + cmd->simulationdata[pEntity->index].entityexists = true; } #ifdef CSTRIKE_DLL diff --git a/game/server/BaseAnimatingOverlay.cpp b/game/server/BaseAnimatingOverlay.cpp index c3201a7d31..9a4d8c0e9d 100644 --- a/game/server/BaseAnimatingOverlay.cpp +++ b/game/server/BaseAnimatingOverlay.cpp @@ -480,7 +480,7 @@ void CBaseAnimatingOverlay::GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Q if ( m_pIk ) { CIKContext auto_ik; - auto_ik.Init( pStudioHdr, GetAbsAngles(), GetAbsOrigin(), gpGlobals->curtime, 0, boneMask ); + auto_ik.Init( pStudioHdr, GetAbsAngles(), GetAbsOrigin(), gpGlobals->curtime, m_iIKCounter, boneMask ); boneSetup.CalcAutoplaySequences( pos, q, gpGlobals->curtime, &auto_ik ); } else @@ -490,7 +490,7 @@ void CBaseAnimatingOverlay::GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Q if ( pStudioHdr->numbonecontrollers() ) { - boneSetup.CalcBoneAdj( pos, q, GetEncodedControllerArray() ); + boneSetup.CalcBoneAdj( pos, q, GetBoneControllerArray() ); } } diff --git a/game/server/baseanimating.cpp b/game/server/baseanimating.cpp index 0ddd94184b..f4fc73c958 100644 --- a/game/server/baseanimating.cpp +++ b/game/server/baseanimating.cpp @@ -2805,8 +2805,12 @@ CBoneCache *CBaseAnimating::GetBoneCache( void ) CStudioHdr *pStudioHdr = GetModelPtr( ); Assert(pStudioHdr); + // Use this for lag compensation + float oldcurtime = gpGlobals->curtime; + gpGlobals->curtime = m_flSimulationTime; + CBoneCache *pcache = Studio_GetBoneCache( m_boneCacheHandle ); - int boneMask = BONE_USED_BY_HITBOX | BONE_USED_BY_ATTACHMENT; + int boneMask = BONE_USED_BY_ANYTHING; // TF queries these bones to position weapons when players are killed #if defined( TF_DLL ) @@ -2814,8 +2818,10 @@ CBoneCache *CBaseAnimating::GetBoneCache( void ) #endif if ( pcache ) { - if ( pcache->IsValid( gpGlobals->curtime ) && (pcache->m_boneMask & boneMask) == boneMask && pcache->m_timeValid <= gpGlobals->curtime) + // Only validate for current time. + if ( (pcache->m_boneMask & boneMask) == boneMask && pcache->m_timeValid == gpGlobals->curtime) { + gpGlobals->curtime = oldcurtime; // Msg("%s:%s:%s (%x:%x:%8.4f) cache\n", GetClassname(), GetDebugName(), STRING(GetModelName()), boneMask, pcache->m_boneMask, pcache->m_timeValid ); // in memory and still valid, use it! return pcache; @@ -2849,6 +2855,8 @@ CBoneCache *CBaseAnimating::GetBoneCache( void ) pcache = Studio_GetBoneCache( m_boneCacheHandle ); } Assert(pcache); + + gpGlobals->curtime = oldcurtime; return pcache; } @@ -3035,7 +3043,7 @@ void CBaseAnimating::GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Quaterni if ( m_pIk ) { CIKContext auto_ik; - auto_ik.Init( pStudioHdr, GetAbsAngles(), GetAbsOrigin(), gpGlobals->curtime, 0, boneMask ); + auto_ik.Init( pStudioHdr, GetAbsAngles(), GetAbsOrigin(), gpGlobals->curtime, m_iIKCounter, boneMask ); boneSetup.CalcAutoplaySequences( pos, q, gpGlobals->curtime, &auto_ik ); } else @@ -3045,7 +3053,7 @@ void CBaseAnimating::GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Quaterni if ( pStudioHdr->numbonecontrollers() ) { - boneSetup.CalcBoneAdj( pos, q, GetEncodedControllerArray() ); + boneSetup.CalcBoneAdj( pos, q, GetBoneControllerArray() ); } } diff --git a/game/server/baseanimating.h b/game/server/baseanimating.h index 7e556342ba..221f9ae8fc 100644 --- a/game/server/baseanimating.h +++ b/game/server/baseanimating.h @@ -325,7 +325,7 @@ public: CBaseEntity *GetLightingOrigin(); const float* GetPoseParameterArray() { return m_flPoseParameter.Base(); } - const float *GetEncodedControllerArray() { return m_flEncodedController.Base(); } + const float *GetBoneControllerArray() { return m_flEncodedController.Base(); } void BuildMatricesWithBoneMerge( const CStudioHdr *pStudioHdr, const QAngle& angles, const Vector& origin, const Vector pos[MAXSTUDIOBONES], diff --git a/game/server/cstrike/cs_player.cpp b/game/server/cstrike/cs_player.cpp index b4224d4ba9..8bff482666 100644 --- a/game/server/cstrike/cs_player.cpp +++ b/game/server/cstrike/cs_player.cpp @@ -240,45 +240,6 @@ IMPLEMENT_SERVERCLASS_ST_NOBASE( CCSRagdoll, DT_CSRagdoll ) SendPropInt( SENDINFO( m_bClientSideAnimation ), 1, SPROP_UNSIGNED ), END_SEND_TABLE() - -// -------------------------------------------------------------------------------- // -// Player animation event. Sent to the client when a player fires, jumps, reloads, etc.. -// -------------------------------------------------------------------------------- // - -class CTEPlayerAnimEvent : public CBaseTempEntity -{ -public: - DECLARE_CLASS( CTEPlayerAnimEvent, CBaseTempEntity ); - DECLARE_SERVERCLASS(); - - CTEPlayerAnimEvent( const char *name ) : CBaseTempEntity( name ) - { - } - - CNetworkHandle( CBasePlayer, m_hPlayer ); - CNetworkVar( int, m_iEvent ); - CNetworkVar( int, m_nData ); -}; - -IMPLEMENT_SERVERCLASS_ST_NOBASE( CTEPlayerAnimEvent, DT_TEPlayerAnimEvent ) - SendPropEHandle( SENDINFO( m_hPlayer ) ), - SendPropInt( SENDINFO( m_iEvent ), Q_log2( PLAYERANIMEVENT_COUNT ) + 1, SPROP_UNSIGNED ), - SendPropInt( SENDINFO( m_nData ), 32 ) -END_SEND_TABLE() - -static CTEPlayerAnimEvent g_TEPlayerAnimEvent( "PlayerAnimEvent" ); - -void TE_PlayerAnimEvent( CBasePlayer *pPlayer, PlayerAnimEvent_t event, int nData ) -{ - CPVSFilter filter( (const Vector&)pPlayer->EyePosition() ); - - g_TEPlayerAnimEvent.m_hPlayer = pPlayer; - g_TEPlayerAnimEvent.m_iEvent = event; - g_TEPlayerAnimEvent.m_nData = nData; - g_TEPlayerAnimEvent.Create( filter, 0 ); -} - - //----------------------------------------------------------------------------- // Purpose: Filters updates to a variable so that only non-local players see // the changes. This is so we can send a low-res origin to non-local players @@ -6664,7 +6625,6 @@ void CCSPlayer::DoAnimationEvent( PlayerAnimEvent_t event, int nData ) else { m_PlayerAnimState->DoAnimationEvent( event, nData ); - TE_PlayerAnimEvent( this, event, nData ); // Send to any clients who can see this guy. } } diff --git a/game/server/cstrike/cs_player.h b/game/server/cstrike/cs_player.h index 0b67f22dd7..de2bc7e6e6 100644 --- a/game/server/cstrike/cs_player.h +++ b/game/server/cstrike/cs_player.h @@ -782,7 +782,7 @@ protected: void PushawayThink(); -private: +public: ICSPlayerAnimState *m_PlayerAnimState; diff --git a/game/server/ilagcompensationmanager.h b/game/server/ilagcompensationmanager.h index 465d3cceab..5970ce6137 100644 --- a/game/server/ilagcompensationmanager.h +++ b/game/server/ilagcompensationmanager.h @@ -22,8 +22,9 @@ abstract_class ILagCompensationManager public: // Called during player movement to set up/restore after lag compensation virtual void StartLagCompensation( CBasePlayer *player, CUserCmd *cmd ) = 0; - virtual void FinishLagCompensation(CBasePlayer * player) = 0; - virtual void BacktrackPlayer( CBasePlayer *player, CUserCmd *cmd ) = 0; + virtual void FinishLagCompensation(CBasePlayer *player) = 0; + virtual void BacktrackPlayer( CBasePlayer *player, CUserCmd *cmd ) = 0; + virtual void TrackPlayerData( CBasePlayer *pPlayer ) = 0; }; extern ILagCompensationManager *lagcompensation; diff --git a/game/server/player_command.cpp b/game/server/player_command.cpp index 410436b7a6..d529f88121 100644 --- a/game/server/player_command.cpp +++ b/game/server/player_command.cpp @@ -342,15 +342,15 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper return; // Don't process this command } - gpGlobals->frametime = playerFrameTime; - gpGlobals->curtime = player->m_nTickBase * TICK_INTERVAL; - - StartCommand( player, ucmd ); + gpGlobals->frametime = playerFrameTime; + gpGlobals->curtime = player->m_nTickBase * TICK_INTERVAL; - g_pGameMovement->StartTrackPredictionErrors( player ); + StartCommand( player, ucmd ); + + g_pGameMovement->StartTrackPredictionErrors( player ); // Prevent hacked clients from sending us invalid view angles to try to get leaf server code to crash - if ( !ucmd->viewangles.IsValid() || !IsEntityQAngleReasonable(ucmd->viewangles) ) + if ( !ucmd->viewangles.IsValid() || !IsEntityQAngleReasonable( ucmd->viewangles ) ) { ucmd->viewangles = vec3_angle; } @@ -363,9 +363,7 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper { // If no clipping and cheats enabled and noclipduring game enabled, then leave // forwardmove and angles stuff in usercmd - if ( player->GetMoveType() == MOVETYPE_NOCLIP && - sv_cheats->GetBool() && - sv_noclipduringpause.GetBool() ) + if ( player->GetMoveType() == MOVETYPE_NOCLIP && sv_cheats->GetBool() && sv_noclipduringpause.GetBool() ) { gpGlobals->frametime = TICK_INTERVAL; } @@ -385,7 +383,7 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper // Do weapon selection if ( ucmd->weaponselect != 0 ) { - CBaseCombatWeapon *weapon = dynamic_cast< CBaseCombatWeapon * >( CBaseEntity::Instance( ucmd->weaponselect ) ); + CBaseCombatWeapon* weapon = dynamic_cast< CBaseCombatWeapon* >( CBaseEntity::Instance( ucmd->weaponselect ) ); if ( weapon ) { VPROF( "player->SelectItem()" ); @@ -393,13 +391,14 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper } } - IServerVehicle *pVehicle = player->GetVehicle(); + IServerVehicle* pVehicle = player->GetVehicle(); // Latch in impulse. if ( ucmd->impulse ) { // Discard impulse commands unless the vehicle allows them. - // FIXME: UsingStandardWeapons seems like a bad filter for this. The flashlight is an impulse command, for example. + // FIXME: UsingStandardWeapons seems like a bad filter for this. The flashlight is an impulse command, for + // example. if ( !pVehicle || player->UsingStandardWeaponsInVehicle() ) { player->m_nImpulse = ucmd->impulse; @@ -420,7 +419,7 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper { player->pl.v_angle = ucmd->viewangles; } - else if( player->pl.fixangle == FIXANGLE_RELATIVE ) + else if ( player->pl.fixangle == FIXANGLE_RELATIVE ) { player->pl.v_angle = ucmd->viewangles + player->pl.anglechange; } @@ -446,18 +445,18 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper VPROF( "pVehicle->ProcessMovement()" ); pVehicle->ProcessMovement( player, g_pMoveData ); } - + // Copy output FinishMove( player, ucmd, g_pMoveData ); - // Let server invoke any needed impact functions + // Let server invoke any needed impact functions VPROF_SCOPE_BEGIN( "moveHelper->ProcessImpacts" ); moveHelper->ProcessImpacts(); - VPROF_SCOPE_END(); + VPROF_SCOPE_END(); - RunPostThink( player ); + RunPostThink( player ); - ServiceEventQueue( player ); + ServiceEventQueue( player ); g_pGameMovement->FinishTrackPredictionErrors( player ); @@ -467,5 +466,7 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper if ( gpGlobals->frametime > 0 ) { player->m_nTickBase++; - } + } + + lagcompensation->TrackPlayerData( player ); } diff --git a/game/server/player_lagcompensation.cpp b/game/server/player_lagcompensation.cpp index 47fef39d3e..df85c372f9 100644 --- a/game/server/player_lagcompensation.cpp +++ b/game/server/player_lagcompensation.cpp @@ -5,9 +5,11 @@ // $NoKeywords: $ //=============================================================================// -#include "bone_setup.h" #include "cbase.h" #include "icvar.h" +#include "player.h" +#include "shareddefs.h" +#include "studio.h" #include "usercmd.h" #include "igamesystem.h" #include "ilagcompensationmanager.h" @@ -20,108 +22,115 @@ // 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_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) -#define LC_POSE_PARAMS_CHANGED (1<<12) -#define LC_ENCD_CONS_CHANGED (1<<13) +#define LC_ORIGIN_CHANGED ( 1 << 8 ) +#define LC_ANGLES_CHANGED ( 1 << 9 ) +#define LC_SIZE_CHANGED ( 1 << 10 ) +#define LC_ANIMATION_CHANGED ( 1 << 11 ) +#define LC_POSE_PARAMS_CHANGED ( 1 << 12 ) +#define LC_ENCD_CONS_CHANGED ( 1 << 13 ) + +#define MAX_TICKS_SAVED 2048 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", "0", FCVAR_DEVELOPMENTONLY, "Flushes entity bone cache on lag compensation" ); -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" ); +ConVar sv_lagflushbonecache( "sv_lagflushbonecache", "0", 0, "Flushes entity bone cache on lag compensation" ); //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- struct LagRecord { -public: + public: LagRecord() { - m_fFlags = 0; + m_fFlags = LC_NONE; m_vecOrigin.Init(); m_vecAngles.Init(); m_vecMinsPreScaled.Init(); m_vecMaxsPreScaled.Init(); m_flSimulationTime = -1; - m_flAnimTime = -1; - m_masterSequence = 0; - m_masterCycle = 0; + m_flAnimTime = -1; + m_masterSequence = 0; + m_masterCycle = 0; - for (int i = 0; i < MAX_LAYER_RECORDS; i++) + for ( int layerIndex = 0; layerIndex < MAX_LAYER_RECORDS; ++layerIndex ) { - m_poseParameters[i] = -1; + m_layerRecords[layerIndex] = {}; } - for (int i = 0; i < MAX_ENCODED_CONTROLLERS; i++) + for ( int i = 0; i < MAXSTUDIOPOSEPARAM; i++ ) { - m_encodedControllers[i] = -1; + m_poseParameters[i] = 0; + } + + for ( int i = 0; i < MAXSTUDIOBONECTRLS; i++ ) + { + m_encodedControllers[i] = 0; } } LagRecord( const LagRecord& src ) { - m_fFlags = src.m_fFlags; - m_vecOrigin = src.m_vecOrigin; - m_vecAngles = src.m_vecAngles; + m_fFlags = src.m_fFlags; + m_vecOrigin = src.m_vecOrigin; + m_vecAngles = src.m_vecAngles; m_vecMinsPreScaled = src.m_vecMinsPreScaled; m_vecMaxsPreScaled = src.m_vecMaxsPreScaled; m_flSimulationTime = src.m_flSimulationTime; - m_flAnimTime = src.m_flAnimTime; - for( int layerIndex = 0; layerIndex < MAX_LAYER_RECORDS; ++layerIndex ) + m_flAnimTime = src.m_flAnimTime; + + for ( int layerIndex = 0; layerIndex < MAX_LAYER_RECORDS; ++layerIndex ) { m_layerRecords[layerIndex] = src.m_layerRecords[layerIndex]; } + m_masterSequence = src.m_masterSequence; - m_masterCycle = src.m_masterCycle; + m_masterCycle = src.m_masterCycle; - for (int i = 0; i < MAX_LAYER_RECORDS; i++) + for ( int i = 0; i < MAXSTUDIOPOSEPARAM; i++ ) { m_poseParameters[i] = src.m_poseParameters[i]; } - for (int i = 0; i < MAX_LAYER_RECORDS; i++) + for ( int i = 0; i < MAXSTUDIOBONECTRLS; i++ ) { m_encodedControllers[i] = src.m_encodedControllers[i]; } } // Did player die this frame - int m_fFlags; + int m_fFlags; // Player position, orientation and bbox - Vector m_vecOrigin; - QAngle m_vecAngles; - Vector m_vecMinsPreScaled; - Vector m_vecMaxsPreScaled; + Vector m_vecOrigin; + QAngle m_vecAngles; + Vector m_vecMinsPreScaled; + Vector m_vecMaxsPreScaled; + + float m_flSimulationTime; + float m_flAnimTime; - float m_flSimulationTime; - float m_flAnimTime; - // Player animation details, so we can get the legs in the right spot. - LayerRecord m_layerRecords[MAX_LAYER_RECORDS]; - int m_masterSequence; - float m_masterCycle; - float m_poseParameters[MAX_POSE_PARAMETERS]; - float m_encodedControllers[MAX_ENCODED_CONTROLLERS]; + LayerRecord m_layerRecords[MAX_LAYER_RECORDS]; + int m_masterSequence; + float m_masterCycle; + float m_poseParameters[MAXSTUDIOPOSEPARAM]; + float m_encodedControllers[MAXSTUDIOBONECTRLS]; }; - // // 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; -static void RestorePlayerTo( CBasePlayer *pPlayer, const Vector &vWantedPos ) + +static void RestorePlayerTo( CBasePlayer* pPlayer, const Vector& vWantedPos ) { // Try to move to the wanted position from our current position. trace_t tr; @@ -132,26 +141,39 @@ static void RestorePlayerTo( CBasePlayer *pPlayer, const Vector &vWantedPos ) if ( sv_unlag_debug.GetBool() ) { DevMsg( "RestorePlayerTo() could not restore player position for client \"%s\" ( %.1f %.1f %.1f )\n", - pPlayer->GetPlayerName(), vWantedPos.x, vWantedPos.y, vWantedPos.z ); + pPlayer->GetPlayerName(), + vWantedPos.x, + vWantedPos.y, + vWantedPos.z ); } - UTIL_TraceEntity( pPlayer, pPlayer->GetLocalOrigin(), vWantedPos, MASK_PLAYERSOLID, pPlayer, COLLISION_GROUP_PLAYER_MOVEMENT, &tr ); + UTIL_TraceEntity( pPlayer, + pPlayer->GetAbsOrigin(), + vWantedPos, + MASK_PLAYERSOLID, + pPlayer, + COLLISION_GROUP_PLAYER_MOVEMENT, + &tr ); 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; - VectorLerp( pPlayer->GetLocalOrigin(), vWantedPos, tr.fraction * g_flFractionScale, vPos ); + VectorLerp( pPlayer->GetAbsOrigin(), vWantedPos, tr.fraction * g_flFractionScale, vPos ); UTIL_SetOrigin( pPlayer, vPos, true ); if ( sv_unlag_debug.GetBool() ) + { DevMsg( " restore got most of the way\n" ); + } } } else @@ -161,14 +183,14 @@ static void RestorePlayerTo( CBasePlayer *pPlayer, const Vector &vWantedPos ) } } - //----------------------------------------------------------------------------- -// Purpose: +// Purpose: //----------------------------------------------------------------------------- -class CLagCompensationManager : public CAutoGameSystemPerFrame, public ILagCompensationManager +class CLagCompensationManager : public CAutoGameSystemPerFrame, + public ILagCompensationManager { -public: - CLagCompensationManager( char const *name ) : CAutoGameSystemPerFrame( name ) + public: + CLagCompensationManager( const char* name ) { } @@ -183,179 +205,124 @@ public: ClearHistory(); } - // called after entities think - virtual void FrameUpdatePostEntityThink(); - // ILagCompensationManager stuff // Called during player movement to set up/restore after lag compensation - void StartLagCompensation( CBasePlayer *player, CUserCmd *cmd ); - void FinishLagCompensation( CBasePlayer *player ); + void StartLagCompensation( CBasePlayer* player, CUserCmd* cmd ); + void FinishLagCompensation( CBasePlayer* player ); + virtual void TrackPlayerData( CBasePlayer* pPlayer ); -private: - virtual void BacktrackPlayer( CBasePlayer *player, CUserCmd *cmd ); + private: + void BacktrackPlayer( CBasePlayer* player, CUserCmd* cmd ); void ClearHistory() { - for ( int i=0; i m_PlayerTrack[ MAX_PLAYERS ]; + // keep a list of lag records for each entities + CUtlCircularBuffer< LagRecord, MAX_TICKS_SAVED > m_EntityTrack[MAX_EDICTS]; // Scratchpad for determining what needs to be restored - CBitVec m_RestorePlayer; - bool m_bNeedToRestore; - - LagRecord m_RestoreData[ MAX_PLAYERS ]; // player data before we moved him back - LagRecord m_ChangeData[ MAX_PLAYERS ]; // player data where we moved him back + CBitVec< MAX_EDICTS > m_RestorePlayer; + bool m_bNeedToRestore; - CBasePlayer *m_pCurrentPlayer; // The player we are doing lag compensation for + LagRecord m_RestoreData[MAX_EDICTS]; // entities data before we moved him back + LagRecord m_ChangeData[MAX_EDICTS]; // entities data where we moved him back }; static CLagCompensationManager g_LagCompensationManager( "CLagCompensationManager" ); -ILagCompensationManager *lagcompensation = &g_LagCompensationManager; - +ILagCompensationManager* lagcompensation = &g_LagCompensationManager; //----------------------------------------------------------------------------- // Purpose: Called once per frame after all entities have had a chance to think //----------------------------------------------------------------------------- -void CLagCompensationManager::FrameUpdatePostEntityThink() +void CLagCompensationManager::TrackPlayerData( CBasePlayer* pPlayer ) { - if ( (gpGlobals->maxClients <= 1) || !sv_unlag.GetBool() ) + if ( ( gpGlobals->maxClients <= 1 ) || !sv_unlag.GetBool() ) { ClearHistory(); return; } - VPROF_BUDGET( "FrameUpdatePostEntityThink", "CLagCompensationManager" ); + VPROF_BUDGET( "TrackPlayerData", "CLagCompensationManager" ); // remove all records before that time: - int flDeadtime = gpGlobals->curtime - sv_maxunlag.GetFloat(); + auto track = &m_EntityTrack[pPlayer->entindex()]; - // Iterate all active players - for ( int i = 1; i <= gpGlobals->maxClients; i++ ) + // add new record to player track + LagRecord record; + + record.m_fFlags = LC_NONE; + if ( pPlayer->IsAlive() ) { - CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + record.m_fFlags |= LC_ALIVE; + } - CUtlFixedLinkedList< LagRecord > *track = &m_PlayerTrack[i-1]; + record.m_flSimulationTime = pPlayer->GetSimulationTime(); + record.m_flAnimTime = pPlayer->GetAnimTime(); + record.m_vecAngles = pPlayer->GetAbsAngles(); + record.m_vecOrigin = pPlayer->GetAbsOrigin(); + record.m_vecMinsPreScaled = pPlayer->CollisionProp()->OBBMinsPreScaled(); + record.m_vecMaxsPreScaled = pPlayer->CollisionProp()->OBBMaxsPreScaled(); - if ( !pPlayer ) + int layerCount = pPlayer->GetNumAnimOverlays(); + + for ( int layerIndex = 0; layerIndex < layerCount; ++layerIndex ) + { + CAnimationLayer* currentLayer = pPlayer->GetAnimOverlay( layerIndex ); + if ( currentLayer ) { - if ( track->Count() > 0 ) - { - track->RemoveAll(); - } - - continue; - } - - Assert( track->Count() < 1000 ); // insanity check - - // remove tail records that are too old - intp 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 >= pPlayer->GetSimulationTime() ) - continue; // don't add new entry for same or older time - } - - // add new record to player track - LagRecord &record = track->Element( track->AddToHead() ); - - record.m_fFlags = 0; - if ( pPlayer->IsAlive() ) - { - record.m_fFlags |= LC_ALIVE; - } - - record.m_flSimulationTime = pPlayer->GetSimulationTime(); - record.m_flAnimTime = pPlayer->GetAnimTime(); - record.m_vecAngles = pPlayer->GetLocalAngles(); - record.m_vecOrigin = pPlayer->GetLocalOrigin(); - record.m_vecMinsPreScaled = pPlayer->CollisionProp()->OBBMinsPreScaled(); - record.m_vecMaxsPreScaled = pPlayer->CollisionProp()->OBBMaxsPreScaled(); - - int layerCount = pPlayer->GetNumAnimOverlays(); - for( int layerIndex = 0; layerIndex < layerCount; ++layerIndex ) - { - CAnimationLayer *currentLayer = pPlayer->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; - } - } - record.m_masterSequence = pPlayer->GetSequence(); - record.m_masterCycle = pPlayer->GetCycle(); - - CStudioHdr *hdr = pPlayer->GetModelPtr(); - if( hdr ) - { - for( int paramIndex = 0; paramIndex < hdr->GetNumPoseParameters(); paramIndex++ ) - { - record.m_poseParameters[paramIndex] = pPlayer->GetPoseParameterArray()[ paramIndex ]; - } - } - - if( hdr ) - { - for( int paramIndex = 0; paramIndex < hdr->GetNumBoneControllers(); paramIndex++ ) - { - record.m_encodedControllers[paramIndex] = pPlayer->GetEncodedControllerArray()[ paramIndex ]; - } + 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; } } - //Clear the current player. - m_pCurrentPlayer = NULL; + record.m_masterSequence = pPlayer->GetSequence(); + record.m_masterCycle = pPlayer->GetCycle(); + + CStudioHdr* hdr = pPlayer->GetModelPtr(); + + if ( hdr ) + { + for ( int paramIndex = 0; paramIndex < hdr->GetNumPoseParameters(); paramIndex++ ) + { + record.m_poseParameters[paramIndex] = pPlayer->GetPoseParameterArray()[paramIndex]; + } + } + + if ( hdr ) + { + for ( int paramIndex = 0; paramIndex < hdr->GetNumBoneControllers(); paramIndex++ ) + { + record.m_encodedControllers[paramIndex] = pPlayer->GetBoneControllerArray()[paramIndex]; + } + } + + track->Push( record ); } // Called during player movement to set up/restore after lag compensation -void CLagCompensationManager::StartLagCompensation( CBasePlayer *player, CUserCmd *cmd ) +void CLagCompensationManager::StartLagCompensation( CBasePlayer* player, CUserCmd* cmd ) { - //DONT LAG COMP AGAIN THIS FRAME IF THERES ALREADY ONE IN PROGRESS - //IF YOU'RE HITTING THIS THEN IT MEANS THERES A CODE BUG - if ( m_pCurrentPlayer ) - { - Assert( m_pCurrentPlayer == NULL ); - Warning( "Trying to start a new lag compensation session while one is already active!\n" ); - return; - } - // Assume no players need to be restored m_RestorePlayer.ClearAll(); 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 - ) + 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; + } // NOTE: Put this here so that it won't show up in single player mode. VPROF_BUDGET( "StartLagCompensation", VPROF_BUDGETGROUP_OTHER_NETWORKING ); @@ -363,10 +330,10 @@ void CLagCompensationManager::StartLagCompensation( CBasePlayer *player, CUserCm Q_memset( m_ChangeData, 0, sizeof( m_ChangeData ) ); // Iterate all active players - const CBitVec *pEntityTransmitBits = engine->GetEntityTransmitBitsForClient( player->entindex() - 1 ); + const CBitVec< MAX_EDICTS >* pEntityTransmitBits = engine->GetEntityTransmitBitsForClient( player->entindex() - 1 ); for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { - CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + CBasePlayer* pPlayer = UTIL_PlayerByIndex( i ); if ( !pPlayer ) { @@ -381,75 +348,73 @@ void CLagCompensationManager::StartLagCompensation( CBasePlayer *player, CUserCm // Custom checks for if things should lag compensate (based on things like what team the player is on). if ( !player->WantsLagCompensationOnEntity( pPlayer, cmd, pEntityTransmitBits ) ) + { continue; + } // Move other player back in time BacktrackPlayer( pPlayer, cmd ); } } -void CLagCompensationManager::BacktrackPlayer( CBasePlayer *pPlayer, CUserCmd *cmd ) +void CLagCompensationManager::BacktrackPlayer( CBasePlayer* pPlayer, CUserCmd* cmd ) { Vector org; Vector minsPreScaled; Vector maxsPreScaled; QAngle ang; + LagRecord* nextRecordSim; + LagRecord* recordSim; + LagRecord* recordAnim; VPROF_BUDGET( "BacktrackPlayer", "CLagCompensationManager" ); - int pl_index = pPlayer->entindex() - 1; + int pl_index = pPlayer->entindex(); + + float flTargetLerpSimTime = cmd->simulationdata[pl_index].lerp_time; + float flTargetAnimatedSimulationTime = cmd->simulationdata[pl_index].animated_sim_time; - float flTargetSimulationTime = cmd->simulationdata[pl_index + 1].m_flSimulationTime; - float flTargetSimulatedAnimationTime = cmd->simulationdata[pl_index + 1].m_flAnimTime; - // get track history of this player - CUtlFixedLinkedList< LagRecord > *trackSim = &m_PlayerTrack[ pl_index ]; - CUtlFixedLinkedList< LagRecord > *trackAnim = &m_PlayerTrack[ pl_index ]; + auto track = &m_EntityTrack[pl_index]; - // check if we have at leat one entry - if ( trackSim->Count() <= 0 || trackAnim->Count() <= 0 ) - return; - - intp currSim = trackSim->Head(); - - LagRecord *prevRecordSim = NULL; - LagRecord *recordSim = NULL; - LagRecord *recordAnim = NULL; - - Vector prevOrg = pPlayer->GetLocalOrigin(); - bool foundAnimationData = false; - - // Walk context looking for any invalidating event - while( trackSim->IsValidIndex(currSim) ) + for ( int i = 0; i < MAX_TICKS_SAVED; i++ ) { - // remember last record - prevRecordSim = recordSim; + recordSim = track->Get( i ); - // get next record - recordSim = &trackSim->Element(currSim); - - if (recordSim->m_flSimulationTime - <= flTargetSimulatedAnimationTime && !foundAnimationData) - { - recordAnim = recordSim; - foundAnimationData = true; - } - - if ( !(recordSim->m_fFlags & LC_ALIVE) ) + if ( !recordSim ) { - // player most be alive, lost track - return; + break; } - // TODO_ENHANCED: do proper teleportation checks. + if ( !( recordSim->m_fFlags & LC_ALIVE ) ) + { + break; + } - // did we find a context smaller than target time ? - if ( recordSim->m_flSimulationTime <= flTargetSimulationTime ) - break; // hurra, stop + if ( flTargetLerpSimTime == recordSim->m_flSimulationTime ) + { + break; + } - prevOrg = recordSim->m_vecOrigin; + if ( recordSim->m_flSimulationTime < flTargetLerpSimTime ) + { + nextRecordSim = track->Get( i - 1 ); + break; + } + } - // go one step back - currSim = trackSim->Next( currSim ); + for ( int i = 0; i < MAX_TICKS_SAVED; i++ ) + { + recordAnim = track->Get( i ); + + if ( !recordAnim ) + { + break; + } + + if ( recordAnim->m_flSimulationTime == flTargetAnimatedSimulationTime ) + { + break; + } } Assert( recordAnim ); @@ -465,127 +430,68 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer *pPlayer, CUserCmd *c return; // that should never happen } - float fracSim = 0.0f; - if ( prevRecordSim && - (recordSim->m_flSimulationTime < flTargetSimulationTime) && - (recordSim->m_flSimulationTime < prevRecordSim->m_flSimulationTime) ) + float fracSim = 0.0f; + if ( nextRecordSim && ( recordSim->m_flSimulationTime < flTargetLerpSimTime ) + && ( recordSim->m_flSimulationTime < nextRecordSim->m_flSimulationTime ) ) { // we didn't find the exact time but have a valid previous record // so interpolate between these two records; Assert( prevRecordSim->m_flSimulationTime > recordSim->m_flSimulationTime ); - Assert( flTargetSimulationTime < prevRecordSim->m_flSimulationTime ); + Assert( flTargetLerpSimTime < prevRecordSim->m_flSimulationTime ); - // calc fraction between both records - fracSim = float(( double(flTargetSimulationTime) - double(recordSim->m_flSimulationTime) ) / - ( double(prevRecordSim->m_flSimulationTime) - double(recordSim->m_flSimulationTime) )); + // calc fraction between both records + fracSim = float( ( double( flTargetLerpSimTime ) - double( recordSim->m_flSimulationTime ) ) + / ( double( nextRecordSim->m_flSimulationTime ) - double( recordSim->m_flSimulationTime ) ) ); Assert( fracSim > 0 && fracSim < 1 ); // should never extrapolate - ang = Lerp( fracSim, recordSim->m_vecAngles, prevRecordSim->m_vecAngles ); - org = Lerp( fracSim, recordSim->m_vecOrigin, prevRecordSim->m_vecOrigin ); - minsPreScaled = Lerp( fracSim, recordSim->m_vecMinsPreScaled, prevRecordSim->m_vecMinsPreScaled ); - maxsPreScaled = Lerp( fracSim, recordSim->m_vecMaxsPreScaled, prevRecordSim->m_vecMaxsPreScaled ); + ang = Lerp( fracSim, recordSim->m_vecAngles, nextRecordSim->m_vecAngles ); + org = Lerp( fracSim, recordSim->m_vecOrigin, nextRecordSim->m_vecOrigin ); + minsPreScaled = Lerp( fracSim, recordSim->m_vecMinsPreScaled, nextRecordSim->m_vecMinsPreScaled ); + maxsPreScaled = Lerp( fracSim, recordSim->m_vecMaxsPreScaled, nextRecordSim->m_vecMaxsPreScaled ); } else { // we found the exact record or no other record to interpolate with // just copy these values since they are the best we have - org = recordSim->m_vecOrigin; - ang = recordSim->m_vecAngles; - minsPreScaled = recordSim->m_vecMinsPreScaled; - maxsPreScaled = recordSim->m_vecMaxsPreScaled; + org = recordSim->m_vecOrigin; + ang = recordSim->m_vecAngles; + minsPreScaled = recordSim->m_vecMinsPreScaled; + maxsPreScaled = recordSim->m_vecMaxsPreScaled; } - // 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; - UTIL_TraceEntity( pPlayer, org, org, MASK_PLAYERSOLID, &tr ); - if ( tr.startsolid || tr.allsolid ) - { - if ( sv_unlag_debug.GetBool() ) - DevMsg( "WARNING: BackupPlayer trying to back player into a bad position - client %s\n", pPlayer->GetPlayerName() ); - - CBasePlayer *pHitPlayer = dynamic_cast( tr.m_pEnt ); - - // don't lag compensate the current player - if ( pHitPlayer && ( pHitPlayer != m_pCurrentPlayer ) ) - { - // If we haven't backtracked this player, do it now - // this deliberately ignores WantsLagCompensationOnEntity. - if ( !m_RestorePlayer.Get( pHitPlayer->entindex() - 1 ) ) - { - // prevent recursion - save a copy of m_RestorePlayer, - // pretend that this player is off-limits - int pl_index = pPlayer->entindex() - 1; - - // Temp turn this flag on - m_RestorePlayer.Set( pl_index ); - - BacktrackPlayer( pHitPlayer, cmd ); - - // Remove the temp flag - m_RestorePlayer.Clear( pl_index ); - } - } - - // now trace us back as far as we can go - UTIL_TraceEntity( pPlayer, pPlayer->GetLocalOrigin(), org, MASK_PLAYERSOLID, &tr ); - - 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; - VectorLerp( pPlayer->GetLocalOrigin(), org, tr.fraction * g_flFractionScale, vPos ); - - // 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" ); - } - } - } - // See if this represents a change for the player - int flags = 0; - LagRecord *restore = &m_RestoreData[ pl_index ]; - LagRecord *change = &m_ChangeData[ pl_index ]; + int flags = 0; + LagRecord* restore = &m_RestoreData[pl_index]; + LagRecord* change = &m_ChangeData[pl_index]; - QAngle angdiff = pPlayer->GetLocalAngles() - ang; - Vector orgdiff = pPlayer->GetLocalOrigin() - org; + QAngle angdiff = pPlayer->GetAbsAngles() - ang; + Vector orgdiff = pPlayer->GetAbsOrigin() - org; // Always remember the pristine simulation time in case we need to restore it. restore->m_flSimulationTime = pPlayer->GetSimulationTime(); - restore->m_flAnimTime = pPlayer->GetAnimTime(); + restore->m_flAnimTime = pPlayer->GetAnimTime(); if ( angdiff.LengthSqr() > 0.0f ) { - flags |= LC_ANGLES_CHANGED; - restore->m_vecAngles = pPlayer->GetLocalAngles(); - pPlayer->SetLocalAngles( ang ); + flags |= LC_ANGLES_CHANGED; + restore->m_vecAngles = pPlayer->GetAbsAngles(); + pPlayer->SetAbsAngles( ang ); change->m_vecAngles = ang; } // Use absolute equality here - if ( minsPreScaled != pPlayer->CollisionProp()->OBBMinsPreScaled() || maxsPreScaled != pPlayer->CollisionProp()->OBBMaxsPreScaled() ) + if ( minsPreScaled != pPlayer->CollisionProp()->OBBMinsPreScaled() + || maxsPreScaled != pPlayer->CollisionProp()->OBBMaxsPreScaled() ) { flags |= LC_SIZE_CHANGED; restore->m_vecMinsPreScaled = pPlayer->CollisionProp()->OBBMinsPreScaled(); restore->m_vecMaxsPreScaled = pPlayer->CollisionProp()->OBBMaxsPreScaled(); - + pPlayer->SetSize( minsPreScaled, maxsPreScaled ); - + change->m_vecMinsPreScaled = minsPreScaled; change->m_vecMaxsPreScaled = maxsPreScaled; } @@ -593,128 +499,125 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer *pPlayer, CUserCmd *c // Note, do origin at end since it causes a relink into the k/d tree if ( orgdiff.LengthSqr() > 0.0f ) { - flags |= LC_ORIGIN_CHANGED; - restore->m_vecOrigin = pPlayer->GetLocalOrigin(); - pPlayer->SetLocalOrigin( org ); + flags |= LC_ORIGIN_CHANGED; + restore->m_vecOrigin = pPlayer->GetAbsOrigin(); + pPlayer->SetAbsOrigin( org ); change->m_vecOrigin = org; } // 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 = pPlayer->GetSequence(); - restore->m_masterCycle = pPlayer->GetCycle(); + flags |= LC_ANIMATION_CHANGED; + restore->m_masterSequence = pPlayer->GetSequence(); + restore->m_masterCycle = pPlayer->GetCycle(); - pPlayer->SetSequence(recordAnim->m_masterSequence); - pPlayer->SetCycle(recordAnim->m_masterCycle); + pPlayer->SetSequence( recordAnim->m_masterSequence ); + pPlayer->SetCycle( recordAnim->m_masterCycle ); //////////////////////// // Now do all the layers int layerCount = pPlayer->GetNumAnimOverlays(); - for( int layerIndex = 0; layerIndex < layerCount; ++layerIndex ) + for ( int layerIndex = 0; layerIndex < layerCount; ++layerIndex ) { - CAnimationLayer *currentLayer = pPlayer->GetAnimOverlay(layerIndex); - if( currentLayer ) + CAnimationLayer* currentLayer = pPlayer->GetAnimOverlay( layerIndex ); + if ( currentLayer ) { - restore->m_layerRecords[layerIndex].m_cycle = currentLayer->m_flCycle; - restore->m_layerRecords[layerIndex].m_order = currentLayer->m_nOrder; + 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; + restore->m_layerRecords[layerIndex].m_weight = currentLayer->m_flWeight; - currentLayer->m_flCycle = recordAnim->m_layerRecords[layerIndex].m_cycle; - currentLayer->m_nOrder = recordAnim->m_layerRecords[layerIndex].m_order; + currentLayer->m_flCycle = recordAnim->m_layerRecords[layerIndex].m_cycle; + currentLayer->m_nOrder = recordAnim->m_layerRecords[layerIndex].m_order; currentLayer->m_nSequence = recordAnim->m_layerRecords[layerIndex].m_sequence; - currentLayer->m_flWeight = recordAnim->m_layerRecords[layerIndex].m_weight; - } + currentLayer->m_flWeight = recordAnim->m_layerRecords[layerIndex].m_weight; + } } - + flags |= LC_POSE_PARAMS_CHANGED; // Now do pose parameters - CStudioHdr *hdr = pPlayer->GetModelPtr(); - if( hdr ) + CStudioHdr* hdr = pPlayer->GetModelPtr(); + if ( hdr ) { - for( int paramIndex = 0; paramIndex < hdr->GetNumPoseParameters(); paramIndex++ ) + for ( int paramIndex = 0; paramIndex < hdr->GetNumPoseParameters(); paramIndex++ ) { restore->m_poseParameters[paramIndex] = pPlayer->GetPoseParameterArray()[paramIndex]; - float poseParameter = recordAnim->m_poseParameters[paramIndex]; + float poseParameter = recordAnim->m_poseParameters[paramIndex]; - pPlayer->SetPoseParameterRaw(paramIndex, poseParameter); + pPlayer->SetPoseParameterRaw( paramIndex, poseParameter ); } } flags |= LC_ENCD_CONS_CHANGED; - if( hdr ) + if ( hdr ) { - for( int paramIndex = 0; paramIndex < hdr->GetNumBoneControllers(); paramIndex++ ) + for ( int encIndex = 0; encIndex < hdr->GetNumBoneControllers(); encIndex++ ) { - restore->m_encodedControllers[paramIndex] = pPlayer->GetEncodedControllerArray()[paramIndex]; - float encodedController = recordAnim->m_encodedControllers[paramIndex]; + restore->m_encodedControllers[encIndex] = pPlayer->GetBoneControllerArray()[encIndex]; + float encodedController = recordAnim->m_encodedControllers[encIndex]; - pPlayer->SetBoneControllerRaw( paramIndex, encodedController ); + pPlayer->SetBoneControllerRaw( encIndex, encodedController ); } } if ( !flags ) + { return; // we didn't change anything + } // Set lag compensated player's times - pPlayer->SetSimulationTime(flTargetSimulationTime); + pPlayer->SetSimulationTime( flTargetLerpSimTime ); // pPlayer->SetAnimTime(animationData->m_flAnimTime); if ( sv_lagflushbonecache.GetBool() ) - pPlayer->InvalidateBoneCache(); + { + pPlayer->InvalidateBoneCache(); + } - /*char text[256]; Q_snprintf( text, sizeof(text), "time %.2f", flTargetTime ); - pPlayer->DrawServerHitboxes( 10 ); - NDebugOverlay::Text( org, text, false, 10 ); - NDebugOverlay::EntityBounds( pPlayer, 255, 0, 0, 32, 10 ); */ - - m_RestorePlayer.Set( pl_index ); //remember that we changed this player - m_bNeedToRestore = true; // we changed at least one player - restore->m_fFlags = flags; // we need to restore these flags - change->m_fFlags = flags; // we have changed these flags + m_RestorePlayer.Set( pl_index ); // remember that we changed this player + m_bNeedToRestore = true; // we changed at least one player + restore->m_fFlags = flags; // we need to restore these flags + change->m_fFlags = flags; // we have changed these flags } - -void CLagCompensationManager::FinishLagCompensation( CBasePlayer *player ) +void CLagCompensationManager::FinishLagCompensation( CBasePlayer* player ) { - VPROF_BUDGET_FLAGS( "FinishLagCompensation", VPROF_BUDGETGROUP_OTHER_NETWORKING, BUDGETFLAG_CLIENT|BUDGETFLAG_SERVER ); - - m_pCurrentPlayer = NULL; + VPROF_BUDGET_FLAGS( "FinishLagCompensation", + VPROF_BUDGETGROUP_OTHER_NETWORKING, + BUDGETFLAG_CLIENT | BUDGETFLAG_SERVER ); if ( !m_bNeedToRestore ) + { return; // no player was changed at all + } // Iterate all active players for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { - int pl_index = i - 1; - - if ( !m_RestorePlayer.Get( pl_index ) ) + if ( !m_RestorePlayer.Get( i ) ) { // player wasn't changed by lag compensation continue; } - CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + CBasePlayer* pPlayer = UTIL_PlayerByIndex( i ); if ( !pPlayer ) { continue; } - LagRecord *restore = &m_RestoreData[ pl_index ]; - LagRecord *change = &m_ChangeData[ pl_index ]; + LagRecord* restore = &m_RestoreData[i]; + LagRecord* change = &m_ChangeData[i]; if ( restore->m_fFlags & LC_SIZE_CHANGED ) { // see if simulation made any changes, if no, then do the restore, otherwise, // leave new values in - if ( pPlayer->CollisionProp()->OBBMinsPreScaled() == change->m_vecMinsPreScaled && - pPlayer->CollisionProp()->OBBMaxsPreScaled() == change->m_vecMaxsPreScaled ) + if ( pPlayer->CollisionProp()->OBBMinsPreScaled() == change->m_vecMinsPreScaled + && pPlayer->CollisionProp()->OBBMaxsPreScaled() == change->m_vecMaxsPreScaled ) { // Restore it pPlayer->SetSize( restore->m_vecMinsPreScaled, restore->m_vecMaxsPreScaled ); @@ -729,66 +632,64 @@ void CLagCompensationManager::FinishLagCompensation( CBasePlayer *player ) if ( restore->m_fFlags & LC_ANGLES_CHANGED ) { - if ( pPlayer->GetLocalAngles() == change->m_vecAngles ) + if ( pPlayer->GetAbsAngles() == change->m_vecAngles ) { - pPlayer->SetLocalAngles( restore->m_vecAngles ); + pPlayer->SetAbsAngles( restore->m_vecAngles ); } } if ( restore->m_fFlags & LC_ORIGIN_CHANGED ) { // Okay, let's see if we can do something reasonable with the change - Vector delta = pPlayer->GetLocalOrigin() - change->m_vecOrigin; - + Vector delta = pPlayer->GetAbsOrigin() - change->m_vecOrigin; + RestorePlayerTo( pPlayer, restore->m_vecOrigin + delta ); } - if( restore->m_fFlags & LC_ANIMATION_CHANGED ) + if ( restore->m_fFlags & LC_ANIMATION_CHANGED ) { - pPlayer->SetSequence(restore->m_masterSequence); - pPlayer->SetCycle(restore->m_masterCycle); + pPlayer->SetSequence( restore->m_masterSequence ); + pPlayer->SetCycle( restore->m_masterCycle ); int layerCount = pPlayer->GetNumAnimOverlays(); - for( int layerIndex = 0; layerIndex < layerCount; ++layerIndex ) + for ( int layerIndex = 0; layerIndex < layerCount; ++layerIndex ) { - CAnimationLayer *currentLayer = pPlayer->GetAnimOverlay(layerIndex); - if( currentLayer ) + CAnimationLayer* currentLayer = pPlayer->GetAnimOverlay( layerIndex ); + if ( currentLayer ) { - currentLayer->m_flCycle = restore->m_layerRecords[layerIndex].m_cycle; - currentLayer->m_nOrder = restore->m_layerRecords[layerIndex].m_order; + 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; + currentLayer->m_flWeight = restore->m_layerRecords[layerIndex].m_weight; } } } - if( restore->m_fFlags & LC_POSE_PARAMS_CHANGED ) + CStudioHdr* hdr = pPlayer->GetModelPtr(); + + if ( restore->m_fFlags & LC_POSE_PARAMS_CHANGED ) { - CStudioHdr *hdr = pPlayer->GetModelPtr(); - if( hdr ) + if ( hdr ) { - for( int paramIndex = 0; paramIndex < hdr->GetNumPoseParameters(); paramIndex++ ) + for ( int paramIndex = 0; paramIndex < hdr->GetNumPoseParameters(); paramIndex++ ) { pPlayer->SetPoseParameterRaw( paramIndex, restore->m_poseParameters[paramIndex] ); } } } - if( restore->m_fFlags & LC_ENCD_CONS_CHANGED ) + if ( restore->m_fFlags & LC_ENCD_CONS_CHANGED ) { - CStudioHdr *hdr = pPlayer->GetModelPtr(); - if( hdr ) + if ( hdr ) { - for( int paramIndex = 0; paramIndex < hdr->GetNumBoneControllers(); paramIndex++ ) + for ( int encIndex = 0; encIndex < hdr->GetNumBoneControllers(); encIndex++ ) { - pPlayer->SetBoneControllerRaw( paramIndex, restore->m_encodedControllers[paramIndex] ); + pPlayer->SetBoneControllerRaw( encIndex, restore->m_encodedControllers[encIndex] ); } } } pPlayer->SetSimulationTime( restore->m_flSimulationTime ); - pPlayer->SetAnimTime(restore->m_flAnimTime); + pPlayer->SetAnimTime( restore->m_flAnimTime ); } } - - diff --git a/game/shared/cstrike/cs_player_shared.cpp b/game/shared/cstrike/cs_player_shared.cpp index 67ebeabadd..11b8c0558c 100644 --- a/game/shared/cstrike/cs_player_shared.cpp +++ b/game/shared/cstrike/cs_player_shared.cpp @@ -5,6 +5,7 @@ //=============================================================================// #include "cbase.h" +#include "const.h" #include "debugoverlay_shared.h" #include "strtools.h" #ifndef CLIENT_DLL @@ -38,7 +39,7 @@ #include "engine/ivdebugoverlay.h" #include "obstacle_pushaway.h" #include "props_shared.h" - +#include ConVar weapon_accuracy_nospread( "weapon_accuracy_nospread", "0", FCVAR_REPLICATED ); #define CS_MASK_SHOOT (MASK_SOLID|CONTENTS_DEBRIS) @@ -193,7 +194,7 @@ void UTIL_ClipTraceToPlayersHull(const Vector& vecAbsStart, const Vector& vecAbs float smallestFraction = tr->fraction; const float maxRange = 60.0f; - ray.Init( vecAbsStart, vecAbsEnd , mins, maxs ); + ray.Init( vecAbsStart, vecAbsEnd, mins, maxs ); for ( int k = 1; k <= gpGlobals->maxClients; ++k ) { @@ -508,7 +509,8 @@ void CCSPlayer::FireBullet( CBasePlayer *lastPlayerHit = NULL; MDLCACHE_CRITICAL_SECTION(); - bool shouldDebugHitboxesOnFire = m_pCurrentCommand->debug_hitboxes & CUserCmd::DEBUG_HITBOXES_ON_FIRE; + // Only show for one bullet. + bool shouldDebugHitboxesOnFire = m_pCurrentCommand->debug_hitboxes & CUserCmd::DEBUG_HITBOXES_ON_FIRE && iBullet == 0; bool shouldDebugHitboxesOnHit = m_pCurrentCommand->debug_hitboxes & CUserCmd::DEBUG_HITBOXES_ON_HIT; // TODO_ENHANCED: @@ -526,7 +528,29 @@ void CCSPlayer::FireBullet( { CBasePlayer* lagPlayer = UTIL_PlayerByIndex(i); if (lagPlayer && lagPlayer != this) - { + { +#ifdef CLIENT_DLL + if ( !m_pCurrentCommand->hasbeenpredicted ) + { + std::string text = "client:\n"; + + auto angles = lagPlayer->GetRenderAngles(); + + text += "x: " + std::to_string(angles.x) + ", y: " + std::to_string(angles.y) + ", z: " + std::to_string(angles.z) + '\n'; + + NDebugOverlay::EntityBounds( lagPlayer, 255, 0, 0, 32, 60 ); + std::cout << text; + } +#else + std::string text = "server:\n"; + + auto angles = lagPlayer->GetAbsAngles(); + + text += "x: " + std::to_string(angles.x) + ", y: " + std::to_string(angles.y) + ", z: " + std::to_string(angles.z) + '\n'; + + NDebugOverlay::EntityBounds( lagPlayer, 0, 255, 0, 32, 60 ); + std::cout << text; +#endif #ifdef CLIENT_DLL if (!m_pCurrentCommand->hasbeenpredicted) { diff --git a/game/shared/cstrike/weapon_basecsgrenade.cpp b/game/shared/cstrike/weapon_basecsgrenade.cpp index 618a96b087..8e93f6061c 100644 --- a/game/shared/cstrike/weapon_basecsgrenade.cpp +++ b/game/shared/cstrike/weapon_basecsgrenade.cpp @@ -108,7 +108,7 @@ bool CBaseCSGrenade::Deploy() bool CBaseCSGrenade::Holster( CBaseCombatWeapon *pSwitchingTo ) { m_bRedraw = false; - m_bPinPulled = false; // when this is holstered make sure the pin isn’t pulled. + m_bPinPulled = false; // when this is holstered make sure the pin isn�t pulled. m_fThrowTime = 0; #ifndef CLIENT_DLL @@ -222,10 +222,11 @@ void CBaseCSGrenade::ItemPostFrame() return; // If they let go of the fire button, they want to throw the grenade. - if ( m_bPinPulled && !(pPlayer->m_nButtons & IN_ATTACK) ) + if ( m_bPinPulled && !(pPlayer->m_nButtons & IN_ATTACK) ) { +#ifndef CLIENT_DLL pPlayer->DoAnimationEvent( PLAYERANIMEVENT_THROW_GRENADE ); - +#endif StartGrenadeThrow(); MDLCACHE_CRITICAL_SECTION(); diff --git a/game/shared/usercmd.cpp b/game/shared/usercmd.cpp index 30968d169b..e35d109b91 100644 --- a/game/shared/usercmd.cpp +++ b/game/shared/usercmd.cpp @@ -162,20 +162,20 @@ void WriteUsercmd( bf_write *buf, const CUserCmd *to, const CUserCmd *from ) // Write finally simulation data with entity index for ( unsigned int i = 0; i <= highestEntityIndex; i++ ) { - if ( from->simulationdata[i].m_flSimulationTime != to->simulationdata[i].m_flSimulationTime ) + if ( from->simulationdata[i].lerp_time != to->simulationdata[i].lerp_time ) { buf->WriteOneBit( 1 ); - buf->WriteBitFloat( to->simulationdata[i].m_flSimulationTime ); + buf->WriteBitFloat( to->simulationdata[i].lerp_time ); } else { buf->WriteOneBit( 0 ); } - if ( from->simulationdata[i].m_flAnimTime != to->simulationdata[i].m_flAnimTime ) + if ( from->simulationdata[i].animated_sim_time != to->simulationdata[i].animated_sim_time ) { buf->WriteOneBit( 1 ); - buf->WriteBitFloat( to->simulationdata[i].m_flAnimTime ); + buf->WriteBitFloat( to->simulationdata[i].animated_sim_time ); } else { @@ -309,12 +309,12 @@ void ReadUsercmd( bf_read *buf, CUserCmd *move, CUserCmd *from ) { if (buf->ReadOneBit()) { - move->simulationdata[i].m_flSimulationTime = buf->ReadBitFloat(); + move->simulationdata[i].lerp_time = buf->ReadBitFloat(); } if (buf->ReadOneBit()) { - move->simulationdata[i].m_flAnimTime = buf->ReadBitFloat(); + move->simulationdata[i].animated_sim_time = buf->ReadBitFloat(); } } diff --git a/game/shared/usercmd.h b/game/shared/usercmd.h index ed06c4e507..fb87799916 100644 --- a/game/shared/usercmd.h +++ b/game/shared/usercmd.h @@ -30,7 +30,7 @@ #endif #define MAX_LAYER_RECORDS (CBaseAnimatingOverlay::MAX_OVERLAYS) -#define MAX_POSE_PARAMETERS (CBaseAnimating::NUM_POSEPAREMETERS) +#define MAX_POSE_PARAMETERS (MAXSTUDIOPOSEPARAM) #define MAX_ENCODED_CONTROLLERS (MAXSTUDIOBONECTRLS) class bf_read; @@ -57,9 +57,9 @@ struct SimulationData // TODO_ENHANCED: // For now we send the last received update for animations. // anim time is unreliable on low fps. - float m_flSimulationTime; - float m_flAnimTime; - bool m_bEntityExists; + float lerp_time; + float animated_sim_time; + bool entityexists; }; class CEntityGroundContact diff --git a/public/tier1/utlvector.h b/public/tier1/utlvector.h index a52afb6dd9..b261fcec2e 100644 --- a/public/tier1/utlvector.h +++ b/public/tier1/utlvector.h @@ -1531,5 +1531,106 @@ private: char *m_szBuffer; // a copy of original string, with '\0' instead of separators }; +template < typename T, size_t N > +class CUtlCircularBuffer +{ + public: + using pT = T*; + + enum PushType + { + Filling, + Updating + }; + + public: + inline pT Get( const int wantedSlot = 0 ) + { + const auto real_slot = _Slot( wantedSlot ); + + return ( real_slot == -1 ) ? NULL : &_buffer[real_slot]; + } + + public: + inline PushType Push( T&& element ) + { + if ( _filled_history >= N ) + { + if ( _index >= ( N - 1 ) ) + { + _index = 0; + } + else + { + _index++; + } + + _buffer[_index] = element; + + return Updating; + } + + _buffer[_filled_history] = element; + _index = _filled_history; + _filled_history++; + + return Filling; + } + + inline PushType Push( const T& element ) + { + if ( _filled_history >= N ) + { + if ( _index >= ( N - 1 ) ) + { + _index = 0; + } + else + { + _index++; + } + + _buffer[_index] = element; + + return Updating; + } + + _buffer[_filled_history] = element; + _index = _filled_history; + _filled_history++; + + return Filling; + } + + inline void Clear() + { + _index = 0; + _filled_history = 0; + } + + private: + inline int _Slot( const int wantedSlot = 0 ) const + { + if ( wantedSlot >= _filled_history || wantedSlot < 0 || _filled_history <= 0 ) + { + return -1; + } + + if ( _filled_history >= N ) + { + const int calc_slot = ( _index - wantedSlot ); + return ( calc_slot < 0 ) ? calc_slot + N : calc_slot; + } + else + { + return ( _filled_history - 1 ) - wantedSlot; + } + } + + private: + T _buffer[N]; + int _filled_history {}; + int _index {}; +}; #endif // CCVECTOR_H