[Bots]: Get additional names from Patreon (#771)

This commit is contained in:
Edo 2023-02-13 20:33:26 +00:00 committed by GitHub
parent 7189eab75c
commit ffd7a59f5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 133 additions and 60 deletions

View File

@ -1,5 +1,8 @@
#include <STDInclude.hpp> #include <STDInclude.hpp>
#include <Utils/InfoString.hpp>
#include "Bots.hpp" #include "Bots.hpp"
#include "ServerList.hpp"
#include "GSC/Script.hpp" #include "GSC/Script.hpp"
@ -11,7 +14,7 @@ namespace Components
{ {
std::vector<Bots::botData> Bots::BotNames; std::vector<Bots::botData> Bots::BotNames;
Dvar::Var Bots::SVRandomBotNames; Dvar::Var Bots::SVClanName;
struct BotMovementInfo struct BotMovementInfo
{ {
@ -49,72 +52,112 @@ namespace Components
{ "activate", Game::CMD_BUTTON_ACTIVATE }, { "activate", Game::CMD_BUTTON_ACTIVATE },
}; };
void Bots::RandomizeBotNames()
{
std::random_device rd;
std::mt19937 gen(rd());
std::ranges::shuffle(BotNames, gen);
}
void Bots::UpdateBotNames()
{
const auto masterPort = (*Game::com_masterPort)->current.integer;
const auto* masterServerName = (*Game::com_masterServerName)->current.string;
Game::netadr_t master;
if (ServerList::GetMasterServer(masterServerName, masterPort, master))
{
Logger::Print("Getting bots...\n");
Network::Send(master, "getbots");
}
}
void Bots::LoadBotNames()
{
FileSystem::File bots("bots.txt");
if (!bots.exists())
{
return;
}
auto data = Utils::String::Split(bots.getBuffer(), '\n');
auto i = 0;
for (auto& entry : data)
{
if (i >= 18)
{
// Only parse 18 names from the file
break;
}
// Take into account for CR line endings
Utils::String::Replace(entry, "\r", "");
// Remove whitespace
Utils::String::Trim(entry);
if (entry.empty())
{
continue;
}
std::string clanAbbrev;
// 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())
{
clanAbbrev = entry.substr(pos + 1);
}
entry = entry.substr(0, pos);
}
BotNames.emplace_back(std::make_pair(entry, clanAbbrev));
++i;
}
if (i)
{
RandomizeBotNames();
}
}
int Bots::BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port) int Bots::BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port)
{ {
static size_t botId = 0; // Loop over the BotNames vector static size_t botId = 0; // Loop over the BotNames vector
static bool loadedNames = false; // Load file only once static bool loadedNames = false; // Load file only once
const char* botName; std::string botName;
const char* clanName; std::string clanName;
if (BotNames.empty() && !loadedNames) if (!loadedNames)
{ {
FileSystem::File bots("bots.txt");
loadedNames = true; loadedNames = true;
LoadBotNames();
if (bots.exists())
{
auto data = Utils::String::Split(bots.getBuffer(), '\n');
if (SVRandomBotNames.get<bool>())
{
std::random_device rd;
std::mt19937 gen(rd());
std::ranges::shuffle(data, gen);
}
for (auto& entry : data)
{
// Take into account for CR line endings
Utils::String::Replace(entry, "\r", "");
// Remove whitespace
Utils::String::Trim(entry);
if (!entry.empty())
{
std::string clanAbbrev;
// 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())
{
clanAbbrev = entry.substr(pos + 1);
}
entry = entry.substr(0, pos);
}
BotNames.emplace_back(std::make_pair(entry, clanAbbrev));
}
}
}
} }
if (!BotNames.empty()) if (!BotNames.empty())
{ {
botId %= BotNames.size(); botId %= BotNames.size();
const auto index = botId++; const auto index = botId++;
botName = BotNames[index].first.data(); botName = BotNames[index].first;
clanName = BotNames[index].second.data(); clanName = BotNames[index].second;
} }
else else
{ {
botName = Utils::String::VA("bot%d", ++botId); botName = std::format("bot{}", ++botId);
clanName = "BOT"; clanName = "BOT"s;
} }
return _snprintf_s(buffer, 0x400, _TRUNCATE, connectString, num, botName, clanName, protocol, checksum, statVer, statStuff, port); if (const auto svClanName = SVClanName.get<std::string>(); !svClanName.empty())
{
clanName = svClanName;
}
return _snprintf_s(buffer, 0x400, _TRUNCATE, connectString, num, botName.data(), clanName.data(), protocol, checksum, statVer, statStuff, port);
} }
void Bots::Spawn(unsigned int count) void Bots::Spawn(unsigned int count)
@ -352,7 +395,30 @@ namespace Components
Utils::Hook(0x441B80, G_SelectWeaponIndex_Hk, HOOK_JUMP).install()->quick(); Utils::Hook(0x441B80, G_SelectWeaponIndex_Hk, HOOK_JUMP).install()->quick();
SVRandomBotNames = Dvar::Register<bool>("sv_randomBotNames", false, Game::DVAR_NONE, "Randomize the bots' names"); Events::OnDvarInit([]
{
SVClanName = Dvar::Register<const char*>("sv_clanName", "", Game::DVAR_NONE, "The clan name for test clients");
});
Scheduler::OnGameInitialized(UpdateBotNames, Scheduler::Pipeline::MAIN);
Network::OnClientPacket("getbotsResponse", [](const Network::Address& address, const std::string& data)
{
const auto masterPort = (*Game::com_masterPort)->current.integer;
const auto* masterServerName = (*Game::com_masterServerName)->current.string;
Network::Address master(Utils::String::VA("%s:%u", masterServerName, masterPort));
if (master == address)
{
auto botNames = Utils::String::Split(data, '\n');
for (const auto& entry : botNames)
{
BotNames.emplace_back(std::make_pair(entry, "BOT"));
}
RandomizeBotNames();
}
});
// Reset BotMovementInfo.active when client is dropped // Reset BotMovementInfo.active when client is dropped
Events::OnClientDisconnect([](const int clientNum) Events::OnClientDisconnect([](const int clientNum)

View File

@ -11,8 +11,11 @@ namespace Components
using botData = std::pair< std::string, std::string>; using botData = std::pair< std::string, std::string>;
static std::vector<botData> BotNames; static std::vector<botData> BotNames;
static Dvar::Var SVRandomBotNames; static Dvar::Var SVClanName;
static void RandomizeBotNames();
static void UpdateBotNames();
static void LoadBotNames();
static int BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port); static int BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port);
static void Spawn(unsigned int count); static void Spawn(unsigned int count);

