Merge pull request #24 from nullworks/master

readifnotgay
This commit is contained in:
LightCat 2018-06-25 16:41:09 +02:00 committed by GitHub
commit c16971471e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 768 additions and 88 deletions

3
.gitmodules vendored
View File

@ -13,3 +13,6 @@
[submodule "ucccccp"]
path = external/ucccccp
url = https://github.com/nullworks/ucccccp.git
[submodule "external/co-library"]
path = external/co-library
url = https://github.com/nullworks/co-library.git

View File

@ -12,6 +12,8 @@ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS ${CMAKE_BUILD_TYPE_VALUES})
cmake_minimum_required(VERSION 3.0)
project(cathook VERSION 0.0.1)
set(CMAKE_CXX_STANDARD 17)
add_library(cathook SHARED "")
set(GameSpecific 1 CACHE BOOL "Build for specific target game (As opposed to universal, but slower, lib)")
@ -31,6 +33,7 @@ set(Textmode 0 CACHE BOOL "Various textmode-only features for bots")
set(EnableTextmodeStdin 0 CACHE BOOL "Textmode Stdin -> Console bridge (EXPERIMENTAL)")
set(EnableWarnings 1 CACHE BOOL "Enable compile warnings")
set(EnableNullGraphics 0 CACHE BOOL "Enable experimental textmode hooks (CRASHES)")
set(EnableOnlineFeatures 1 CACHE BOOL "Enable online features (WIP)")
if(NOT EnableVisuals)
set(EnableGUI 0)
@ -62,6 +65,11 @@ if(EnableIPC)
target_link_libraries(cathook SimpleIPC)
endif()
if(EnableOnlineFeatures)
add_subdirectory(external/co-library)
target_link_libraries(cathook co-library)
endif()
if(EnableVisuals)
add_subdirectory(external/libglez)
target_include_directories(cathook PRIVATE include/visual)

1
external/co-library vendored Submodule

@ -0,0 +1 @@
Subproject commit 393948a6bf7ee4a62f5a3bd82d94c05d4bc405aa

View File

@ -39,7 +39,8 @@ target_sources(cathook PRIVATE
"${CMAKE_CURRENT_LIST_DIR}/velocity.hpp"
"${CMAKE_CURRENT_LIST_DIR}/votelogger.hpp"
"${CMAKE_CURRENT_LIST_DIR}/MiscTemporary.hpp"
"${CMAKE_CURRENT_LIST_DIR}/Options.hpp")
"${CMAKE_CURRENT_LIST_DIR}/Options.hpp"
"${CMAKE_CURRENT_LIST_DIR}/PlayerTools.hpp")
target_include_directories(cathook PRIVATE "${CMAKE_CURRENT_LIST_DIR}")
@ -50,6 +51,7 @@ add_subdirectory(hacks)
add_subdirectory(hooks)
add_subdirectory(reclasses)
add_subdirectory(sdk)
add_subdirectory(online)
if(EnableVisuals)
add_subdirectory(visual)

44
include/PlayerTools.hpp Normal file
View File

@ -0,0 +1,44 @@
/*
Created on 23.06.18.
*/
#pragma once
#include "config.h"
#include <optional>
#if ENABLE_VISUALS
#include <colors.hpp>
#endif
class CachedEntity;
namespace player_tools
{
enum class IgnoreReason
{
DO_NOT_IGNORE,
IS_HOOVY,
IS_TAUNTING,
LOCAL_PLAYER_LIST,
ONLINE_NO_TARGET,
ONLINE_FRIENDLY_SOFTWARE,
DEVELOPER,
OTHER
};
IgnoreReason shouldTargetSteamId(unsigned id);
IgnoreReason shouldTarget(CachedEntity *player);
bool shouldAlwaysRenderEspSteamId(unsigned id);
bool shouldAlwaysRenderEsp(CachedEntity *entity);
#if ENABLE_VISUALS
std::optional<colors::rgba_t> forceEspColorSteamId(unsigned id);
std::optional<colors::rgba_t> forceEspColor(CachedEntity *entity);
#endif
void onKilledBy(CachedEntity *entity);
}

View File

@ -23,7 +23,6 @@ extern ConVar *cl_interpolate;
extern CatVar event_log;
extern CatVar cathook; // Master switch
extern CatVar ignore_taunting;
extern bool *bSendPackets;
extern CatVar show_antiaim;
extern CatVar force_thirdperson;

View File

@ -7,5 +7,7 @@
#pragma once
class CachedEntity;
void UpdateHoovyList();
bool IsHoovy(CachedEntity *entity);

View File

@ -0,0 +1,2 @@
target_sources(cathook PRIVATE
"${CMAKE_CURRENT_LIST_DIR}/Online.hpp")

40
include/online/Online.hpp Normal file
View File

@ -0,0 +1,40 @@
/*
Created on 23.06.18.
*/
#pragma once
#include <string>
#include <colors.hpp>
#include <config.h>
#include <vector>
namespace online
{
struct user_data
{
bool is_anonymous{ false };
bool is_using_friendly_software{ false };
bool is_steamid_verified{ false };
std::string username{};
std::vector<std::string> shown_groups{};
std::string software_name{};
bool has_software{ false };
bool no_target{ false };
bool is_developer{};
#if ENABLE_VISUALS
bool has_color{ false };
colors::rgba_t color{};
bool rainbow{ false };
#endif
};
/*
* Identify unidentified users, send online status, etc
*/
void update();
user_data *getUserData(unsigned steamId);
}

