From ae6f1f2e5f924fbe7554052e2ebc7e6e6018e931 Mon Sep 17 00:00:00 2001 From: nullifiedcat Date: Sat, 30 Dec 2017 19:14:53 +0300 Subject: [PATCH] catbot-update --- include/hacks/CatBot.hpp | 22 ++++ include/hacks/hacklist.hpp | 1 + include/ipc.hpp | 61 +++++++--- src/hack.cpp | 1 + src/hacks/CatBot.cpp | 234 +++++++++++++++++++++++++++++++++++++ src/hacks/Walkbot.cpp | 78 +------------ src/hooks/others.cpp | 7 +- src/ipc.cpp | 101 +++++++++------- 8 files changed, 362 insertions(+), 143 deletions(-) create mode 100644 include/hacks/CatBot.hpp create mode 100644 src/hacks/CatBot.cpp diff --git a/include/hacks/CatBot.hpp b/include/hacks/CatBot.hpp new file mode 100644 index 00000000..5a4d6b5e --- /dev/null +++ b/include/hacks/CatBot.hpp @@ -0,0 +1,22 @@ +/* + * CatBot.hpp + * + * Created on: Dec 30, 2017 + * Author: nullifiedcat + */ + +#pragma once + +#include "common.hpp" + +namespace hacks { namespace shared { namespace catbot { + +bool is_a_catbot(unsigned steamID); +bool should_ignore_player(CachedEntity *player); +void update_ipc_data(ipc::user_data_s& data); +void update(); +void init(); + +} +} +} diff --git a/include/hacks/hacklist.hpp b/include/hacks/hacklist.hpp index 2c433a33..46c2b48f 100644 --- a/include/hacks/hacklist.hpp +++ b/include/hacks/hacklist.hpp @@ -42,5 +42,6 @@ #include "FollowBot.hpp" #include "Announcer.hpp" #include "Killstreak.hpp" +#include "CatBot.hpp" #endif /* HACKS_HACKLIST_HPP_ */ diff --git a/include/ipc.hpp b/include/ipc.hpp index 65e3166b..c27b279c 100644 --- a/include/ipc.hpp +++ b/include/ipc.hpp @@ -48,34 +48,63 @@ struct server_data_s struct user_data_s { char name[32]; - char server[22]; unsigned friendid; + bool connected; - bool good; - int health; - int health_max; - char life_state; - int team; - int clazz; - int score; - int last_score; - int total_score; + time_t heartbeat; - float x; - float y; - float z; + time_t ts_injected; time_t ts_connected; time_t ts_disconnected; - int shots; - int hits; - int headshots; + + struct accumulated_t + { + int kills; // TODO + int deaths; // TODO + int score; + + int shots; + int hits; + int headshots; + } accumulated; + + struct + { + bool good; + + int kills; // TODO + int deaths; // TODO + int score; + + int shots; // TODO + int hits; // TODO + int headshots; // TODO + + int team; + int role; // class + char life_state; + int health; + int health_max; + + float x; + float y; + float z; + + int player_count; + int bot_count; + + char server[24]; + char mapname[32]; + } ingame; }; using peer_t = cat_ipc::Peer; extern peer_t *peer; +void update_mapname(); + void Heartbeat(); void UpdateTemporaryData(); void UpdateServerAddress(bool shutdown = false); diff --git a/src/hack.cpp b/src/hack.cpp index 9723bb50..68ae0b94 100644 --- a/src/hack.cpp +++ b/src/hack.cpp @@ -343,6 +343,7 @@ g_pGUI->Setup(); hacks::shared::killsay::Init(); hacks::shared::announcer::init(); hacks::tf2::killstreak::init(); + hacks::shared::catbot::init(); logging::Info("Hooked!"); velocity::Init(); playerlist::Load(); diff --git a/src/hacks/CatBot.cpp b/src/hacks/CatBot.cpp new file mode 100644 index 00000000..c1862219 --- /dev/null +++ b/src/hacks/CatBot.cpp @@ -0,0 +1,234 @@ +/* + * CatBot.cpp + * + * Created on: Dec 30, 2017 + * Author: nullifiedcat + */ + +#include "common.hpp" +#include "hack.hpp" + +namespace hacks { namespace shared { namespace catbot { + +static CatVar enabled(CV_SWITCH, "cbu", "0", "CatBot Utils"); +static CatVar abandon_if_bots_gte(CV_INT, "cbu_abandon_if_bots_gte", "0", "Abandon if bots >="); +static CatVar abandon_if_ipc_bots_gte(CV_INT, "cbu_abandon_if_ipc_bots_gte", "0", "Abandon if IPC bots >="); +static CatVar abandon_if_humans_lte(CV_INT, "cbu_abandon_if_humans_lte", "0", "Abandon if humans <="); +static CatVar abandon_if_players_lte(CV_INT, "cbu_abandon_if_players_lte", "0", "Abandon if players <="); +static CatVar mark_human_threshold(CV_INT, "cbu_mark_human_threshold", "2", "Mark human after N kills"); +static CatVar random_votekicks(CV_SWITCH, "cbu_random_votekicks", "0", "Randomly initiate votekicks"); + +struct catbot_user_state +{ + int treacherous_kills{ 0 }; +}; + +std::unordered_map human_detecting_map{}; + +bool is_a_catbot(unsigned steamID) +{ + auto it = human_detecting_map.find(steamID); + if (it == human_detecting_map.end()) + return false; + + //if (!(*it).second.has_bot_name) + // return false; + + if ((*it).second.treacherous_kills <= int(mark_human_threshold)) + { + return true; + } + + return false; +} + +void on_killed_by(int userid) +{ + CachedEntity *player = ENTITY(g_IEngine->GetPlayerForUserID(userid)); + + if (CE_BAD(player)) + return; + + unsigned steamID = player->player_info.friendsID; + + if (human_detecting_map.find(steamID) == human_detecting_map.end()) + return; + + //if (human_detecting_map[steamID].has_bot_name) + human_detecting_map[steamID].treacherous_kills++; +} + +void do_random_votekick() +{ + std::vector targets; + for (int i = 1; i <= g_GlobalVars->maxClients; ++i) + { + player_info_s info; + if (!g_IEngine->GetPlayerInfo(i, &info)) + continue; + + if (g_pPlayerResource->GetTeam(i) != g_pLocalPlayer->team) + continue; + + if (is_a_catbot(info.friendsID)) + continue; + + targets.push_back(info.userID); + } + + if (targets.empty()) + return; + + hack::ExecuteCommand("callvote kick " + std::to_string(targets[rand() % targets.size()])); +} + +void update_catbot_list() +{ + for (int i = 1; i < g_GlobalVars->maxClients; ++i) + { + player_info_s info; + if (!g_IEngine->GetPlayerInfo(i, &info)) + continue; + + info.name[31] = 0; + if (strcasestr(info.name, "cat-bot") || + strcasestr(info.name, "just disable vac tf")) + { + if (human_detecting_map.find(info.friendsID) == human_detecting_map.end()) + { + human_detecting_map.insert(std::make_pair(info.friendsID, catbot_user_state{ 0 })); + } + } + } + +} + +class CatBotEventListener : public IGameEventListener2 +{ + virtual void FireGameEvent(IGameEvent *event) + { + if (!enabled) + return; + + int killer_id = g_IEngine->GetPlayerForUserID(event->GetInt("attacker")); + int victim_id = g_IEngine->GetPlayerForUserID(event->GetInt("userid")); + + if (victim_id == g_IEngine->GetLocalPlayer()) + { + on_killed_by(killer_id); + return; + } + } +}; + +CatBotEventListener &listener() +{ + static CatBotEventListener object{}; + return object; +} + +Timer timer_votekicks{}; +Timer timer_catbot_list{}; +Timer timer_abandon{}; + +int count_bots{ 0 }; + +bool should_ignore_player(CachedEntity *player) +{ + if (CE_BAD(player)) + return false; + + return is_a_catbot(player->player_info.friendsID); +} + +void update_ipc_data(ipc::user_data_s& data) +{ + data.ingame.bot_count = count_bots; +} + +void update() +{ + if (!enabled) + return; + + if (g_Settings.bInvalid) + return; + + if (CE_BAD(LOCAL_E)) + return; + + if (random_votekicks && timer_votekicks.test_and_set(5000)) + do_random_votekick(); + if (timer_catbot_list.test_and_set(3000)) + update_catbot_list(); + if (timer_abandon.test_and_set(2000)) + { + count_bots = 0; + int count_ipc = 0; + int count_total = 0; + + for (int i = 1; i <= g_GlobalVars->maxClients; ++i) + { + if (g_IEntityList->GetClientEntity(i)) + ++count_total; + else + continue; + + player_info_s info; + if (!g_IEngine->GetPlayerInfo(i, &info)) + continue; + + if (is_a_catbot(info.friendsID)) + ++count_bots; + + if (playerlist::AccessData(info.friendsID).state == playerlist::k_EState::IPC) + ++count_ipc; + } + + if (abandon_if_bots_gte) + { + if (count_bots >= int(abandon_if_bots_gte)) + { + logging::Info("Abandoning because there are %d bots in game, and abandon_if_bots_gte is %d.", count_bots, int(abandon_if_bots_gte)); + tfmm::abandon(); + return; + } + } + if (abandon_if_ipc_bots_gte) + { + if (count_ipc >= int(abandon_if_ipc_bots_gte)) + { + logging::Info("Abandoning because there are %d local players in game, and abandon_if_ipc_bots_gte is %d.", count_ipc, int(abandon_if_ipc_bots_gte)); + tfmm::abandon(); + return; + } + } + if (abandon_if_humans_lte) + { + if (count_total - count_bots <= int(abandon_if_humans_lte)) + { + logging::Info("Abandoning because there are %d non-bots in game, and abandon_if_humans_lte is %d.", count_total - count_bots, int(abandon_if_ipc_bots_gte)); + tfmm::abandon(); + return; + } + } + if (abandon_if_players_lte) + { + if (count_total <= int(abandon_if_humans_lte)) + { + logging::Info("Abandoning because there are %d total players in game, and abandon_if_humans_lte is %d.", count_total, int(abandon_if_humans_lte)); + tfmm::abandon(); + return; + } + } + } +} + +void init() +{ + g_IEventManager2->AddListener(&listener(), "player_death", false); +} + +} +} +} diff --git a/src/hacks/Walkbot.cpp b/src/hacks/Walkbot.cpp index 87ebbd0e..4c94b1fc 100644 --- a/src/hacks/Walkbot.cpp +++ b/src/hacks/Walkbot.cpp @@ -1148,78 +1148,6 @@ void OnLevelInit() } } -static CatVar wb_abandon_too_many_bots(CV_INT, "wb_population_control", "0", - "Abandon if bots >"); - -Timer abandon_timer{}; - -CatVar wb_abandon_players_leq(CV_SWITCH, "wb_abandon_players_leq", "0", "Leave if non-bot player count <="); - -void check_player_count() -{ -#if ENABLE_IPC - if (ipc::peer && wb_abandon_players_leq) - { - - } -#endif -} - -void CheckLivingSpace() -{ -#if ENABLE_IPC - if (ipc::peer && wb_abandon_too_many_bots) - { - std::vector players{}; - for (int j = 1; j < 32; j++) - { - player_info_s info; - if (g_IEngine->GetPlayerInfo(j, &info)) - { - if (info.friendsID) - players.push_back(info.friendsID); - } - } - int count = 0; - unsigned highest = 0; - std::vector botlist{}; - for (unsigned i = 0; i < cat_ipc::max_peers; i++) - { - if (!ipc::peer->memory->peer_data[i].free) - { - for (auto &k : players) - { - if (ipc::peer->memory->peer_user_data[i].friendid && - k == ipc::peer->memory->peer_user_data[i].friendid) - { - botlist.push_back(i); - count++; - highest = i; - } - } - } - } - - if (ipc::peer->client_id == highest && - count > int(wb_abandon_too_many_bots)) - { - if (abandon_timer.test_and_set(1000 * 5)) - { - logging::Info("Found %d other bots in-game, abandoning (%u)", - count, ipc::peer->client_id); - for (auto i : botlist) - { - logging::Info( - "-> Bot %d with ID %u", i, - ipc::peer->memory->peer_user_data[i].friendid); - } - tfmm::abandon(); - } - } - } -#endif -} - Timer quit_timer{}; void Move() @@ -1254,14 +1182,10 @@ void Move() { logging::Info("No map file, abandon"); tfmm::abandon(); + return; } } } - static Timer livingspace_timer{}; - if (livingspace_timer.test_and_set(1000 * 2)) - { - CheckLivingSpace(); - } if (nodes.size() == 0) return; if (force_slot) diff --git a/src/hooks/others.cpp b/src/hooks/others.cpp index c98830bc..6d31562d 100644 --- a/src/hooks/others.cpp +++ b/src/hooks/others.cpp @@ -384,7 +384,7 @@ void Shutdown_hook(void *_this, const char *reason) const Shutdown_t original = (Shutdown_t) hooks::netchannel.GetMethod(offsets::Shutdown()); logging::Info("Disconnect: %s", reason); - if (strstr(reason, "VAC banned")) + if (strstr(reason, "banned")) { if (die_if_vac) { @@ -393,10 +393,6 @@ void Shutdown_hook(void *_this, const char *reason) exit(1); } } - else if (strstr(reason, "VAC")) - { - logging::Info("VAC error?"); - } #if ENABLE_IPC ipc::UpdateServerAddress(true); #endif @@ -618,6 +614,7 @@ void FrameStageNotify_hook(void *_this, int stage) if (resolver && cathook && !g_Settings.bInvalid && stage == FRAME_NET_UPDATE_POSTDATAUPDATE_START) { + hacks::shared::catbot::update(); PROF_SECTION(FSN_resolver); for (int i = 1; i < 32 && i < HIGHEST_ENTITY; i++) { diff --git a/src/ipc.cpp b/src/ipc.cpp index d121a55c..ad79b8f0 100644 --- a/src/ipc.cpp +++ b/src/ipc.cpp @@ -51,10 +51,13 @@ CatCommand connect("ipc_connect", "Connect to IPC server", []() { }); hacks::shared::followbot::AddMessageHandlers(peer); user_data_s &data = peer->memory->peer_user_data[peer->client_id]; - // Preserve total score - int o_total_score = data.total_score; + + // Preserve accumulated data + ipc::user_data_s::accumulated_t accumulated; + memcpy(&accumulated, &data.accumulated, sizeof(accumulated)); memset(&data, 0, sizeof(data)); - data.total_score = o_total_score; + memcpy(&data.accumulated, &accumulated, sizeof(accumulated)); + StoreClientData(); Heartbeat(); } @@ -66,21 +69,6 @@ CatCommand connect("ipc_connect", "Connect to IPC server", []() { } }); -CatCommand lobby("ipc_lobby", "Join a lobby", [](const CCommand &args) { - std::string input(args.ArgS()); - std::size_t lobby_id_start = input.find("[L:1:"); - if (lobby_id_start == std::string::npos) - { - logging::Info("couldn't find lobby ID!"); - return; - } - input = input.substr(lobby_id_start + 5); - unsigned long lobby32 = strtoul(input.c_str(), nullptr, 10); - unsigned long long lobby64 = ((25559040ull << 32) | lobby32); - logging::Info("lobby64 ID: %llu", lobby64); - peer->SendMessage(format("connect_lobby ", lobby64).c_str(), 0, - ipc::commands::execute_client_cmd, 0, 0); -}); CatCommand disconnect("ipc_disconnect", "Disconnect from IPC server", []() { if (peer) delete peer; @@ -194,49 +182,72 @@ void UpdateServerAddress(bool shutdown) user_data_s &data = peer->memory->peer_user_data[peer->client_id]; data.friendid = g_ISteamUser->GetSteamID().GetAccountID(); - strncpy(data.server, s_addr, sizeof(data.server)); + strncpy(data.ingame.server, s_addr, sizeof(data.ingame.server)); +} + +void update_mapname() +{ + if (not peer) + return; + + user_data_s &data = peer->memory->peer_user_data[peer->client_id]; + strncpy(data.ingame.mapname, GetLevelName().c_str(), sizeof(data.ingame.mapname)); } void UpdateTemporaryData() { user_data_s &data = peer->memory->peer_user_data[peer->client_id]; + data.connected = g_IEngine->IsInGame(); - data.shots = hitrate::count_shots; - data.hits = hitrate::count_hits; - data.headshots = hitrate::count_hits_head; + // TODO kills, deaths + data.accumulated.shots = hitrate::count_shots; + data.accumulated.hits = hitrate::count_hits; + data.accumulated.headshots = hitrate::count_hits_head; + if (data.connected) { IClientEntity *player = g_IEntityList->GetClientEntity(g_IEngine->GetLocalPlayer()); if (player) { - data.good = true; - data.health = NET_INT(player, netvar.iHealth); - data.health_max = g_pPlayerResource->GetMaxHealth(LOCAL_E); - data.clazz = g_pPlayerResource->GetClass(LOCAL_E); - data.life_state = NET_BYTE(player, netvar.iLifeState); - data.score = + data.ingame.good = true; + // TODO kills, deaths, shots, hits, headshots + + int score_saved = data.ingame.score; + + data.ingame.score = g_pPlayerResource->GetScore(g_IEngine->GetLocalPlayer()); - if (data.last_score != data.score) - { - if (data.last_score > data.score) - { - data.total_score += data.score; - } - else - { - data.total_score += (data.score - data.last_score); - } - data.last_score = data.score; - } - data.team = g_pPlayerResource->GetTeam(g_IEngine->GetLocalPlayer()); - data.x = g_pLocalPlayer->v_Origin.x; - data.y = g_pLocalPlayer->v_Origin.y; - data.z = g_pLocalPlayer->v_Origin.z; + data.ingame.team = g_pPlayerResource->GetTeam(g_IEngine->GetLocalPlayer()); + data.ingame.role = g_pPlayerResource->GetClass(LOCAL_E); + data.ingame.life_state = NET_BYTE(player, netvar.iLifeState); + data.ingame.health = NET_INT(player, netvar.iHealth); + data.ingame.health_max = g_pPlayerResource->GetMaxHealth(LOCAL_E); + + if (score_saved > data.ingame.score) + score_saved = 0; + + data.accumulated.score += data.ingame.score - score_saved; + + data.ingame.x = g_pLocalPlayer->v_Origin.x; + data.ingame.y = g_pLocalPlayer->v_Origin.y; + data.ingame.z = g_pLocalPlayer->v_Origin.z; + + int players = 0; + + for (int i = 1; i <= g_GlobalVars->maxClients; ++i) + { + if (g_IEntityList->GetClientEntity(i)) + ++players; + else + continue; + } + + data.ingame.player_count = players; + hacks::shared::catbot::update_ipc_data(data); } else { - data.good = false; + data.ingame.good = false; } } }