Rewrite Backtrack

I mean when i already rewrite crithack why not Backtrack too
This commit is contained in:
BenCat07 2020-05-06 21:59:29 +02:00
parent 7516c62b3c
commit 9dcfc6b8cf
14 changed files with 872 additions and 929 deletions

View File

@ -113,14 +113,15 @@
</Box>
<Box padding="12 6 6 6" width="content" height="content" name="Backtracking" x="340" y="125">
<List width="150">
<AutoVariable width="fill" target="backtrack.enable" label="Enable Backtracking" tooltip="Allow hitting enemies in the past."/>
<AutoVariable width="fill" target="aimbot.backtrack" label="Aim at Backtracking"/>
<AutoVariable width="fill" target="backtrack.enabled" label="Enable Backtracking" tooltip="Allow hitting enemies in the past."/>
<AutoVariable width="fill" target="aimbot.backtrack" label="Aim at Backtracking" tooltip="Aim at Backtrack ticks"/>
<AutoVariable width="fill" target="misc.ping-reducer.enable" label="Ping Reducer"/>
<AutoVariable width="fill" target="misc.ping-reducer.target" label="Target Ping"/>
<AutoVariable width="fill" target="backtrack.latency" label="Fake Latency" min="0" max="1000" step="25"/>
<AutoVariable width="fill" target="backtrack.draw" label="Draw Backtrack"/>
<AutoVariable width="fill" target="backtrack.draw-skeleton" label="Draw Skeleton"/>
<AutoVariable width="fill" target="backtrack.chams_glow" label="Chams/Glow Support"/>
<AutoVariable width="fill" target="backtrack.draw" label="Draw Backtrack" tooltip="Draw ticks on screen"/>
<AutoVariable width="fill" target="backtrack.chams" label="Backtrack Chams" tooltip="Draw Chams for the ticks"/>
<AutoVariable width="fill" target="backtrack.chams.ticks" label="Chams Amount" tooltip="How many ticks to draw (WARNING, this can cause lag)"/>
<AutoVariable width="fill" target="backtrack.chams.team-color" label="Teamcolor Chams" tooltip="Use Teamcolor instead of grey chams"/>
<LabeledObject width="fill" label="Backtrack Slots">
<Select target="backtrack.slots">
<Option name="All" value="0"/>

View File

