aboutsummaryrefslogtreecommitdiff
path: root/NorthstarDedicatedTest/masterserver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'NorthstarDedicatedTest/masterserver.cpp')
-rw-r--r--NorthstarDedicatedTest/masterserver.cpp213
1 files changed, 213 insertions, 0 deletions
diff --git a/NorthstarDedicatedTest/masterserver.cpp b/NorthstarDedicatedTest/masterserver.cpp
new file mode 100644
index 00000000..dcbed9b4
--- /dev/null
+++ b/NorthstarDedicatedTest/masterserver.cpp
@@ -0,0 +1,213 @@
+#include "pch.h"
+#include "masterserver.h"
+#include "httplib.h"
+#include "rapidjson/document.h"
+#include "rapidjson/error/en.h"
+
+#include "concommand.h"
+
+ConVar* Cvar_ns_masterserver_hostname;
+ConVar* Cvar_ns_masterserver_port;
+
+MasterServerManager* g_MasterServerManager;
+
+// requires password constructor
+RemoteServerInfo::RemoteServerInfo(const char* newId, const char* newName, const char* newDescription, const char* newMap, const char* newPlaylist, int newPlayerCount, int newMaxPlayers)
+{
+ // passworded servers don't have public ips
+ requiresPassword = true;
+ memset(&ip, 0, sizeof(ip));
+ port = 0;
+
+ strncpy((char*)id, newId, 31);
+ id[31] = 0;
+ strncpy((char*)name, newName, 63);
+ name[63] = 0;
+
+ description = new char[strlen(newDescription) + 1];
+ strcpy(description, newDescription);
+
+ strncpy((char*)map, newMap, 31);
+ map[31] = 0;
+ strncpy((char*)playlist, newPlaylist, 15);
+ playlist[15] = 0;
+
+ playerCount = newPlayerCount;
+ maxPlayers = newMaxPlayers;
+}
+
+// doesnt require password constructor
+RemoteServerInfo::RemoteServerInfo(const char* newId, const char* newName, const char* newDescription, const char* newMap, const char* newPlaylist, int newPlayerCount, int newMaxPlayers, in_addr newIp, int newPort)
+{
+ requiresPassword = false;
+
+ strncpy((char*)id, newId, 31);
+ id[31] = 0;
+ strncpy((char*)name, newName, 63);
+ name[63] = 0;
+
+ description = new char[strlen(newDescription) + 1];
+ strcpy(description, newDescription);
+
+ strncpy((char*)map, newMap, 31);
+ map[31] = 0;
+ strncpy((char*)playlist, newPlaylist, 15);
+ playlist[15] = 0;
+
+ playerCount = newPlayerCount;
+ maxPlayers = newMaxPlayers;
+
+ ip = newIp;
+ port = newPort;
+}
+
+RemoteServerInfo::~RemoteServerInfo()
+{
+ delete[] description;
+}
+
+void MasterServerManager::ClearServerList()
+{
+ // this doesn't really do anything lol, probably isn't threadsafe
+ m_requestingServerList = true;
+
+ m_remoteServers.clear();
+
+ m_requestingServerList = false;
+}
+
+void MasterServerManager::RequestServerList()
+{
+ m_scriptRequestingServerList = true;
+
+ std::thread requestThread([this]()
+ {
+ // make sure we never have 2 threads writing at once
+ // i sure do hope this is actually threadsafe
+ while (m_requestingServerList)
+ Sleep(100);
+
+ m_requestingServerList = true;
+ m_scriptRequestingServerList = true;
+
+ httplib::Client http(Cvar_ns_masterserver_hostname->m_pszString, Cvar_ns_masterserver_port->m_nValue);
+ http.set_connection_timeout(10);
+
+ spdlog::info("Requesting server list from {}", Cvar_ns_masterserver_hostname->m_pszString);
+
+ if (auto result = http.Get("/servers"))
+ {
+ m_successfullyConnected = true;
+
+ rapidjson::Document serverInfoJson;
+ serverInfoJson.Parse(result->body.c_str());
+
+ if (serverInfoJson.HasParseError())
+ {
+ spdlog::error("Failed reading masterserver response: encountered parse error \"{}\"", rapidjson::GetParseError_En(serverInfoJson.GetParseError()));
+ goto REQUEST_SERVER_LIST_END;
+ }
+
+ if (!serverInfoJson.IsArray())
+ {
+ spdlog::error("Failed reading masterserver response: root object is not an array");
+ goto REQUEST_SERVER_LIST_END;
+ }
+
+ rapidjson::GenericArray<false, rapidjson::Value> serverArray = serverInfoJson.GetArray();
+
+ spdlog::info("Got {} servers", serverArray.Size());
+
+ for (auto& serverObj : serverArray)
+ {
+ if (!serverObj.IsObject())
+ {
+ spdlog::error("Failed reading masterserver response: member of server array is not an object");
+ goto REQUEST_SERVER_LIST_END;
+ }
+
+ // todo: verify json props are fine before adding to m_remoteServers
+
+ if (!serverObj.HasMember("id") || !serverObj["id"].IsString()
+ || !serverObj.HasMember("name") || !serverObj["name"].IsString()
+ || !serverObj.HasMember("description") || !serverObj["description"].IsString()
+ || !serverObj.HasMember("map") || !serverObj["map"].IsString()
+ || !serverObj.HasMember("playlist") || !serverObj["playlist"].IsString()
+ || !serverObj.HasMember("playerCount") || !serverObj["playerCount"].IsNumber()
+ || !serverObj.HasMember("maxPlayers") || !serverObj["maxPlayers"].IsNumber()
+ || !serverObj.HasMember("hasPassword") || !serverObj["hasPassword"].IsBool())
+ {
+ spdlog::error("Failed reading masterserver response: malformed server object");
+ goto REQUEST_SERVER_LIST_END;
+ }
+
+ bool hasPassword = serverObj["hasPassword"].GetBool();
+ const char* id = serverObj["id"].GetString();
+
+ bool createNewServerInfo = true;
+ for (RemoteServerInfo& server : m_remoteServers)
+ {
+ // server already exists, update info
+ if (!strncmp((const char*)server.id, id, 31))
+ {
+ if (hasPassword)
+ server = RemoteServerInfo(id, serverObj["name"].GetString(), serverObj["description"].GetString(), serverObj["map"].GetString(), serverObj["playlist"].GetString(), serverObj["playerCount"].GetInt(), serverObj["maxPlayers"].GetInt());
+ else
+ {
+ in_addr addr;
+ addr.S_un.S_addr = serverObj["ip"].GetUint64();
+
+ server = RemoteServerInfo(id, serverObj["name"].GetString(), serverObj["description"].GetString(), serverObj["map"].GetString(), serverObj["playlist"].GetString(), serverObj["playerCount"].GetInt(), serverObj["maxPlayers"].GetInt(), addr, serverObj["port"].GetInt());
+ }
+
+ createNewServerInfo = false;
+ break;
+ }
+ }
+
+ // server didn't exist
+ if (createNewServerInfo)
+ {
+ // passworded servers shouldn't send ip/port
+ if (hasPassword)
+ m_remoteServers.emplace_back(id, serverObj["name"].GetString(), serverObj["description"].GetString(), serverObj["map"].GetString(), serverObj["playlist"].GetString(), serverObj["playerCount"].GetInt(), serverObj["maxPlayers"].GetInt());
+ else
+ {
+ in_addr addr;
+ addr.S_un.S_addr = serverObj["ip"].GetUint64();
+
+ m_remoteServers.emplace_back(id, serverObj["name"].GetString(), serverObj["description"].GetString(), serverObj["map"].GetString(), serverObj["playlist"].GetString(), serverObj["playerCount"].GetInt(), serverObj["maxPlayers"].GetInt(), addr, serverObj["port"].GetInt());
+ }
+ }
+
+ spdlog::info("Server {} on map {} with playlist {} has {}/{} players", serverObj["name"].GetString(), serverObj["map"].GetString(), serverObj["playlist"].GetString(), serverObj["playerCount"].GetInt(), serverObj["maxPlayers"].GetInt());
+ }
+ }
+ else
+ {
+ spdlog::error("Failed requesting servers: error {}", result.error());
+ m_successfullyConnected = false;
+ }
+
+ // we goto this instead of returning so we always hit this
+ REQUEST_SERVER_LIST_END:
+ m_requestingServerList = false;
+ m_scriptRequestingServerList = false;
+ });
+
+ requestThread.detach();
+}
+
+void ConCommand_ns_fetchservers(const CCommand& args)
+{
+ g_MasterServerManager->RequestServerList();
+}
+
+void InitialiseSharedMasterServer(HMODULE baseAddress)
+{
+ Cvar_ns_masterserver_hostname = RegisterConVar("ns_masterserver_hostname", "localhost", FCVAR_NONE, "");
+ Cvar_ns_masterserver_port = RegisterConVar("ns_masterserver_port", "8080", FCVAR_NONE, "");
+ g_MasterServerManager = new MasterServerManager;
+
+ RegisterConCommand("ns_fetchservers", ConCommand_ns_fetchservers, "", FCVAR_CLIENTDLL);
+} \ No newline at end of file