-
Автор темы
- #1
C++:
/ cpp
#include "../includes.h"
#include "lagcompensation.hpp"
#include "bone_setup.hpp"
lag_record_t::lag_record_t( Player* player ) {
m_recv_time = g_csgo.m_globals->m_curtime;
m_index = player->index( );
m_valid = true;
m_origin = player->m_vecOrigin( );
m_abs_origin = player->GetAbsOrigin( );
m_layers = player->get_anim_layers( ); // check when come back
m_poses = player->get_pose_params( ); // check when come back
m_old_simtime = player->m_flOldSimulationTime( );
m_simtime = player->m_flSimulationTime( );
m_duckamt = player->m_flDuckAmount( );
m_lby = player->m_flLowerBodyYawTarget( );
m_eye_angles = player->m_angEyeAngles( );
m_eye_angles.NormalizeNoClamp( );// check when come back
m_abs_ang = player->GetAbsAngles( );
do_not_set = false;
m_strafing = player->m_strafing( );// check when come back
m_dormant = player->dormant( );
m_velocity_modifier = player->velocity_modifier( );// check when come back
for ( auto& state : m_state ) {
state.m_abs_ang = player->GetAbsAngles( );
state.m_poses = m_poses;
}
m_flags = player->m_fFlags( );
m_ground_entity = player->GetGroundEntity( );
m_lagamt = game::TIME_TO_TICKS( m_simtime - m_old_simtime );
m_velocity = ( player->m_vecOrigin( ) - player->m_vecOldOrigin( ) ) * ( 1.f / game::TIME_TO_TICKS( m_lagamt ) );
if ( m_flags & FL_ONGROUND )
m_velocity.z = 0.f;
if ( !m_velocity.IsValid( ) ) // check
m_velocity = vec3_t( 0.f, 0.f, 0.f );
m_shot = false;
//const auto collideable = player->GetCollideable( );
m_obb_maxs = player->m_vecMaxs( );
m_obb_mins = player->m_vecMins( );
m_net_time_deviation = game::TICKS_TO_TIME( g_csgo.m_cl->m_server_tick ) - m_simtime;
m_server_tick = g_csgo.m_cl->m_server_tick;
if ( player->m_PlayerAnimState( ) ) {
for ( auto& state : m_state ) {
state.m_animstate = *player->m_PlayerAnimState( );
state.m_abs_ang = { 0.f, state.m_animstate.m_flFootYaw, 0.f };
}
has_animstate = true;
}
m_tickbase_shift = false;
// get parser
// m_collision_viewheight = player->get_collision_viewheight( );
// m_collision_change_time = player->get_collision_bounds_change_time( );
}
bool setup_bones( lag_record_t& record, resolver_direction dir, bool extrapolated ) {
const auto ret = bone_setup::handle_bone_setup( record, dir, false, extrapolated );
if ( !ret ) {
memset( record.m_state[ dir ].m_matrix, 0, sizeof( record.m_state[ dir ].m_matrix ) );
record.m_valid = false;
}
return ret;
}
void lag_record_t::setup_matrices( resolver_direction dir, bool extrapolated ) {
if ( dir != resolver_direction::resolver_invalid ) {
if ( extrapolated || this->m_state[ dir ].m_setup_tick != g_csgo.m_globals->m_tick_count )
setup_bones( *this, dir, extrapolated );
return;
}
const auto player = g_csgo.m_entlist->GetClientEntity( this->m_index );
player_info_t info;
if ( !g_csgo.m_engine->GetPlayerInfo( this->m_index, &info ) )
return;
for ( auto j = resolver_direction::resolver_networked; j < resolver_direction::resolver_max_extra; j++ ) {
if ( info.m_fake_player && j != resolver_direction::resolver_networked )
continue;
if ( extrapolated || this->m_state[ j ].m_setup_tick != g_csgo.m_globals->m_tick_count )
setup_bones( *this, j, extrapolated );
}
}
bool lag_record_t::can_delay_shot( ) const {
return false;
}
int lag_record_t::ticks_behind( int lag ) const {
return std::clamp( g_csgo.m_cl->m_server_tick - this->m_server_tick, 0, 16 );
}
bool lag_record_t::delay_shot( ) const {
return false;
}
bool lag_record_t::breaking_lc( ) const {
return this->m_breaking_lc && !this->m_dormant || !g_csgo.cl_lagcompensation;
}
#include "player_log.hpp"
bool lagcomp::run_extrapolation( Player* player, const bool simple ) {
auto& log = player_log::get_log( player->index( ) );
const auto& first = log.record.back( );
if ( g_csgo.cl_lagcompensation && ( first.m_simtime < log.m_highest_simtime || first.m_simtime == log.m_highest_simtime && first.m_ignore ) ) {
for ( auto i = log.record.size( ) - 1; i >= 0; i-- ) {
const auto record = &log.record[ i ];
if ( record->m_simtime == log.m_highest_simtime ) {
// record->setup_matrices();
log.extrapolated_record = *record;
log.extrapolated_record.m_breaking_lc = record->breaking_lc( );
log.extrapolated_record.m_shot_info.breaking_lc = log.extrapolated_record.m_breaking_lc;
log.extrapolated_record.m_extrapolated = false;
log.extrapolated_record.m_to_server_position = true;
log.extrapolated_record.m_valid = true;
log.extrapolated_record.m_shot_info.has_info = true;
log.extrapolated_record.m_shot_info.delay_shot = false;
log.extrapolated_record.m_shot_info.extrapolated = false;
log.extrapolated_record.m_shot_info.backtrack_ticks = 0;
return true;
}
}
return false;
}
const auto record = &log.record.back( );
//const auto rtt = misc::get_latency();
const auto rtt = g_cl.m_latency;
const auto lag = std::max( 1, record->m_lagamt );
log.extrapolated_record = *record;
log.extrapolated_record.m_breaking_lc = record->breaking_lc( );
log.extrapolated_record.m_shot_info.breaking_lc = log.extrapolated_record.m_breaking_lc;
const auto possible_future_tick = g_csgo.m_cl->m_server_tick + 1 + game::TIME_TO_TICKS( rtt ) + 8;
const auto max_future_ticks = possible_future_tick - game::TIME_TO_TICKS( log.extrapolated_record.m_simtime + get_lerp_time( ) );
const auto ticks_behind = record->ticks_behind( lag );
auto to_server_position = log.extrapolated_record.m_breaking_lc;
if ( !to_server_position ) {
for ( auto i = 1; i <= max_future_ticks; i++ ) {
log.extrapolated_record.m_lagamt = std::clamp( ( i + ticks_behind ) / lag * lag, 0, 64 );
if ( valid_simtime( record->m_simtime + game::TICKS_TO_TIME( log.extrapolated_record.m_lagamt ) ) )
break;
}
if ( !valid_simtime( record->m_simtime + game::TICKS_TO_TIME( log.extrapolated_record.m_lagamt ) ) )
to_server_position = true;
}
const auto server_position = std::clamp( ( game::TIME_TO_TICKS( rtt ) + ticks_behind ) / lag * lag, 0, 64 );
if ( to_server_position || server_position < log.extrapolated_record.m_lagamt )
log.extrapolated_record.m_lagamt = server_position;
log.extrapolated_record.m_tickbase_shift = false;
// resolver::extrapolate_record(log.extrapolated_record.m_lagamt, log.extrapolated_record, simple);
log.extrapolated_record.m_extrapolate_amt = log.extrapolated_record.m_lagamt;
if ( log.extrapolated_record.m_lagamt > max_future_ticks )
log.extrapolated_record.m_simtime = game::TICKS_TO_TIME( possible_future_tick - 2 ) - get_lerp_time( );
log.extrapolated_record.m_extrapolated = true;
log.extrapolated_record.m_to_server_position = to_server_position;
log.extrapolated_record.m_valid = true;
log.extrapolated_record.m_lagamt = lag;
log.extrapolated_record.m_shot_info.has_info = true;
log.extrapolated_record.m_shot_info.delay_shot = false;
log.extrapolated_record.m_shot_info.extrapolated = true;
log.extrapolated_record.m_shot_info.backtrack_ticks = game::TIME_TO_TICKS( log.m_highest_simtime - log.extrapolated_record.m_simtime );
return true;
}
int lagcomp::fix_tickcount( const float& simtime ) {
return game::TIME_TO_TICKS( simtime + get_lerp_time( ) );
}
float lagcomp::get_lerp_time( ) {
static auto ret = 0.f;
static auto last_tick = 0;
if ( g_csgo.m_globals->m_tick_count == last_tick )
return ret;
//var(cl_updaterate);
//var(cl_interp);
//var(cl_interp_ratio);
const auto update_rate = g_csgo.cl_updaterate->GetInt( );
const auto interp_ratio = g_csgo.cl_interp_ratio->GetFloat( );
auto lerp = interp_ratio / update_rate;
if ( lerp <= g_csgo.cl_interp->GetFloat( ) )
lerp = g_csgo.cl_interp->GetFloat( );
ret = roundf( lerp * 1000.0f ) / 1000.0f;
last_tick = g_csgo.m_globals->m_tick_count;
return ret;
}
void lagcomp::extrapolate( Player* player, vec3_t& origin, vec3_t& velocity, vec3_t base_velocity, int& flags, bool wasonground ) {
//var(sv_gravity);
// DOUBLECHECK
//var(sv_jump_impulse);
auto sv_gravity = g_csgo.sv_gravity;
auto sv_jump_impulse = g_csgo.sv_jump_impulse;
if ( !( flags & FL_ONGROUND ) ) {
velocity.z -= g_csgo.m_globals->m_interval * sv_gravity->GetFloat( );
velocity.z += g_csgo.m_globals->m_interval * base_velocity.z;
} else if ( !wasonground )
velocity.z = sv_jump_impulse->GetFloat( ) - g_csgo.m_globals->m_interval * sv_gravity->GetFloat( );
const vec3_t mins = player->m_vecMins( );
const vec3_t max = player->m_vecMaxs( );
const vec3_t src = origin;
vec3_t end = src + ( velocity * g_csgo.m_globals->m_interval );
Ray ray( src, end, mins, max );
//ray.(src, end, mins, max);
CGameTrace trace;
CTraceFilterSimple filter; // ig
g_csgo.m_engine_trace->TraceRay( ray, MASK_PLAYERSOLID, &filter, &trace );
if ( trace.m_fraction != 1.f ) {
for ( int i = 0; i < 2; i++ ) {
velocity -= trace.m_plane.m_normal * velocity.Dot( trace.m_plane.m_normal );
const float dot = velocity.Dot( trace.m_plane.m_normal );
if ( dot < 0.f ) {
velocity.x -= dot * trace.m_plane.m_normal.x;
velocity.y -= dot * trace.m_plane.m_normal.y;
velocity.z -= dot * trace.m_plane.m_normal.z;
}
end = trace.m_endpos + ( velocity * ( g_csgo.m_globals->m_interval * ( 1.f - trace.m_fraction ) ) );
Ray second_ray( trace.m_endpos, end, mins, max );
g_csgo.m_engine_trace->TraceRay( second_ray, MASK_PLAYERSOLID, &filter, &trace );
if ( trace.m_fraction == 1.f )
break;
}
}
origin = trace.m_endpos;
end = trace.m_endpos;
end.z -= 2.f;
Ray final_r( origin, end, mins, max );
g_csgo.m_engine_trace->TraceRay( final_r, MASK_PLAYERSOLID, &filter, &trace );
flags &= ~FL_ONGROUND;
if ( trace.hit( ) && trace.m_plane.m_normal.z > 0.7f )
flags |= FL_ONGROUND;
}
#include "../menu/menu_include.h"
bool lagcomp::valid_simtime( const float& simtime, const float time ) {
const auto nci = g_csgo.m_engine->GetNetChannelInfo( );
if ( !nci )
return false;
if ( !g_csgo.cl_lagcompensation )
return false;
const auto last_server_tick = g_csgo.m_cl->m_server_tick;
const auto rtt = g_cl.m_latency;
const auto possible_future_tick = last_server_tick + game::TIME_TO_TICKS( rtt ) + 8;
float correct = 0;
correct += rtt;
correct += get_lerp_time( );
// var(sv_maxunlag);
auto sv_maxunlag = g_csgo.sv_maxunlag;
const auto deadtime = static_cast< int >( game::TICKS_TO_TIME( last_server_tick ) + rtt - sv_maxunlag->GetFloat( ) );
if ( simtime <= static_cast< float >( deadtime ) || game::TIME_TO_TICKS( simtime + get_lerp_time( ) ) > possible_future_tick )
return false;
correct = std::clamp( correct, 0.f, sv_maxunlag->GetFloat( ) );
const auto delta_time = correct - ( time - simtime );
const auto delta_time1 = correct - ( time - g_csgo.m_globals->m_interval - simtime );
const auto delta_time2 = correct - ( time + g_csgo.m_globals->m_interval - simtime );
if ( vars::legit.enabled->get<bool>( ) && vars::legit.backtrack->get<float>( ) != 0.f ) {
if ( g_csgo.m_globals->m_curtime - simtime > vars::legit.backtrack->get<float>( ) / 1000.f )
return false;
}
//if ((tickbase::fast_fire || tickbase::hide_shot) && tickbase::compute_current_limit() > 2)
// return fabsf(delta_time1) < 0.2f && fabsf(delta_time2) < 0.2f;
return fabsf( delta_time ) < 0.2f;
}
int lagcomp::get_real_lag( Player* player, const lag_record_t* current ) {
auto& log = player_log::get_log( player->index( ) );
if ( log.record.size( ) < 2 )
return false;
const auto& server_tick = current->m_server_tick;
for ( auto i = log.record.size( ) - 2; i >= 0; i-- ) {
const auto record = &log.record[ i ];
if ( record->m_ignore )
continue;
return server_tick - record->m_server_tick;
}
return current->m_lagamt;
}
bool lagcomp::is_breaking_lagcomp( Player* player, const lag_record_t* current ) {
static constexpr auto teleport_distance_sqr = 64 * 64;
auto& log = player_log::get_log( player->index( ) );
if ( log.record.size( ) < 2 )
return false;
const auto& previous = log.record[ log.record.size( ) - 2 ];
const auto force_breaking_lc = previous.m_ignore && current->m_ignore && ( !( previous.m_flags & FL_ONGROUND ) || !( current->m_flags & FL_ONGROUND ) );
const auto& origin = current->m_origin;
for ( auto i = log.record.size( ) - 2; i >= 0; i-- ) {
const auto record = &log.record[ i ];
if ( record->m_ignore )
continue;
const auto delta = record->m_origin - origin;
if ( delta.length_2d_sqr( ) > teleport_distance_sqr || force_breaking_lc && ( delta.length_2d_sqr( ) > 3000 || record->m_velocity.length_2d_sqr( ) > 250 * 250 || previous.m_velocity.length_2d_sqr( ) > 250 * 250 ) )
return true;
break;
}
return false;
}
bool lagcomp::is_breaking_lagcomp( Player* player ) {
static constexpr auto teleport_distance_sqr = 64 * 64;
auto& log = player_log::get_log( player->index( ) );
if ( log.record.size( ) < 2 )
return false;
const auto& first = log.record.back( );
const auto force_breaking_lc = first.m_simtime < log.m_highest_simtime || first.m_ignore && !( first.m_flags & FL_ONGROUND );
vec3_t origin{};
auto first_lagcomp = true;
for ( auto i = log.record.size( ) - 1; i >= 0; i-- ) {
const auto record = &log.record[ i ];
if ( record->m_ignore )
continue;
if ( first_lagcomp ) {
origin = record->m_origin;
first_lagcomp = false;
continue;
}
const auto delta = record->m_origin - origin;
if ( delta.length_2d_sqr( ) > teleport_distance_sqr || force_breaking_lc && ( delta.length_2d_sqr( ) > 3000 || record->m_velocity.length_2d_sqr( ) > 250 * 250 || first.m_velocity.length_2d_sqr( ) > 250 * 250 ) )
return true;
break;
}
return false;
}
// hpp
#pragma once
template <class T, size_t N = 0>
struct circular_buffer {
circular_buffer( ) {
if ( ( max_size_ = N ) > 0 )
elem_.resize( max_size_ );
}
circular_buffer( const size_t sz ) : max_size_( sz ) {
elem_.resize( max_size_ );
}
circular_buffer( const circular_buffer& other ) {
max_size_ = other.max_size_;
current_ = other.current_;
size_ = other.size_;
elem_ = other.elem_;
}
circular_buffer& operator=( const circular_buffer& other ) {
max_size_ = other.max_size_;
current_ = other.current_;
size_ = other.size_;
elem_ = other.elem_;
return *this;
}
circular_buffer( circular_buffer&& other ) noexcept {
max_size_ = other.max_size_;
current_ = other.current_;
size_ = other.size_;
elem_ = other.elem_;
other.elem_.clear( );
other.size_ = other.max_size_ = 0u;
}
~circular_buffer( ) = default;
[[nodiscard]] inline T& operator[] ( size_t idx ) { return elem_[ ( current_ + idx ) % max_size_ ]; }
[[nodiscard]] inline const T& operator[] ( size_t idx ) const { return elem_[ ( current_ + idx ) % max_size_ ]; }
[[nodiscard]] inline int size( ) const { return size_; }
[[nodiscard]] inline bool empty( ) const { return size_ == 0; }
inline void clear( ) { size_ = 0; current_ = 0; }
inline void clear_all_but_last( ) { current_ += size_ - 1; size_ = 1; }
inline void pop_back( ) { --size_; }
inline void pop_front( ) { ++current_; --size_; }
inline void reserve( size_t newsize ) {
if ( newsize == max_size_ )
return;
elem_.resize( max_size_ = newsize );
size_ = 0;
current_ = 0;
}
inline void resize( size_t newsize ) {
elem_.resize( max_size_ = newsize );
size_ = max_size_;
}
template<class... Args>
inline T* emplace_back( Args&&... args ) { assert( size_ < max_size_ ); elem_[ ( current_ + size_++ ) % max_size_ ] = T( std::forward<Args>( args )... ); return &elem_[ ( current_ + size_ - 1 ) % max_size_ ]; }
inline T* push_back( T&& item ) { return emplace_back( std::move( item ) ); }
[[nodiscard]] inline T* push_back( ) { return size_ >= max_size_ ? nullptr : &elem_[ ( current_ + size_++ ) % max_size_ ]; }
template<class... Args>
inline void emplace_front( Args&&... args ) { assert( size_ < max_size_ ); elem_[ --current_ % max_size_ ] = T( std::forward<Args>( args )... ); ++size_; }
inline void push_front( T&& item ) { emplace_front( std::move( item ) ); }
[[nodiscard]] inline T* push_front( ) { if ( size_ >= max_size_ ) return nullptr; ++size_; return &elem_[ --current_ % max_size_ ]; }
[[nodiscard]] inline T& back( ) { return elem_[ ( current_ + size_ - 1 ) % max_size_ ]; }
[[nodiscard]] inline T& front( ) { return elem_[ current_ % max_size_ ]; }
inline void sort( std::function<bool( const T&, const T& )> pred ) {
current_ = current_ % max_size_;
assert( current_ % max_size_ + size_ <= max_size_ );
std::sort( elem_.begin( ) + current_ % max_size_, elem_.begin( ) + current_ % max_size_ + size_, pred );
}
std::vector<T> elem_{};
size_t current_ = 0;
size_t size_ = 0;
size_t max_size_ = 0;
};
struct addons_t {
bool in_jump = false;
bool in_idle = false;
bool swing_left = false;
std::vector<uint16_t> activity_modifiers{};
float next_lby_update = 0.f;
bool in_deploy_rate_limit = false;
float previous_action_weight = 0.f;
float previous_action_delta = 0.f;
float adjust_weight = 0.f;
int vm = 0;
};
struct record_shot_info_t {
bool has_info = false;
bool delay_shot = false;
bool breaking_lc = false;
bool extrapolated = false;
int backtrack_ticks = 0;
float hitchance = 0.f;
float target_dmg = 0.f;
int safety = 0;
std::string extra_info{};
};
enum class resolver_side {
resolver_invalid = -1,
resolver_left = 0,
resolver_right,
resolver_side_max
};
enum class resolver_mode {
resolver_invalid = -1,
resolver_default = 0,
resolver_flip,
resolver_shot,
resolver_mode_max
};
enum class resolver_direction {
resolver_invalid = -1,
resolver_networked = 0,
resolver_max,
resolver_zero,
resolver_min,
resolver_max_extra,
resolver_max_max,
resolver_min_min,
resolver_min_extra,
resolver_direction_max,
};
inline resolver_direction& operator++( resolver_direction& s, int ) {
#ifndef RELEASE
if ( s == resolver_direction::resolver_direction_max )
__debugbreak( );
#endif
s = static_cast< resolver_direction >( static_cast< int >( s ) + 1 );
return s;
}
inline resolver_mode& operator++( resolver_mode& s, int ) {
#ifndef RELEASE
if ( s == resolver_mode::resolver_mode_max )
__debugbreak( );
#endif
s = static_cast< resolver_mode >( static_cast< int >( s ) + 1 );
return s;
}
#define DECL_ALIGN(x) __declspec( align( x ) )
#define ALIGN16 DECL_ALIGN(16)
struct state_info {
CCSGOPlayerAnimState m_animstate = {};
ALIGN16 matrix3x4_t m_pristine_matrix[ 128 ] = {};
ALIGN16 matrix3x4_t m_matrix[ 128 ] = {};
ALIGN16 matrix3x4_t m_extra_matrix[ 128 ] = {};
ang_t m_abs_ang = {};
std::array<float, 24> m_poses = {};
float m_simulated_yaw = {};
std::array<C_AnimationLayer, 13> m_own_layers = {};
int m_setup_tick = -1;
addons_t addon = {};
};
template<typename E, class T, E N>
class enum_array : public std::array<T, static_cast< std::size_t >( N )> {
public:
T& operator[] ( E e ) {
return std::array<T, static_cast< std::size_t >( N )>::operator[]( static_cast< std::size_t >( e ) );
}
const T& operator[] ( E e ) const {
return std::array<T, static_cast< std::size_t >( N )>::operator[]( static_cast< std::size_t >( e ) );
}
E distance( T e ) const {
return static_cast< E >( std::distance( std::array< T, static_cast< std::size_t >( N ) >::begin( ), e ) );
}
};
struct lag_record_t {
lag_record_t( ) { }
lag_record_t( Player* player );
void setup_matrices( resolver_direction direction = resolver_direction::resolver_invalid, bool extrapolated = false );
matrix3x4_t* matrix( resolver_direction direction ) {
return m_state[ direction ].m_matrix;
}
matrix3x4_t* extra_matrix( resolver_direction direction ) {
if ( !m_last_ang_differs )
return nullptr;
return m_state[ direction ].m_extra_matrix;
}
static float get_resolver_roll( const resolver_direction state ) {
switch ( state ) {
case resolver_direction::resolver_min_min:
case resolver_direction::resolver_max_extra:
return 50.f;
case resolver_direction::resolver_max_max:
case resolver_direction::resolver_min_extra:
return -50.f;
default:
return 0.f;
}
}
bool can_delay_shot( ) const;
int ticks_behind( int lag = -1 ) const;
bool delay_shot( ) const;
bool breaking_lc( ) const;
matrix3x4_t m_visual_matrix[ 128 ] = {};
int m_index = {};
enum_array<resolver_direction, state_info, resolver_direction::resolver_direction_max> m_state = {};
int m_mode = {};
record_shot_info_t m_shot_info = {};
bool m_last_ang_differs = false;
bool m_pitch_jitter = false;
bool has_animstate = false;
bool do_not_set = false;
bool m_valid = false;
bool m_dormant = {};
bool m_did_wall_detect = {};
vec3_t m_velocity = {};
vec3_t m_calculated_velocity = {};
vec3_t m_origin = {};
vec3_t m_abs_origin = {};
vec3_t m_obb_mins = {};
vec3_t m_obb_maxs = {};
CBaseHandle m_ground_entity = {};
float m_net_time_deviation = {};
float m_old_simtime = {};
float m_simtime = {};
float m_duckamt = {};
float m_lby = {};
bool m_strafing = {};
int m_server_tick = {};
ang_t m_eye_angles = {};
float m_yaw_modifier = {};
int m_flags = {};
float m_velocity_modifier = {};
int m_lagamt = {};
int m_real_lag = {};
float m_recv_time = {};
int m_extrapolate_amt = {};
bool m_extrapolated = {};
bool m_to_server_position = {};
bool m_ignore = false;
bool m_unknown = false;
int m_pitch_cycle = 0;
bool m_tickbase_shift = false;
bool m_breaking_lc = false;
// float m_collision_viewheight = {};
// float m_collision_change_time = {};
addons_t addon{};
std::array<C_AnimationLayer, 13> m_layers = {};
std::array<float, 24> m_poses = {};
ang_t m_abs_ang = {};
bool m_has_visual_matrix = false;
bool m_shot = {};
int m_cmdnum = {}; //lp only
};
namespace lagcomp {
bool run_extrapolation( Player* player, const bool simple = false );
int fix_tickcount( const float& simtime );
bool valid_simtime( const float& simtime, const float time = game::TICKS_TO_TIME( g_cl.m_local->m_nTickBase( ) - 1 ) );
bool is_breaking_lagcomp( Player* player );
bool is_breaking_lagcomp( Player* player, const lag_record_t* current );
int get_real_lag( Player* player, const lag_record_t* current );
float get_lerp_time( );
void extrapolate( Player* player, vec3_t& origin, vec3_t& velocity, vec3_t base_velocity, int& flags, bool wasonground );
};