This repository has been archived on 2024-06-01. You can view files and clone it, but cannot push or open issues or pull requests.
cathook/src/hacks/Backtrack.cpp
2019-06-02 21:39:03 +02:00

459 lines
16 KiB
C++

/*
* Backtrack.cpp
*
* Created on: May 15, 2018
* Author: bencat07
*/
#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>
namespace hacks::shared::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" };
void EmptyBacktrackData(BacktrackData &i);
std::pair<int, int> getBestEntBestTick();
bool shouldBacktrack();
BacktrackData headPositions[33][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)
{
if (!isBacktrackEnabled)
return;
float Latency = *latency;
if (Latency > 1000.0f)
Latency = 800.0f;
Latency -= getRealLatency();
if (Latency < 0.0f)
Latency = 0.0f;
for (auto &seq : sequences)
{
if (g_GlobalVars->realtime - seq.curtime > Latency / 1000.0f)
{
ch->m_nInReliableState = seq.inreliablestate;
ch->m_nInSequenceNr = seq.sequencenr;
break;
}
}
}
void Init()
{
for (int i = 0; i < 33; i++)
for (int j = 0; j < 66; j++)
headPositions[i][j] = {};
BestTick = iBestTarget = -1;
}
int BestTick = -1;
int iBestTarget = -1;
bool istickvalid[32][66]{};
bool istickinvalid[32][66]{};
static void Run()
{
if (!shouldBacktrack())
{
isBacktrackEnabled = false;
return;
}
UpdateIncomingSequences();
isBacktrackEnabled = true;
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;
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)
return false;
CachedEntity *wep = g_pLocalPlayer->weapon();
if (CE_BAD(wep))
return false;
if (*slots == 0)
return true;
int slot = re::C_BaseCombatWeapon::GetSlot(RAW_ENT(wep));
switch ((int) slots)
{
case 1:
if (slot == 0)
return true;
break;
case 2:
if (slot == 1)
return true;
break;
case 3:
if (slot == 2)
return true;
break;
case 4:
if (slot == 0 || slot == 1)
return true;
break;
case 5:
if (slot == 0 || slot == 2)
return true;
break;
case 6:
if (slot == 1 || slot == 2)
return true;
break;
}
return false;
}
float getRealLatency()
{
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)
Latency += -0.5f / cl_updaterate->GetFloat();
else if (!cl_updaterate)
cl_updaterate = g_ICvar->FindVar("cl_updaterate");
return MAX(0.0f, Latency) * 1000.f;
}
float getLatency()
{
auto ch = (INetChannel *) g_IEngine->GetNetChannelInfo();
if (!ch)
return 0;
float Latency = *latency;
if (Latency > 1000.0f)
Latency = 800.0f;
Latency -= getRealLatency();
if (Latency < 0.0f)
Latency = 0.0f;
return Latency;
}
int getTicks()
{
return max(min(int(getLatency() / 200.0f * 13.0f) + 12, 65), 12);
}
bool ValidTick(BacktrackData &i, CachedEntity *ent)
{
if (!(fabsf(NET_FLOAT(RAW_ENT(ent), netvar.m_flSimulationTime) * 1000.0f - getLatency() - i.simtime * 1000.0f) < 200.0f))
return false;
return true;
}
void EmptyBacktrackData(BacktrackData &i)
{
i = {};
}
// 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 bestDist = 9999.0f;
for (int i = 0; i <= g_IEngine->GetMaxClients(); i++)
{
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], 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;
}
}
}
}
}
}
}
else
{
float bestFov = 180.0f;
for (int i = 0; i <= g_IEngine->GetMaxClients(); i++)
{
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;
}
}
}
}
}
}
}
}
return std::make_pair(bestEnt, bestTick);
}
static InitRoutine EC([]() {
EC::Register(EC::LevelInit, Init, "INIT_Backtrack", EC::average);
EC::Register(EC::CreateMove, Run, "CM_Backtrack", EC::early);
#if ENABLE_VISUALS
EC::Register(EC::Draw, Draw, "DRAW_Backtrack", EC::average);
#endif
});
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