t7x/src/client/component/bots.cpp

163 lines
3.5 KiB
C++
Raw Normal View History

2022-11-20 11:44:08 -05:00
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "command.hpp"
2022-11-20 11:44:08 -05:00
#include <utils/hook.hpp>
2023-02-16 17:29:24 -05:00
#include <utils/io.hpp>
#include <utils/string.hpp>
2022-11-20 11:44:08 -05:00
#include <game/game.hpp>
2023-02-27 06:32:29 -05:00
#include "getinfo.hpp"
2022-11-20 11:44:08 -05:00
namespace bots
{
namespace
{
2023-02-16 17:29:24 -05:00
constexpr auto* bot_format_string = "connect \"\\invited\\1\\cg_predictItems\\1\\cl_anonymous\\0\\color\\4\\head\\default\\model\\multi\\snaps\\20\\rate\\"
"5000\\name\\%s\\clanAbbrev\\%s\\xuid\\%s\\xnaddr\\%s\\natType\\2\\protocol\\%d\\netfieldchk\\%d\\sessionmode\\%s\\qport\\%d\"";
using bot_name = std::pair<std::string, std::string>;
std::vector<bot_name> load_bots_names()
{
std::vector<bot_name> bot_names =
{
{"momo5502", "IW5x"},
{"Jasmin", "<3"},
{"Dss0", "IW3x"},
{"FutureRave", "FR"},
{"Diamante", "IW2x"},
{"St0rm", "NN"},
{"Joel", "NN"},
{"Louve", "IW5x"},
2023-02-16 17:29:24 -05:00
};
std::string buffer;
if (!utils::io::read_file("boiii_cfg/bots.txt", &buffer) || buffer.empty())
{
return bot_names;
}
auto data = utils::string::split(buffer, '\n');
for (auto& entry : data)
{
utils::string::replace(entry, "\r", "");
utils::string::trim(entry);
if (entry.empty())
{
continue;
}
std::string clan_abbrev;
// Check if there is a clan tag
if (const auto pos = entry.find(','); pos != std::string::npos)
{
// Only start copying over from non-null characters (otherwise it can be "<=")
if ((pos + 1) < entry.size())
{
clan_abbrev = entry.substr(pos + 1);
}
entry = entry.substr(0, pos);
}
bot_names.emplace_back(entry, clan_abbrev);
2023-02-16 17:29:24 -05:00
}
return bot_names;
}
const std::vector<bot_name>& get_bot_names()
2022-11-20 11:44:08 -05:00
{
static const auto bot_names = []
{
2023-02-16 17:29:24 -05:00
auto names = load_bots_names();
2022-11-20 11:44:08 -05:00
std::random_device rd;
std::mt19937 gen(rd());
std::ranges::shuffle(names, gen);
return names;
}();
return bot_names;
}
const char* get_bot_name()
{
static size_t current = 0;
const auto& names = get_bot_names();
current = (current + 1) % names.size();
2023-02-16 17:29:24 -05:00
return names.at(current).first.data();
}
int format_bot_string(char* buffer, [[maybe_unused]] const char* format, const char* name, const char* xuid,
const char* xnaddr, int protocol, int net_field_chk, const char* session_mode, int qport)
2023-02-16 17:29:24 -05:00
{
2023-02-16 17:32:05 -05:00
const auto find_name = [](const std::string& needle) -> const char*
2023-02-16 17:29:24 -05:00
{
for (const auto& entry : get_bot_names())
{
2023-02-16 17:32:05 -05:00
if (entry.first == needle)
2023-02-16 17:29:24 -05:00
{
return entry.second.data();
}
}
return "3arc";
};
return sprintf_s(buffer, 1024, bot_format_string, name, find_name(name), xuid, xnaddr, protocol, net_field_chk, session_mode, qport);
2022-11-20 11:44:08 -05:00
}
}
2023-01-02 09:18:37 -05:00
struct component final : generic_component
2022-11-20 11:44:08 -05:00
{
2023-04-03 13:14:45 -04:00
static_assert(offsetof(game::client_s, bIsTestClient) == 0xBB360);
2022-11-20 11:44:08 -05:00
void post_unpack() override
{
2023-01-02 09:18:37 -05:00
utils::hook::jump(game::select(0x141653B70, 0x1402732E0), get_bot_name);
2023-03-04 11:34:04 -05:00
utils::hook::call(game::select(0x142249097, 0x14052E53A), format_bot_string);
2023-01-02 09:18:37 -05:00
if (!game::is_server())
{
2023-02-16 17:29:24 -05:00
utils::hook::jump(0x141654280_g, get_bot_name); // SV_ZombieNameRandom
2023-01-02 09:18:37 -05:00
}
command::add("spawnBot", [](const command::params& params)
{
2023-02-27 06:32:29 -05:00
if (!getinfo::is_host())
{
2023-02-27 06:32:29 -05:00
return;
}
size_t count = 1;
if (params.size() > 1)
{
if (params[1] == "all"s)
{
count = 18;
}
else
{
count = atoi(params[1]);
}
}
for (size_t i = 0; i < count; ++i)
{
if (!game::SV_AddTestClient())
{
break;
}
}
});
2022-11-20 11:44:08 -05:00
}
};
}
REGISTER_COMPONENT(bots::component)