@ -14,7 +14,6 @@ class IClientEntity;
namespace hacks::shared::aimbot
{
bool IsBacktracking();
extern settings::Boolean ignore_cloak;
// Used to store aimbot data to prevent calculating it again
@ -30,7 +29,7 @@ struct AimbotCalculatedData_s
// Functions used to calculate aimbot data, and if already calculated use it
const Vector &PredictEntity(CachedEntity *entity);
bool VischeckPredictedEntity(CachedEntity *entity, bool Backtracking);
bool VischeckPredictedEntity(CachedEntity *entity);
bool BacktrackVisCheck(CachedEntity *entity);
// Variable used to tell when the aimbot has found a target
@ -45,7 +44,7 @@ void Reset();
// Stuff to make storing functions easy
CachedEntity *CurrentTarget();
bool ShouldAim();
CachedEntity *RetrieveBestTarget(bool aimkey_state, bool Backtracking = false);
CachedEntity *RetrieveBestTarget(bool aimkey_state);
bool IsTargetStateGood(CachedEntity *entity);
void Aim(CachedEntity *entity);
void DoAutoshoot(CachedEntity *target = nullptr);

View File

@ -1,63 +1,105 @@
/*
* Backtrack.hpp
*
* Created on: May 15, 2018
* Author: bencat07
*/
#pragma once
#include "common.hpp"
#include <boost/circular_buffer.hpp>
namespace hacks::shared::backtrack
namespace hacks::tf2::backtrack
{
extern settings::Int latency;
extern settings::Boolean enable;
class CIncomingSequence
{
public:
int inreliablestate;
int sequencenr;
float curtime;
struct hitboxData
{
Vector center{ 0.0f, 0.0f, 0.0f };
Vector min{ 0.0f, 0.0f, 0.0f };
Vector max{ 0.0f, 0.0f, 0.0f };
};
struct BacktrackData
{
int tickcount{ 0 };
std::array<hitboxData, 18> hitboxes;
hitboxData collidable{};
float viewangles{ 0.0f };
float simtime{ 0.0f };
Vector entorigin{ 0.0f, 0.0f, 0.0f };
int index{ 0 };
matrix3x4_t bones[128]{};
};
void Init();
void AddLatencyToNetchan(INetChannel *);
void UpdateIncomingSequences();
extern int lastincomingsequencenumber;
extern int BestTick;
extern int iBestTarget;
struct CIncomingSequence
{
CIncomingSequence(int instate, int seqnr, float time)
{
inreliablestate = instate;
sequencenr = seqnr;
curtime = time;
}
int inreliablestate;
int sequencenr;
float curtime;
};
typedef boost::circular_buffer_space_optimized<CIncomingSequence> circular_buf;
extern circular_buf sequences;
extern BacktrackData headPositions[PLAYER_ARRAY_SIZE][66];
extern bool isBacktrackEnabled;
extern bool Vischeck_Success;
float getLatency();
float getRealLatency();
int getTicks();
bool ValidTick(BacktrackData &i, CachedEntity *ent);
} // namespace hacks::shared::backtrack
class hitboxData
{
public:
Vector center{ 0.0f, 0.0f, 0.0f };
Vector min{ 0.0f, 0.0f, 0.0f };
Vector max{ 0.0f, 0.0f, 0.0f };
};
class BacktrackData
{
public:
int tickcount{};
std::array<hitboxData, 18> hitboxes{};
Vector m_vecOrigin{};
Vector m_vecAngles{};
Vector m_vecMins{};
Vector m_vecMaxs{};
float m_flSimulationTime{};
bool has_updated{};
matrix3x4_t bones[128]{};
};
// Let's try OOP for once
class Backtrack
{
// Internal rvars
settings::Boolean enabled{ "backtrack.enabled", "false" };
settings::Boolean draw{ "backtrack.draw", "false" };
std::vector<CIncomingSequence> sequences;
int current_tickcount;
std::array<std::unique_ptr<std::array<BacktrackData, 67>>, PLAYER_ARRAY_SIZE> backtrack_data;
int lastincomingsequence{ 0 };
// Used to make transition smooth(er)
float latency_rampup = 0.0f;
// Which data to apply in the late CreateMove
CachedEntity *bt_ent;
std::optional<BacktrackData> bt_data;
bool isEnabled();
float getLatency();
int getTicks();
bool getBestInternalTick(CachedEntity *, BacktrackData &, std::optional<BacktrackData> &);
void ApplyBacktrack();
// Stuff that has to be accessible from outside, mostly functions
public:
settings::Float latency{ "backtrack.latency", "0" };
settings::Int bt_slots{ "backtrack.slots", "0" };
settings::Boolean chams{ "backtrack.chams", "false" };
settings::Int chams_ticks{ "backtrack.chams.ticks", "1" };
settings::Boolean team_color{ "backtrack.chams.team-color", "true" };
// Check if backtrack is enabled
bool isBacktrackEnabled;
// Event callbacks
void CreateMove();
void CreateMoveLate();
#if ENABLE_VISUALS
void Draw();
#endif
void LevelShutdown();
void adjustPing(INetChannel *);
void updateDatagram();
void resetData(int);
bool isGoodTick(BacktrackData &);
bool defaultTickFilter(CachedEntity *, BacktrackData);
bool defaultEntFilter(CachedEntity *);
// Various functions for getting backtrack ticks
std::vector<BacktrackData> getGoodTicks(int);
std::optional<BacktrackData> getBestTick(CachedEntity *, std::function<bool(CachedEntity *, BacktrackData &, std::optional<BacktrackData> &)>);
std::optional<BacktrackData> getClosestEntTick(CachedEntity *, Vector, std::function<bool(CachedEntity *, BacktrackData)>);
std::optional<std::pair<CachedEntity *, BacktrackData>> getClosestTick(Vector, std::function<bool(CachedEntity *)>, std::function<bool(CachedEntity *, BacktrackData)>);
void SetBacktrackData(CachedEntity *ent, BacktrackData);
};
} // namespace hacks::tf2::backtrack
extern hacks::tf2::backtrack::Backtrack g_IBacktrack;

View File

@ -8,15 +8,15 @@
#pragma once
#include "common.hpp"
#include "Backtrack.hpp"
namespace hacks::shared::triggerbot
{
void CreateMove();
CachedEntity *FindEntInSight(float range);
bool ShouldShoot();
bool IsTargetStateGood(CachedEntity *entity, bool backtrack = false);
CachedEntity *FindEntInSight(float range);
bool IsTargetStateGood(CachedEntity *entity, std::optional<hacks::tf2::backtrack::BacktrackData> bt_data = {});
CachedEntity *FindEntInSight(float range, bool no_players = false);
bool HeadPreferable(CachedEntity *target);
bool UpdateAimkey();
float EffectiveTargetingRange();

View File

@ -556,10 +556,6 @@ void fixBucket(IClientEntity *weapon, CUserCmd *cmd)
INetChannel *ch = (INetChannel *) g_IEngine->GetNetChannelInfo();
if (!ch)
return;
auto addr = ch->GetRemoteAddress();
// Local server needs no fixing
if (addr.type == NA_LOOPBACK)
return;
static int last_weapon;
// This tracks only when bucket is updated

View File

@ -114,9 +114,14 @@ int GetSentry()
}
settings::Boolean ignore_cloak{ "aimbot.target.ignore-cloaked-spies", "1" };
// Projectile info
bool projectile_mode{ false };
float cur_proj_speed{ 0.0f };
float cur_proj_grav{ 0.0f };
bool shouldBacktrack()
{
return *enable && (*backtrackAimbot || force_backtrack_aimbot) && hacks::shared::backtrack::isBacktrackEnabled;
return *enable && !projectile_mode && (*backtrackAimbot || force_backtrack_aimbot) && g_IBacktrack.isBacktrackEnabled;
}
bool IsBacktracking()
@ -170,15 +175,10 @@ int target_eid{ 0 };
CachedEntity *target = 0;
CachedEntity *target_last = 0;
bool foundTarget = false;
// Projectile info
bool projectile_mode{ false };
float cur_proj_speed{ 0.0f };
float cur_proj_grav{ 0.0f };
// If slow aimbot allows autoshoot
bool slow_can_shoot = false;
bool projectileAimbotRequired;
// Track Backtrack tick for Entity
static std::pair<int, int> good_tick = { -1, -1 };
// This array will store calculated projectile/hitscan predictions
// for current frame, to avoid performing them again
@ -200,6 +200,7 @@ static void CreateMove()
// aimkey would break so we save it to a var to use it twice
bool aimkey_status = UpdateAimkey();
// check if we need to run projectile Aimbot code
projectile_mode = false;
projectileAimbotRequired = false;
if (projectile_aimbot && (g_pLocalPlayer->weapon_mode == weapon_projectile || g_pLocalPlayer->weapon_mode == weapon_throwable))
projectileAimbotRequired = true;
@ -464,9 +465,8 @@ bool ShouldAim()
}
// Function to find a suitable target
CachedEntity *RetrieveBestTarget(bool aimkey_state, bool Backtracking)
CachedEntity *RetrieveBestTarget(bool aimkey_state)
{
// If we have a previously chosen target, target lock is on, and the aimkey
// is allowed, then attemt to keep the previous target
if (target_lock && foundTarget && aimkey_state)
@ -499,7 +499,6 @@ CachedEntity *RetrieveBestTarget(bool aimkey_state, bool Backtracking)
// Check whether the current ent is good enough to target
if (IsTargetStateGood(ent))
{
// Distance Priority, Uses this is melee is used
if (GetWeaponMode() == weaponmode::weapon_melee || (int) priority_mode == 2)
scr = 4096.0f - calculated_data_array[i].aim_position.DistTo(g_pLocalPlayer->v_Eye);
@ -589,31 +588,17 @@ bool IsTargetStateGood(CachedEntity *entity)
}
else
{
namespace bt = hacks::shared::backtrack;
for (int i = 0; i < 66; i++)
{
if (!bt::ValidTick(bt::headPositions[entity->m_IDX][i], entity))
continue;
Vector bbox_min = bt::headPositions[entity->m_IDX][i].collidable.min;
Vector bbox_max = bt::headPositions[entity->m_IDX][i].collidable.max;
for (int j = 17; j >= 0; j--)
{
Vector aim_at = bt::headPositions[entity->m_IDX][i].hitboxes[j].center;
Vector newangle = GetAimAtAngles(g_pLocalPlayer->v_Eye, aim_at);
Vector new_vec = GetForwardVector(g_pLocalPlayer->v_Eye, newangle, swingrange);
if (new_vec.x > bbox_min.x && new_vec.x < bbox_max.x && new_vec.y > bbox_min.y && new_vec.y < bbox_max.y && new_vec.z > bbox_min.z && new_vec.z < bbox_max.z)
{
/*auto it = bt::headPositions[entity->m_IDX][i];
current_user_cmd->tick_count = it.tickcount;
Vector &angles = NET_VECTOR(RAW_ENT(entity), netvar.m_angEyeAngles);
float &simtime = NET_FLOAT(RAW_ENT(entity), netvar.m_flSimulationTime);
angles.y = it.viewangles;
simtime = it.simtime;*/
return true;
}
}
}
return false;
// This does vischecks and everything
auto data = g_IBacktrack.getClosestEntTick(entity, LOCAL_E->m_vecOrigin(), std::bind(&hacks::tf2::backtrack::Backtrack::defaultTickFilter, &g_IBacktrack, std::placeholders::_1, std::placeholders::_2));
// No data found
if (!data)
return false;
// Is within range?
Vector melee_vec = data->m_vecOrigin;
melee_vec.z = g_pLocalPlayer->v_Eye.z;
// Out of range
if (melee_vec.DistTo(g_pLocalPlayer->v_Eye) >= swingrange)
return false;
}
}
}
@ -711,7 +696,7 @@ bool IsTargetStateGood(CachedEntity *entity)
cd.hitbox = hitbox;
// Vis check + fov check
if (!VischeckPredictedEntity(entity, IsBacktracking() && !projectile_mode))
if (!VischeckPredictedEntity(entity))
return false;
if (LOCAL_W->m_iClassID() == CL_CLASS(CTFLaserPointer))
{
@ -721,7 +706,15 @@ bool IsTargetStateGood(CachedEntity *entity)
Vector pos = GetBuildingPosition(ENTITY(sentry));
if (hitbox == -1 || !entity->hitboxes.GetHitbox(cd.hitbox))
return false;
if (!IsVectorVisible(pos, entity->hitboxes.GetHitbox(cd.hitbox)->center, false, ENTITY(sentry)))
if (shouldBacktrack())
{
// This does vischecks and everything
auto data = g_IBacktrack.getClosestEntTick(entity, LOCAL_E->m_vecOrigin(), std::bind(&hacks::tf2::backtrack::Backtrack::defaultTickFilter, &g_IBacktrack, std::placeholders::_1, std::placeholders::_2));
// No data found
if (!data)
return false;
}
else if (!IsVectorVisible(pos, entity->hitboxes.GetHitbox(cd.hitbox)->center, false, ENTITY(sentry)))
return false;
}
if (*fov > 0.0f && cd.fov > *fov)
@ -771,7 +764,7 @@ bool IsTargetStateGood(CachedEntity *entity)
AimbotCalculatedData_s &cd = calculated_data_array[entity->m_IDX];
// Vis and fov checks
if (!VischeckPredictedEntity(entity, false))
if (!VischeckPredictedEntity(entity))
return false;
if (LOCAL_W->m_iClassID() == CL_CLASS(CTFLaserPointer))
{
@ -823,7 +816,7 @@ bool IsTargetStateGood(CachedEntity *entity)
AimbotCalculatedData_s &cd = calculated_data_array[entity->m_IDX];
// Vis and fov check
if (!VischeckPredictedEntity(entity, false))
if (!VischeckPredictedEntity(entity))
return false;
if (LOCAL_W->m_iClassID() == CL_CLASS(CTFLaserPointer))
{
@ -831,7 +824,15 @@ bool IsTargetStateGood(CachedEntity *entity)
if (sentry == -1)
return false;
Vector pos = GetBuildingPosition(ENTITY(sentry));
if (!IsVectorVisible(pos, entity->m_vecOrigin(), false))
if (shouldBacktrack())
{
// This does vischecks and everything
auto data = g_IBacktrack.getClosestEntTick(entity, LOCAL_E->m_vecOrigin(), std::bind(&hacks::tf2::backtrack::Backtrack::defaultTickFilter, &g_IBacktrack, std::placeholders::_1, std::placeholders::_2));
// No data found
if (!data)
return false;
}
else if (!IsVectorVisible(pos, entity->m_vecOrigin(), false))
return false;
}
if (*fov > 0.0f && cd.fov > *fov)
@ -849,7 +850,6 @@ bool IsTargetStateGood(CachedEntity *entity)
// A function to aim at a specific entitiy
void Aim(CachedEntity *entity)
{
namespace bt = hacks::shared::backtrack;
if (*miss_chance > 0 && UniformRandomInt(0, 99) < *miss_chance)
return;
@ -872,10 +872,12 @@ void Aim(CachedEntity *entity)
auto hitboxcenter = hb->center;
if (shouldBacktrack() && entity->m_Type() == ENTITY_PLAYER)
{
const auto &bt_hb = bt::headPositions[entity->m_IDX][good_tick.first].hitboxes[cd.hitbox];
hitboxcenter = bt_hb.center;
hitboxmin = bt_hb.min;
hitboxmax = bt_hb.max;
// This does vischecks and everything
auto data = g_IBacktrack.getClosestEntTick(entity, LOCAL_E->m_vecOrigin(), std::bind(&hacks::tf2::backtrack::Backtrack::defaultTickFilter, &g_IBacktrack, std::placeholders::_1, std::placeholders::_2));
auto bt_hb = data->hitboxes.at(cd.hitbox);
hitboxcenter = bt_hb.center;
hitboxmin = bt_hb.min;
hitboxmax = bt_hb.max;
}
// get positions
minx = hitboxmin.x;
@ -898,7 +900,7 @@ void Aim(CachedEntity *entity)
// Create Vectors
const Vector positions[13] = { { minx, centery, minz }, { maxx, centery, minz }, { minx, centery, maxz }, { maxx, centery, maxz }, { centerx, miny, minz }, { centerx, maxy, minz }, { centerx, miny, maxz }, { centerx, maxy, maxz }, { minx, miny, centerz }, { maxx, maxy, centerz }, { minx, miny, centerz }, { maxx, maxy, centerz }, hitboxcenter };
for (int i = 0; i < 13; ++i)
if (IsVectorVisible(g_pLocalPlayer->v_Eye, positions[i]))
if (IsEntityVectorVisible(entity, positions[i]))
{
tr = (positions[i] - g_pLocalPlayer->v_Eye);
break;
@ -919,8 +921,15 @@ void Aim(CachedEntity *entity)
if (silent && !slow_aim)
g_pLocalPlayer->bUseSilentAngles = true;
// Set tick count to target's (backtrack messes with this)
if (!bt::isBacktrackEnabled && nolerp && entity->m_IDX <= g_IEngine->GetMaxClients())
if (!shouldBacktrack() && nolerp && entity->m_IDX <= g_IEngine->GetMaxClients())
current_user_cmd->tick_count = TIME_TO_TICKS(CE_FLOAT(entity, netvar.m_flSimulationTime));
// Set Backtrack data
if (shouldBacktrack() && entity->m_Type() == ENTITY_PLAYER)
{
auto data = g_IBacktrack.getClosestEntTick(entity, LOCAL_E->m_vecOrigin(), std::bind(&hacks::tf2::backtrack::Backtrack::defaultTickFilter, &g_IBacktrack, std::placeholders::_1, std::placeholders::_2));
if (data)
g_IBacktrack.SetBacktrackData(entity, *data);
}
// Finish function
return;
}
@ -1016,7 +1025,7 @@ void DoAutoshoot(CachedEntity *target_entity)
if (attack)
{
// TO DO: Sending both reload and attack will activate the hitmans heatmaker ability
// Don't activate it only on first kill (or somehow activate it before shoot)
// Don't activate it only on first kill (or somehow activate it before a shot)
current_user_cmd->buttons |= IN_ATTACK | (*autoreload && CarryingHeatmaker() ? IN_RELOAD : 0);
if (target_entity)
{
@ -1037,9 +1046,8 @@ const Vector &PredictEntity(CachedEntity *entity)
if (cd.predict_tick == tickcount)
return result;
if (!shouldBacktrack() || projectile_mode || entity->m_Type() != ENTITY_PLAYER)
if (!shouldBacktrack() || entity->m_Type() != ENTITY_PLAYER)
{
// Players
if ((entity->m_Type() == ENTITY_PLAYER))
{
@ -1082,45 +1090,12 @@ const Vector &PredictEntity(CachedEntity *entity)
}
else
{
// Players only
if ((entity->m_Type() == ENTITY_PLAYER))
auto data = g_IBacktrack.getClosestEntTick(entity, LOCAL_E->m_vecOrigin(), std::bind(&hacks::tf2::backtrack::Backtrack::defaultTickFilter, &g_IBacktrack, std::placeholders::_1, std::placeholders::_2));
if (data)
{
if (GetWeaponMode() != weapon_melee)
{
namespace bt = hacks::shared::backtrack;
auto hb = bt::headPositions[entity->m_IDX][good_tick.first];
cd.predict_tick = tickcount;
result = hb.hitboxes[cd.hitbox].center;
cd.fov = GetFov(g_pLocalPlayer->v_OrigViewangles, g_pLocalPlayer->v_Eye, result);
}
else
{
namespace bt = hacks::shared::backtrack;
for (int i = 0; i < 66; i++)
{
if (!bt::ValidTick(bt::headPositions[entity->m_IDX][i], entity))
continue;
Vector bbox_min = bt::headPositions[entity->m_IDX][i].collidable.min;
Vector bbox_max = bt::headPositions[entity->m_IDX][i].collidable.max;
for (int j = 17; j >= 0; j--)
{
float swingrange = re::C_TFWeaponBaseMelee::GetSwingRange(RAW_ENT(LOCAL_W));
Vector aim_at = bt::headPositions[entity->m_IDX][i].hitboxes[j].center;
Vector newangle = GetAimAtAngles(g_pLocalPlayer->v_Eye, aim_at);
Vector new_vec = GetForwardVector(g_pLocalPlayer->v_Eye, newangle, swingrange);
if (new_vec.x > bbox_min.x && new_vec.x < bbox_max.x && new_vec.y > bbox_min.y && new_vec.y < bbox_max.y && new_vec.z > bbox_min.z && new_vec.z < bbox_max.z)
{
result = new_vec;
auto it = bt::headPositions[entity->m_IDX][i];
current_user_cmd->tick_count = it.tickcount;
Vector &angles = NET_VECTOR(RAW_ENT(entity), netvar.m_angEyeAngles);
float &simtime = NET_FLOAT(RAW_ENT(entity), netvar.m_flSimulationTime);
angles.y = it.viewangles;
simtime = it.simtime;
}
}
}
}
result = data->hitboxes.at(cd.hitbox).center;
cd.predict_tick = tickcount;
cd.fov = GetFov(g_pLocalPlayer->v_OrigViewangles, g_pLocalPlayer->v_Eye, result);
}
}
// Return the found vector
@ -1154,7 +1129,6 @@ int BestHitbox(CachedEntity *target)
float begincharge = CE_FLOAT(g_pLocalPlayer->weapon(), netvar.flChargeBeginTime);
float charge = g_GlobalVars->curtime - begincharge;
int damage = std::floor(50.0f + 70.0f * fminf(1.0f, charge));
int charge_damage = std::floor(50.0f + 70.0f * fminf(1.0f, charge)) * 3.0f;
if (damage >= target->m_iHealth())
preferred = hitbox_t::spine_3;
else
@ -1239,34 +1213,6 @@ int BestHitbox(CachedEntity *target)
{
headonly = true;
}
if (IsBacktracking() && !projectile_mode)
{
namespace bt = hacks::shared::backtrack;
good_tick = { -1, -1 };
auto ticks = bt::headPositions[target->m_IDX];
for (int i = 0; i < 66; i++)
{
if (!ticks[i].tickcount)
continue;
if (!bt::ValidTick(ticks[i], target))
continue;
if (*backtrackVischeckAll)
for (int j = 0; j < 18; j++)
{
if (IsEntityVectorVisible(target, ticks[i].hitboxes[j].center))
{
good_tick = { i, target->m_IDX };
break;
}
}
else if (IsEntityVectorVisible(target, ticks[i].hitboxes[0].center))
{
good_tick = { i, target->m_IDX };
break;
}
}
}
// Head only
if (headonly)
{
@ -1275,24 +1221,39 @@ int BestHitbox(CachedEntity *target)
IF_GAME(IsCSS())
return 12;
}
// If the prefered hitbox vis check passes, use it
if (IsBacktracking() && !projectile_mode)
// Backtracking and preferred hitbox
if (IsBacktracking())
{
namespace bt = hacks::shared::backtrack;
if (good_tick.first != -1)
if (IsEntityVectorVisible(target, bt::headPositions[target->m_IDX - 1][good_tick.first].hitboxes[preferred].center))
return preferred;
auto data = g_IBacktrack.getClosestEntTick(target, LOCAL_E->m_vecOrigin(), std::bind(&hacks::tf2::backtrack::Backtrack::defaultTickFilter, &g_IBacktrack, std::placeholders::_1, std::placeholders::_2));
if (data)
{
if (*backtrackVischeckAll)
for (int j = 0; j < 18; j++)
{
if (IsEntityVectorVisible(target, (*data).hitboxes[j].center))
return j;
}
else if (IsEntityVectorVisible(target, (*data).hitboxes[0].center))
return 0;
}
// Nothing found, falling through to further below
}
else if (target->hitboxes.VisibilityCheck(preferred))
return preferred;
// Else attempt to find a hitbox at all
if (IsBacktracking() && !projectile_mode && good_tick.first != -1)
if (IsBacktracking())
{
namespace bt = hacks::shared::backtrack;
for (int i = 0; i < 18; i++)
if (IsEntityVectorVisible(target, bt::headPositions[target->m_IDX][good_tick.first].hitboxes.at(i).center))
return i;
// We already vischecked
if (!*backtrackVischeckAll)
{
auto data = g_IBacktrack.getClosestEntTick(target, LOCAL_E->m_vecOrigin(), std::bind(&hacks::tf2::backtrack::Backtrack::defaultTickFilter, &g_IBacktrack, std::placeholders::_1, std::placeholders::_2));
for (int i = 0; i < 18; i++)
if (IsEntityVectorVisible(target, (*data).hitboxes.at(i).center))
return i;
}
}
else
for (int i = projectile_mode ? 1 : 0; i < target->hitboxes.GetNumHitboxes() && i < 6; i++)
@ -1304,52 +1265,12 @@ int BestHitbox(CachedEntity *target)
break;
case 1:
{ // AUTO-CLOSEST priority, Return closest hitbox to crosshair
int hb = ClosestHitbox(target);
if (IsBacktracking() && !projectile_mode)
{
hb = std::min(hb, 17);
namespace bt = hacks::shared::backtrack;
good_tick = { -1, -1 };
auto ticks = bt::headPositions[target->m_IDX];
for (int i = 0; i < 66; i++)
{
if (!ticks[i].tickcount)
continue;
if (!bt::ValidTick(ticks[i], target))
continue;
if (IsEntityVectorVisible(target, ticks[i].hitboxes[hb].center))
{
good_tick = { i, target->m_IDX };
break;
}
}
}
return hb;
return ClosestHitbox(target);
}
break;
case 2:
{ // STATIC priority, Return a user chosen hitbox
int hb = *hitbox;
if (IsBacktracking() && !projectile_mode)
{
hb = std::min(hb, 17);
namespace bt = hacks::shared::backtrack;
good_tick = { -1, -1 };
auto ticks = bt::headPositions[target->m_IDX];
for (int i = 0; i < 66; i++)
{
if (!ticks[i].tickcount)
continue;
if (!bt::ValidTick(ticks[i], target))
continue;
if (IsEntityVectorVisible(target, ticks[i].hitboxes[hb].center))
{
good_tick = { i, target->m_IDX };
break;
}
}
}
return hb;
return *hitbox;
}
break;
default:
@ -1382,13 +1303,13 @@ int ClosestHitbox(CachedEntity *target)
}
// Function to get predicted visual checks
bool VischeckPredictedEntity(CachedEntity *entity, bool Backtracking)
bool VischeckPredictedEntity(CachedEntity *entity)
{
// Retrieve predicted data
AimbotCalculatedData_s &cd = calculated_data_array[entity->m_IDX];
if (cd.vcheck_tick == tickcount)
return cd.visible;
if (!Backtracking)
if (!shouldBacktrack() || entity->m_Type() != ENTITY_PLAYER)
{
// Update info
cd.vcheck_tick = tickcount;
@ -1404,21 +1325,9 @@ bool VischeckPredictedEntity(CachedEntity *entity, bool Backtracking)
}
else
{
namespace bt = hacks::shared::backtrack;
auto ticks = bt::headPositions[entity->m_IDX];
if (good_tick.first != -1 && good_tick.second == entity->m_IDX && IsEntityVectorVisible(entity, PredictEntity(entity)))
{
cd.vcheck_tick = tickcount;
cd.visible = true;
if (g_pLocalPlayer->weapon_mode != weapon_melee)
{
current_user_cmd->tick_count = ticks[good_tick.first].tickcount;
Vector &angles = CE_VECTOR(entity, netvar.m_angEyeAngles);
float &simtime = CE_FLOAT(entity, netvar.m_flSimulationTime);
angles.y = ticks[good_tick.first].viewangles;
simtime = ticks[good_tick.first].simtime;
}
}
auto data = g_IBacktrack.getClosestEntTick(entity, LOCAL_E->m_vecOrigin(), std::bind(&hacks::tf2::backtrack::Backtrack::defaultTickFilter, &g_IBacktrack, std::placeholders::_1, std::placeholders::_2));
if (data && IsEntityVectorVisible(entity, data->hitboxes.at(cd.hitbox == -1 ? 0 : cd.hitbox).center, MASK_SHOT))
cd.visible = true;
else
cd.visible = false;
}
@ -1606,12 +1515,12 @@ static void DrawText()
}
}
#endif
void rvarCallback(settings::VariableBase<int> &, float after)
void rvarCallback(settings::VariableBase<float> &, float after)
{
force_backtrack_aimbot = after > 200.0f;
force_backtrack_aimbot = after >= 200.0f;
}
static InitRoutine EC([]() {
hacks::shared::backtrack::latency.installChangeCallback(rvarCallback);
g_IBacktrack.latency.installChangeCallback(rvarCallback);
EC::Register(EC::LevelInit, Reset, "INIT_Aimbot", EC::average);
EC::Register(EC::LevelShutdown, Reset, "RESET_Aimbot", EC::average);
EC::Register(EC::CreateMove, CreateMove, "CM_Aimbot", EC::late);

View File

@ -34,7 +34,7 @@ int ClosestDistanceHitbox(CachedEntity *target)
}
return closest;
}
int ClosestDistanceHitbox(hacks::shared::backtrack::BacktrackData btd)
int ClosestDistanceHitbox(hacks::tf2::backtrack::BacktrackData btd)
{
int closest = -1;
float closest_dist = FLT_MAX, dist = 0.0f;
@ -59,29 +59,29 @@ bool angleCheck(CachedEntity *from, CachedEntity *to, std::optional<Vector> targ
{
Vector tarAngle = CE_VECTOR(to, netvar.m_angEyeAngles);
Vector wsc_spy_to_victim;
Vector vecToTarget;
if (target_pos)
wsc_spy_to_victim = *target_pos - from->m_vecOrigin();
vecToTarget = *target_pos - from->m_vecOrigin();
else
wsc_spy_to_victim = to->m_vecOrigin() - from->m_vecOrigin();
wsc_spy_to_victim.z = 0;
wsc_spy_to_victim.NormalizeInPlace();
vecToTarget = to->m_vecOrigin() - from->m_vecOrigin();
vecToTarget.z = 0;
vecToTarget.NormalizeInPlace();
Vector eye_spy;
AngleVectors2(VectorToQAngle(from_angle), &eye_spy);
eye_spy.z = 0;
eye_spy.NormalizeInPlace();
Vector vecOwnerForward;
AngleVectors2(VectorToQAngle(from_angle), &vecOwnerForward);
vecOwnerForward.z = 0;
vecOwnerForward.NormalizeInPlace();
Vector eye_victim;
AngleVectors2(VectorToQAngle(tarAngle), &eye_victim);
eye_victim.z = 0;
eye_victim.NormalizeInPlace();
Vector vecTargetForward;
AngleVectors2(VectorToQAngle(tarAngle), &vecTargetForward);
vecTargetForward.z = 0;
vecTargetForward.NormalizeInPlace();
if (DotProduct(wsc_spy_to_victim, eye_victim) <= 0.0f)
if (DotProduct(vecToTarget, vecTargetForward) <= 0.0f)
return false;
if (DotProduct(wsc_spy_to_victim, eye_spy) <= 0.5f)
if (DotProduct(vecToTarget, vecOwnerForward) <= 0.5f)
return false;
if (DotProduct(eye_spy, eye_victim) <= -0.3f)
if (DotProduct(vecOwnerForward, vecTargetForward) <= -0.3f)
return false;
return true;
}
@ -90,31 +90,43 @@ static bool angleCheck(CachedEntity *target, std::optional<Vector> target_pos, V
{
Vector tarAngle = CE_VECTOR(target, netvar.m_angEyeAngles);
Vector wsc_spy_to_victim;
// Get a vector from owner origin to target origin
Vector vecToTarget;
Vector local_worldspace;
VectorLerp(RAW_ENT(LOCAL_E)->GetCollideable()->OBBMins(), RAW_ENT(LOCAL_E)->GetCollideable()->OBBMaxs(), 0.5f, local_worldspace);
local_worldspace += LOCAL_E->m_vecOrigin();
if (target_pos)
wsc_spy_to_victim = *target_pos - LOCAL_E->m_vecOrigin();
vecToTarget = *target_pos - local_worldspace;
else
wsc_spy_to_victim = target->m_vecOrigin() - LOCAL_E->m_vecOrigin();
wsc_spy_to_victim.z = 0;
wsc_spy_to_victim.NormalizeInPlace();
{
Vector target_worldspace;
VectorLerp(RAW_ENT(target)->GetCollideable()->OBBMins(), RAW_ENT(target)->GetCollideable()->OBBMaxs(), 0.5f, target_worldspace);
target_worldspace += LOCAL_E->m_vecOrigin();
vecToTarget = target_worldspace - local_worldspace;
}
Vector eye_spy;
AngleVectors2(VectorToQAngle(local_angle), &eye_spy);
eye_spy.z = 0;
eye_spy.NormalizeInPlace();
vecToTarget.z = 0;
vecToTarget.NormalizeInPlace();
Vector eye_victim;
AngleVectors2(VectorToQAngle(tarAngle), &eye_victim);
eye_victim.z = 0;
eye_victim.NormalizeInPlace();
// Get owner forward view vector
Vector vecOwnerForward;
AngleVectors2(VectorToQAngle(local_angle), &vecOwnerForward);
vecOwnerForward.z = 0;
vecOwnerForward.NormalizeInPlace();
if (DotProduct(wsc_spy_to_victim, eye_victim) <= 0.0f)
return false;
if (DotProduct(wsc_spy_to_victim, eye_spy) <= 0.5f)
return false;
if (DotProduct(eye_spy, eye_victim) <= -0.3f)
return false;
return true;
// Get target forward view vector
Vector vecTargetForward;
AngleVectors2(VectorToQAngle(tarAngle), &vecTargetForward);
vecTargetForward.z = 0;
vecTargetForward.NormalizeInPlace();
// Make sure owner is behind, facing and aiming at target's back
float flPosVsTargetViewDot = DotProduct(vecToTarget, vecTargetForward); // Behind?
float flPosVsOwnerViewDot = DotProduct(vecToTarget, vecOwnerForward); // Facing?
float flViewAnglesDot = DotProduct(vecTargetForward, vecOwnerForward); // Facestab?
return (flPosVsTargetViewDot > 0.f && flPosVsOwnerViewDot > 0.5 && flViewAnglesDot > -0.3f);
}
static bool doLegitBackstab()
@ -212,119 +224,79 @@ static bool doRageBackstab()
}
return false;
}
// Make accessible to the filter
static bool legit_stab = false;
static Vector newangle_apply;
static bool doBacktrackStab()
bool backtrackFilter(CachedEntity *ent, hacks::tf2::backtrack::BacktrackData tick, std::optional<hacks::tf2::backtrack::BacktrackData> &best_tick)
{
float swingrange = re::C_TFWeaponBaseMelee::GetSwingRange(RAW_ENT(LOCAL_W));
CachedEntity *ent;
if (hacks::shared::backtrack::iBestTarget < 1)
return false;
// Get the best ent decided by backtrack (no reason to do work twice)
ent = ENTITY(hacks::shared::backtrack::iBestTarget);
if (!ent->m_bEnemy() || !player_tools::shouldTarget(ent))
return false;
// Get the ent's backtrack ticks
auto &btd = hacks::shared::backtrack::headPositions[ent->m_IDX];
for (int i = 0; i < 66; i++)
Vector target_worldspace;
VectorLerp(tick.m_vecMins, tick.m_vecMaxs, 0.5f, target_worldspace);
target_worldspace += tick.m_vecOrigin;
Vector distcheck = target_worldspace;
distcheck.z = g_pLocalPlayer->v_Eye.z;
// Angle check
Vector newangle;
if (legit_stab)
newangle = g_pLocalPlayer->v_OrigViewangles;
else
newangle = GetAimAtAngles(g_pLocalPlayer->v_Eye, distcheck);
if (!angleCheck(ent, target_worldspace, newangle) && !canFaceStab(ent))
return false;
Vector min = tick.m_vecMins + tick.m_vecOrigin;
Vector max = tick.m_vecMaxs + tick.m_vecOrigin;
Vector hit;
// Check if we can hit the enemies hitbox
if (hacks::shared::triggerbot::CheckLineBox(min, max, g_pLocalPlayer->v_Eye, GetForwardVector(g_pLocalPlayer->v_Eye, newangle, swingrange * 0.95f), hit))
{
auto &btp = btd[i];
// Check if the backtrack positions are valid
if (!hacks::shared::backtrack::ValidTick(btp, ent))
continue;
Vector distcheck = btp.entorigin;
distcheck.z = g_pLocalPlayer->v_Eye.z;
// dont stab while inside the enemy
if (distcheck.DistTo(g_pLocalPlayer->v_Eye) < 20.0f)
continue;
// Get and calculate an angle to use to backstab the ent
auto hitbox = ClosestDistanceHitbox(btp);
if (hitbox == -1)
continue;
Vector newangle = GetAimAtAngles(g_pLocalPlayer->v_Eye, btp.hitboxes.at(hitbox).center);
if (!angleCheck(ent, btp.entorigin, newangle) && !canFaceStab(ent))
continue;
Vector &min = btp.collidable.min;
Vector &max = btp.collidable.max;
Vector hit;
// Check if we can hit the enemies hitbox
if (hacks::shared::triggerbot::CheckLineBox(min, max, g_pLocalPlayer->v_Eye, GetForwardVector(g_pLocalPlayer->v_Eye, newangle, swingrange * 0.95f), hit))
// Check if this tick is closer
if (!best_tick || (*best_tick).m_vecOrigin.DistTo(LOCAL_E->m_vecOrigin()) > tick.m_vecOrigin.DistTo(LOCAL_E->m_vecOrigin()))
{
current_user_cmd->tick_count = btp.tickcount;
current_user_cmd->viewangles = newangle;
current_user_cmd->buttons |= IN_ATTACK;
g_pLocalPlayer->bUseSilentAngles = true;
*bSendPackets = true;
newangle_apply = newangle;
return true;
}
}
return false;
}
// TODO: Don't duplicate these loops
static bool doLegitBacktrackStab() // lol
static bool doBacktrackStab(bool legit = false)
{
float swingrange = re::C_TFWeaponBaseMelee::GetSwingRange(RAW_ENT(LOCAL_W));
CachedEntity *ent;
if (hacks::shared::backtrack::iBestTarget < 1)
return false;
ent = ENTITY(hacks::shared::backtrack::iBestTarget);
if (!ent->m_bEnemy() || !player_tools::shouldTarget(ent))
return false;
auto &btd = hacks::shared::backtrack::headPositions[ent->m_IDX];
Vector newangle = g_pLocalPlayer->v_OrigViewangles;
std::vector<float> yangles;
float best_scr = FLT_MAX;
hacks::shared::backtrack::BacktrackData *best_tick;
for (int ii = 0; ii < 66; ii++)
CachedEntity *stab_ent = nullptr;
hacks::tf2::backtrack::BacktrackData stab_data;
// Set for our filter
legit_stab = legit;
// Get the Best tick
for (int i = 0; i <= g_IEngine->GetMaxClients(); i++)
{
std::vector<float> yangles_tmp;
auto &i = btd[ii];
CachedEntity *ent = ENTITY(i);
// Targeting checks
if (CE_BAD(ent) || !ent->m_bAlivePlayer() || !ent->m_bEnemy() || !player_tools::shouldTarget(ent))
continue;
Vector distcheck = i.entorigin;
distcheck.z = g_pLocalPlayer->v_Eye.z;
if (distcheck.DistTo(g_pLocalPlayer->v_Eye) < best_scr && distcheck.DistTo(g_pLocalPlayer->v_Eye) > 20.0f)
// Get the best tick for that ent
auto tick_data = g_IBacktrack.getBestTick(ent, backtrackFilter);
// We found something matching the criterias, break out
if (tick_data)
{
if (!hacks::shared::backtrack::ValidTick(i, ent))
continue;
if (!angleCheck(ent, i.entorigin, newangle) && !canFaceStab(ent))
continue;
Vector &min = i.collidable.min;
Vector &max = i.collidable.max;
// Get the min and max for the hitbox
Vector minz(fminf(min.x, max.x), fminf(min.y, max.y), fminf(min.z, max.z));
Vector maxz(fmaxf(min.x, max.x), fmaxf(min.y, max.y), fmaxf(min.z, max.z));
// Shrink the hitbox here
Vector size = maxz - minz;
Vector smod = { size.x * 0.20f, size.y * 0.20f, 0 };
// Save the changes to the vectors
minz += smod;
maxz -= smod;
maxz.z += 20.0f;
Vector hit;
if (hacks::shared::triggerbot::CheckLineBox(minz, maxz, g_pLocalPlayer->v_Eye, GetForwardVector(g_pLocalPlayer->v_Eye, newangle, swingrange), hit))
yangles_tmp.push_back(newangle.y);
if (!yangles_tmp.empty())
{
yangles.clear();
best_scr = distcheck.DistTo(g_pLocalPlayer->v_Eye);
best_tick = &i;
yangles = yangles_tmp;
}
stab_data = *tick_data;
stab_ent = ent;
break;
}
}
if (!yangles.empty() && best_tick)
// We found a good ent
if (stab_ent)
{
newangle.y = yangles.at(std::floor((float) yangles.size() / 2));
current_user_cmd->tick_count = best_tick->tickcount;
current_user_cmd->viewangles = newangle;
g_IBacktrack.SetBacktrackData(stab_ent, stab_data);
current_user_cmd->buttons |= IN_ATTACK;
current_user_cmd->viewangles = newangle_apply;
g_pLocalPlayer->bUseSilentAngles = true;
*bSendPackets = true;
return true;
@ -349,11 +321,11 @@ void CreateMove()
doRageBackstab();
break;
case 2:
if (hacks::shared::backtrack::isBacktrackEnabled)
if (g_IBacktrack.isBacktrackEnabled)
{
if (*hacks::shared::backtrack::latency <= 190 && doRageBackstab())
if (*g_IBacktrack.latency <= 190 && doRageBackstab())
break;
doBacktrackStab();
doBacktrackStab(false);
}
else
{
@ -361,11 +333,11 @@ void CreateMove()
}
break;
case 3:
if (hacks::shared::backtrack::isBacktrackEnabled)
if (g_IBacktrack.isBacktrackEnabled)
{
if (*hacks::shared::backtrack::latency <= 190 && doLegitBackstab())
if (*g_IBacktrack.latency <= 190 && doLegitBackstab())
break;
doLegitBacktrackStab();
doBacktrackStab(true);
}
else
{

View File

@ -1,317 +1,75 @@
/*
* Backtrack.cpp
* Remade on May the 3rd 2020
* Author: BenCat07
*
* Created on: May 15, 2018
* Author: bencat07
*/
#include "Backtrack.hpp"
#include "common.hpp"
#include "hacks/Aimbot.hpp"
#include "hacks/Backtrack.hpp"
#include <boost/circular_buffer.hpp>
#include <settings/Bool.hpp>
#include "PlayerTools.hpp"
#include <hacks/Backtrack.hpp>
#include "MiscTemporary.hpp"
namespace hacks::shared::backtrack
namespace hacks::tf2::backtrack
{
static settings::Boolean draw_bt{ "backtrack.draw", "false" };
static settings::Boolean draw_skeleton{ "backtrack.draw-skeleton", "false" };
static settings::Float mindistance{ "backtrack.min-distance", "60" };
static settings::Int slots{ "backtrack.slots", "0" };
settings::Boolean enable{ "backtrack.enable", "false" };
settings::Boolean backtrack_chams_glow{ "backtrack.chams_glow", "true" };
settings::Int latency{ "backtrack.latency", "0" };
settings::Boolean enable_latency_rampup{ "backtrack.latency.rampup", "true" };
void EmptyBacktrackData(BacktrackData &i);
std::pair<int, int> getBestEntBestTick();
bool shouldBacktrack();
BacktrackData headPositions[PLAYER_ARRAY_SIZE][66]{};
int lastincomingsequencenumber = 0;
bool isBacktrackEnabled = false;
bool Vischeck_Success = false;
circular_buf sequences{ 2048 };
void UpdateIncomingSequences()
{
INetChannel *ch = (INetChannel *) g_IEngine->GetNetChannelInfo();
if (ch)
{
int m_nInSequenceNr = ch->m_nInSequenceNr;
int instate = ch->m_nInReliableState;
if (m_nInSequenceNr > lastincomingsequencenumber)
{
lastincomingsequencenumber = m_nInSequenceNr;
sequences.push_front(CIncomingSequence(instate, m_nInSequenceNr, g_GlobalVars->realtime));
}
if (sequences.size() > 2048)
sequences.pop_back();
}
}
void AddLatencyToNetchan(INetChannel *ch)
// Apply Backtrack
void Backtrack::ApplyBacktrack()
{
if (!isBacktrackEnabled)
return;
for (auto &seq : sequences)
if (bt_ent)
{
if (g_GlobalVars->realtime - seq.curtime > getLatency() / 1000.0f)
{
ch->m_nInReliableState = seq.inreliablestate;
ch->m_nInSequenceNr = seq.sequencenr;
break;
}
current_user_cmd->tick_count = (*bt_data).tickcount;
CE_FLOAT(bt_ent, netvar.m_angEyeAngles) = (*bt_data).m_vecAngles.x;
CE_FLOAT(bt_ent, netvar.m_angEyeAngles + 4) = (*bt_data).m_vecAngles.y;
CE_FLOAT(bt_ent, netvar.m_flSimulationTime) = (*bt_data).m_flSimulationTime;
}
}
void Init()
{
for (int i = 0; i < PLAYER_ARRAY_SIZE; i++)
for (int j = 0; j < 66; j++)
headPositions[i][j] = {};
BestTick = iBestTarget = -1;
// Update tick to apply
void Backtrack::SetBacktrackData(CachedEntity *ent, BacktrackData tick)
{
bt_ent = ent;
bt_data = tick;
}
int BestTick = -1;
int iBestTarget = -1;
bool istickvalid[PLAYER_ARRAY_SIZE][66]{};
bool istickinvalid[PLAYER_ARRAY_SIZE][66]{};
static float latency_rampup = 0.0f;
static void Run()
// Get Best tick for Backtrack (crosshair/fov based)
bool Backtrack::getBestInternalTick(CachedEntity *, BacktrackData &data, std::optional<BacktrackData> &best_tick)
{
if (!shouldBacktrack())
{
isBacktrackEnabled = false;
latency_rampup = 0;
return;
}
// Best Score
float bestScore = FLT_MAX;
// Limit to 2 ticks
latency_rampup += 1.0f / 66.0f;
latency_rampup = std::min(latency_rampup, 1.0f);
// Are we using a melee weapon?
bool is_melee = false;
if (g_pLocalPlayer->weapon_mode == weapon_melee)
is_melee = true;
UpdateIncomingSequences();
isBacktrackEnabled = true;
// Get the FOV of the best tick if available
if (best_tick)
bestScore = GetFov(g_pLocalPlayer->v_OrigViewangles, g_pLocalPlayer->v_Eye, (*best_tick).hitboxes.at(head).center);
if (CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer() || CE_BAD(LOCAL_W))
return;
if (g_Settings.bInvalid)
return;
if (!current_user_cmd)
return;
for (auto &a : istickvalid)
for (auto &b : a)
b = false;
for (auto &a : istickinvalid)
for (auto &b : a)
b = false;
CUserCmd *cmd = current_user_cmd;
float bestFov = 99999;
float prev_distance = 9999;
std::pair<int, int> bestEntBestTick = getBestEntBestTick();
BestTick = bestEntBestTick.second;
iBestTarget = bestEntBestTick.first;
// Fill backtrack data (stored in headPositions)
{
PROF_SECTION(cm_bt_ent_loop)
for (int i = 1; i <= g_IEngine->GetMaxClients(); i++)
{
CachedEntity *pEntity = ENTITY(i);
if (CE_BAD(pEntity) || !pEntity->m_bAlivePlayer())
{
for (BacktrackData &btd : headPositions[i])
btd.simtime = FLT_MAX;
continue;
}
if (!pEntity->m_bEnemy())
continue;
if (pEntity->m_Type() != ENTITY_PLAYER)
continue;
if (!pEntity->hitboxes.GetHitbox(0))
continue;
if (HasCondition<TFCond_HalloweenGhostMode>(pEntity))
continue;
if (!*bSendPackets)
headPositions[i][cmd->command_number % getTicks()] = {};
else
{
auto &hbd = headPositions[i][cmd->command_number % getTicks()];
float _viewangles = CE_VECTOR(pEntity, netvar.m_angEyeAngles).y;
hbd.viewangles = (_viewangles > 180) ? _viewangles - 360 : _viewangles;
hbd.simtime = CE_FLOAT(pEntity, netvar.m_flSimulationTime);
hbd.entorigin = pEntity->InternalEntity()->GetAbsOrigin();
hbd.tickcount = cmd->tick_count;
if (nolerp)
{
static const ConVar *pUpdateRate = g_pCVar->FindVar("cl_updaterate");
if (!pUpdateRate)
pUpdateRate = g_pCVar->FindVar("cl_updaterate");
else
{
float interp = MAX(cl_interp->GetFloat(), cl_interp_ratio->GetFloat() / pUpdateRate->GetFloat());
hbd.tickcount += TIME_TO_TICKS(interp);
hbd.simtime += interp;
}
}
pEntity->hitboxes.InvalidateCache();
for (size_t i = 0; i < 18; i++)
{
hbd.hitboxes[i].center = pEntity->hitboxes.GetHitbox(i)->center;
hbd.hitboxes[i].min = pEntity->hitboxes.GetHitbox(i)->min;
hbd.hitboxes[i].max = pEntity->hitboxes.GetHitbox(i)->max;
}
hbd.collidable.min = RAW_ENT(pEntity)->GetCollideable()->OBBMins() + hbd.entorigin;
hbd.collidable.max = RAW_ENT(pEntity)->GetCollideable()->OBBMaxs() + hbd.entorigin;
hbd.collidable.center = (hbd.collidable.min + hbd.collidable.max) / 2;
memcpy((void *) hbd.bones, (void *) pEntity->hitboxes.bones, sizeof(matrix3x4_t) * 128);
}
}
}
if (iBestTarget != -1 && CanShoot())
{
CachedEntity *tar = ENTITY(iBestTarget);
if (CE_GOOD(tar))
{
if (cmd->buttons & IN_ATTACK)
{
// ok just in case
if (CE_BAD(tar))
return;
auto i = headPositions[iBestTarget][BestTick];
cmd->tick_count = i.tickcount;
Vector &angles = NET_VECTOR(RAW_ENT(tar), netvar.m_angEyeAngles);
float &simtime = NET_FLOAT(RAW_ENT(tar), netvar.m_flSimulationTime);
angles.y = i.viewangles;
simtime = i.simtime;
}
}
}
}
CatCommand print_bones("debug_print_bones", "debug print bone id + name", []() {
if (CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer())
return;
// Get player model
const model_t *model = RAW_ENT(LOCAL_E)->GetModel();
if (not model)
return;
// Get Studio models (for bones)
studiohdr_t *hdr = g_IModelInfo->GetStudiomodel(model);
if (not hdr)
return;
// Get the name of the bones
for (int i = 0; i < hdr->numbones; i++)
logging::Info(format(std::string(hdr->pBone(i)->pszName()), " ", i).c_str());
});
static std::vector<int> bones_leg_r = { 17, 16, 15 };
static std::vector<int> bones_leg_l = { 14, 13, 12 };
static std::vector<int> bones_bottom = { 15, 1, 12 };
static std::vector<int> bones_spine = { 1, 2, 3, 4, 5, 0 };
static std::vector<int> bones_arm_r = { 9, 10, 11 };
static std::vector<int> bones_arm_l = { 6, 7, 8 };
static std::vector<int> bones_up = { 9, 5, 6 };
#if ENABLE_VISUALS
void DrawBone(std::vector<int> hitbox, std::array<hitboxData, 18> hitboxes)
{
for (int i = 0; i < hitbox.size() - 1; i++)
{
Vector bone1 = hitboxes.at(hitbox.at(i)).center;
Vector bone2 = hitboxes.at(hitbox.at(i + 1)).center;
Vector draw_position1, draw_position2;
if (draw::WorldToScreen(bone1, draw_position1) && draw::WorldToScreen(bone2, draw_position2))
draw::Line(draw_position1.x, draw_position1.y, draw_position2.x - draw_position1.x, draw_position2.y - draw_position1.y, colors::white, 1.0f);
}
}
#endif
static void Draw()
{
#if ENABLE_VISUALS
if (!isBacktrackEnabled)
return;
// :b:ones for non drawable ents
if (draw_skeleton)
for (int i = 0; i <= g_IEngine->GetMaxClients(); i++)
{
CachedEntity *ent = ENTITY(i);
if (CE_BAD(ent) || !ent->m_bAlivePlayer() || i == g_IEngine->GetLocalPlayer())
continue;
auto head_pos = headPositions[i];
// Usable vector instead of ptr to c style array, also used to filter valid and invalid ticks
std::vector<BacktrackData> usable;
for (int i = 0; i < 66; i++)
{
if (ValidTick(head_pos[i], ent))
usable.push_back(head_pos[i]);
}
// Crash much?
if (usable.size())
{
DrawBone(bones_leg_l, usable[0].hitboxes);
DrawBone(bones_leg_r, usable[0].hitboxes);
DrawBone(bones_bottom, usable[0].hitboxes);
DrawBone(bones_spine, usable[0].hitboxes);
DrawBone(bones_arm_l, usable[0].hitboxes);
DrawBone(bones_arm_r, usable[0].hitboxes);
DrawBone(bones_up, usable[0].hitboxes);
}
}
if (!draw_bt)
return;
for (int i = 0; i <= g_IEngine->GetMaxClients(); i++)
{
CachedEntity *ent = ENTITY(i);
if (CE_BAD(ent))
continue;
for (int j = 0; j < getTicks(); j++)
{
if (!ValidTick(headPositions[i][j], ent))
continue;
auto hbpos = headPositions[i][j].hitboxes.at(head).center;
auto min = headPositions[i][j].hitboxes.at(head).min;
auto max = headPositions[i][j].hitboxes.at(head).max;
if (!hbpos.x && !hbpos.y && !hbpos.z)
continue;
Vector out;
if (draw::WorldToScreen(hbpos, out))
{
float size = 0.0f;
if (abs(max.x - min.x) > abs(max.y - min.y))
size = abs(max.x - min.x);
else
size = abs(max.y - min.y);
if (i == iBestTarget && j == BestTick)
draw::Rectangle(out.x, out.y, size / 2, size / 2, colors::red);
else
draw::Rectangle(out.x, out.y, size / 4, size / 4, colors::green);
}
}
}
#endif
}
// Internal only, use isBacktrackEnabled var instead
bool shouldBacktrack()
{
if (!*enable)
float FOVDistance = GetFov(g_pLocalPlayer->v_OrigViewangles, g_pLocalPlayer->v_Eye, data.hitboxes.at(head).center);
if (FOVDistance >= bestScore)
return false;
CachedEntity *wep = g_pLocalPlayer->weapon();
// Found new best fov, now vischeck
if (FOVDistance < bestScore)
{
// Vischeck check
if (!is_melee && !IsVectorVisible(g_pLocalPlayer->v_Eye, data.hitboxes.at(head).center, false))
return false;
return true;
}
// Should never be called but gcc likes to complain anyways
return false;
}
// Is backtrack enabled?
bool Backtrack::isEnabled()
{
if (!*enabled)
return false;
CachedEntity *wep = LOCAL_W;
if (CE_BAD(wep))
return false;
if (*slots == 0)
if (*bt_slots == 0)
return true;
int slot = re::C_BaseCombatWeapon::GetSlot(RAW_ENT(wep));
switch ((int) slots)
switch ((int) bt_slots)
{
case 1:
if (slot == 0)
@ -340,153 +98,386 @@ bool shouldBacktrack()
}
return false;
}
float getRealLatency()
// Main Tracking logic
void Backtrack::CreateMove()
{
auto ch = (INetChannel *) g_IEngine->GetNetChannelInfo();
if (!ch)
return 0.0f;
float Latency = ch->GetLatency(FLOW_OUTGOING);
static auto cl_updaterate = g_ICvar->FindVar("cl_updaterate");
if (cl_updaterate && cl_updaterate->GetFloat() > 0.001f)
// Update enabled status
isBacktrackEnabled = isEnabled();
if (!isBacktrackEnabled)
{
Latency += -0.5f / cl_updaterate->GetFloat();
if (nolerp)
latency_rampup = 0.0f;
return;
}
if (CE_BAD(LOCAL_E) || CE_BAD(LOCAL_W) || !LOCAL_E->m_bAlivePlayer())
{
latency_rampup = 0.0f;
return;
}
// Make Latency not instantly spike
latency_rampup += 1.0f / 66.0f;
latency_rampup = std::min(latency_rampup, 1.0f);
updateDatagram();
// Clear data
bt_ent = nullptr;
bt_data.reset();
current_tickcount = current_user_cmd->tick_count;
// the "Base" Backtracking
std::optional<BacktrackData> best_data;
CachedEntity *best_ent = nullptr;
for (int i = 0; i <= g_IEngine->GetMaxClients(); i++)
{
if (i == g_pLocalPlayer->entity_idx)
{
float interp = MAX(cl_interp->GetFloat(), cl_interp_ratio->GetFloat() / cl_updaterate->GetFloat());
Latency += interp;
resetData(i);
continue;
}
CachedEntity *ent = ENTITY(i);
if (CE_BAD(ent) || !ent->m_bAlivePlayer() || ent->m_Type() != ENTITY_PLAYER || !ent->m_bEnemy())
{
resetData(i);
continue;
}
if (!ent->hitboxes.GetHitbox(0))
{
resetData(i);
continue;
}
auto &backtrack_ent = backtrack_data.at(i);
// Have no data, create it
if (!backtrack_ent)
backtrack_ent.reset(new std::array<BacktrackData, 67>);
int current_index = current_user_cmd->tick_count % getTicks();
// Our current tick
auto &current_tick = (*backtrack_ent).at(current_index);
// Previous tick
int last_index = current_index - 1;
if (last_index < 0)
last_index = getTicks() - 1;
auto &previous_tick = (*backtrack_ent).at(last_index);
// Update basics
current_tick.tickcount = current_user_cmd->tick_count;
current_tick.m_vecAngles.x = CE_FLOAT(ent, netvar.m_angEyeAngles);
current_tick.m_vecAngles.y = CE_FLOAT(ent, netvar.m_angEyeAngles + 4);
current_tick.m_vecOrigin = ent->m_vecOrigin();
current_tick.m_vecMins = RAW_ENT(ent)->GetCollideable()->OBBMins();
current_tick.m_vecMaxs = RAW_ENT(ent)->GetCollideable()->OBBMaxs();
current_tick.m_flSimulationTime = CE_FLOAT(ent, netvar.m_flSimulationTime);
// Update hitboxes
ent->hitboxes.InvalidateCache();
for (size_t i = 0; i < 18; i++)
{
current_tick.hitboxes[i].center = ent->hitboxes.GetHitbox(i)->center;
current_tick.hitboxes[i].min = ent->hitboxes.GetHitbox(i)->min;
current_tick.hitboxes[i].max = ent->hitboxes.GetHitbox(i)->max;
}
// Copy bones (for chams/glow)
memcpy((void *) current_tick.bones, (void *) ent->hitboxes.bones, sizeof(matrix3x4_t) * 128);
// Check if tick updated or not (fakelag)
current_tick.has_updated = !previous_tick.m_flSimulationTime || previous_tick.m_flSimulationTime != current_tick.m_flSimulationTime;
// Get best tick for this ent
std::optional<BacktrackData> data = getBestTick(ent, std::bind(&Backtrack::getBestInternalTick, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
// Check if actually the best tick we have in total
if (data && (!best_data || getBestInternalTick(ent, *data, best_data)))
{
best_data = data;
best_ent = ent;
}
}
else if (!cl_updaterate)
cl_updaterate = g_ICvar->FindVar("cl_updaterate");
return MAX(0.0f, Latency) * 1000.f;
if (best_data && best_ent)
SetBacktrackData(best_ent, *best_data);
}
float getLatency()
void Backtrack::CreateMoveLate()
{
auto ch = (INetChannel *) g_IEngine->GetNetChannelInfo();
if (!ch)
return 0;
float Latency = *latency;
Latency = std::min(Latency, 800.0f);
Latency -= getRealLatency();
Latency = std::max(Latency, 0.0f);
if (enable_latency_rampup)
Latency = Latency * latency_rampup;
Latency = ROUND_TO_TICKS(Latency / 1000.0f) * 1000.0f;
return Latency;
ApplyBacktrack();
}
int getTicks()
#if ENABLE_VISUALS
// Drawing
void Backtrack::Draw()
{
// Get Latency in seconds
float latency = getLatency() / 1000.0f;
// Latency in ticks
int ticks = TIME_TO_TICKS(latency);
// We can backtrack 200ms into the future of the latency
ticks += TIME_TO_TICKS(0.2f);
// Clamp between 0.2s and 1s in ticks (12-66)
return clamp(ticks, TIME_TO_TICKS(0.2f), TIME_TO_TICKS(1.0f));
if (!isBacktrackEnabled || !draw)
return;
if (CE_BAD(LOCAL_E))
return;
for (int i = 0; i <= g_IEngine->GetMaxClients(); i++)
{
auto data = getGoodTicks(i);
if (data.empty())
continue;
for (auto &tick : data)
{
auto hbpos = tick.hitboxes.at(head).center;
auto min = tick.hitboxes.at(head).min;
auto max = tick.hitboxes.at(head).max;
if (!hbpos.x && !hbpos.y && !hbpos.z)
continue;
Vector out;
if (draw::WorldToScreen(hbpos, out))
{
float size = 0.0f;
if (abs(max.x - min.x) > abs(max.y - min.y))
size = abs(max.x - min.x);
else
size = abs(max.y - min.y);
rgba_t draw_color = colors::green;
// Found our target tick
if (bt_ent && tick.tickcount == (*bt_data).tickcount && i == bt_ent->m_IDX)
draw_color = colors::red;
draw::Rectangle(out.x, out.y, size / 4, size / 4, draw_color);
}
}
}
}
#endif
// Reset things
void Backtrack::LevelShutdown()
{
lastincomingsequence = 0;
sequences.clear();
for (int i = 0; i < PLAYER_ARRAY_SIZE; i++)
resetData(i);
}
bool ValidTick(BacktrackData &i, CachedEntity *ent)
// Change Datagram data
void Backtrack::adjustPing(INetChannel *ch)
{
if (!(fabsf(NET_FLOAT(RAW_ENT(ent), netvar.m_flSimulationTime) * 1000.0f - getLatency() - i.simtime * 1000.0f) < ROUND_TO_TICKS(200.0f)))
if (!isBacktrackEnabled)
return;
for (auto &seq : sequences)
{
if (g_GlobalVars->realtime - seq.curtime >= getLatency() / 1000.0f)
{
ch->m_nInReliableState = seq.inreliablestate;
ch->m_nInSequenceNr = seq.sequencenr;
break;
}
}
}
// Latency to add for backtrack
float Backtrack::getLatency()
{
INetChannel *ch = (INetChannel *) g_IEngine->GetNetChannelInfo();
// Track what actual latency we have
float real_latency = 0.0f;
// If we have a netchannel (just in case) set real latency to it
if (ch)
real_latency = ch->GetLatency(FLOW_OUTGOING) * 1000.0f;
// Fix the latency
float backtrack_latency = *latency - real_latency;
// Clamp and apply rampup
backtrack_latency = latency_rampup * std::clamp(backtrack_latency, 0.0f, std::max(800.0f - real_latency, 0.0f));
return backtrack_latency;
}
// Update our sequences
void Backtrack::updateDatagram()
{
INetChannel *ch = (INetChannel *) g_IEngine->GetNetChannelInfo();
if (ch)
{
int m_nInSequenceNr = ch->m_nInSequenceNr;
int instate = ch->m_nInReliableState;
if (m_nInSequenceNr > lastincomingsequence)
{
lastincomingsequence = m_nInSequenceNr;
sequences.insert(sequences.begin(), CIncomingSequence(instate, m_nInSequenceNr, g_GlobalVars->realtime));
}
if (sequences.size() > 2048)
sequences.pop_back();
}
}
// Get How many ticks we should Store and use
int Backtrack::getTicks()
{
float max_lat = getLatency() + 200.0f;
// Clamp
max_lat = std::min(1000.0f, max_lat);
// Get difference
int ticks = TIME_TO_TICKS(max_lat / 1000.0f);
return ticks;
};
void Backtrack::resetData(int entidx)
{
// Clear everything
backtrack_data[entidx].reset();
}
bool Backtrack::isGoodTick(BacktrackData &tick)
{
// This tick hasn't updated since the last one, Entity might be dropping packets
if (!tick.has_updated)
return false;
// How big a difference is between the ping we fake and the tick passed?
int delta_ticks = current_tickcount - TIME_TO_TICKS(getLatency() / 1000.0f) - tick.tickcount;
// Difference may not be greater than 200ms, shave off a few ticks just to be safe.
if (fabsf(TICKS_TO_TIME(delta_ticks)) <= 0.2f - TICKS_TO_TIME(3))
return true;
return false;
}
// Read only vector of good ticks
std::vector<BacktrackData> Backtrack::getGoodTicks(int entidx)
{
std::vector<BacktrackData> to_return;
// Invalid
if (!backtrack_data.at(entidx))
return to_return;
// Check all ticks
for (auto &tick : *backtrack_data.at(entidx))
if (isGoodTick(tick))
to_return.push_back(tick);
// Sort so that oldest ticks come first
std::sort(to_return.begin(), to_return.end(), [](BacktrackData &a, BacktrackData &b) { return a.tickcount < b.tickcount; });
return to_return;
}
// This function is so other files can Easily get the best tick matching their criteria
std::optional<BacktrackData> Backtrack::getBestTick(CachedEntity *ent, std::function<bool(CachedEntity *ent, BacktrackData &data, std::optional<BacktrackData> &best_tick)> callback)
{
std::optional<BacktrackData> best_tick;
// No data recorded
if (!backtrack_data.at(ent->m_IDX))
return best_tick;
// Let the callback do the lifting
for (auto &tick : getGoodTicks(ent->m_IDX))
// Call the callback, and if we have a new best tick assign it
if (callback(ent, tick, best_tick))
best_tick = tick;
// Return best result
return best_tick;
}
// Default filter method. Checks for vischeck on Hitscan weapons.
bool Backtrack::defaultTickFilter(CachedEntity *ent, BacktrackData tick)
{
// Not hitscan, no vischeck needed
if (g_pLocalPlayer->weapon_mode != weapon_hitscan)
return true;
// Return visibility
return IsEntityVectorVisible(ent, tick.hitboxes.at(head).center, MASK_SHOT);
}
bool Backtrack::defaultEntFilter(CachedEntity *ent)
{
// Dormant
if (CE_BAD(ent))
return false;
// Friend check
if (playerlist::IsFriend(ent))
return false;
return true;
}
void EmptyBacktrackData(BacktrackData &i)
// Get Closest tick of a specific entity
std::optional<BacktrackData> Backtrack::getClosestEntTick(CachedEntity *ent, Vector vec, std::function<bool(CachedEntity *, BacktrackData)> tick_filter)
{
i = {};
}
std::optional<BacktrackData> return_value;
// No entry
if (!backtrack_data.at(ent->m_IDX))
return return_value;
// This func is internal only
std::pair<int, int> getBestEntBestTick()
{
int bestEnt = -1;
int bestTick = -1;
bool vischeck_priority = false;
Vischeck_Success = false;
if (GetWeaponMode() == weapon_melee)
float distance = FLT_MAX;
// Go through all Good ticks
for (auto &tick : getGoodTicks(ent->m_IDX))
{
float bestDist = 9999.0f;
for (int i = 0; i <= g_IEngine->GetMaxClients(); i++)
// Found Closer tick
if (tick.m_vecOrigin.DistTo(vec) < distance)
{
CachedEntity *tar = ENTITY(i);
if (CE_GOOD(tar))
// Does the tick pass the filter
if (tick_filter(ent, tick))
{
if (tar != LOCAL_E && tar->m_bEnemy())
{
for (int j = 0; j < getTicks(); j++)
{
if (ValidTick(headPositions[i][j], ENTITY(i)))
{
float dist = headPositions[i][j].hitboxes.at(spine_3).center.DistTo(g_pLocalPlayer->v_Eye);
if (dist < bestDist)
{
bestEnt = i;
bestTick = j;
bestDist = dist;
Vischeck_Success = true;
vischeck_priority = true;
}
}
}
}
return_value = tick;
distance = tick.m_vecOrigin.DistTo(vec);
}
}
}
else
return return_value;
}
// Get Closest tick of any (enemy) entity, Second Parameter is to allow custom filters for entity criteria, third for ticks. We provide defaults for vischecks + melee for the second one
std::optional<std::pair<CachedEntity *, BacktrackData>> Backtrack::getClosestTick(Vector vec, std::function<bool(CachedEntity *)> ent_filter, std::function<bool(CachedEntity *, BacktrackData)> tick_filter)
{
float distance = FLT_MAX;
CachedEntity *best_ent = nullptr;
BacktrackData best_data;
std::optional<std::pair<CachedEntity *, BacktrackData>> return_val;
for (int i = 0; i <= g_IEngine->GetMaxClients(); i++)
{
float bestFov = 180.0f;
for (int i = 0; i <= g_IEngine->GetMaxClients(); i++)
CachedEntity *ent = ENTITY(i);
// These checks are always present
if (CE_INVALID(ent) || !ent->m_bAlivePlayer() || !ent->m_bEnemy())
continue;
// true = passes check
if (!ent_filter(ent))
continue;
auto closest_entdata = getClosestEntTick(ent, vec, tick_filter);
// Closer than the stuff we have
if (closest_entdata && (*closest_entdata).m_vecOrigin.DistTo(vec) <= distance)
{
CachedEntity *tar = ENTITY(i);
if (CE_GOOD(tar))
{
if (tar != LOCAL_E && tar->m_bEnemy())
{
for (int j = 0; j < getTicks(); j++)
{
if (ValidTick(headPositions[i][j], tar))
{
float FOVDistance = GetFov(g_pLocalPlayer->v_OrigViewangles, g_pLocalPlayer->v_Eye, headPositions[i][j].hitboxes.at(head).center);
if (FOVDistance > bestFov && vischeck_priority)
continue;
bool Vischeck_suceeded = IsVectorVisible(g_pLocalPlayer->v_Eye, headPositions[i][j].hitboxes.at(0).center, true);
if (FOVDistance < bestFov || (Vischeck_suceeded && !vischeck_priority))
{
bestEnt = i;
bestTick = j;
bestFov = FOVDistance;
if (Vischeck_suceeded)
{
Vischeck_Success = true;
vischeck_priority = true;
}
}
}
}
}
}
distance = (*closest_entdata).m_vecOrigin.DistTo(vec);
best_data = *closest_entdata;
best_ent = ent;
}
}
return std::make_pair(bestEnt, bestTick);
if (best_ent)
return_val = std::pair<CachedEntity *, BacktrackData>(best_ent, best_data);
return return_val;
}
static InitRoutine EC([]() {
EC::Register(EC::LevelInit, Init, "INIT_Backtrack", EC::average);
EC::Register(EC::CreateMove, Run, "CM_Backtrack", EC::early);
static InitRoutine init([]() {
EC::Register(EC::CreateMove, std::bind(&Backtrack::CreateMove, &g_IBacktrack), "backtrack_cm", EC::early);
EC::Register(EC::CreateMove, std::bind(&Backtrack::CreateMoveLate, &g_IBacktrack), "backtrack_cmlate", EC::very_late);
#if ENABLE_VISUALS
EC::Register(EC::Draw, Draw, "DRAW_Backtrack", EC::average);
EC::Register(EC::Draw, std::bind(&Backtrack::Draw, &g_IBacktrack), "backtrack_draw");
#endif
EC::Register(EC::LevelShutdown, std::bind(&Backtrack::LevelShutdown, &g_IBacktrack), "backtrack_levelshutdown");
});
static CatCommand debug_flowout("debug_flowout", "test", []() {
auto ch = (INetChannel *) g_IEngine->GetNetChannelInfo();
logging::Info("Out Avg: %f In Avg: %f Out current: %f In Current: %f", 1000.0f * ch->GetAvgLatency(FLOW_OUTGOING), 1000.0f * ch->GetAvgLatency(FLOW_INCOMING), 1000.0f * ch->GetLatency(FLOW_OUTGOING), 1000.0f * ch->GetLatency(FLOW_INCOMING));
});
static CatCommand debug_richpresence("debug_presence", "Debug stuff", []() {
g_ISteamFriends->SetRichPresence("steam_display", "#TF_RichPresence_State_PlayingGeneric");
g_ISteamFriends->SetRichPresence("currentmap", "Cathooking");
});
} // namespace hacks::shared::backtrack
} // namespace hacks::tf2::backtrack
// Global interface
hacks::tf2::backtrack::Backtrack g_IBacktrack;

View File

@ -54,8 +54,17 @@ std::pair<CachedEntity *, Vector> FindBestEnt(bool teammate, bool Predict, bool
if (zcheck && (ent->m_vecOrigin().z - LOCAL_E->m_vecOrigin().z) > 200.0f)
continue;
float scr = ent->m_flDistance();
if (g_IBacktrack.isBacktrackEnabled && demoknight_mode)
{
auto data = g_IBacktrack.getClosestEntTick(ent, LOCAL_E->m_vecOrigin(), std::bind(&hacks::tf2::backtrack::Backtrack::defaultTickFilter, &g_IBacktrack, std::placeholders::_1, std::placeholders::_2));
// No entity
if (!data)
scr = FLT_MAX;
else
scr = (*data).m_vecOrigin.DistTo(LOCAL_E->m_vecOrigin());
}
// Demoknight
if (fov_check)
if (demoknight_mode)
{
if (scr >= range)
continue;
@ -100,8 +109,17 @@ std::pair<CachedEntity *, Vector> FindBestEnt(bool teammate, bool Predict, bool
if (zcheck && (ent->m_vecOrigin().z - LOCAL_E->m_vecOrigin().z) > 200.0f)
continue;
float scr = ent->m_flDistance();
if (g_IBacktrack.isBacktrackEnabled && demoknight_mode)
{
auto data = g_IBacktrack.getClosestEntTick(ent, LOCAL_E->m_vecOrigin(), std::bind(&hacks::tf2::backtrack::Backtrack::defaultTickFilter, &g_IBacktrack, std::placeholders::_1, std::placeholders::_2));
// No entity
if (!data)
scr = FLT_MAX;
else
scr = (*data).m_vecOrigin.DistTo(LOCAL_E->m_vecOrigin());
}
// Demoknight
if (fov_check)
if (demoknight_mode)
{
if (scr >= range)
continue;

View File

@ -38,57 +38,50 @@ float target_time = 0.0f;
int last_hb_traced = 0;
Vector forward;
void DoBacktrack()
// Filters for backtrack
bool tick_filter(CachedEntity *entity, hacks::tf2::backtrack::BacktrackData tick)
{
namespace bt = hacks::shared::backtrack;
// Check if it intersects any hitbox
int num_hitboxes = 18;
CachedEntity *tar = (bt::iBestTarget != -1) ? ENTITY(bt::iBestTarget) : nullptr;
if (CE_BAD(tar))
return;
if (bt::BestTick == -1)
return;
if (!IsTargetStateGood(tar, true))
return;
auto &tick = bt::headPositions[bt::iBestTarget][bt::BestTick];
if (!ValidTick(tick, tar))
return;
auto min = tick.hitboxes.at(head).min;
auto max = tick.hitboxes.at(head).max;
if (!min.x && !max.x)
return;
// Get the min and max for the hitbox
Vector minz(fminf(min.x, max.x), fminf(min.y, max.y), fminf(min.z, max.z));
Vector maxz(fmaxf(min.x, max.x), fmaxf(min.y, max.y), fmaxf(min.z, max.z));
// Shrink the hitbox here
Vector size = maxz - minz;
Vector smod = size * 0.05f * (int) accuracy;
// Save the changes to the vectors
minz += smod;
maxz -= smod;
// Trace and test if it hits the smaller hitbox, if it fails
// we
// return false
Vector hit;
if (!IsVectorVisible(g_pLocalPlayer->v_Eye, minz) && !IsVectorVisible(g_pLocalPlayer->v_Eye, maxz))
return;
if (CheckLineBox(minz, maxz, g_pLocalPlayer->v_Eye, forward, hit))
// Only need head hitbox
if (HeadPreferable(entity))
num_hitboxes = 1;
for (int i = 0; i < num_hitboxes; i++)
{
current_user_cmd->tick_count = tick.tickcount;
current_user_cmd->buttons |= IN_ATTACK;
return;
auto hitbox = tick.hitboxes.at(i);
auto min = hitbox.min;
auto max = hitbox.max;
// Get the min and max for the hitbox
Vector minz(fminf(min.x, max.x), fminf(min.y, max.y), fminf(min.z, max.z));
Vector maxz(fmaxf(min.x, max.x), fmaxf(min.y, max.y), fmaxf(min.z, max.z));
// Shrink the hitbox here
Vector size = maxz - minz;
// Use entire hitbox if accuracy unset
Vector smod = size * 0.05f * (*accuracy > 0 ? *accuracy : 20);
// Save the changes to the vectors
minz += smod;
maxz -= smod;
// Trace and test if it hits the smaller hitbox, if it hits we can return true
Vector hit;
// Found a good one
if (CheckLineBox(minz, maxz, g_pLocalPlayer->v_Eye, forward, hit))
{
// Is tick visible
if (IsVectorVisible(g_pLocalPlayer->v_Eye, hitbox.center))
return true;
}
}
return false;
}
// The main function of the triggerbot
void CreateMove()
{
float backup_time = target_time;
target_time = 0;
@ -99,26 +92,51 @@ void CreateMove()
// Reset our last hitbox traced
last_hb_traced = -1;
// Get and ent in front of the player
CachedEntity *ent = FindEntInSight(EffectiveTargetingRange());
// Get an ent in front of the player
CachedEntity *ent = nullptr;
std::optional<hacks::tf2::backtrack::BacktrackData> bt_data;
// Check if can backtrack, shoot if we can
if (hacks::shared::backtrack::isBacktrackEnabled)
if (!g_IBacktrack.isBacktrackEnabled)
ent = FindEntInSight(EffectiveTargetingRange());
// Backtrack, use custom filter to check if tick is in crosshair
else
{
// We need to return because we can't hit non backtrackable ticks if we
// have backtrack latency.
DoBacktrack();
return;
// Set up forward Vector
float sp, sy, cp, cy;
QAngle angle;
g_IEngine->GetViewAngles(angle);
sy = sinf(DEG2RAD(angle[1]));
cy = cosf(DEG2RAD(angle[1]));
sp = sinf(DEG2RAD(angle[0]));
cp = cosf(DEG2RAD(angle[0]));
forward.x = cp * cy;
forward.y = cp * sy;
forward.z = -sp;
forward = forward * EffectiveTargetingRange() + g_pLocalPlayer->v_Eye;
// Call closest tick with our Tick filter func
auto closest_data = g_IBacktrack.getClosestTick(g_pLocalPlayer->v_Eye, std::bind(&hacks::tf2::backtrack::Backtrack::defaultEntFilter, &g_IBacktrack, std::placeholders::_1), tick_filter);
// No results, try to grab a building
if (!closest_data)
{
ent = FindEntInSight(EffectiveTargetingRange(), true);
}
else
{
// Assign entity
ent = (*closest_data).first;
bt_data = (*closest_data).second;
g_IBacktrack.SetBacktrackData(ent, *bt_data);
}
}
// Check if dormant or null to prevent crashes
if (CE_BAD(ent))
{
return;
}
// Determine whether the triggerbot should shoot, then act accordingly
if (IsTargetStateGood(ent))
if (IsTargetStateGood(ent, bt_data))
{
target_time = backup_time;
if (delay)
@ -224,9 +242,8 @@ bool ShouldShoot()
}
// A second check to determine whether a target is good enough to be aimed at
bool IsTargetStateGood(CachedEntity *entity, bool backtrack)
bool IsTargetStateGood(CachedEntity *entity, std::optional<hacks::tf2::backtrack::BacktrackData> tick)
{
// Check for Players
if (entity->m_Type() == ENTITY_PLAYER)
{
@ -273,44 +290,47 @@ bool IsTargetStateGood(CachedEntity *entity, bool backtrack)
return false;
}
// Head hitbox detection
if (HeadPreferable(entity) && !backtrack)
// Backtrack did these in the tick check
if (!tick)
{
if (last_hb_traced != hitbox_t::head)
return false;
}
// If usersettings tell us to use accuracy improvements and the cached
// hitbox isnt null, then we check if it hits here
if (*accuracy && !backtrack)
{
// Get a cached hitbox for the one traced
hitbox_cache::CachedHitbox *hb = entity->hitboxes.GetHitbox(last_hb_traced);
// Check for null
if (hb)
// Head hitbox detection
if (HeadPreferable(entity))
{
// Get the min and max for the hitbox
Vector minz(fminf(hb->min.x, hb->max.x), fminf(hb->min.y, hb->max.y), fminf(hb->min.z, hb->max.z));
Vector maxz(fmaxf(hb->min.x, hb->max.x), fmaxf(hb->min.y, hb->max.y), fmaxf(hb->min.z, hb->max.z));
// Shrink the hitbox here
Vector size = maxz - minz;
Vector smod = size * 0.05f * *accuracy;
// Save the changes to the vectors
minz += smod;
maxz -= smod;
// Trace and test if it hits the smaller hitbox, if it fails
// we
// return false
Vector hit;
if (!CheckLineBox(minz, maxz, g_pLocalPlayer->v_Eye, forward, hit))
if (last_hb_traced != hitbox_t::head)
return false;
}
}
// If usersettings tell us to use accuracy improvements and the cached
// hitbox isnt null, then we check if it hits here
if (*accuracy)
{
// Get a cached hitbox for the one traced
hitbox_cache::CachedHitbox *hb = entity->hitboxes.GetHitbox(last_hb_traced);
// Check for null
if (hb)
{
// Get the min and max for the hitbox
Vector minz(fminf(hb->min.x, hb->max.x), fminf(hb->min.y, hb->max.y), fminf(hb->min.z, hb->max.z));
Vector maxz(fmaxf(hb->min.x, hb->max.x), fmaxf(hb->min.y, hb->max.y), fmaxf(hb->min.z, hb->max.z));
// Shrink the hitbox here
Vector size = maxz - minz;
Vector smod = size * 0.05f * *accuracy;
// Save the changes to the vectors
minz += smod;
maxz -= smod;
// Trace and test if it hits the smaller hitbox, if it fails
// we
// return false
Vector hit;
if (!CheckLineBox(minz, maxz, g_pLocalPlayer->v_Eye, forward, hit))
return false;
}
}
}
// Target passed the tests so return true
return true;
@ -376,9 +396,8 @@ bool IsTargetStateGood(CachedEntity *entity, bool backtrack)
}
// A function to return a potential entity in front of the player
CachedEntity *FindEntInSight(float range)
CachedEntity *FindEntInSight(float range, bool no_players)
{
// We dont want to hit ourself so we set an ignore
trace_t trace;
trace::filter_default.SetSelf(RAW_ENT(g_pLocalPlayer->entity));
@ -407,9 +426,11 @@ CachedEntity *FindEntInSight(float range)
// Return an ent if that is what we hit
if (trace.m_pEnt)
{
last_hb_traced = trace.hitbox;
return ENTITY(((IClientEntity *) trace.m_pEnt)->entindex());
last_hb_traced = trace.hitbox;
CachedEntity *ent = ENTITY(((IClientEntity *) trace.m_pEnt)->entindex());
// Player check
if (!no_players || ent->m_Type() != ENTITY_PLAYER)
return ent;
}
// Since we didnt hit and entity, the vis check failed so return 0

View File

@ -395,14 +395,13 @@ DEFINE_HOOKED_METHOD(CreateMove, bool, void *this_, float input_sample_time, CUs
// LoadSavedState();
}
g_Settings.is_create_move = false;
if (nolerp && !hacks::shared::backtrack::isBacktrackEnabled)
if (nolerp)
{
static const ConVar *pUpdateRate = g_pCVar->FindVar("cl_updaterate");
if (!pUpdateRate)
pUpdateRate = g_pCVar->FindVar("cl_updaterate");
else
{
float interp = MAX(cl_interp->GetFloat(), cl_interp_ratio->GetFloat() / pUpdateRate->GetFloat());
cmd->tick_count += TIME_TO_TICKS(interp);
}

View File

@ -26,8 +26,6 @@ namespace hooked_methods
DEFINE_HOOKED_METHOD(LevelInit, void, void *this_, const char *name)
{
hacks::shared::backtrack::lastincomingsequencenumber = 0;
hacks::shared::backtrack::sequences.clear();
firstcm = true;
// nav::init = false;
playerlist::Save();

View File

@ -5,17 +5,18 @@
* Author: bencat07
*/
#include "HookedMethods.hpp"
#include "hacks/Backtrack.hpp"
#include "Backtrack.hpp"
namespace hooked_methods
{
DEFINE_HOOKED_METHOD(SendDatagram, int, INetChannel *ch, bf_write *buf)
{
if (!isHackActive() || !ch || CE_BAD(LOCAL_E) || !round(*hacks::shared::backtrack::latency))
if (!isHackActive() || !ch || CE_BAD(LOCAL_E) || std::floor(*g_IBacktrack.latency) == 0)
return original::SendDatagram(ch, buf);
int in = ch->m_nInSequenceNr;
int state = ch->m_nInReliableState;
hacks::shared::backtrack::AddLatencyToNetchan(ch);
// Do backtrack things
g_IBacktrack.adjustPing(ch);
int ret = original::SendDatagram(ch, buf);
ch->m_nInSequenceNr = in;

View File

@ -22,10 +22,6 @@ namespace effect_chams
{
extern settings::Boolean enable;
} // namespace effect_chams
namespace hacks::shared::backtrack
{
extern settings::Boolean backtrack_chams_glow;
}
namespace hooked_methods
{
// Global scope so we can deconstruct on shutdown
@ -49,7 +45,7 @@ DEFINE_HOOKED_METHOD(DrawModelExecute, void, IVModelRender *this_, const DrawMod
if (!isHackActive())
return original::DrawModelExecute(this_, state, info, bone);
if (!(hacks::shared::backtrack::isBacktrackEnabled /*|| (hacks::shared::antiaim::force_fakelag && hacks::shared::antiaim::isEnabled())*/ || spectator_target || no_arms || no_hats || (*clean_screenshots && g_IEngine->IsTakingScreenshot()) || CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer()))
if (!(g_IBacktrack.isBacktrackEnabled /*|| (hacks::shared::antiaim::force_fakelag && hacks::shared::antiaim::isEnabled())*/ || spectator_target || no_arms || no_hats || (*clean_screenshots && g_IEngine->IsTakingScreenshot()) || CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer()))
{
return original::DrawModelExecute(this_, state, info, bone);
}
@ -118,7 +114,7 @@ DEFINE_HOOKED_METHOD(DrawModelExecute, void, IVModelRender *this_, const DrawMod
aa_draw = false;
angles.y = backup;
}*/
if (hacks::shared::backtrack::isBacktrackEnabled && hacks::shared::backtrack::backtrack_chams_glow)
if (g_IBacktrack.isBacktrackEnabled && g_IBacktrack.chams)
{
const char *name = g_IModelInfo->GetModelName(info.pModel);
if (name)
@ -126,48 +122,48 @@ DEFINE_HOOKED_METHOD(DrawModelExecute, void, IVModelRender *this_, const DrawMod
std::string sname = name;
if (sname.find("models/player") || sname.find("models/weapons") || sname.find("models/workshop/player") || sname.find("models/workshop/weapons"))
{
if (IDX_GOOD(info.entity_index) && info.entity_index <= g_IEngine->GetMaxClients() && info.entity_index != g_IEngine->GetLocalPlayer())
{
CachedEntity *ent = ENTITY(info.entity_index);
if (CE_GOOD(ent) && ent->m_bAlivePlayer())
{
// Backup Blend
float orig_blend = g_IVRenderView->GetBlend();
// Make Backtrack stuff seethrough
g_IVRenderView->SetBlend(1.0f);
// Get Backtrack data for target entity
auto head_pos = hacks::shared::backtrack::headPositions[info.entity_index];
// Usable vector instead of ptr to c style array, also used to filter valid and invalid ticks
std::vector<hacks::shared::backtrack::BacktrackData> usable;
for (int i = 0; i < 66; i++)
auto good_ticks = g_IBacktrack.getGoodTicks(info.entity_index);
// Check if valid
if (!good_ticks.empty())
{
if (hacks::shared::backtrack::ValidTick(head_pos[i], ent))
usable.push_back(head_pos[i]);
}
// Crash much?
if (usable.size())
{
// Sort
std::sort(usable.begin(), usable.end(), [](hacks::shared::backtrack::BacktrackData &a, hacks::shared::backtrack::BacktrackData &b) { return a.tickcount < b.tickcount; });
// Make our own Chamsish Material
// Render Chams/Glow stuff
CMatRenderContextPtr ptr(GET_RENDER_CONTEXT);
// Backup Blend
float orig_blend = g_IVRenderView->GetBlend();
// Make Backtrack stuff Solid color
g_IVRenderView->SetBlend(1.0f);
rgba_t mod_original;
// Save color just in case, then set to white
// Save color just in case, then set to team color
g_IVRenderView->GetColorModulation(mod_original.rgba);
g_IVRenderView->SetColorModulation(colors::white);
g_IVRenderView->SetColorModulation(g_IBacktrack.team_color ? colors::EntityF(ent) : colors::FromRGBA8(100, 100, 100, 255));
// Important for Depth
ptr->DepthRange(0.0f, 1.0f);
// Apply our material
g_IVModelRender->ForcedMaterialOverride(mat_dme_chams);
// Run Original
original::DrawModelExecute(this_, state, info, usable[0].bones);
if (!g_IBacktrack.team_color)
g_IVModelRender->ForcedMaterialOverride(mat_dme_chams);
// Draw as many ticks as desired
for (unsigned i = 0; i <= (unsigned) std::max(*g_IBacktrack.chams_ticks, 1); i++)
{
// Can't draw more than we have
if (i >= good_ticks.size())
break;
original::DrawModelExecute(this_, state, info, good_ticks[i].bones);
}
// Revert
g_IVRenderView->SetColorModulation(mod_original.rgba);
g_IVModelRender->ForcedMaterialOverride(nullptr);
g_IVRenderView->SetBlend(orig_blend);
}
g_IVRenderView->SetBlend(orig_blend);
}
}
}