View File

@ -36,7 +36,7 @@ public:
void SetEntityColor(CachedEntity *ent, rgba_t color);
rgba_t ChamsColor(IClientEntity *entity);
bool ShouldRenderChams(IClientEntity *entity);
void RenderChams(int idx);
void RenderChams(IClientEntity *entity);
void BeginRenderChams();
void EndRenderChams();
void RenderChamsRecursive(IClientEntity *entity);
@ -54,4 +54,4 @@ public:
extern EffectChams g_EffectChams;
extern CScreenSpaceEffectRegistration *g_pEffectChams;
}
}

View File

@ -48,6 +48,8 @@ struct rgba_t
constexpr rgba_t(float _r, float _g, float _b, float _a = 1.0f)
: r(_r), g(_g), b(_b), a(_a){};
explicit rgba_t(const char hex[6]);
constexpr operator glez::rgba() const
{
return *reinterpret_cast<const glez::rgba *>(this);
@ -76,6 +78,16 @@ struct rgba_t
}
};
constexpr bool operator==(const rgba_t& lhs, const rgba_t& rhs)
{
return rhs.r == lhs.r && rhs.g == lhs.g && rhs.b == lhs.b && rhs.a == lhs.a;
}
constexpr bool operator!=(const rgba_t& lhs, const rgba_t& rhs)
{
return !(lhs == rhs);
}
constexpr rgba_t FromRGBA8(float r, float g, float b, float a)
{
return rgba_t{ r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f };

View File

@ -32,7 +32,8 @@ target_sources(cathook PRIVATE
"${CMAKE_CURRENT_LIST_DIR}/velocity.cpp"
"${CMAKE_CURRENT_LIST_DIR}/votelogger.cpp"
"${CMAKE_CURRENT_LIST_DIR}/MiscTemporary.cpp"
"${CMAKE_CURRENT_LIST_DIR}/Options.cpp")
"${CMAKE_CURRENT_LIST_DIR}/Options.cpp"
"${CMAKE_CURRENT_LIST_DIR}/PlayerTools.cpp")
add_subdirectory(core)
add_subdirectory(classinfo)
@ -41,6 +42,7 @@ add_subdirectory(hacks)
add_subdirectory(hooks)
add_subdirectory(reclasses)
add_subdirectory(sdk)
add_subdirectory(online)
if(EnableVisuals)
add_subdirectory(visual)

161
src/PlayerTools.cpp Normal file
View File

@ -0,0 +1,161 @@
/*
Created on 23.06.18.
*/
#include <core/cvwrapper.hpp>
#include <unordered_map>
#include <hoovy.hpp>
#include <playerlist.hpp>
#include <online/Online.hpp>
#include "PlayerTools.hpp"
#include "entitycache.hpp"
static std::unordered_map<unsigned, unsigned> betrayal_list{};
static CatCommand forgive_all("pt_forgive_all", "Clear betrayal list", []() {
betrayal_list.clear();
});
namespace settings
{
static CatVar online_notarget(CV_SWITCH, "pt_ignore_notarget", "1", "Ignore notarget", "Ignore online players with notarget role");
static CatVar hoovy(CV_SWITCH, "pt_ignore_hoovy", "1", "Ignore hoovy");
static CatVar online_friendly_software(CV_SWITCH, "pt_ignore_friendly_software", "1", "Ignore friendly software", "Ignore CO-compatible software");
static CatVar online_only_verified(CV_SWITCH, "pt_ignore_only_verified", "0", "Only ignore verified", "If online checks are enabled, only apply ignore if SteamID is verified (not recommended right now)");
static CatVar online_anonymous(CV_SWITCH, "pt_ignore_anonymous", "1", "Ignore anonymous", "Apply ignore checks to anonymous accounts too");
static CatVar betrayal_limit(CV_INT, "pt_betrayal_limit", "3", "Betrayal limit", "Stop ignoring a player after N kills while you ignored them");
static CatVar taunting(CV_SWITCH, "pt_ignore_taunting", "1", "Ignore taunting", "Don't shoot taunting players");
}
namespace player_tools
{
IgnoreReason shouldTargetSteamId(unsigned id)
{
if (id == 0)
return IgnoreReason::DO_NOT_IGNORE;
if (settings::betrayal_limit)
{
if (betrayal_list[id] > int(settings::betrayal_limit))
return IgnoreReason::DO_NOT_IGNORE;
}
auto& pl = playerlist::AccessData(id);
if (playerlist::IsFriendly(pl.state))
return IgnoreReason::LOCAL_PLAYER_LIST;
auto *co = online::getUserData(id);
if (co)
{
bool check_verified = !settings::online_only_verified || co->is_steamid_verified;
bool check_anonymous = settings::online_anonymous || !co->is_anonymous;
if (check_verified && check_anonymous)
{
if (settings::online_notarget && co->no_target)
return IgnoreReason::ONLINE_NO_TARGET;
if (settings::online_friendly_software && co->is_using_friendly_software)
return IgnoreReason::ONLINE_FRIENDLY_SOFTWARE;
}
// Always check developer status, no exceptions
if (co->is_developer)
return IgnoreReason::DEVELOPER;
}
return IgnoreReason::DO_NOT_IGNORE;
}
IgnoreReason shouldTarget(CachedEntity *entity)
{
if (entity->m_Type() == ENTITY_PLAYER)
{
if (settings::hoovy && IsHoovy(entity))
return IgnoreReason::IS_HOOVY;
if (settings::taunting && HasCondition<TFCond_Taunting>(entity))
return IgnoreReason::IS_TAUNTING;
return shouldTargetSteamId(entity->player_info.friendsID);
}
return IgnoreReason::DO_NOT_IGNORE;
}
bool shouldAlwaysRenderEspSteamId(unsigned id)
{
if (id == 0)
return false;
auto& pl = playerlist::AccessData(id);
if (pl.state != playerlist::k_EState::DEFAULT)
return true;
auto *co = online::getUserData(id);
if (co)
return true;
return false;
}
bool shouldAlwaysRenderEsp(CachedEntity *entity)
{
if (entity->m_Type() == ENTITY_PLAYER)
{
return shouldAlwaysRenderEspSteamId(entity->player_info.friendsID);
}
return false;
}
#if ENABLE_VISUALS
std::optional<colors::rgba_t> forceEspColorSteamId(unsigned id)
{
if (id == 0)
return std::nullopt;
auto pl = playerlist::Color(id);
if (pl != colors::empty)
return std::optional<colors::rgba_t>{ pl };
auto *co = online::getUserData(id);
if (co)
{
if (co->has_color)
return std::optional<colors::rgba_t>{ co->color };
if (co->rainbow)
return std::optional<colors::rgba_t>{ colors::RainbowCurrent() };
}
return std::nullopt;
}
std::optional<colors::rgba_t> forceEspColor(CachedEntity *entity)
{
if (entity->m_Type() == ENTITY_PLAYER)
{
return forceEspColorSteamId(entity->player_info.friendsID);
}
return std::nullopt;
}
#endif
void onKilledBy(unsigned id)
{
auto reason = shouldTargetSteamId(id);
if (reason != IgnoreReason::DO_NOT_IGNORE)
{
// We ignored the gamer, but they still shot us
if (betrayal_list.find(id) == betrayal_list.end())
betrayal_list[id] = 0;
betrayal_list[id]++;
}
}
void onKilledBy(CachedEntity *entity)
{
onKilledBy(entity->player_info.friendsID);
}
}

