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/CatBot.cpp
2017-12-31 10:54:15 +03:00

242 lines
5.8 KiB
C++

/*
* 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<unsigned, catbot_user_state> 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++;
logging::Info("Treacherous kill #%d: %s [U:1:%u]", human_detecting_map[steamID].treacherous_kills, player->player_info.name, player->player_info.friendsID);
}
void do_random_votekick()
{
std::vector<int> 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;
int target = targets[rand() % targets.size()];
player_info_s info;
if (!g_IEngine->GetPlayerInfo(g_IEngine->GetPlayerForUserID(target), &info))
return;
logging::Info("Calling vote to kick '%s' [U:1:%u] (%d / %u)", info.name, info.friendsID, target, targets.size());
hack::ExecuteCommand("callvote kick " + std::to_string(target) + " cheating");
}
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())
{
logging::Info("Found bot %s [U:1:%u]", info.name, info.friendsID);
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);
}
}
}
}