You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
280 lines
9.0 KiB
280 lines
9.0 KiB
#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<CASW_Lag_Compensation*> 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;i<ASW_LAG_NUM_POSITION_HISTORY_SAMPLES;i++) |
|
{ |
|
m_vecPositionHistory[i] = Vector(0,0,0); |
|
m_fPositionHistoryTime[i] = 0; |
|
} |
|
m_vecRealPosition = Vector(0,0,0); |
|
m_bSetRealPosition = false; |
|
m_fRealSimulationTime = 0; |
|
m_hOwnerEntity = NULL; |
|
} |
|
|
|
CASW_Lag_Compensation::~CASW_Lag_Compensation() |
|
{ |
|
g_LagCompensatingEntities.FindAndRemove(this); |
|
} |
|
|
|
void CASW_Lag_Compensation::Init(CBaseAnimating *pOwner) |
|
{ |
|
m_hOwnerEntity = pOwner; |
|
} |
|
|
|
CAI_BaseNPC *CASW_Lag_Compensation::GetOwnerNPC() |
|
{ |
|
if ( !m_hOwnerEntity.Get() || !m_hOwnerEntity->IsNPC() ) |
|
return NULL; |
|
|
|
return static_cast<CAI_BaseNPC*>( 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;i<ASW_LAG_NUM_POSITION_HISTORY_SAMPLES;i++) // go through all our history samples and find the oldest one that's just past the lagged time requested |
|
{ |
|
if (m_fPositionHistoryTime[iCurrentIndex] == 0) // this isn't a real index |
|
{ |
|
break; |
|
} |
|
iChosenIndex = iCurrentIndex; // it's a real sample, so we might use this one |
|
if (m_fPositionHistoryTime[iCurrentIndex] <= fLaggedTime) // if the sample is behind our lagged time, then stop, we'll use this one |
|
{ |
|
break; |
|
} |
|
iCurrentIndex--; // count the index back |
|
if (iCurrentIndex == -1) // loop around if we need to |
|
iCurrentIndex = ASW_LAG_NUM_POSITION_HISTORY_SAMPLES-1; |
|
} |
|
|
|
static Vector vecResult; |
|
vecResult = m_hOwnerEntity->GetAbsOrigin(); |
|
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;i<ASW_LAG_NUM_POSITION_HISTORY_SAMPLES;i++) // go through all our history samples and find the oldest one that's just past the lagged time requested |
|
{ |
|
if (m_fPositionHistoryTime[iCurrentIndex] == 0) // this isn't a real index |
|
{ |
|
break; |
|
} |
|
iChosenIndex = iCurrentIndex; // it's a real sample, so we might use this one |
|
if (m_fPositionHistoryTime[iCurrentIndex] <= fLaggedTime) // if the sample is behind our lagged time, then stop, we'll use this one |
|
{ |
|
break; |
|
} |
|
iCurrentIndex--; // count the index back |
|
if (iCurrentIndex == -1) // loop around if we need to |
|
iCurrentIndex = ASW_LAG_NUM_POSITION_HISTORY_SAMPLES-1; |
|
} |
|
|
|
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; |
|
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;i<g_LagCompensatingEntities.Count();i++) |
|
{ |
|
if ( g_LagCompensatingEntities[i]->m_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;i<g_LagCompensatingEntities.Count();i++) |
|
{ |
|
g_LagCompensatingEntities[i]->UndoLaggedPosition(); |
|
} |
|
} |