Merge branch 'develop' into rcon
This commit is contained in:
commit
d5becf4955
@ -19,6 +19,8 @@ namespace Components
|
|||||||
|
|
||||||
std::size_t Bots::BotDataIndex;
|
std::size_t Bots::BotDataIndex;
|
||||||
|
|
||||||
|
std::vector<Bots::botData> Bots::RemoteBotNames;
|
||||||
|
|
||||||
struct BotMovementInfo
|
struct BotMovementInfo
|
||||||
{
|
{
|
||||||
std::int32_t buttons; // Actions
|
std::int32_t buttons; // Actions
|
||||||
@ -55,6 +57,17 @@ namespace Components
|
|||||||
{ "activate", Game::CMD_BUTTON_ACTIVATE },
|
{ "activate", Game::CMD_BUTTON_ACTIVATE },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void Bots::UpdateBotNames()
|
||||||
|
{
|
||||||
|
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));
|
||||||
|
|
||||||
|
Logger::Print("Getting bots...\n");
|
||||||
|
Network::Send(master, "getbots");
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<Bots::botData> Bots::LoadBotNames()
|
std::vector<Bots::botData> Bots::LoadBotNames()
|
||||||
{
|
{
|
||||||
std::vector<botData> result;
|
std::vector<botData> result;
|
||||||
@ -101,7 +114,7 @@ namespace Components
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 stats, int port)
|
||||||
{
|
{
|
||||||
std::string botName;
|
std::string botName;
|
||||||
std::string clanName;
|
std::string clanName;
|
||||||
@ -109,6 +122,11 @@ namespace Components
|
|||||||
static const auto botNames = []() -> std::vector<botData>
|
static const auto botNames = []() -> std::vector<botData>
|
||||||
{
|
{
|
||||||
auto names = LoadBotNames();
|
auto names = LoadBotNames();
|
||||||
|
if (names.empty())
|
||||||
|
{
|
||||||
|
Logger::Print("bots.txt was empty. Using the names from the master server\n");
|
||||||
|
names = RemoteBotNames;
|
||||||
|
}
|
||||||
|
|
||||||
if (sv_randomBotNames->current.enabled)
|
if (sv_randomBotNames->current.enabled)
|
||||||
{
|
{
|
||||||
@ -133,7 +151,7 @@ namespace Components
|
|||||||
clanName = "BOT"s;
|
clanName = "BOT"s;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _snprintf_s(buffer, 0x400, _TRUNCATE, connectString, num, botName.data(), clanName.data(), protocol, checksum, statVer, statStuff, port);
|
return _snprintf_s(buffer, 0x400, _TRUNCATE, connectString, num, botName.data(), clanName.data(), protocol, checksum, statVer, stats, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bots::Spawn(unsigned int count)
|
void Bots::Spawn(unsigned int count)
|
||||||
@ -446,9 +464,9 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
count = std::min(Game::MAX_CLIENTS, count);
|
count = std::clamp<std::size_t>(count, 1, Game::MAX_CLIENTS);
|
||||||
|
|
||||||
Logger::Print("Spawning {} {}", count, (count == 1 ? "bot" : "bots"));
|
Logger::Print("Spawning {} {}\n", count, (count == 1 ? "bot" : "bots"));
|
||||||
|
|
||||||
Spawn(count);
|
Spawn(count);
|
||||||
});
|
});
|
||||||
@ -476,6 +494,26 @@ namespace Components
|
|||||||
sv_randomBotNames = Game::Dvar_RegisterBool("sv_randomBotNames", false, Game::DVAR_NONE, "Randomize the bots' names");
|
sv_randomBotNames = Game::Dvar_RegisterBool("sv_randomBotNames", false, Game::DVAR_NONE, "Randomize the bots' names");
|
||||||
sv_replaceBots = Game::Dvar_RegisterBool("sv_replaceBots", false, Game::DVAR_NONE, "Test clients will be replaced by connecting players when the server is full.");
|
sv_replaceBots = Game::Dvar_RegisterBool("sv_replaceBots", false, Game::DVAR_NONE, "Test clients will be replaced by connecting players when the server is full.");
|
||||||
|
|
||||||
|
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');
|
||||||
|
Logger::Print("Got {} names from the master server\n", botNames.size());
|
||||||
|
|
||||||
|
for (const auto& entry : botNames)
|
||||||
|
{
|
||||||
|
RemoteBotNames.emplace_back(entry, "BOT");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Reset BotMovementInfo.active when client is dropped
|
// Reset BotMovementInfo.active when client is dropped
|
||||||
Events::OnClientDisconnect([](const int clientNum) -> void
|
Events::OnClientDisconnect([](const int clientNum) -> void
|
||||||
{
|
{
|
||||||
|
@ -10,13 +10,17 @@ namespace Components
|
|||||||
static void SV_DirectConnect_Full_Check();
|
static void SV_DirectConnect_Full_Check();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using botData = std::pair< std::string, std::string>;
|
using botData = std::pair<std::string, std::string>;
|
||||||
|
|
||||||
static const Game::dvar_t* sv_randomBotNames;
|
static const Game::dvar_t* sv_randomBotNames;
|
||||||
static const Game::dvar_t* sv_replaceBots;
|
static const Game::dvar_t* sv_replaceBots;
|
||||||
|
|
||||||
static std::size_t BotDataIndex;
|
static std::size_t BotDataIndex;
|
||||||
|
|
||||||
|
static std::vector<botData> RemoteBotNames;
|
||||||
|
|
||||||
|
static void UpdateBotNames();
|
||||||
|
|
||||||
static std::vector<botData> LoadBotNames();
|
static std::vector<botData> 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);
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@ namespace Components
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto masterPort = (*Game::com_masterPort)->current.integer;
|
const auto masterPort = (*Game::com_masterPort)->current.unsignedInt;
|
||||||
const auto* masterServerName = (*Game::com_masterServerName)->current.string;
|
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));
|
||||||
|
@ -184,6 +184,8 @@ namespace Components
|
|||||||
|
|
||||||
if (!Dedicated::IsEnabled())
|
if (!Dedicated::IsEnabled())
|
||||||
{
|
{
|
||||||
|
if (ServerList::UseMasterServer) return; // don't run node frame if master server is active
|
||||||
|
|
||||||
if (Game::CL_GetLocalClientConnectionState(0) != Game::CA_DISCONNECTED)
|
if (Game::CL_GetLocalClientConnectionState(0) != Game::CA_DISCONNECTED)
|
||||||
{
|
{
|
||||||
WasIngame = true;
|
WasIngame = true;
|
||||||
@ -264,7 +266,7 @@ namespace Components
|
|||||||
|
|
||||||
if (list.isnode() && (!list.port() || list.port() == address.getPort()))
|
if (list.isnode() && (!list.port() || list.port() == address.getPort()))
|
||||||
{
|
{
|
||||||
if (!Dedicated::IsEnabled() && ServerList::IsOnlineList() && list.protocol() == PROTOCOL)
|
if (!Dedicated::IsEnabled() && ServerList::IsOnlineList() && !ServerList::UseMasterServer && list.protocol() == PROTOCOL)
|
||||||
{
|
{
|
||||||
#ifdef NODE_SYSTEM_DEBUG
|
#ifdef NODE_SYSTEM_DEBUG
|
||||||
Logger::Debug("Inserting {} into the serverlist", address.getString());
|
Logger::Debug("Inserting {} into the serverlist", address.getString());
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
#include <Utils/InfoString.hpp>
|
#include <Utils/InfoString.hpp>
|
||||||
|
#include <Utils/WebIO.hpp>
|
||||||
|
|
||||||
#include "Discovery.hpp"
|
#include "Discovery.hpp"
|
||||||
#include "Events.hpp"
|
#include "Events.hpp"
|
||||||
@ -10,6 +11,10 @@
|
|||||||
#include "Toast.hpp"
|
#include "Toast.hpp"
|
||||||
#include "UIFeeder.hpp"
|
#include "UIFeeder.hpp"
|
||||||
|
|
||||||
|
#include <rapidjson/document.h>
|
||||||
|
#include <rapidjson/prettywriter.h>
|
||||||
|
#include <rapidjson/stringbuffer.h>
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
bool ServerList::SortAsc = true;
|
bool ServerList::SortAsc = true;
|
||||||
@ -24,6 +29,8 @@ namespace Components
|
|||||||
|
|
||||||
std::vector<unsigned int> ServerList::VisibleList;
|
std::vector<unsigned int> ServerList::VisibleList;
|
||||||
|
|
||||||
|
bool ServerList::UseMasterServer = false;
|
||||||
|
|
||||||
Dvar::Var ServerList::UIServerSelected;
|
Dvar::Var ServerList::UIServerSelected;
|
||||||
Dvar::Var ServerList::UIServerSelectedMap;
|
Dvar::Var ServerList::UIServerSelectedMap;
|
||||||
Dvar::Var ServerList::NETServerQueryLimit;
|
Dvar::Var ServerList::NETServerQueryLimit;
|
||||||
@ -271,6 +278,69 @@ namespace Components
|
|||||||
SortList();
|
SortList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ServerList::ParseNewMasterServerResponse(const std::string& servers)
|
||||||
|
{
|
||||||
|
std::lock_guard _(RefreshContainer.mutex);
|
||||||
|
|
||||||
|
rapidjson::Document doc{};
|
||||||
|
const rapidjson::ParseResult result = doc.Parse(servers);
|
||||||
|
if (!result || !doc.IsObject())
|
||||||
|
{
|
||||||
|
UseMasterServer = false;
|
||||||
|
Logger::Print("Unable to parse JSON response. Using the Node System\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!doc.HasMember("servers"))
|
||||||
|
{
|
||||||
|
UseMasterServer = false;
|
||||||
|
Logger::Print("Unable to parse JSON response: we were unable to find any server. Using the Node System\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rapidjson::Value& list = doc["servers"];
|
||||||
|
if (!list.IsArray() || list.Empty())
|
||||||
|
{
|
||||||
|
UseMasterServer = false;
|
||||||
|
Logger::Print("Unable to parse JSON response: we were unable to find any server. Using the Node System\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::Print("Response from the master server contains {} servers\n", list.Size());
|
||||||
|
|
||||||
|
std::size_t count = 0;
|
||||||
|
|
||||||
|
for (const auto& entry : list.GetArray())
|
||||||
|
{
|
||||||
|
if (!entry.HasMember("ip") || !entry.HasMember("port"))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entry["ip"].IsString() || !entry["port"].IsInt())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Using VA because it's faster
|
||||||
|
Network::Address server(Utils::String::VA("%s:%u", entry["ip"].GetString(), entry["port"].GetInt()));
|
||||||
|
server.setType(Game::NA_IP); // Just making sure...
|
||||||
|
|
||||||
|
InsertRequest(server);
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!count)
|
||||||
|
{
|
||||||
|
UseMasterServer = false;
|
||||||
|
Logger::Print("Despite receiving what looked like a valid response from the master server, we got {} servers. Using the Node System\n", count);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UseMasterServer = true;
|
||||||
|
Logger::Print("Response from the master server was successfully parsed. We got {} servers\n", count);
|
||||||
|
}
|
||||||
|
|
||||||
void ServerList::Refresh([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
void ServerList::Refresh([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
Dvar::Var("ui_serverSelected").set(false);
|
Dvar::Var("ui_serverSelected").set(false);
|
||||||
@ -294,29 +364,30 @@ namespace Components
|
|||||||
#ifdef IW4_USE_MASTER_SERVER
|
#ifdef IW4_USE_MASTER_SERVER
|
||||||
else if (IsOnlineList())
|
else if (IsOnlineList())
|
||||||
{
|
{
|
||||||
const auto masterPort = (*Game::com_masterPort)->current.integer;
|
const auto masterPort = (*Game::com_masterPort)->current.unsignedInt;
|
||||||
const auto* masterServerName = (*Game::com_masterServerName)->current.string;
|
const auto* masterServerName = (*Game::com_masterServerName)->current.string;
|
||||||
|
|
||||||
// Check if our dvars can properly convert to a address
|
RefreshContainer.awatingList = true;
|
||||||
Game::netadr_t masterServerAddr;
|
RefreshContainer.awaitTime = Game::Sys_Milliseconds();
|
||||||
if (!GetMasterServer(masterServerName, masterPort, masterServerAddr))
|
|
||||||
|
Toast::Show("cardicon_headshot", "Server Browser", "Fetching servers...", 3000);
|
||||||
|
|
||||||
|
const auto* url = "http://iw4x.plutools.pw/v1/servers/iw4x";
|
||||||
|
const auto reply = Utils::WebIO("IW4x", url).setTimeout(5000)->get();
|
||||||
|
if (reply.empty())
|
||||||
{
|
{
|
||||||
Logger::Print("Could not resolve address for {}:{}", masterServerName, masterPort);
|
Logger::Print("Response from {} was empty or the request timed out. Using the Node System\n", url);
|
||||||
Toast::Show("cardicon_headshot", "^1Error", std::format("Could not resolve address for {}:{}", masterServerName, masterPort), 5000);
|
Toast::Show("cardicon_headshot", "^1Error", std::format("Could not get a response from {}. Using the Node System", url), 5000);
|
||||||
UseMasterServer = false;
|
UseMasterServer = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Toast::Show("cardicon_headshot", "Server Browser", "Fetching servers...", 3000);
|
RefreshContainer.awatingList = false;
|
||||||
|
|
||||||
UseMasterServer = true;
|
ParseNewMasterServerResponse(reply);
|
||||||
|
|
||||||
RefreshContainer.awatingList = true;
|
// TODO: Figure out what to do with this. Leave it to avoid breaking other code
|
||||||
RefreshContainer.awaitTime = Game::Sys_Milliseconds();
|
|
||||||
RefreshContainer.host = Network::Address(std::format("{}:{}", masterServerName, masterPort));
|
RefreshContainer.host = Network::Address(std::format("{}:{}", masterServerName, masterPort));
|
||||||
|
|
||||||
Logger::Print("Sending server list request to master\n");
|
|
||||||
Network::SendCommand(RefreshContainer.host, "getservers", std::format("IW4 {} full empty", PROTOCOL));
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
else if (IsFavouriteList())
|
else if (IsFavouriteList())
|
||||||
@ -729,7 +800,9 @@ namespace Components
|
|||||||
if (Game::Sys_Milliseconds() - RefreshContainer.awaitTime > 5000)
|
if (Game::Sys_Milliseconds() - RefreshContainer.awaitTime > 5000)
|
||||||
{
|
{
|
||||||
RefreshContainer.awatingList = false;
|
RefreshContainer.awatingList = false;
|
||||||
|
Logger::Print("We haven't received a response from the master within {} seconds!\n", (Game::Sys_Milliseconds() - RefreshContainer.awaitTime) / 1000);
|
||||||
|
|
||||||
|
UseMasterServer = false;
|
||||||
Node::Synchronize();
|
Node::Synchronize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,8 @@ namespace Components
|
|||||||
|
|
||||||
static void UpdateVisibleInfo();
|
static void UpdateVisibleInfo();
|
||||||
|
|
||||||
|
static bool UseMasterServer;
|
||||||
|
|
||||||
static bool GetMasterServer(const char* ip, int port, Game::netadr_t& address);
|
static bool GetMasterServer(const char* ip, int port, Game::netadr_t& address);
|
||||||
|
|
||||||
static Dvar::Var UIServerSelected;
|
static Dvar::Var UIServerSelected;
|
||||||
@ -123,6 +125,8 @@ namespace Components
|
|||||||
std::recursive_mutex mutex;
|
std::recursive_mutex mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void ParseNewMasterServerResponse(const std::string& servers);
|
||||||
|
|
||||||
static unsigned int GetServerCount();
|
static unsigned int GetServerCount();
|
||||||
static const char* GetServerText(unsigned int index, int column);
|
static const char* GetServerText(unsigned int index, int column);
|
||||||
static const char* GetServerInfoText(ServerInfo* server, int column, bool sorting = false);
|
static const char* GetServerInfoText(ServerInfo* server, int column, bool sorting = false);
|
||||||
|
Loading…
Reference in New Issue
Block a user