summaryrefslogtreecommitdiff
path: root/internal_rewrite/prediction.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'internal_rewrite/prediction.cpp')
-rw-r--r--internal_rewrite/prediction.cpp841
1 files changed, 841 insertions, 0 deletions
diff --git a/internal_rewrite/prediction.cpp b/internal_rewrite/prediction.cpp
new file mode 100644
index 0000000..1d643de
--- /dev/null
+++ b/internal_rewrite/prediction.cpp
@@ -0,0 +1,841 @@
+#include "prediction.hpp"
+#include "hooks.hpp"
+#include "context.hpp"
+
+NAMESPACE_REGION( features )
+
+void c_prediction::player_data_t::update( int ent_index ) {
+ auto player = g_csgo.m_entlist( )->GetClientEntity( ent_index );
+ if( !player || !player->is_valid( ) || player == g_ctx.m_local ) {
+ m_valid = false;
+ return;
+ }
+
+ float simtime = player->m_flSimulationTime( );
+
+ if( std::abs( simtime - m_simtime ) ) {
+ float delta = std::abs( m_simtime - simtime );
+ if( !m_valid ) {
+ m_old_velocity = player->m_vecVelocity( );
+ }
+ else {
+ m_last_choke = TIME_TO_TICKS( delta );
+ m_old_velocity = m_velocity;
+ m_breaking_lc = player->m_vecOrigin( ).dist_to( m_position ) > 64;
+ }
+
+ m_velocity = player->get_animdata( ).m_last_velocity;
+ m_position = player->m_vecOrigin( );
+
+ m_valid = true;
+
+ if( m_breaking_lc )
+ m_records.push_front( { m_velocity, m_last_choke } );
+ }
+
+ m_simtime = simtime;
+
+ while( m_records.size( ) > 32 )
+ m_records.pop_back( );
+}
+
+void c_prediction::frame_stage_notify( ) {
+ for( int i{ 1 }; i < 65; ++i ) {
+ m_players[ i ].update( i );
+ }
+}
+
+int c_prediction::get_predicted_choke( int idx ) {
+ auto& data = m_players[ idx ];
+
+ int adaptive_dtc = 0;
+ int static_dtc = 0;
+ int min_choke = 16;
+ int max_choke = 0;
+
+ if( !data.m_records.empty( ) ) {
+ int prev_choke = data.m_last_choke;
+ float last_dist = 0.f;
+ for( auto& it : data.m_records ) {
+ if( it.m_tick == prev_choke )
+ static_dtc++;
+
+ float speed = it.m_velocity.length2d( );
+ float dist = speed * it.m_tick * TICK_INTERVAL( );
+
+ if( dist > 62.f && std::abs( last_dist - dist ) < 12.f )
+ adaptive_dtc++;
+
+ prev_choke = it.m_tick;
+ last_dist = dist;
+ min_choke = math::min( it.m_tick, min_choke );
+ max_choke = math::max( it.m_tick, max_choke );
+ }
+ }
+
+ if( adaptive_dtc > 10 ) {
+ return TIME_TO_TICKS( 64.f / data.m_velocity.length2d( ) ) + 1;
+ }
+ else if( static_dtc > 16 || data.m_records.empty( ) ) {
+ return data.m_last_choke;
+ }
+
+ float factor = math::random_number( 0.f, 1.f );
+ return ( 1.0f - factor ) * min_choke + factor * max_choke;
+}
+
+vec3_t c_prediction::aimware_extrapolate( c_base_player* ent, vec3_t origin, vec3_t& velocity ) {
+ static auto sv_jump_impulse = g_csgo.m_cvar( )->FindVar( xors( "sv_jump_impulse" ) );
+ static auto sv_gravity = g_csgo.m_cvar( )->FindVar( xors( "sv_gravity" ) );
+
+ auto min = ent->m_vecMins( );
+ auto max = ent->m_vecMaxs( );
+
+ auto start = origin;
+ auto end = start + velocity * TICK_INTERVAL( );
+
+ CTraceFilter f;
+ CGameTrace tr;
+ Ray_t ray;
+
+ ray.Init( start, end, min, max );
+ f.pSkip = ent;
+
+ g_csgo.m_trace( )->TraceRay( ray, MASK_PLAYERSOLID, &f, &tr );
+
+ if( tr.fraction != 1.f ) {
+ for( int i{ }; i < 2; ++i ) {
+ velocity -= tr.plane.normal * velocity.dot( tr.plane.normal );
+
+ auto dot = velocity.dot( tr.plane.normal );
+ if( dot < 0.f ) {
+ velocity -= dot * tr.plane.normal;
+ }
+
+ end = tr.endpos + ( velocity * TICK_INTERVAL( ) * ( 1.f - tr.fraction ) );
+ ray.Init( tr.endpos, end, min, max );
+ g_csgo.m_trace( )->TraceRay( ray, MASK_PLAYERSOLID, &f, &tr );
+
+ if( tr.fraction == 1.f )
+ break;
+ }
+ }
+
+ end = tr.endpos;
+ end.z -= 2.f;
+
+ ray.Init( tr.endpos, end, min, max );
+ g_csgo.m_trace( )->TraceRay( ray, MASK_PLAYERSOLID, &f, &tr );
+
+ if( tr.fraction != 1.f && tr.plane.normal.z > 0.7f ) {
+ velocity.z = sv_jump_impulse->get_float( );
+ }
+ else {
+ velocity.z -= sv_gravity->get_float( ) * TICK_INTERVAL( );
+ }
+
+ return tr.endpos;
+}
+
+vec3_t c_prediction::full_walk_move( c_base_player* player, int ticks ) {
+ auto index = player->ce( )->GetIndex( );
+ auto data = get_player_data( index );
+ vec3_t origin = data.m_position;
+ vec3_t velocity = data.m_velocity;
+ vec3_t old_velocity = data.m_velocity;
+ vec3_t acceleration;
+
+ bool is_on_ground = player->m_fFlags( ) & FL_ONGROUND;
+
+ m_player = data;
+
+ if( !data.m_valid )
+ return origin;
+
+ float velocity_dir = RAD2DEG( atan2( velocity.y, velocity.x ) );
+ float angle_dir = velocity_dir - RAD2DEG( atan2( data.m_old_velocity.y, data.m_old_velocity.x ) );
+
+ angle_dir *= TICKS_TO_TIME( data.m_last_choke );
+
+ if( velocity_dir <= 180.f ) {
+ if( velocity_dir < -180.f )
+ velocity_dir += 360.f;
+ }
+ else {
+ velocity_dir -= 360.f;
+ }
+
+ float length = velocity.length2d( );
+
+ for( int i{ }; i < ticks; ++i ) {
+ float extrapolated_dir = velocity_dir + angle_dir;
+
+ velocity.x = cos( DEG2RAD( extrapolated_dir ) ) * length;
+ velocity.y = sin( DEG2RAD( extrapolated_dir ) ) * length; // hey.... fix please In Fucking Correct
+
+ start_gravity( player, origin, velocity );
+
+ if( is_on_ground ) {
+ check_jump_button( player, origin, velocity ); // Won't jump all the time
+ }
+
+ if( is_on_ground ) {
+ //velocity.z = 0.f; go to hell
+ friction( player, origin, velocity );
+ }
+
+ check_velocity( player, origin, velocity );
+
+ // fuck walking
+ if( !is_on_ground ) {
+ air_move( player, origin, velocity, old_velocity, acceleration );
+ }
+
+ try_player_move( player, origin, velocity );
+
+ is_on_ground = categorize_position( player, origin, velocity );
+
+ check_velocity( player, origin, velocity );
+
+ finish_gravity( player, origin, velocity );
+
+ if( is_on_ground ) {
+ velocity.z = 0.f;
+ }
+
+ old_velocity = velocity;
+
+ velocity_dir = extrapolated_dir;
+ }
+
+ return origin;
+}
+
+void c_prediction::check_jump_button( c_base_player* player, vec3_t& origin, vec3_t& velocity ) {
+ static auto sv_jump_impulse = g_csgo.m_cvar( )->FindVar( xors( "sv_jump_impulse" ) );
+ float ground_factor = 1.f;
+ vec3_t ground_point = origin;
+
+ ground_point.z -= 2.f;
+
+ CGameTrace pm;
+ trace_player_bbox( player, origin, ground_point, &pm );
+
+ if( pm.m_pEnt ) {
+ auto surface_data = g_csgo.m_phys_props( )->GetSurfaceData( pm.surface.surfaceProps );
+ if( surface_data ) {
+ ground_factor = surface_data->game.jumpfactor;
+ }
+ }
+
+
+ if( !ground_factor ) {
+ ground_factor = 1.f;
+ }
+
+ //if( player->m_fFlags( ) & FL_DUCKING ) {
+ //velocity.z += ground_factor * sv_jump_impulse->get_float( ); // how they do it in csgo
+ //}
+ //else {
+ velocity.z = ground_factor * sv_jump_impulse->get_float( );
+ //}
+
+ finish_gravity( player, origin, velocity );
+
+}
+
+void c_prediction::start_gravity( c_base_player* player, vec3_t& origin, vec3_t& velocity ) {
+ static auto sv_gravity = g_csgo.m_cvar( )->FindVar( xors( "sv_gravity" ) );
+
+ float m_flGravity = player->m_flGravity( );
+
+ //if( !m_flGravity ) {
+ m_flGravity = 1.f;
+ //}
+
+ velocity.z -= ( m_flGravity * sv_gravity->get_float( ) * 0.5f * TICK_INTERVAL( ) );
+
+ check_velocity( player, origin, velocity );
+}
+
+void c_prediction::finish_gravity( c_base_player* player, vec3_t& origin, vec3_t& velocity ) {
+ static auto sv_gravity = g_csgo.m_cvar( )->FindVar( xors( "sv_gravity" ) );
+
+ float m_flGravity = player->m_flGravity( );
+ //if( !m_flGravity ) {
+ m_flGravity = 1.f;
+ //}
+
+ velocity.z -= ( m_flGravity * sv_gravity->get_float( ) * 0.5f * TICK_INTERVAL( ) );
+
+ check_velocity( player, origin, velocity );
+}
+
+void c_prediction::friction( c_base_player* player, vec3_t& origin, vec3_t& velocity ) {
+ static auto sv_friction = g_csgo.m_cvar( )->FindVar( xors( "sv_friction" ) );
+ static auto sv_stopspeed = g_csgo.m_cvar( )->FindVar( xors( "sv_stopspeed" ) );
+ const float m_surfaceFriction = player->m_surfaceFriction( );
+
+ float speed = velocity.length( );
+
+ if( speed < 0.1f )
+ return;
+
+ float friction = sv_friction->get_float( ) * m_surfaceFriction;
+ float control = ( speed < sv_stopspeed->get_float( ) ) ? sv_stopspeed->get_float( ) : speed;
+ float drop = control * friction * TICK_INTERVAL( );
+
+ float newspeed = speed - drop;
+ if( newspeed < 0.f )
+ newspeed = 0.f;
+
+ if( newspeed != speed ) {
+ newspeed /= speed;
+ velocity *= newspeed;
+ }
+}
+
+void c_prediction::air_move( c_base_player* player, vec3_t& origin, vec3_t& velocity, vec3_t& old_velocity, vec3_t& acceleration ) {
+ vec3_t wishvel;
+ vec3_t wishdir;
+ float wishspeed;
+ vec3_t forward, right, up;
+
+ //fmove = m_player.m_movement.x;
+ //smove = m_player.m_movement.y;
+ //
+ //math::angle_vectors( m_player.m_angles, &forward, &right, &up );
+ //
+ //forward.z = right.z = 0.f;
+ //
+ //forward.normalize_vector( );
+ //right.normalize_vector( );
+ //
+ //for( int i{ }; i < 2; ++i ) {
+ // wishvel[ i ] = forward[ i ] * fmove + right[ i ] * smove;
+ //}
+
+
+ wishvel = velocity;
+
+ wishvel[ 2 ] = 0.f;
+
+ wishdir = wishvel;
+ wishdir.normalize_vector( );
+
+ wishspeed = acceleration.length( ); // probably wrong
+
+ air_accelerate( player, origin, old_velocity, wishdir, wishspeed );
+}
+
+void c_prediction::air_accelerate( c_base_player* player, vec3_t& origin, vec3_t& velocity, vec3_t& wishdir, float wishspeed ) {
+ static auto sv_airaccelerate = g_csgo.m_cvar( )->FindVar( xors( "sv_airaccelerate" ) );
+
+ float wishspd = wishspeed;
+
+ if( wishspd > 30.f )
+ wishspd = 30.f;
+
+ float currentspeed = velocity.dot( wishdir );
+
+ float addspeed = wishspd - currentspeed;
+
+ if( addspeed <= 0 )
+ return;
+
+ float accelspeed = sv_airaccelerate->get_float( ) * wishspeed * TICK_INTERVAL( );
+
+ if( accelspeed > addspeed )
+ accelspeed = addspeed;
+
+ for( int i{ }; i < 2; ++i ) {
+ velocity[ i ] += accelspeed * wishdir[ i ];
+ }
+}
+
+void c_prediction::try_player_move( c_base_player* player, vec3_t& origin, vec3_t& velocity ) {
+ CGameTrace pm;
+ vec3_t end_pos = origin + velocity * TICK_INTERVAL( );
+
+ trace_player_bbox( player, origin, end_pos, &pm );
+
+ if( pm.fraction != 1.f ) {
+ end_pos = pm.endpos;
+ }
+
+ origin = end_pos;
+}
+
+
+
+//there are supposed to be some ladder checks etc here, but we're really only supposed to be doing this when people are breaking lag comp etc
+//also ugly code, please fix :(
+bool c_prediction::categorize_position( c_base_player* player, vec3_t& origin, vec3_t& velocity ) {
+ constexpr float NON_JUMP_VELOCITY = 140.f;
+ vec3_t ground_point = origin;
+ bool is_moving_up_rapidally = velocity.z > NON_JUMP_VELOCITY;
+ CGameTrace pm;
+
+ ground_point.z -= 2.f;
+
+ if( is_moving_up_rapidally ) {
+ return false;
+ }
+ else {
+ trace_player_bbox( player, origin, ground_point, &pm );
+ if( !pm.m_pEnt || pm.plane.normal.z < 0.7 ) {
+ try_touch_ground_in_quadrants( player, origin, ground_point, &pm );
+ if( !pm.m_pEnt || pm.plane.normal.z < 0.7 ) {
+ return false;
+ }
+ else {
+ return true;
+ }
+ }
+ else {
+ return true;
+ }
+ }
+
+ return true;
+}
+
+
+void c_prediction::check_velocity( c_base_player* player, vec3_t& origin, vec3_t& velocity ) {
+ static auto sv_max_velocity = g_csgo.m_cvar( )->FindVar( xors( "sv_maxvelocity" ) );
+ const float max_velocity = sv_max_velocity->get_float( );
+
+ for( int i{ }; i < 3; ++i ) {
+ if( !std::isfinite( velocity[ i ] ) ) {
+ velocity[ i ] = 0.f;
+ }
+
+ if( !std::isfinite( origin[ i ] ) ) {
+ origin[ i ] = 0.f;
+ }
+
+ velocity[ i ] = std::clamp( velocity[ i ], -max_velocity, max_velocity );
+ }
+}
+
+
+
+// https://github.com/ValveSoftware/source-sdk-2013/blob/master/mp/src/game/shared/gamemovement.cpp#L2025
+vec3_t c_prediction::extrapolate_player( c_base_player* player, int ticks ) {
+ static auto sv_gravity = g_csgo.m_cvar( )->FindVar( xors( "sv_gravity" ) );
+ static auto sv_jump_impulse = g_csgo.m_cvar( )->FindVar( xors( "sv_jump_impulse" ) );
+
+ auto index = player->ce( )->GetIndex( );
+ auto data = get_player_data( index );
+
+ vec3_t& m_vecBaseVelocity = player->get< vec3_t >( 0x11C );
+
+ vec3_t predicted( data.m_position );
+ vec3_t velocity( data.m_velocity );
+ vec3_t acceleration{ };
+
+ if( !data.m_valid )
+ return predicted;
+
+ float velocity_dir = RAD2DEG( atan2( velocity.y, velocity.x ) );
+ float angle_dir = velocity_dir - RAD2DEG( atan2( data.m_old_velocity.y, data.m_old_velocity.x ) );
+
+ angle_dir *= TICKS_TO_TIME( data.m_last_choke );
+
+ if( velocity_dir <= 180.f ) {
+ if( velocity_dir < -180.f )
+ velocity_dir += 360.f;
+ }
+ else {
+ velocity_dir -= 360.f;
+ }
+
+ float length = velocity.length2d( );
+
+ float dir = velocity_dir;
+
+ for( int i = 0; i < ticks; ++i ) {
+ float extrapolated_dir = velocity_dir + angle_dir;
+
+ velocity.x = cos( DEG2RAD( extrapolated_dir ) ) * length;
+ velocity.y = sin( DEG2RAD( extrapolated_dir ) ) * length;
+
+ predicted = aimware_extrapolate( player, predicted, velocity );
+
+ velocity_dir = extrapolated_dir;
+ }
+
+ return predicted;
+}
+
+void c_prediction::trace_player_bbox( c_base_player* player, const vec3_t& start, const vec3_t& end, CGameTrace* pm ) {
+ Ray_t ray;
+ ray.Init( start, end, player->m_vecMins( ), player->m_vecMaxs( ) );
+
+ CTraceFilter filter;
+ filter.pSkip = player;
+
+ g_csgo.m_trace( )->TraceRay( ray, MASK_SOLID & ~CONTENTS_MONSTER, &filter, pm );
+}
+
+void c_prediction::try_touch_ground( c_base_player* player, const vec3_t& start, const vec3_t& end, const vec3_t& mins, const vec3_t& maxs, CGameTrace* pm ) {
+ Ray_t ray;
+ ray.Init( start, end, mins, maxs );
+
+ CTraceFilter filter;
+ filter.pSkip = player;
+
+ g_csgo.m_trace( )->TraceRay( ray, MASK_SOLID & ~CONTENTS_MONSTER, &filter, pm );
+}
+
+void c_prediction::try_touch_ground_in_quadrants( c_base_player* player, const vec3_t& start, const vec3_t& end, CGameTrace* pm ) {
+ vec3_t mins, maxs;
+
+ vec3_t mins_src = player->m_vecMins( );
+ vec3_t maxs_src = player->m_vecMaxs( );
+
+ float fraction = pm->fraction;
+ vec3_t end_pos = pm->endpos;
+
+ mins = mins_src;
+ maxs = vec3_t( std::min( 0.f, maxs_src.x ), std::min( 0.f, maxs_src.y ), maxs_src.z );
+
+ try_touch_ground( player, start, end, mins, maxs, pm );
+ if( pm->m_pEnt && pm->plane.normal.z >= 0.7 ) {
+ pm->fraction = fraction;
+ pm->endpos = end_pos;
+ return;
+ }
+
+ mins = vec3_t( std::max( 0.f, mins_src.x ), std::max( 0.f, mins_src.y ), mins_src.z );
+ maxs = maxs_src;
+
+ try_touch_ground( player, start, end, mins, maxs, pm );
+ if( pm->m_pEnt && pm->plane.normal.z >= 0.7 ) {
+ pm->fraction = fraction;
+ pm->endpos = end_pos;
+ return;
+ }
+
+ mins = vec3_t( mins_src.x, std::max( 0.f, mins_src.y ), mins_src.z );
+ maxs = vec3_t( std::min( 0.f, maxs_src.x ), maxs_src.y, maxs_src.z );
+
+ try_touch_ground( player, start, end, mins, maxs, pm );
+ if( pm->m_pEnt && pm->plane.normal.z >= 0.7 ) {
+ pm->fraction = fraction;
+ pm->endpos = end_pos;
+ return;
+ }
+
+ mins = vec3_t( std::max( 0.f, mins_src.x ), mins_src.y, mins_src.z );
+ maxs = vec3_t( maxs_src.x, std::min( 0.f, maxs_src.y ), maxs_src.z );
+
+ try_touch_ground( player, start, end, mins, maxs, pm );
+ if( pm->m_pEnt && pm->plane.normal.z >= 0.7 ) {
+ pm->fraction = fraction;
+ pm->endpos = end_pos;
+ return;
+ }
+
+ pm->fraction = fraction;
+ pm->endpos = end_pos;
+}
+
+// This is extremely gay, don't use it.
+// void c_prediction::local_pred( user_cmd_t* ucmd ) {
+// if( !ucmd || !g_ctx.m_local || !g_ctx.m_local->is_alive( ) )
+// return;
+//
+// static uintptr_t run_command_address = g_csgo.m_prediction->get_old_function< uintptr_t >( 19 );
+//
+// CMoveData move_data{ };
+// IClientEntity* local_ent = g_ctx.m_local->ce( );
+//
+// //backup data
+// int old_buttons = ucmd->m_buttons;
+// float old_curtime = g_csgo.m_globals->m_curtime;
+// float old_frame_time = g_csgo.m_globals->m_frametime;
+// int old_tickbase = g_ctx.m_local->m_nTickBase( );
+// int old_flags = g_ctx.m_local->m_fFlags( );
+// MoveType_t old_move_type = g_ctx.m_local->m_nMoveType( );
+// vec3_t old_velocity = g_ctx.m_local->m_vecVelocity( );
+//
+// //set globals
+// g_csgo.m_globals->m_curtime = g_csgo.m_globals->m_interval_per_tick * old_tickbase;
+// g_csgo.m_globals->m_frametime = g_csgo.m_globals->m_interval_per_tick;
+//
+// //random seed is already being calculated and set in createmove
+// **( uintptr_t** )( run_command_address + 0x3E ) = ucmd->m_random_seed; //prediction seed
+// **( uintptr_t** )( run_command_address + 0x54 ) = uintptr_t( g_ctx.m_local ); //prediction player
+//
+// //start prediction
+// g_csgo.m_move_helper( )->SetHost( local_ent );
+// g_csgo.m_game_movement( )->StartTrackPredictionErrors( local_ent );
+//
+// //run prediction
+// g_csgo.m_prediction( )->SetupMove( local_ent, ucmd, g_csgo.m_move_helper( ), &move_data );
+// g_csgo.m_game_movement( )->ProcessMovement( local_ent, &move_data );
+// g_csgo.m_prediction( )->FinishMove( local_ent, ucmd, &move_data );
+//
+// //finish prediction
+// g_csgo.m_game_movement( )->FinishTrackPredictionErrors( local_ent );
+// g_csgo.m_move_helper( )->SetHost( nullptr );
+//
+// **( uintptr_t** )( run_command_address + 0x3E ) = 0xffffffff;
+// **( uintptr_t*** )( run_command_address + 0x54 ) = nullptr;
+//
+// //good to have, can be used for edge jump and such
+// m_predicted_flags = g_ctx.m_local->m_fFlags( );
+//
+// //restore
+// ucmd->m_buttons = old_buttons;
+// g_csgo.m_globals->m_curtime = old_curtime;
+// g_csgo.m_globals->m_frametime = old_frame_time;
+// g_ctx.m_local->m_nTickBase( ) = old_tickbase;
+// g_ctx.m_local->m_fFlags( ) = old_flags;
+// g_ctx.m_local->m_nMoveType( ) = old_move_type;
+// g_ctx.m_local->m_vecVelocity( ) = old_velocity;
+// }
+
+void c_prediction::pre_run_command( ) {
+ // The game does not advance the tickbase if the engine is paused.
+ if ( g_csgo.m_prediction( )->m_bEnginePaused )
+ return;
+
+ auto *cl = g_csgo.m_global_state->get_client_state( );
+
+ // Ideally, you should only be doing this if the last stage is
+ // FRAME_NET_UPDATE_END.
+
+ if ( cl && cl->m_delta_tick >= 0 ) {
+ // This should fix the issues when bunny-hopping on a low frame-rate.
+ g_csgo.m_prediction( )->Update(
+ cl->m_delta_tick,
+ cl->m_delta_tick > 0,
+ cl->m_last_acknowledged_cmd,
+ cl->m_lastoutgoingcommand + cl->m_chokedcommands
+ );
+ }
+}
+
+// This code got nasty after a while, imo. You can clean it up if you care enough.
+void c_prediction::run_command( user_cmd_t *ucmd ) {
+ CMoveData movedata{ };
+
+ if ( !ucmd || !g_csgo.m_engine( )->IsConnected( ) || !g_csgo.m_engine( )->IsInGame( ) || !g_csgo.m_move_helper.get( ) )
+ return;
+
+ // Time saver.
+ auto *player = g_ctx.m_local;
+ auto *player_ce = player->ce( );
+
+ if ( !player || !player_ce )
+ return;
+
+ // Set random seed and player.
+ if ( !m_prediction_seed || !m_prediction_player ) {
+ m_prediction_seed = *reinterpret_cast< int * >( g_csgo.m_prediction->get_old_function< uint32_t >( 19 ) + 0x30 );
+ m_prediction_player = *reinterpret_cast< int * >( g_csgo.m_prediction->get_old_function< uint32_t >( 19 ) + 0x3E );
+ }
+
+ *reinterpret_cast< int * >( m_prediction_seed ) = ucmd ? ucmd->m_random_seed : -1;
+ *reinterpret_cast< uint32_t * >( m_prediction_player ) = uint32_t( player );
+
+ // Copy user command to m_pCurrentCommand and m_pPlayerCommand.
+ *reinterpret_cast< uint32_t * >( uint32_t( player ) + 0x3314 ) = uint32_t( ucmd );
+ *reinterpret_cast< uint32_t * >( uint32_t( player ) + 0x326C ) = uint32_t( ucmd );
+
+ // Store entity variables that will get updated.
+ auto old_flags = player->m_fFlags( );
+ auto old_move = player->m_nMoveType( );
+ auto old_tickbase = player->m_nTickBase( );
+
+ m_velocity = player->m_vecVelocity( );
+
+ // Store globals prior to prediction.
+ m_old_time = g_csgo.m_globals->m_curtime;
+ m_old_frametime = g_csgo.m_globals->m_frametime;
+ m_old_tickcount = g_csgo.m_globals->m_tickcount;
+
+ // Set up globals.
+ g_csgo.m_globals->m_curtime = old_tickbase * TICK_INTERVAL( );
+ g_csgo.m_globals->m_frametime = TICK_INTERVAL( );
+ g_csgo.m_globals->m_tickcount = old_tickbase;
+
+ // This is in order to avoid double footsteps
+ // NOTE: This is being a meme in Moneybot for some reason, not sure why.
+ // Worked perfectly fine in my cheat.
+ bool old_predicted = g_csgo.m_prediction( )->m_bIsFirstTimePredicted;
+ bool old_prediction = g_csgo.m_prediction( )->m_bInPrediction;
+
+ g_csgo.m_prediction( )->m_bIsFirstTimePredicted = false;
+ g_csgo.m_prediction( )->m_bInPrediction = true;
+
+ // Handle frame-time as the engine would.
+ if ( g_csgo.m_prediction( )->m_bEnginePaused )
+ g_csgo.m_globals->m_frametime = 0.0f;
+
+ ucmd->m_buttons |= *( uint32_t * ) ( ( uint32_t ) player + 0x3310 ); // unk01
+
+ g_csgo.m_game_movement( )->StartTrackPredictionErrors( player_ce );
+
+ if ( ucmd->m_impulse )
+ *( uint32_t * ) ( ( uint32_t ) player + 0x31EC ) = ucmd->m_impulse;
+
+ // CPrediction::UpdateButtonState
+ {
+ /*
+ v16 = ucmd->m_fButtons;
+ v17 = v16 ^ *(_DWORD *)(player + 0x31E8);
+ *(_DWORD *)(player + 0x31DC) = *(_DWORD *)(player + 0x31E8);
+ *(_DWORD *)(player + 0x31E8) = v16;
+ *(_DWORD *)(player + 0x31E0) = v16 & v17;
+ v18 = gpGlobals;
+ *(_DWORD *)(player + 0x31E4) = v17 & ~v16;
+ */
+
+ auto original_buttons = ucmd->m_buttons;
+
+ auto *m_afButtonBackup = reinterpret_cast< int * >( uint32_t( player ) + 0x31E8 );
+ auto unk03 = original_buttons ^ *m_afButtonBackup;
+
+ *reinterpret_cast< int * >( uint32_t( player ) + 0x31DC ) = *m_afButtonBackup; // m_afButtonLast
+ *reinterpret_cast< int * >( uint32_t( player ) + 0x31E8 ) = original_buttons; // m_afButtonBackup (??)
+ *reinterpret_cast< int * >( uint32_t( player ) + 0x31E0 ) = original_buttons & unk03; // m_afButtonPressed
+ *reinterpret_cast< int * >( uint32_t( player ) + 0x31E4 ) = unk03 & ~original_buttons;// m_afButtonReleased
+ }
+
+ // NOTE: The IDA generated call is WRONG, check the assembly!
+ // The function only uses the lower bits of the double, though. Valve code.....
+
+ // using fnCheckMovingGround = void( __thiscall * )( void *, se::C_BaseEntity *, double );
+ // Memory::Virtual< fnCheckMovingGround >( this, 16 )( this, player, frametime );
+
+ // This might not be needed, tbh.
+ /*
+ (*(void (__stdcall **)(int, _DWORD, _DWORD))(*(_DWORD *)prediction->pad00 + 72))(
+ player,
+ COERCE_UNSIGNED_INT64(v18->frametime),
+ COERCE_UNSIGNED_INT64(v18->frametime) >> 32);
+ */
+
+ g_csgo.m_prediction( )->CheckMovingGround( player_ce, g_csgo.m_globals->m_frametime );
+
+ // CPrediction::RunPreThink
+ {
+ player->set_local_view_angles( &ucmd->m_viewangles );
+
+ if ( player->run_physics_think( 0 ) )
+ player->pre_think( );
+ }
+
+ // CPrediction::RunThink
+ {
+ auto *next_think = reinterpret_cast< int * >( uint32_t( player ) + 0xF8 );
+
+ if ( *next_think > 0 && *next_think <= player->m_nTickBase( ) )
+ {
+ *next_think = -1;
+
+ // sub_1017E880
+ /*
+ v2 = (int)this;
+ v3 = this[57];
+ result = (this[57] >> 22) & 1;
+ if ( result )
+ {
+ if ( a2 )
+ {
+ this[57] = v3 & 0xFFBFFFFF;
+ return result;
+ }
+ }
+ else if ( a2 )
+ {
+ return result;
+ }
+ if ( result )
+ return result;
+ result = sub_1017E840();
+ if ( !result )
+ *(_DWORD *)(v2 + 228) = v3 | 0x400000;
+ return result;
+ */
+
+ auto v3 = *reinterpret_cast< uint32_t * >( uint32_t( player ) + 0xE4 ); // m_iEFlags
+ auto v4 = v3 & 0x400000;
+
+ // Referenced in sub_1017E840.
+ /*
+ v3 = *(_DWORD *)(this + 0x2B8);
+ v4 = 0;
+ if ( v3 <= 0 )
+ return 0;
+ */
+ auto v5 = *reinterpret_cast< uint32_t * >( uint32_t( player ) + 0x2B8 );
+
+ if ( !v4 && !v5 )
+ *reinterpret_cast< uint32_t * >( uint32_t( player ) + 0xE4 ) = v3 | 0x400000;
+
+ player->think( );
+ }
+ }
+
+ g_csgo.m_move_helper( )->SetHost( player_ce );
+
+ g_csgo.m_prediction( )->SetupMove( player_ce, ucmd, g_csgo.m_move_helper( ), &movedata );
+ g_csgo.m_game_movement( )->ProcessMovement( player_ce, &movedata );
+ g_csgo.m_prediction( )->FinishMove( player_ce, ucmd, &movedata );
+
+ // CPrediction::RunPostThink
+ {
+ g_csgo.m_move_helper( )->ProcessImpacts( );
+
+ // Calling C_BasePlayer::PostThink will cause C_BasePlayer::ItemPostFrame
+ // to be called, resulting in all of our timers being fucked up.
+
+ // player->PostThink( );
+ }
+
+ // Restore prediction flags.
+ g_csgo.m_prediction( )->m_bIsFirstTimePredicted = old_predicted;
+ g_csgo.m_prediction( )->m_bInPrediction = old_prediction;
+
+ // Important: restore the old tick-base.
+ player->m_nTickBase( ) = old_tickbase;
+ player->m_fFlags( ) = old_flags;
+ player->m_nMoveType( ) = old_move;
+
+ // IMLAZY:
+ // I'd rather just restore this instead of fixing it everywhere in the cheat.
+ player->m_vecVelocity( ) = m_velocity;
+}
+
+void c_prediction::post_run_command( ) {
+ // Time saver.
+ auto *player = g_ctx.m_local;
+ auto *player_ce = player->ce( );
+
+ if ( !player || !player_ce )
+ return;
+
+ g_csgo.m_game_movement( )->FinishTrackPredictionErrors( player_ce );
+ g_csgo.m_move_helper( )->SetHost( nullptr );
+
+ // CPrediction::FinishCommand
+ {
+ // Restore globals.
+ g_csgo.m_globals->m_curtime = m_old_time;
+ g_csgo.m_globals->m_frametime = m_old_frametime;
+ g_csgo.m_globals->m_tickcount = m_old_tickcount;
+
+ // Invalidate seed, player.
+ *reinterpret_cast< int * >( m_prediction_seed ) = int( -1 );
+ *reinterpret_cast< int * >( m_prediction_player ) = 0;
+ *reinterpret_cast< uint32_t * >( uint32_t( player ) + 0x3314 ) = 0;
+ }
+
+ g_csgo.m_game_movement( )->Reset( );
+}
+
+END_REGION