View File

@ -33,8 +33,6 @@ CatVar force_name(CV_STRING, "name", "", "Force name");
CatVar
cathook(CV_SWITCH, "enabled", "1", "CatHook enabled",
"Disabling this completely disables cathook (can be re-enabled)");
CatVar ignore_taunting(CV_SWITCH, "ignore_taunting", "1", "Ignore taunting",
"Aimbot/Triggerbot won't attack taunting enemies");
// CatVar send_packets(CV_SWITCH, "sendpackets", "1", "Send packets", "Internal
// use");
CatVar show_antiaim(CV_SWITCH, "thirdperson_angles", "1", "Real TP angles",

View File

@ -532,6 +532,13 @@ free(logname);*/
#if ENABLE_VISUALS
hacks::shared::esp::Init();
#endif
#if not ENABLE_VISUALS
hack::command_stack().push("exec cat_autoexec_textmode");
#endif
hack::command_stack().push("exec cat_autoexec");
hack::command_stack().push("cat_killsay_reload");
hack::command_stack().push("cat_spam_reload");
logging::Info("Clearing initializer stack");
while (!init_stack().empty())
{
@ -540,12 +547,6 @@ free(logname);*/
}
logging::Info("Initializer stack done");
#if not ENABLE_VISUALS
hack::command_stack().push("exec cat_autoexec_textmode");
#endif
hack::command_stack().push("exec cat_autoexec");
hack::command_stack().push("cat_killsay_reload");
hack::command_stack().push("cat_spam_reload");
hack::initialized = true;
}

View File

@ -10,6 +10,7 @@
#include <hacks/AntiAim.hpp>
#include <hacks/ESP.hpp>
#include <glez/draw.hpp>
#include <PlayerTools.hpp>
#include "common.hpp"
namespace hacks::shared::aimbot
@ -60,8 +61,6 @@ static CatVar
static CatVar ignore_vaccinator(
CV_SWITCH, "aimbot_ignore_vaccinator", "1", "Ignore Vaccinator",
"Hitscan weapons won't fire if enemy is vaccinated against bullets");
static CatVar ignore_hoovy(CV_SWITCH, "aimbot_ignore_hoovy", "0",
"Ignore Hoovies", "Aimbot won't attack hoovies");
static CatVar ignore_cloak(CV_SWITCH, "aimbot_ignore_cloak", "1",
"Ignore cloaked", "Don't aim at invisible enemies");
static CatVar ignore_deadringer(CV_SWITCH, "aimbot_ignore_deadringer", "1",
@ -574,8 +573,10 @@ bool IsTargetStateGood(CachedEntity *entity)
}
}
// Taunting
if (ignore_taunting && HasCondition<TFCond_Taunting>(entity))
// Some global checks
if (player_tools::shouldTarget(entity) != player_tools::IgnoreReason::DO_NOT_IGNORE)
return false;
if (hacks::shared::catbot::should_ignore_player(entity))
return false;
// Invulnerable players, ex: uber, bonk
if (IsPlayerInvulnerable(entity))
@ -605,19 +606,7 @@ bool IsTargetStateGood(CachedEntity *entity)
HasCondition<TFCond_UberBulletResist>(entity))
return false;
}
// Friendly player
if (playerlist::IsFriendly(playerlist::AccessData(entity).state))
return false;
IF_GAME(IsTF())
{
// Hoovys
if (ignore_hoovy && IsHoovy(entity))
{
return false;
}
}
if (hacks::shared::catbot::should_ignore_player(entity))
return false;
// Preform hitbox prediction
int hitbox = BestHitbox(entity);
AimbotCalculatedData_s &cd = calculated_data_array[entity->m_IDX];