View File

@ -165,8 +165,8 @@ namespace Components
return; return;
} }
auto masterPort = Dvar::Var("masterPort").get<int>(); const auto masterPort = (*Game::com_masterPort)->current.integer;
const auto* masterServerName = Dvar::Var("masterServerName").get<const char*>(); const auto* masterServerName = (*Game::com_masterServerName)->current.string;
Network::Address master(Utils::String::VA("%s:%u", masterServerName, masterPort)); Network::Address master(Utils::String::VA("%s:%u", masterServerName, masterPort));

View File

@ -299,8 +299,8 @@ namespace Components
} }
else if (IsOnlineList()) else if (IsOnlineList())
{ {
const auto masterPort = Dvar::Var("masterPort").get<int>(); const auto masterPort = (*Game::com_masterPort)->current.integer;
const auto masterServerName = Dvar::Var("masterServerName").get<const char*>(); const auto* masterServerName = (*Game::com_masterServerName)->current.string;
// Check if our dvars can properly convert to a address // Check if our dvars can properly convert to a address
Game::netadr_t masterServerAddr; Game::netadr_t masterServerAddr;

View File

@ -37,6 +37,8 @@ namespace Game
const dvar_t** com_timescale = reinterpret_cast<const dvar_t**>(0x1AD7920); const dvar_t** com_timescale = reinterpret_cast<const dvar_t**>(0x1AD7920);
const dvar_t** com_maxFrameTime = reinterpret_cast<const dvar_t**>(0x1AD78F4); const dvar_t** com_maxFrameTime = reinterpret_cast<const dvar_t**>(0x1AD78F4);
const dvar_t** com_sv_running = reinterpret_cast<const dvar_t**>(0x1AD7934); const dvar_t** com_sv_running = reinterpret_cast<const dvar_t**>(0x1AD7934);
const dvar_t** com_masterServerName = reinterpret_cast<const dvar_t**>(0x1AD8F48);
const dvar_t** com_masterPort = reinterpret_cast<const dvar_t**>(0x1AD8F30);
const dvar_t** dev_timescale = reinterpret_cast<const dvar_t**>(0x1AD8F20); const dvar_t** dev_timescale = reinterpret_cast<const dvar_t**>(0x1AD8F20);

View File

@ -89,6 +89,8 @@ namespace Game
extern const dvar_t** com_timescale; extern const dvar_t** com_timescale;
extern const dvar_t** com_maxFrameTime; extern const dvar_t** com_maxFrameTime;
extern const dvar_t** com_sv_running; extern const dvar_t** com_sv_running;
extern const dvar_t** com_masterServerName;
extern const dvar_t** com_masterPort;
extern const dvar_t** dev_timescale; extern const dvar_t** dev_timescale;

View File

@ -272,7 +272,7 @@ namespace Game
typedef bool(*NET_CompareAdr_t)(netadr_t a, netadr_t b); typedef bool(*NET_CompareAdr_t)(netadr_t a, netadr_t b);
extern NET_CompareAdr_t NET_CompareAdr; extern NET_CompareAdr_t NET_CompareAdr;
typedef void(*NET_DeferPacketToClient_t)(netadr_t *, msg_t *); typedef void(*NET_DeferPacketToClient_t)(netadr_t*, msg_t*);
extern NET_DeferPacketToClient_t NET_DeferPacketToClient; extern NET_DeferPacketToClient_t NET_DeferPacketToClient;
typedef const char* (*NET_ErrorString_t)(); typedef const char* (*NET_ErrorString_t)();
@ -284,19 +284,19 @@ namespace Game
typedef bool(*NET_IsLocalAddress_t)(netadr_t adr); typedef bool(*NET_IsLocalAddress_t)(netadr_t adr);
extern NET_IsLocalAddress_t NET_IsLocalAddress; extern NET_IsLocalAddress_t NET_IsLocalAddress;
typedef int(*NET_StringToAdr_t)(const char *s, netadr_t *a); typedef int(*NET_StringToAdr_t)(const char* s, netadr_t* a);
extern NET_StringToAdr_t NET_StringToAdr; extern NET_StringToAdr_t NET_StringToAdr;
typedef void(*NET_OutOfBandPrint_t)(netsrc_t sock, netadr_t adr, const char *data); typedef void(*NET_OutOfBandPrint_t)(netsrc_t sock, netadr_t adr, const char* data);
extern NET_OutOfBandPrint_t NET_OutOfBandPrint; extern NET_OutOfBandPrint_t NET_OutOfBandPrint;
typedef void(*NET_OutOfBandData_t)(netsrc_t sock, netadr_t adr, const char *format, int len); typedef void(*NET_OutOfBandData_t)(netsrc_t sock, netadr_t adr, const char* format, int len);
extern NET_OutOfBandData_t NET_OutOfBandData; extern NET_OutOfBandData_t NET_OutOfBandData;
typedef int(*NET_OutOfBandVoiceData_t)(netsrc_t sock, netadr_t adr, unsigned char* format, int len, bool voiceData); typedef int(*NET_OutOfBandVoiceData_t)(netsrc_t sock, netadr_t adr, unsigned char* format, int len, bool voiceData);
extern NET_OutOfBandVoiceData_t NET_OutOfBandVoiceData; extern NET_OutOfBandVoiceData_t NET_OutOfBandVoiceData;
typedef void(*Live_MPAcceptInvite_t)(_XSESSION_INFO *hostInfo, const int controllerIndex, bool fromGameInvite); typedef void(*Live_MPAcceptInvite_t)(_XSESSION_INFO *hostInfo, int controllerIndex, bool fromGameInvite);
extern Live_MPAcceptInvite_t Live_MPAcceptInvite; extern Live_MPAcceptInvite_t Live_MPAcceptInvite;
typedef int(*Live_GetMapIndex_t)(const char* mapname); typedef int(*Live_GetMapIndex_t)(const char* mapname);