catbot-update

This commit is contained in:
nullifiedcat 2017-12-30 19:14:53 +03:00
parent b5d67b257e
commit ae6f1f2e5f
8 changed files with 362 additions and 143 deletions

22
include/hacks/CatBot.hpp Normal file
View File

@ -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();
}
}
}

View File

@ -42,5 +42,6 @@
#include "FollowBot.hpp"
#include "Announcer.hpp"
#include "Killstreak.hpp"
#include "CatBot.hpp"
#endif /* HACKS_HACKLIST_HPP_ */

View File

@ -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<server_data_s, user_data_s>;
extern peer_t *peer;
void update_mapname();
void Heartbeat();
void UpdateTemporaryData();
void UpdateServerAddress(bool shutdown = false);

View File

@ -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();

234
src/hacks/CatBot.cpp Normal file
View File

@ -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<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++;
}
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;
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);
}
}
}
}

View File

@ -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<unsigned> 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<unsigned> 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)

View File

@ -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++)
{

View File

@ -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;
}
}
}