View File

@ -5,6 +5,7 @@
* Author: nullifiedcat & Lighty
*/
#include <PlayerTools.hpp>
#include "common.hpp"
namespace hacks::tf::autodetonator
@ -53,19 +54,16 @@ bool IsTarget(CachedEntity *ent)
// Dont detonate on dead players
if (!ent->m_bAlivePlayer())
return false;
// Dont detonate on friendly players
if (playerlist::IsFriendly(playerlist::AccessData(ent).state))
return false;
// Global checks
if (player_tools::shouldTarget(ent) != player_tools::IgnoreReason::DO_NOT_IGNORE)
return false;
IF_GAME(IsTF())
{
// Dont target invulnerable players, ex: uber, bonk
if (IsPlayerInvulnerable(ent))
return false;
// If settings allow, ignore taunting players
if (ignore_taunting && HasCondition<TFCond_Taunting>(ent))
return false;
// If settings allow, dont target cloaked players
if (legit && IsPlayerInvisible(ent))

View File

@ -24,7 +24,7 @@ int m_iNewTarget{ 0 };
static CatVar pop_uber_auto(CV_SWITCH, "autoheal_uber", "1", "AutoUber",
"Use ubercharge automatically");
static CatVar
pop_uber_percent(CV_FLOAT, "autoheal_uber_health", "30",
pop_uber_percent(CV_FLOAT, "autoheal_uber_health", "0",
"Pop uber if health% <",
"When under a percentage of health, use ubercharge");
static CatVar share_uber(
@ -351,9 +351,17 @@ bool IsPopped()
bool ShouldChargePlayer(int idx)
{
CachedEntity *target = ENTITY(idx);
const int health = target->m_iHealth();
if (float(pop_uber_percent) > 0)
{
const float pophealth = target->m_iMaxHealth() * (float(pop_uber_percent) / 100);
if (health < pophealth)
return true;
}
else
{
const float damage_accum_duration =
g_GlobalVars->curtime - data[idx].accum_damage_start;
const int health = target->m_iHealth();
if (!data[idx].accum_damage_start)
return false;
if (health > 30 && data[idx].accum_damage < 45)
@ -366,6 +374,8 @@ bool ShouldChargePlayer(int idx)
if (health < 30 && data[idx].accum_damage > 10)
return true;
return false;
}
return false;
}
bool ShouldPop()

View File

@ -7,6 +7,7 @@
#include "common.hpp"
#include <hacks/AutoSticky.hpp>
#include <PlayerTools.hpp>
namespace hacks::tf::autosticky
{
@ -58,8 +59,9 @@ bool IsTarget(CachedEntity *ent)
// Dont detonate on dead players
if (!ent->m_bAlivePlayer())
return false;
// Dont detonate on friendly players
if (playerlist::IsFriendly(playerlist::AccessData(ent).state))
// Global checks
if (player_tools::shouldTarget(ent) != player_tools::IgnoreReason::DO_NOT_IGNORE)
return false;
IF_GAME(IsTF())

View File

@ -7,6 +7,8 @@
#include <hacks/ESP.hpp>
#include <glez/draw.hpp>
#include <online/Online.hpp>
#include <PlayerTools.hpp>
#include "common.hpp"
namespace hacks::shared::esp
@ -146,6 +148,11 @@ static CatVar entity_model(CV_SWITCH, "esp_model_name", "0", "Model name ESP",
static CatVar entity_id(CV_SWITCH, "esp_entity_id", "1", "Entity ID",
"Used with Entity ESP. Shows entityID");
// Online
static CatVar online(CV_SWITCH, "esp_online", "1", "Show online info", "Username, etc");
static CatVar online_groups(CV_SWITCH, "esp_online_groups", "1", "Show online groups", "Admin, developer, etc");
static CatVar online_software(CV_SWITCH, "esp_online_software", "1", "Show software", "cathook, lmaobox, etc");
// CatVar draw_hitbox(CV_SWITCH, "esp_hitbox", "1", "Draw Hitbox");
// Unknown
@ -1153,10 +1160,12 @@ void _FASTCALL ProcessEntity(CachedEntity *ent)
if (!g_IEngine->GetPlayerInfo(ent->m_IDX, &info))
return;
online::user_data *data = online ? online::getUserData(info.friendsID) : nullptr;
// TODO, check if u can just use "ent->m_bEnemy()" instead of m_iTeam
// Legit mode handling
if (legit && ent->m_iTeam() != g_pLocalPlayer->team &&
playerlist::IsDefault(info.friendsID))
playerlist::IsDefault(info.friendsID) && !(data))
{
if (IsPlayerInvisible(ent))
return; // Invis check
@ -1168,6 +1177,23 @@ void _FASTCALL ProcessEntity(CachedEntity *ent)
// return;
}
if (data)
{
AddEntityString(ent, "CO: " + data->username, colors::yellow);
if (data->is_steamid_verified)
AddEntityString(ent, "Verified SteamID", colors::green);
if (online_groups)
for (auto& s: data->shown_groups)
AddEntityString(ent, s, colors::orange);
if (online_software && data->has_software)
{
if (data->is_using_friendly_software)
AddEntityString(ent, "Software: " + data->software_name);
else
AddEntityString(ent, "Software: " + data->software_name, colors::red);
}
}
// Powerup handling
if (powerup_esp)
{
@ -1177,8 +1203,7 @@ void _FASTCALL ProcessEntity(CachedEntity *ent)
}
// Dont understand reasoning for this check
if (ent->m_bEnemy() || teammates ||
!playerlist::IsDefault(info.friendsID))
if (ent->m_bEnemy() || teammates || player_tools::shouldAlwaysRenderEsp(ent))
{
// Playername

View File

@ -23,7 +23,7 @@ static CatVar draw_crumb(CV_SWITCH, "fb_draw", "1", "Draw crumbs",
"Self explanitory");
static CatVar follow_distance(CV_INT, "fb_distance", "175", "Follow Distance",
"How close the bots should stay to the target");
static CatVar follow_activation(CV_INT, "fb_activation", "175",
static CatVar follow_activation(CV_INT, "fb_activation", "1000",
"Activation Distance",
"How close a player should be until the "
"followbot will pick them as a target");
@ -45,14 +45,49 @@ static CatVar always_medigun(CV_SWITCH, "fb_always_medigun", "0",
"Always Medigun", "Always use medigun");
static CatVar sync_taunt(CV_SWITCH, "fb_sync_taunt", "0", "Synced taunt",
"Taunt when follow target does");
static CatVar change(CV_SWITCH, "fb_switch", "1", "Change followbot target",
static CatVar change(CV_SWITCH, "fb_switch", "0", "Change followbot target",
"Always change roaming target when possible");
static CatVar autojump(CV_SWITCH, "fb_autojump", "1", "Autojump",
"Automatically jump if stuck");
static CatVar afk(CV_SWITCH, "fb_afk", "1", "Switch target if AFK",
"Automatically switch target if the target is afk");
static CatVar afktime(
CV_INT, "fb_afk_time", "15000", "Max AFK Time",
"Max time in ms spent standing still before player gets declared afk");
// Something to store breadcrumbs created by followed players
static std::vector<Vector> breadcrumbs;
static const int crumb_limit = 64; // limit
// Followed entity, externed for highlight color
int follow_target = 0;
bool inited;
Timer lastTaunt{}; //time since taunt was last executed, used to avoid kicks
std::array<Timer, 32> afkTicks; //for how many ms the player hasn't been moving
void checkAFK()
{
for (int i = 0; i < g_GlobalVars->maxClients; i++)
{
auto entity = ENTITY(i);
if (CE_BAD(entity))
continue;
if (!CE_VECTOR(entity, netvar.vVelocity).IsZero(5.0f))
{
afkTicks[i].update();
}
}
}
void init()
{
for (int i; i < afkTicks.size(); i++)
{
afkTicks[i].update();
}
inited = true;
}
void WorldTick()
{
@ -61,6 +96,8 @@ void WorldTick()
follow_target = 0;
return;
}
if (!inited)
init();
// We need a local player to control
if (CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer())
@ -69,6 +106,9 @@ void WorldTick()
return;
}
if (afk)
checkAFK();
// Still good check
if (follow_target)
{
@ -122,6 +162,12 @@ void WorldTick()
continue;
if (entity == LOCAL_E) // Follow self lol
continue;
if (entity->m_bEnemy())
continue;
if (afk && afkTicks[i].check(int(afktime))) //don't follow target that was determined afk
continue;
if (IsPlayerDisguised(entity) || IsPlayerInvisible(entity))
continue;
if (!entity->m_bAlivePlayer()) // Dont follow dead players
continue;
if (follow_activation &&
@ -148,29 +194,51 @@ void WorldTick()
entity->m_flDistance()) // favor closer entitys
continue;
// ooooo, a target
follow_target = entity->m_IDX;
follow_target = i;
afkTicks[i].update(); //set afk time to 0
}
}
// last check for entity before we continue
if (!follow_target)
return;
// If the player is close enough, we dont need to follow the path
CachedEntity *followtar = ENTITY(follow_target);
// wtf is this needed
if (CE_BAD(followtar))
return;
auto tar_orig = followtar->m_vecOrigin();
auto loc_orig = LOCAL_E->m_vecOrigin();
auto dist_to_target = loc_orig.DistTo(tar_orig);
if (dist_to_target < 30)
breadcrumbs.clear();
// Check if we are following a disguised/spy
if (IsPlayerDisguised(followtar) || IsPlayerInvisible(followtar))
{
follow_target = 0;
return;
}
//check if target is afk
if (afk)
{
if (afkTicks[follow_target].check(int(afktime)))
{
follow_target = 0;
return;
}
}
// Update timer on new target
static Timer idle_time{};
if (breadcrumbs.empty())
idle_time.update();
// If the player is close enough, we dont need to follow the path
auto tar_orig = followtar->m_vecOrigin();
auto loc_orig = LOCAL_E->m_vecOrigin();
auto dist_to_target = loc_orig.DistTo(tar_orig);
if ((dist_to_target < (float) follow_distance) &&
VisCheckEntFromEnt(LOCAL_E, followtar))
{
idle_time.update();
}
// New crumbs, we add one if its empty so we have something to follow
if ((breadcrumbs.empty() ||
tar_orig.DistTo(breadcrumbs.at(breadcrumbs.size() - 1)) > 40.0F) &&
@ -179,26 +247,32 @@ void WorldTick()
// Prune old and close crumbs that we wont need anymore, update idle timer
// too
while (breadcrumbs.size() > 1 && loc_orig.DistTo(breadcrumbs.at(0)) < 60.f)
for (int i = 0; i < breadcrumbs.size(); i++)
{
idle_time.update();
breadcrumbs.erase(breadcrumbs.begin());
if (loc_orig.DistTo(breadcrumbs.at(i)) < 60.f)
{
idle_time.update();
for (int j = 0; j <= i; j++)
breadcrumbs.erase(breadcrumbs.begin());
}
}
//moved because its worthless otherwise
if (sync_taunt && HasCondition<TFCond_Taunting>(followtar) && lastTaunt.test_and_set(1000)) {
g_IEngine->ClientCmd("taunt");
}
// Follow the crumbs when too far away, or just starting to follow
if (dist_to_target > (float) follow_distance)
{
// Check for idle
if (idle_time.check(3000) ||
(breadcrumbs.size() > 1 && LOCAL_E->m_vecVelocity.IsZero(5.0f)))
if (autojump && idle_time.check(3000))
g_pUserCmd->buttons |= IN_JUMP;
if (idle_time.test_and_set(5000))
{
follow_target = 0;
return;
}
if (sync_taunt && HasCondition<TFCond_Taunting>(ENTITY(follow_target)))
g_IEngine->ClientCmd("taunt");
static float last_slot_check = 0.0f;
if (g_GlobalVars->curtime < last_slot_check)
last_slot_check = 0.0f;

View File

@ -9,6 +9,7 @@
#include <hacks/Trigger.hpp>
#include "common.hpp"
#include <hacks/Backtrack.hpp>
#include <PlayerTools.hpp>
namespace hacks::shared::triggerbot
{
@ -272,6 +273,10 @@ bool IsTargetStateGood(CachedEntity *entity)
if (!entity->m_bEnemy() && !teammates)
return false;
// Global checks
if (player_tools::shouldTarget(entity) != player_tools::IgnoreReason::DO_NOT_IGNORE)
return false;
IF_GAME(IsTF())
{
// If settings allow waiting for charge, and current charge cant
@ -291,9 +296,6 @@ bool IsTargetStateGood(CachedEntity *entity)
return false;
}
}
// If settings allow, ignore taunting players
if (ignore_taunting && HasCondition<TFCond_Taunting>(entity))
return false;
// Dont target invulnerable players, ex: uber, bonk
if (IsPlayerInvulnerable(entity))
return false;
@ -307,17 +309,6 @@ bool IsTargetStateGood(CachedEntity *entity)
HasCondition<TFCond_UberBulletResist>(entity))
return false;
}
// Dont target players marked as friendly
if (playerlist::IsFriendly(playerlist::AccessData(entity).state))
return false;
IF_GAME(IsTF())
{
// If settings allow, ignore hoovys
if (ignore_hoovy && IsHoovy(entity))
{
return false;
}
}
// Head hitbox detection
if (HeadPreferable(entity))

View File

@ -6,6 +6,7 @@
*/
#include <hacks/hacklist.hpp>
#include <online/Online.hpp>
#include "common.hpp"
#include "hitrate.hpp"
#include "hack.hpp"
@ -31,6 +32,7 @@ DEFINE_HOOKED_METHOD(Paint, void, IEngineVGui *this_, PaintMode_t mode)
{
hitrate::Update();
}
online::update();
#if ENABLE_IPC
static Timer nametimer{};
if (nametimer.test_and_set(1000 * 10))

View File

@ -0,0 +1,2 @@
target_sources(cathook PRIVATE
"${CMAKE_CURRENT_LIST_DIR}/Online.cpp")

289
src/online/Online.cpp Normal file
View File

@ -0,0 +1,289 @@
/*
Created on 23.06.18.
*/
#include <online/Online.hpp>
#include <core/cvwrapper.hpp>
#include <unordered_map>
#include <optional>
#include <timer.hpp>
#include <sstream>
#undef null
#include <co/OnlineService.hpp>
#include <fstream>
#include <init.hpp>
#include <thread>
namespace online
{
void saveApiKeyAndHost(std::string host);
void claimSteamId();
static co::OnlineService cathookOnlineService{};
static std::unordered_map<unsigned, std::optional<user_data>> data{};
static std::unordered_map<unsigned, bool> identify_queue{};
static Timer identify_timer{};
static bool identify_stale{ false };
static std::string api_key{};
static CatVar enable(CV_SWITCH, "online", "1", "Enable online features");
static CatCommand login("online_login", "Login", [](const CCommand& args) {
if (args.ArgC() != 3)
{
logging::Info("\nUsage: online_login <API_KEY> \"<IP:PORT>\"\nKey will be saved in your data folder");
return;
}
std::string host(args.Arg(2));
logging::Info("[CO] Host = %s", host.c_str());
try {
cathookOnlineService.setHost(host);
} catch (std::exception& ex)
{
logging::Info("[CO] Error setting host: %s", ex.what());
return;
}
std::string key(args.Arg(1));
try
{
cathookOnlineService.login(key, [key, host](co::ApiCallResult result, std::optional<co::logged_in_user> me) {
if (result == co::ApiCallResult::OK)
{
logging::Info("[CO] Successfully logged in. Welcome, %s", me->username.c_str());
api_key = key;
saveApiKeyAndHost(host);
claimSteamId();
}
else
{
logging::Info("[CO] There was an error logging in: code %d", result);
}
});
} catch (std::exception& ex) {
logging::Info("[CO] Exception: %s", ex.what());
}
});
static CatCommand flush("online_flush_cache", "Flush player cache", [](const CCommand& args) {
data.clear();
identify_queue.clear();
identify_stale = true;
});
// INTERNAL METHODS
void claimSteamId()
{
auto id = g_ISteamUser->GetSteamID();
logging::Info("[CO] Claiming SteamID %u", id.GetAccountID());
try {
cathookOnlineService.gameStartup(id.GetAccountID());
} catch (std::exception& ex) {
logging::Info("[CO] Exception: %s", ex.what());
}
}
bool tryLoadApiKeyAndHost()
{
std::ifstream keyfile(DATA_PATH "/api_key", std::ios::in);
if (keyfile)
{
std::string host{};
keyfile >> api_key >> host;
if (!api_key.empty() && !host.empty())
{
try {
cathookOnlineService.setHost(host);
} catch (std::exception& ex) {
logging::Info("Error while setting host: %s", ex.what());
}
return true;
}
}
return false;
}
void saveApiKeyAndHost(std::string host)
{
std::ofstream keyfile(DATA_PATH "/api_key", std::ios::out);
if (!keyfile)
{
logging::Info("[CO] Something went wrong while saving API key");
return;
}
keyfile << api_key << '\n' << host << '\n';
}
void queueUserForIdentification(unsigned steamId)
{
identify_queue[steamId] = false;
identify_timer.update();
identify_stale = true;
}
void markSteamIdNonOnline(unsigned id)
{
logging::Info("[CO] %u - not online", id);
data[id] = std::nullopt;
}
void processOnlineIdentity(unsigned id, co::identified_user& user)
{
logging::Info("[CO] %u - online", id);
user_data udata{};
udata.username = user.username;
udata.is_anonymous = (user.username == "anonymous");
udata.is_steamid_verified = user.steamid_verified;
for (auto& i: user.groups)
{
if (i.display_name.has_value())
udata.shown_groups.push_back(*i.display_name);
if (i.name == "notarget")
udata.no_target = true;
if (i.name == "owner" || i.name == "contributor")
{
udata.is_developer = true;
#if ENABLE_VISUALS
udata.rainbow = true;
#endif
}
}
#if ENABLE_VISUALS
if (user.color.has_value())
{
udata.has_color = true;
udata.color = colors::rgba_t(user.color->c_str());
}
#endif
if (user.uses_software.has_value())
{
udata.has_software = true;
udata.is_using_friendly_software = user.uses_software->friendly;
udata.software_name = user.uses_software->name;
}
data[id] = std::move(udata);
}
void processIdentifyResponse(std::vector<unsigned> input, co::identified_user_group& group)
{
logging::Info("[CO] Processing identify response containing %u / %u entries", group.users.size(), input.size());
for (auto i: input)
{
auto u = group.users.find(i);
if (u == group.users.end())
markSteamIdNonOnline(i);
else
processOnlineIdentity(i, (*u).second);
identify_queue.erase(i);
logging::Info("[CO] Removed %u from identify queue, left %u\n", i, identify_queue.size());
}
}
void sendIdentifyRequest()
{
std::vector<unsigned> steamIds{};
auto it = identify_queue.begin();
// Create a list of up to 32 steamId's
for (int i = 0; i < 32 && it != identify_queue.end(); ++i, ++it)
{
if (!it->second)
{
it->second = true;
steamIds.push_back(it->first);
}
}
logging::Info("[CO] Sending identify request for %u players", steamIds.size());
try {
cathookOnlineService.userIdentify(steamIds, (std::function<void(co::ApiCallResult, std::optional<co::identified_user_group>)>)[steamIds](co::ApiCallResult result, std::optional<co::identified_user_group> group) {
if (result == co::ApiCallResult::OK)
{
processIdentifyResponse(steamIds, *group);
}
else
{
logging::Info("[CO] Something went wrong while identifying %u players: code %d", steamIds.size(), result);
for (auto i: steamIds)
{
identify_queue[i] = false;
}
identify_stale = true;
}
});
} catch (std::exception& ex) {
logging::Info("[CO] Exception: %s", ex.what());
}
}
InitRoutine init([]() {
cathookOnlineService.setErrorHandler((std::function<void(std::string)>)[](std::string error) {
logging::Info("[CO] Error: %s", error.c_str());
});
if (tryLoadApiKeyAndHost())
{
logging::Info("[CO] API key loaded successfully");
try {
cathookOnlineService.login(api_key, [](co::ApiCallResult result, std::optional<co::logged_in_user> me) {
if (result == co::ApiCallResult::OK)
{
logging::Info("[CO] Successfully logged in. Welcome, %s", me->username.c_str());
claimSteamId();
}
else
{
logging::Info("[CO] There was an error logging in: code %d", result);
}
});
} catch (std::exception& ex) {
logging::Info("[CO] Exception: %s", ex.what());
}
}
});
// EXTERNAL METHODS
void update()
{
if (!enable)
return;
// Only send a request after 3 seconds passed since last unknown steamId was added to the queue
if (!api_key.empty() && identify_stale && identify_timer.check(3000) && !identify_queue.empty())
{
sendIdentifyRequest();
identify_stale = false;
}
try {
cathookOnlineService.processPendingCalls();
} catch (std::exception& ex) {
logging::Info("[CO] Exception: %s", ex.what());
}
}
user_data *getUserData(unsigned steamId)
{
if (!enable)
return nullptr;
if (!steamId)
return nullptr;
auto it = data.find(steamId);
// User not identified
if (it == data.end())
{
// Queue user for identification
if (identify_queue.find(steamId) == identify_queue.end())
queueUserForIdentification(steamId);
return nullptr;
}
// SteamID belongs to online user
if (it->second.has_value())
return &*it->second;
// SteamID does not belong to online user
return nullptr;
}
}

View File

@ -227,8 +227,6 @@ bool EffectChams::ShouldRenderChams(IClientEntity *entity)
if (entity->entindex() < 0)
return false;
CachedEntity *ent = ENTITY(entity->entindex());
if (CE_BAD(ent))
return false;
if (ent->m_IDX == LOCAL_E->m_IDX && !chamsself)
return false;
switch (ent->m_Type())
@ -319,12 +317,9 @@ void EffectChams::RenderChamsRecursive(IClientEntity *entity)
}
}
void EffectChams::RenderChams(int idx)
void EffectChams::RenderChams(IClientEntity *entity)
{
CMatRenderContextPtr ptr(GET_RENDER_CONTEXT);
IClientEntity *entity = g_IEntityList->GetClientEntity(idx);
if (entity && !entity->IsDormant())
{
if (ShouldRenderChams(entity))
{
rgba_t color = ChamsColor(entity);
@ -348,7 +343,6 @@ void EffectChams::RenderChams(int idx)
g_IVModelRender->ForcedMaterialOverride(flat ? mat_unlit
: mat_lit);
RenderChamsRecursive(entity);
}
}
}
}
@ -366,11 +360,10 @@ void EffectChams::Render(int x, int y, int w, int h)
BeginRenderChams();
for (int i = 1; i < HIGHEST_ENTITY; i++)
{
IClientEntity *ent = g_IEntityList->GetClientEntity(i);
if (ent && !ent->IsDormant())
{
RenderChams(i);
}
IClientEntity *entity = g_IEntityList->GetClientEntity(i);
if (!entity || entity->IsDormant() || CE_BAD(ENTITY(i)))
return;
RenderChams(entity);
}
EndRenderChams();
}

View File

@ -5,6 +5,8 @@
* Author: nullifiedcat
*/
#include <online/Online.hpp>
#include <PlayerTools.hpp>
#include "common.hpp"
static CatVar user_red_blue(CV_INT, "esp_color_red_b", "0", "Red Team: Blue",
@ -130,9 +132,10 @@ rgba_t colors::EntityF(CachedEntity *ent)
else if (ent->m_iTeam() == TEAM_RED)
result = red_v;
}
plclr = playerlist::Color(ent);
if (plclr.a)
result = plclr;
auto o = player_tools::forceEspColor(ent);
if (o.has_value())
return *o;
}
}
@ -144,3 +147,30 @@ rgba_t colors::RainbowCurrent()
return colors::FromHSL(fabs(sin(g_GlobalVars->curtime / 2.0f)) * 360.0f,
0.85f, 0.9f);
}
static unsigned char hexToChar(char i)
{
if (i >= '0' && i <= '9')
return i - '0';
if (i >= 'a' && i <= 'f')
return i - 'a' + 10;
if (i >= 'A' && i <= 'F')
return i - 'A' + 10;
return 0;
}
static unsigned int hexToByte(char hi, char lo)
{
return (hexToChar(hi) << 4) | (hexToChar(lo));
}
colors::rgba_t::rgba_t(const char hex[6])
{
auto ri = hexToByte(hex[0], hex[1]);
auto gi = hexToByte(hex[2], hex[3]);
auto bi = hexToByte(hex[4], hex[5]);
r = float(ri) / 255.0f;
g = float(gi) / 255.0f;
b = float(bi) / 255.0f;
a = 1.0f;
}