#include "cbase.h" #include "asw_lag_compensation.h" #include "asw_player.h" #include "asw_marine.h" #include "inetchannelinfo.h" #include "ai_basenpc.h" #include "asw_game_resource.h" #include "asw_marine_resource.h" #include "asw_lag_compensation.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" CUtlVector g_LagCompensatingEntities; bool CASW_Lag_Compensation::s_bInLagCompensation = false; CBasePlayer* CASW_Lag_Compensation::s_pLagCompensatingPlayer = NULL; ConVar asw_alien_unlag("asw_alien_unlag", "1", 0, "Unlag alien positions by player's ping"); extern ConVar sv_maxunlag; extern ConVar sv_showlagcompensation; CASW_Lag_Compensation::CASW_Lag_Compensation() { g_LagCompensatingEntities.AddToTail(this); m_iPositionHistoryTail = 0; for (int i=0;iIsNPC() ) return NULL; return static_cast( m_hOwnerEntity.Get() ); } void CASW_Lag_Compensation::StorePositionHistory() { if ( !m_hOwnerEntity.Get() || !asw_alien_unlag.GetBool() ) return; if ( GetOwnerNPC() && GetOwnerNPC()->GetSleepState() != AISS_AWAKE ) return; if (gpGlobals->curtime - m_fPositionHistoryTime[m_iPositionHistoryTail] < ASW_LAG_MIN_SAMPLE_TIME_DIFFERENCE) return; m_iPositionHistoryTail++; if ( m_iPositionHistoryTail >= ASW_LAG_NUM_POSITION_HISTORY_SAMPLES) m_iPositionHistoryTail = 0; m_vecPositionHistory[m_iPositionHistoryTail] = m_hOwnerEntity->GetAbsOrigin(); m_fPositionHistoryTime[m_iPositionHistoryTail] = gpGlobals->curtime; } const Vector& CASW_Lag_Compensation::GetLaggedPosition( const float fLaggedTime ) { if ( !m_hOwnerEntity.Get() ) return vec3_origin; if ( GetOwnerNPC() && GetOwnerNPC()->GetSleepState() != AISS_AWAKE ) return m_hOwnerEntity->GetAbsOrigin(); int iCurrentIndex = m_iPositionHistoryTail; int iChosenIndex = -1; for (int i=0;iGetAbsOrigin(); if (iChosenIndex != -1) // if we found an index, then work out what % of the movement we need to do based on the difference between now and the lagged time { const float base = (gpGlobals->curtime - m_fPositionHistoryTime[iChosenIndex]); if (base <= 0) // if our only sample is 'now' then we can't do any lag compensation return vecResult; const float fFraction = (gpGlobals->curtime - fLaggedTime) / base; vecResult = m_hOwnerEntity->GetAbsOrigin() + (m_vecPositionHistory[iChosenIndex] - m_hOwnerEntity->GetAbsOrigin()) * fFraction; } return vecResult; } void CASW_Lag_Compensation::MoveToLaggedPosition(const float fLaggedTime) { if ( !m_hOwnerEntity.Get() ) return; // if entity is attached to a marine, don't do lag compensation (fixes parasites detaching when the marine is shot) if ( m_hOwnerEntity->GetMoveParent() && m_hOwnerEntity->GetMoveParent()->Classify() == CLASS_ASW_MARINE ) return; if ( GetOwnerNPC() && GetOwnerNPC()->GetSleepState() != AISS_AWAKE ) return; int iCurrentIndex = m_iPositionHistoryTail; int iChosenIndex = -1; for (int i=0;icurtime - m_fPositionHistoryTime[iChosenIndex]); if (base <= 0) // if our only sample is 'now' then we can't do any lag compensation return; const float fFraction = (gpGlobals->curtime - fLaggedTime) / base; m_vecRealPosition = m_hOwnerEntity->GetAbsOrigin(); // store our real position, so we can restore it once lag compensation is done m_fRealSimulationTime = m_hOwnerEntity->GetSimulationTime(); const Vector vecNewPos = m_vecRealPosition + (m_vecPositionHistory[iChosenIndex] - m_vecRealPosition) * fFraction; m_bSetRealPosition = true; if (asw_alien_unlag.GetInt() < 2) { m_hOwnerEntity->SetAbsOrigin(vecNewPos); // move us to the lagged position } } } void CASW_Lag_Compensation::UndoLaggedPosition() { if ( !m_hOwnerEntity.Get() ) return; // if entity is attached to a marine, don't do lag compensation (fixes parasites detaching when the marine is shot) if ( m_hOwnerEntity->GetMoveParent() && m_hOwnerEntity->GetMoveParent()->Classify() == CLASS_ASW_MARINE ) return; if ( m_bSetRealPosition ) { m_hOwnerEntity->SetAbsOrigin(m_vecRealPosition); m_hOwnerEntity->SetSimulationTime(m_fRealSimulationTime); m_bSetRealPosition = false; } } Vector CASW_Lag_Compensation::GetLagCompensationOffset() { if (!m_bSetRealPosition) return vec3_origin; return m_vecRealPosition - m_hOwnerEntity->GetAbsOrigin(); } void CASW_Lag_Compensation::AllowLagCompensation(CBasePlayer *player) { s_pLagCompensatingPlayer = player; } void CASW_Lag_Compensation::RequestLagCompensation(CASW_Player *player, const CUserCmd *cmd ) { if (player != s_pLagCompensatingPlayer) return; if ( !player || !player->m_bLagCompensation // Player not wanting lag compensation || (gpGlobals->maxClients <= 1) // no lag compensation in single player || !asw_alien_unlag.GetBool() // disabled by server admin || player->IsBot() // not for bots //|| player->IsObserver() // not for spectators ) return; if (s_bInLagCompensation) { return; } s_bInLagCompensation = true; // Get true latency // correct is the amout of time we have to correct game time float correct = 0.0f; INetChannelInfo *nci = engine->GetPlayerNetInfo( player->entindex() ); if ( nci ) { // add network latency correct+= nci->GetLatency( FLOW_OUTGOING ); } // calc number of view interpolation ticks - 1 int lerpTicks = TIME_TO_TICKS( player->m_fLerpTime ); // add view interpolation latency see C_BaseEntity::GetInterpolationAmount() correct += TICKS_TO_TIME( lerpTicks ); // check bouns [0,sv_maxunlag] correct = clamp( correct, 0.0f, sv_maxunlag.GetFloat() ); // correct tick send by player int targettick = cmd->tick_count - lerpTicks; // calc difference between tick send by player and our latency based tick float deltaTime = correct - TICKS_TO_TIME(gpGlobals->tickcount - targettick); 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 ); targettick = gpGlobals->tickcount - TIME_TO_TICKS( correct ); } // check the player has enough lag to warrant doing the work of lag compensation const float fLaggedTime = TICKS_TO_TIME( targettick ); if (gpGlobals->curtime - fLaggedTime < ASW_MIN_LAG_TIME) return; CASW_Marine *pMarine = player->GetMarine(); for (int i=0;im_hOwnerEntity.Get() == pMarine ) // don't lag compensate my own marine continue; g_LagCompensatingEntities[i]->MoveToLaggedPosition( fLaggedTime ); if( sv_showlagcompensation.GetInt() == 1) { CBaseAnimating *pAnim = g_LagCompensatingEntities[i]->m_hOwnerEntity.Get(); if (pAnim) pAnim->DrawServerHitboxes(4, true); } } } void CASW_Lag_Compensation::FinishLagCompensation() { if (!s_bInLagCompensation) { return; } s_bInLagCompensation = false; s_pLagCompensatingPlayer = NULL; for (int i=0;iUndoLaggedPosition(); } }