Skip to content

Commit

Permalink
Merge pull request #37 from diamante0018/master-server
Browse files Browse the repository at this point in the history
feat: go back to a main master server, node as fallback
  • Loading branch information
Rackover authored Aug 28, 2023
2 parents 762c32d + ea7cc4b commit a3263be
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 15 deletions.
2 changes: 1 addition & 1 deletion src/Components/Modules/Dedicated.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ namespace Components
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;

Network::Address master(Utils::String::VA("%s:%u", masterServerName, masterPort));
Expand Down
4 changes: 3 additions & 1 deletion src/Components/Modules/Node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ namespace Components

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)
{
WasIngame = true;
Expand Down Expand Up @@ -264,7 +266,7 @@ namespace Components

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
Logger::Debug("Inserting {} into the serverlist", address.getString());
Expand Down
99 changes: 86 additions & 13 deletions src/Components/Modules/ServerList.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <STDInclude.hpp>
#include <Utils/InfoString.hpp>
#include <Utils/WebIO.hpp>

#include "Discovery.hpp"
#include "Events.hpp"
Expand All @@ -10,6 +11,10 @@
#include "Toast.hpp"
#include "UIFeeder.hpp"

#include <rapidjson/document.h>
#include <rapidjson/prettywriter.h>
#include <rapidjson/stringbuffer.h>

namespace Components
{
bool ServerList::SortAsc = true;
Expand All @@ -24,6 +29,8 @@ namespace Components

std::vector<unsigned int> ServerList::VisibleList;

bool ServerList::UseMasterServer = false;

Dvar::Var ServerList::UIServerSelected;
Dvar::Var ServerList::UIServerSelectedMap;
Dvar::Var ServerList::NETServerQueryLimit;
Expand Down Expand Up @@ -271,6 +278,69 @@ namespace Components
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)
{
Dvar::Var("ui_serverSelected").set(false);
Expand All @@ -294,29 +364,30 @@ namespace Components
#ifdef IW4_USE_MASTER_SERVER
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;

// Check if our dvars can properly convert to a address
Game::netadr_t masterServerAddr;
if (!GetMasterServer(masterServerName, masterPort, masterServerAddr))
RefreshContainer.awatingList = true;
RefreshContainer.awaitTime = Game::Sys_Milliseconds();

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);
Toast::Show("cardicon_headshot", "^1Error", std::format("Could not resolve address for {}:{}", masterServerName, masterPort), 5000);
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 get a response from {}. Using the Node System", url), 5000);
UseMasterServer = false;
return;
}

Toast::Show("cardicon_headshot", "Server Browser", "Fetching servers...", 3000);
RefreshContainer.awatingList = false;

UseMasterServer = true;
ParseNewMasterServerResponse(reply);

RefreshContainer.awatingList = true;
RefreshContainer.awaitTime = Game::Sys_Milliseconds();
// TODO: Figure out what to do with this. Leave it to avoid breaking other code
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
else if (IsFavouriteList())
Expand Down Expand Up @@ -729,7 +800,9 @@ namespace Components
if (Game::Sys_Milliseconds() - RefreshContainer.awaitTime > 5000)
{
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();
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/Components/Modules/ServerList.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ namespace Components

static void UpdateVisibleInfo();

static bool UseMasterServer;

static bool GetMasterServer(const char* ip, int port, Game::netadr_t& address);

static Dvar::Var UIServerSelected;
Expand Down Expand Up @@ -123,6 +125,8 @@ namespace Components
std::recursive_mutex mutex;
};

static void ParseNewMasterServerResponse(const std::string& servers);

static unsigned int GetServerCount();
static const char* GetServerText(unsigned int index, int column);
static const char* GetServerInfoText(ServerInfo* server, int column, bool sorting = false);
Expand Down

0 comments on commit a3263be

Please sign in to comment.