aboutsummaryrefslogtreecommitdiff
path: root/NorthstarDLL/masterserver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'NorthstarDLL/masterserver.cpp')
-rw-r--r--NorthstarDLL/masterserver.cpp1111
1 files changed, 384 insertions, 727 deletions
diff --git a/NorthstarDLL/masterserver.cpp b/NorthstarDLL/masterserver.cpp
index d5ef2d9f..a1fb839a 100644
--- a/NorthstarDLL/masterserver.cpp
+++ b/NorthstarDLL/masterserver.cpp
@@ -1,151 +1,27 @@
#include "pch.h"
#include "masterserver.h"
#include "concommand.h"
-#include "gameutils.h"
-#include "hookutils.h"
+#include "playlist.h"
#include "serverauthentication.h"
-#include "gameutils.h"
+#include "tier0.h"
+#include "r2engine.h"
+#include "modmanager.h"
+#include "misccommands.h"
+#include "version.h"
+
#include "rapidjson/document.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
#include "rapidjson/error/en.h"
-#include "modmanager.h"
-#include "misccommands.h"
+
#include <cstring>
#include <regex>
-#include "version.h"
-#include <chrono>
-// NOTE for anyone reading this: we used to use httplib for requests here, but it had issues, so we're moving to curl now for masterserver
-// requests so httplib is used exclusively for server stuff now
-ConVar* Cvar_ns_masterserver_hostname;
-ConVar* Cvar_ns_report_server_to_masterserver;
-ConVar* Cvar_ns_report_sp_server_to_masterserver;
-
-ConVar* Cvar_ns_server_name;
-ConVar* Cvar_ns_server_desc;
-ConVar* Cvar_ns_server_password;
+MasterServerManager* g_pMasterServerManager;
+ConVar* Cvar_ns_masterserver_hostname;
ConVar* Cvar_ns_curl_log_enable;
-// Source ConVar
-ConVar* Cvar_hostname;
-
-MasterServerManager* g_MasterServerManager;
-
-typedef void (*CHostState__State_NewGameType)(CHostState* hostState);
-CHostState__State_NewGameType CHostState__State_NewGame;
-
-typedef void (*CHostState__State_ChangeLevelMPType)(CHostState* hostState);
-CHostState__State_ChangeLevelMPType CHostState__State_ChangeLevelMP;
-
-typedef void (*CHostState__State_ChangeLevelSPType)(CHostState* hostState);
-CHostState__State_ChangeLevelSPType CHostState__State_ChangeLevelSP;
-
-typedef void (*CHostState__State_GameShutdownType)(CHostState* hostState);
-CHostState__State_GameShutdownType CHostState__State_GameShutdown;
-
-// Convert a hex digit char to integer.
-inline int hctod(char c)
-{
- if (c >= 'A' && c <= 'F')
- {
- return c - 'A' + 10;
- }
- else if (c >= 'a' && c <= 'f')
- {
- return c - 'a' + 10;
- }
- else
- {
- return c - '0';
- }
-}
-
-// This function interprets all 4-hexadecimal-digit unicode codepoint characters like \u4E2D to UTF-8 encoding.
-std::string unescape_unicode(const std::string& str)
-{
- std::string result;
- std::regex r("\\\\u([a-f\\d]{4})", std::regex::icase);
- auto matches_begin = std::sregex_iterator(str.begin(), str.end(), r);
- auto matches_end = std::sregex_iterator();
- std::smatch last_match;
- for (std::sregex_iterator i = matches_begin; i != matches_end; ++i)
- {
- last_match = *i;
- result.append(last_match.prefix());
- unsigned int cp = 0;
- for (int i = 2; i <= 5; ++i)
- {
- cp *= 16;
- cp += hctod(last_match.str()[i]);
- }
- if (cp <= 0x7F)
- {
- result.push_back(cp);
- }
- else if (cp <= 0x7FF)
- {
- result.push_back((cp >> 6) | 0b11000000 & (~(1 << 5)));
- result.push_back(cp & ((1 << 6) - 1) | 0b10000000 & (~(1 << 6)));
- }
- else if (cp <= 0xFFFF)
- {
- result.push_back((cp >> 12) | 0b11100000 & (~(1 << 4)));
- result.push_back((cp >> 6) & ((1 << 6) - 1) | 0b10000000 & (~(1 << 6)));
- result.push_back(cp & ((1 << 6) - 1) | 0b10000000 & (~(1 << 6)));
- }
- }
- if (!last_match.ready())
- {
- return str;
- }
- else
- {
- result.append(last_match.suffix());
- }
- return result;
-}
-
-void UpdateServerInfoFromUnicodeToUTF8()
-{
- g_MasterServerManager->ns_auth_srvName = unescape_unicode(Cvar_ns_server_name->GetString());
- g_MasterServerManager->ns_auth_srvDesc = unescape_unicode(Cvar_ns_server_desc->GetString());
-}
-
-const char* HttplibErrorToString(httplib::Error error)
-{
- switch (error)
- {
- case httplib::Error::Success:
- return "httplib::Error::Success";
- case httplib::Error::Unknown:
- return "httplib::Error::Unknown";
- case httplib::Error::Connection:
- return "httplib::Error::Connection";
- case httplib::Error::BindIPAddress:
- return "httplib::Error::BindIPAddress";
- case httplib::Error::Read:
- return "httplib::Error::Read";
- case httplib::Error::Write:
- return "httplib::Error::Write";
- case httplib::Error::ExceedRedirectCount:
- return "httplib::Error::ExceedRedirectCount";
- case httplib::Error::Canceled:
- return "httplib::Error::Canceled";
- case httplib::Error::SSLConnection:
- return "httplib::Error::SSLConnection";
- case httplib::Error::SSLLoadingCerts:
- return "httplib::Error::SSLLoadingCerts";
- case httplib::Error::SSLServerVerification:
- return "httplib::Error::SSLServerVerification";
- case httplib::Error::UnsupportedMultipartBoundaryChars:
- return "httplib::Error::UnsupportedMultipartBoundaryChars";
- }
-
- return "";
-}
-
RemoteServerInfo::RemoteServerInfo(
const char* newId,
const char* newName,
@@ -159,29 +35,25 @@ RemoteServerInfo::RemoteServerInfo(
// passworded servers don't have public ips
requiresPassword = newRequiresPassword;
- strncpy((char*)id, newId, sizeof(id));
- id[sizeof(id) - 1] = 0;
- strncpy((char*)name, newName, sizeof(name));
- name[sizeof(name) - 1] = 0;
+ strncpy_s((char*)id, sizeof(id), newId, sizeof(id) - 1);
+ strncpy_s((char*)name, sizeof(name), newName, sizeof(name) - 1);
description = std::string(newDescription);
- strncpy((char*)map, newMap, sizeof(map));
- map[sizeof(map) - 1] = 0;
- strncpy((char*)playlist, newPlaylist, sizeof(playlist));
- playlist[sizeof(playlist) - 1] = 0;
+ strncpy_s((char*)map, sizeof(map), newMap, sizeof(map) - 1);
+ strncpy_s((char*)playlist, sizeof(playlist), newPlaylist, sizeof(playlist) - 1);
playerCount = newPlayerCount;
maxPlayers = newMaxPlayers;
}
-void MasterServerManager::SetCommonHttpClientOptions(CURL* curl)
+void SetCommonHttpClientOptions(CURL* curl)
{
curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
curl_easy_setopt(curl, CURLOPT_VERBOSE, Cvar_ns_curl_log_enable->GetBool());
curl_easy_setopt(curl, CURLOPT_USERAGENT, &NSUserAgent);
// curl_easy_setopt(curl, CURLOPT_STDERR, stdout);
- if (CommandLine()->FindParm("-msinsecure")) // TODO: this check doesn't seem to work
+ if (Tier0::CommandLine()->FindParm("-msinsecure")) // TODO: this check doesn't seem to work
{
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
@@ -191,11 +63,11 @@ void MasterServerManager::SetCommonHttpClientOptions(CURL* curl)
void MasterServerManager::ClearServerList()
{
// this doesn't really do anything lol, probably isn't threadsafe
- m_requestingServerList = true;
+ m_bRequestingServerList = true;
- m_remoteServers.clear();
+ m_vRemoteServers.clear();
- m_requestingServerList = false;
+ m_bRequestingServerList = false;
}
size_t CurlWriteToStringBufferCallback(char* contents, size_t size, size_t nmemb, void* userp)
@@ -204,7 +76,7 @@ size_t CurlWriteToStringBufferCallback(char* contents, size_t size, size_t nmemb
return size * nmemb;
}
-void MasterServerManager::AuthenticateOriginWithMasterServer(char* uid, char* originToken)
+void MasterServerManager::AuthenticateOriginWithMasterServer(const char* uid, const char* originToken)
{
if (m_bOriginAuthWithMasterServerInProgress)
return;
@@ -234,7 +106,7 @@ void MasterServerManager::AuthenticateOriginWithMasterServer(char* uid, char* or
if (result == CURLcode::CURLE_OK)
{
- m_successfullyConnected = true;
+ m_bSuccessfullyConnected = true;
rapidjson_document originAuthInfo;
originAuthInfo.Parse(readBuffer.c_str());
@@ -255,8 +127,10 @@ void MasterServerManager::AuthenticateOriginWithMasterServer(char* uid, char* or
if (originAuthInfo["success"].IsTrue() && originAuthInfo.HasMember("token") && originAuthInfo["token"].IsString())
{
- strncpy(m_ownClientAuthToken, originAuthInfo["token"].GetString(), sizeof(m_ownClientAuthToken));
- m_ownClientAuthToken[sizeof(m_ownClientAuthToken) - 1] = 0;
+ strncpy_s(
+ m_sOwnClientAuthToken,
+ sizeof(m_sOwnClientAuthToken), originAuthInfo["token"].GetString(),
+ sizeof(m_sOwnClientAuthToken) - 1);
spdlog::info("Northstar origin authentication completed successfully!");
}
else
@@ -265,7 +139,7 @@ void MasterServerManager::AuthenticateOriginWithMasterServer(char* uid, char* or
else
{
spdlog::error("Failed performing northstar origin auth: error {}", curl_easy_strerror(result));
- m_successfullyConnected = false;
+ m_bSuccessfullyConnected = false;
}
// we goto this instead of returning so we always hit this
@@ -281,18 +155,18 @@ void MasterServerManager::AuthenticateOriginWithMasterServer(char* uid, char* or
void MasterServerManager::RequestServerList()
{
// do this here so it's instantly set on call for scripts
- m_scriptRequestingServerList = true;
+ m_bScriptRequestingServerList = 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)
+ while (m_bRequestingServerList)
Sleep(100);
- m_requestingServerList = true;
- m_scriptRequestingServerList = true;
+ m_bRequestingServerList = true;
+ m_bScriptRequestingServerList = true;
spdlog::info("Requesting server list from {}", Cvar_ns_masterserver_hostname->GetString());
@@ -309,7 +183,7 @@ void MasterServerManager::RequestServerList()
if (result == CURLcode::CURLE_OK)
{
- m_successfullyConnected = true;
+ m_bSuccessfullyConnected = true;
rapidjson_document serverInfoJson;
serverInfoJson.Parse(readBuffer.c_str());
@@ -365,7 +239,7 @@ void MasterServerManager::RequestServerList()
RemoteServerInfo* newServer = nullptr;
bool createNewServerInfo = true;
- for (RemoteServerInfo& server : m_remoteServers)
+ for (RemoteServerInfo& server : m_vRemoteServers)
{
// if server already exists, update info rather than adding to it
if (!strncmp((const char*)server.id, id, 32))
@@ -387,7 +261,7 @@ void MasterServerManager::RequestServerList()
// server didn't exist
if (createNewServerInfo)
- newServer = &m_remoteServers.emplace_back(
+ newServer = &m_vRemoteServers.emplace_back(
id,
serverObj["name"].GetString(),
serverObj["description"].GetString(),
@@ -424,20 +298,20 @@ void MasterServerManager::RequestServerList()
}
std::sort(
- m_remoteServers.begin(),
- m_remoteServers.end(),
+ m_vRemoteServers.begin(),
+ m_vRemoteServers.end(),
[](RemoteServerInfo& a, RemoteServerInfo& b) { return a.playerCount > b.playerCount; });
}
else
{
spdlog::error("Failed requesting servers: error {}", curl_easy_strerror(result));
- m_successfullyConnected = false;
+ m_bSuccessfullyConnected = false;
}
// we goto this instead of returning so we always hit this
REQUEST_END_CLEANUP:
- m_requestingServerList = false;
- m_scriptRequestingServerList = false;
+ m_bRequestingServerList = false;
+ m_bScriptRequestingServerList = false;
curl_easy_cleanup(curl);
});
@@ -468,7 +342,7 @@ void MasterServerManager::RequestMainMenuPromos()
if (result == CURLcode::CURLE_OK)
{
- m_successfullyConnected = true;
+ m_bSuccessfullyConnected = true;
rapidjson_document mainMenuPromoJson;
mainMenuPromoJson.Parse(readBuffer.c_str());
@@ -491,10 +365,6 @@ void MasterServerManager::RequestMainMenuPromos()
{
spdlog::error("Failed reading masterserver response: got fastify error response");
spdlog::error(readBuffer);
- if (mainMenuPromoJson["error"].HasMember("enum"))
- s_authfail_reason = std::string(mainMenuPromoJson["error"]["enum"].GetString());
- else
- s_authfail_reason = std::string("No error message provided");
goto REQUEST_END_CLEANUP;
}
@@ -526,29 +396,29 @@ void MasterServerManager::RequestMainMenuPromos()
goto REQUEST_END_CLEANUP;
}
- m_MainMenuPromoData.newInfoTitle1 = mainMenuPromoJson["newInfo"]["Title1"].GetString();
- m_MainMenuPromoData.newInfoTitle2 = mainMenuPromoJson["newInfo"]["Title2"].GetString();
- m_MainMenuPromoData.newInfoTitle3 = mainMenuPromoJson["newInfo"]["Title3"].GetString();
+ m_sMainMenuPromoData.newInfoTitle1 = mainMenuPromoJson["newInfo"]["Title1"].GetString();
+ m_sMainMenuPromoData.newInfoTitle2 = mainMenuPromoJson["newInfo"]["Title2"].GetString();
+ m_sMainMenuPromoData.newInfoTitle3 = mainMenuPromoJson["newInfo"]["Title3"].GetString();
- m_MainMenuPromoData.largeButtonTitle = mainMenuPromoJson["largeButton"]["Title"].GetString();
- m_MainMenuPromoData.largeButtonText = mainMenuPromoJson["largeButton"]["Text"].GetString();
- m_MainMenuPromoData.largeButtonUrl = mainMenuPromoJson["largeButton"]["Url"].GetString();
- m_MainMenuPromoData.largeButtonImageIndex = mainMenuPromoJson["largeButton"]["ImageIndex"].GetInt();
+ m_sMainMenuPromoData.largeButtonTitle = mainMenuPromoJson["largeButton"]["Title"].GetString();
+ m_sMainMenuPromoData.largeButtonText = mainMenuPromoJson["largeButton"]["Text"].GetString();
+ m_sMainMenuPromoData.largeButtonUrl = mainMenuPromoJson["largeButton"]["Url"].GetString();
+ m_sMainMenuPromoData.largeButtonImageIndex = mainMenuPromoJson["largeButton"]["ImageIndex"].GetInt();
- m_MainMenuPromoData.smallButton1Title = mainMenuPromoJson["smallButton1"]["Title"].GetString();
- m_MainMenuPromoData.smallButton1Url = mainMenuPromoJson["smallButton1"]["Url"].GetString();
- m_MainMenuPromoData.smallButton1ImageIndex = mainMenuPromoJson["smallButton1"]["ImageIndex"].GetInt();
+ m_sMainMenuPromoData.smallButton1Title = mainMenuPromoJson["smallButton1"]["Title"].GetString();
+ m_sMainMenuPromoData.smallButton1Url = mainMenuPromoJson["smallButton1"]["Url"].GetString();
+ m_sMainMenuPromoData.smallButton1ImageIndex = mainMenuPromoJson["smallButton1"]["ImageIndex"].GetInt();
- m_MainMenuPromoData.smallButton2Title = mainMenuPromoJson["smallButton2"]["Title"].GetString();
- m_MainMenuPromoData.smallButton2Url = mainMenuPromoJson["smallButton2"]["Url"].GetString();
- m_MainMenuPromoData.smallButton2ImageIndex = mainMenuPromoJson["smallButton2"]["ImageIndex"].GetInt();
+ m_sMainMenuPromoData.smallButton2Title = mainMenuPromoJson["smallButton2"]["Title"].GetString();
+ m_sMainMenuPromoData.smallButton2Url = mainMenuPromoJson["smallButton2"]["Url"].GetString();
+ m_sMainMenuPromoData.smallButton2ImageIndex = mainMenuPromoJson["smallButton2"]["ImageIndex"].GetInt();
m_bHasMainMenuPromoData = true;
}
else
{
spdlog::error("Failed requesting main menu promos: error {}", curl_easy_strerror(result));
- m_successfullyConnected = false;
+ m_bSuccessfullyConnected = false;
}
REQUEST_END_CLEANUP:
@@ -559,15 +429,15 @@ void MasterServerManager::RequestMainMenuPromos()
requestThread.detach();
}
-void MasterServerManager::AuthenticateWithOwnServer(char* uid, char* playerToken)
+void MasterServerManager::AuthenticateWithOwnServer(const char* uid, const char* playerToken)
{
// dont wait, just stop if we're trying to do 2 auth requests at once
- if (m_authenticatingWithGameServer)
+ if (m_bAuthenticatingWithGameServer)
return;
- m_authenticatingWithGameServer = true;
- m_scriptAuthenticatingWithGameServer = true;
- m_successfullyAuthenticatedWithGameServer = false;
+ m_bAuthenticatingWithGameServer = true;
+ m_bScriptAuthenticatingWithGameServer = true;
+ m_bSuccessfullyAuthenticatedWithGameServer = false;
std::string uidStr(uid);
std::string tokenStr(playerToken);
@@ -592,7 +462,7 @@ void MasterServerManager::AuthenticateWithOwnServer(char* uid, char* playerToken
if (result == CURLcode::CURLE_OK)
{
- m_successfullyConnected = true;
+ m_bSuccessfullyConnected = true;
rapidjson_document authInfoJson;
authInfoJson.Parse(readBuffer.c_str());
@@ -615,10 +485,12 @@ void MasterServerManager::AuthenticateWithOwnServer(char* uid, char* playerToken
{
spdlog::error("Failed reading masterserver response: got fastify error response");
spdlog::error(readBuffer);
+
if (authInfoJson["error"].HasMember("enum"))
- s_authfail_reason = std::string(authInfoJson["error"]["enum"].GetString());
+ m_sAuthFailureReason = authInfoJson["error"]["enum"].GetString();
else
- s_authfail_reason = std::string("No error message provided");
+ m_sAuthFailureReason = "No error message provided";
+
goto REQUEST_END_CLEANUP;
}
@@ -636,9 +508,8 @@ void MasterServerManager::AuthenticateWithOwnServer(char* uid, char* playerToken
goto REQUEST_END_CLEANUP;
}
- AuthData newAuthData {};
- strncpy(newAuthData.uid, authInfoJson["id"].GetString(), sizeof(newAuthData.uid));
- newAuthData.uid[sizeof(newAuthData.uid) - 1] = 0;
+ RemoteAuthData newAuthData {};
+ strncpy_s(newAuthData.uid, sizeof(newAuthData.uid), authInfoJson["id"].GetString(), sizeof(newAuthData.uid) - 1);
newAuthData.pdataSize = authInfoJson["persistentData"].GetArray().Size();
newAuthData.pdata = new char[newAuthData.pdataSize];
@@ -658,28 +529,28 @@ void MasterServerManager::AuthenticateWithOwnServer(char* uid, char* playerToken
newAuthData.pdata[i++] = static_cast<char>(byte.GetUint());
}
- std::lock_guard<std::mutex> guard(g_ServerAuthenticationManager->m_authDataMutex);
- g_ServerAuthenticationManager->m_authData.clear();
- g_ServerAuthenticationManager->m_authData.insert(std::make_pair(authInfoJson["authToken"].GetString(), newAuthData));
+ std::lock_guard<std::mutex> guard(g_pServerAuthentication->m_AuthDataMutex);
+ g_pServerAuthentication->m_RemoteAuthenticationData.clear();
+ g_pServerAuthentication->m_RemoteAuthenticationData.insert(std::make_pair(authInfoJson["authToken"].GetString(), newAuthData));
- m_successfullyAuthenticatedWithGameServer = true;
+ m_bSuccessfullyAuthenticatedWithGameServer = true;
}
else
{
spdlog::error("Failed authenticating with own server: error {}", curl_easy_strerror(result));
- m_successfullyConnected = false;
- m_successfullyAuthenticatedWithGameServer = false;
- m_scriptAuthenticatingWithGameServer = false;
+ m_bSuccessfullyConnected = false;
+ m_bSuccessfullyAuthenticatedWithGameServer = false;
+ m_bScriptAuthenticatingWithGameServer = false;
}
REQUEST_END_CLEANUP:
- m_authenticatingWithGameServer = false;
- m_scriptAuthenticatingWithGameServer = false;
+ m_bAuthenticatingWithGameServer = false;
+ m_bScriptAuthenticatingWithGameServer = false;
if (m_bNewgameAfterSelfAuth)
{
// pretty sure this is threadsafe?
- Cbuf_AddText(Cbuf_GetCurrentPlayer(), "ns_end_reauth_and_leave_to_lobby", cmd_source_t::kCommandSrcCode);
+ R2::Cbuf_AddText(R2::Cbuf_GetCurrentPlayer(), "ns_end_reauth_and_leave_to_lobby", R2::cmd_source_t::kCommandSrcCode);
m_bNewgameAfterSelfAuth = false;
}
@@ -689,15 +560,15 @@ void MasterServerManager::AuthenticateWithOwnServer(char* uid, char* playerToken
requestThread.detach();
}
-void MasterServerManager::AuthenticateWithServer(char* uid, char* playerToken, char* serverId, char* password)
+void MasterServerManager::AuthenticateWithServer(const char* uid, const char* playerToken, const char* serverId, const char* password)
{
// dont wait, just stop if we're trying to do 2 auth requests at once
- if (m_authenticatingWithGameServer)
+ if (m_bAuthenticatingWithGameServer)
return;
- m_authenticatingWithGameServer = true;
- m_scriptAuthenticatingWithGameServer = true;
- m_successfullyAuthenticatedWithGameServer = false;
+ m_bAuthenticatingWithGameServer = true;
+ m_bScriptAuthenticatingWithGameServer = true;
+ m_bSuccessfullyAuthenticatedWithGameServer = false;
std::string uidStr(uid);
std::string tokenStr(playerToken);
@@ -708,7 +579,7 @@ void MasterServerManager::AuthenticateWithServer(char* uid, char* playerToken, c
[this, uidStr, tokenStr, serverIdStr, passwordStr]()
{
// esnure that any persistence saving is done, so we know masterserver has newest
- while (m_savingPersistentData)
+ while (m_bSavingPersistentData)
Sleep(100);
spdlog::info("Attempting authentication with server of id \"{}\"", serverIdStr);
@@ -743,7 +614,7 @@ void MasterServerManager::AuthenticateWithServer(char* uid, char* playerToken, c
if (result == CURLcode::CURLE_OK)
{
- m_successfullyConnected = true;
+ m_bSuccessfullyConnected = true;
rapidjson_document connectionInfoJson;
connectionInfoJson.Parse(readBuffer.c_str());
@@ -766,10 +637,12 @@ void MasterServerManager::AuthenticateWithServer(char* uid, char* playerToken, c
{
spdlog::error("Failed reading masterserver response: got fastify error response");
spdlog::error(readBuffer);
+
if (connectionInfoJson["error"].HasMember("enum"))
- s_authfail_reason = std::string(connectionInfoJson["error"]["enum"].GetString());
+ m_sAuthFailureReason = connectionInfoJson["error"]["enum"].GetString();
else
- s_authfail_reason = std::string("No error message provided");
+ m_sAuthFailureReason = "No error message provided";
+
goto REQUEST_END_CLEANUP;
}
@@ -791,63 +664,61 @@ void MasterServerManager::AuthenticateWithServer(char* uid, char* playerToken, c
m_pendingConnectionInfo.ip.S_un.S_addr = inet_addr(connectionInfoJson["ip"].GetString());
m_pendingConnectionInfo.port = (unsigned short)connectionInfoJson["port"].GetUint();
- strncpy(m_pendingConnectionInfo.authToken, connectionInfoJson["authToken"].GetString(), 31);
- m_pendingConnectionInfo.authToken[31] = 0;
+ strncpy_s(
+ m_pendingConnectionInfo.authToken,
+ sizeof(m_pendingConnectionInfo.authToken),
+ connectionInfoJson["authToken"].GetString(),
+ sizeof(m_pendingConnectionInfo.authToken) - 1);
- m_hasPendingConnectionInfo = true;
- m_successfullyAuthenticatedWithGameServer = true;
+ m_bHasPendingConnectionInfo = true;
+ m_bSuccessfullyAuthenticatedWithGameServer = true;
}
else
{
spdlog::error("Failed authenticating with server: error {}", curl_easy_strerror(result));
- m_successfullyConnected = false;
- m_successfullyAuthenticatedWithGameServer = false;
- m_scriptAuthenticatingWithGameServer = false;
+ m_bSuccessfullyConnected = false;
+ m_bSuccessfullyAuthenticatedWithGameServer = false;
+ m_bScriptAuthenticatingWithGameServer = false;
}
REQUEST_END_CLEANUP:
- m_authenticatingWithGameServer = false;
- m_scriptAuthenticatingWithGameServer = false;
+ m_bAuthenticatingWithGameServer = false;
+ m_bScriptAuthenticatingWithGameServer = false;
curl_easy_cleanup(curl);
});
requestThread.detach();
}
-void MasterServerManager::AddSelfToServerList(
- int port, int authPort, char* name, char* description, char* map, char* playlist, int maxPlayers, char* password)
+void MasterServerManager::WritePlayerPersistentData(const char* playerId, const char* pdata, size_t pdataSize)
{
- if (!Cvar_ns_report_server_to_masterserver->GetBool())
- return;
-
- if (!Cvar_ns_report_sp_server_to_masterserver->GetBool() && !strncmp(map, "sp_", 3))
+ // still call this if we don't have a server id, since lobbies that aren't port forwarded need to be able to call it
+ m_bSavingPersistentData = true;
+ if (!pdataSize)
{
- m_bRequireClientAuth = false;
+ spdlog::warn("attempted to write pdata of size 0!");
return;
}
- m_bRequireClientAuth = true;
-
- std::string strName(name);
- std::string strDescription(description);
- std::string strMap(map);
- std::string strPlaylist(playlist);
- std::string strPassword(password);
+ std::string strPlayerId(playerId);
+ std::string strPdata(pdata, pdataSize);
std::thread requestThread(
- [this, port, authPort, strName, strDescription, strMap, strPlaylist, maxPlayers, strPassword]
+ [this, strPlayerId, strPdata, pdataSize]
{
- // Keep track of attempted connects in case of DUPLICATE_SERVER response
- int retry_counter = 0;
-
- START_REQUEST:
- m_ownServerId[0] = 0;
- m_ownServerAuthToken[0] = 0;
-
CURL* curl = curl_easy_init();
SetCommonHttpClientOptions(curl);
std::string readBuffer;
+ curl_easy_setopt(
+ curl,
+ CURLOPT_URL,
+ fmt::format(
+ "{}/accounts/write_persistence?id={}&serverId={}",
+ Cvar_ns_masterserver_hostname->GetString(),
+ strPlayerId,
+ m_sOwnServerId)
+ .c_str());
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
@@ -855,554 +726,340 @@ void MasterServerManager::AddSelfToServerList(
curl_mime* mime = curl_mime_init(curl);
curl_mimepart* part = curl_mime_addpart(mime);
- curl_mime_data(part, m_ownModInfoJson.c_str(), m_ownModInfoJson.size());
- curl_mime_name(part, "modinfo");
- curl_mime_filename(part, "modinfo.json");
- curl_mime_type(part, "application/json");
+ curl_mime_data(part, strPdata.c_str(), pdataSize);
+ curl_mime_name(part, "pdata");
+ curl_mime_filename(part, "file.pdata");
+ curl_mime_type(part, "application/octet-stream");
curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);
- // format every paramter because computers hate me
- {
- char* nameEscaped = curl_easy_escape(curl, strName.c_str(), strName.length());
- char* descEscaped = curl_easy_escape(curl, strDescription.c_str(), strDescription.length());
- char* mapEscaped = curl_easy_escape(curl, strMap.c_str(), strMap.length());
- char* playlistEscaped = curl_easy_escape(curl, strPlaylist.c_str(), strPlaylist.length());
- char* passwordEscaped = curl_easy_escape(curl, strPassword.c_str(), strPassword.length());
-
- curl_easy_setopt(
- curl,
- CURLOPT_URL,
- fmt::format(
- "{}/server/add_server?port={}&authPort={}&name={}&description={}&map={}&playlist={}&maxPlayers={}&password={}",
- Cvar_ns_masterserver_hostname->GetString(),
- port,
- authPort,
- nameEscaped,
- descEscaped,
- mapEscaped,
- playlistEscaped,
- maxPlayers,
- passwordEscaped)
- .c_str());
-
- curl_free(nameEscaped);
- curl_free(descEscaped);
- curl_free(mapEscaped);
- curl_free(playlistEscaped);
- curl_free(passwordEscaped);
- }
-
CURLcode result = curl_easy_perform(curl);
if (result == CURLcode::CURLE_OK)
- {
- m_successfullyConnected = true;
-
- rapidjson_document serverAddedJson;
- serverAddedJson.Parse(readBuffer.c_str());
+ m_bSuccessfullyConnected = true;
+ else
+ m_bSuccessfullyConnected = false;
- if (serverAddedJson.HasParseError())
- {
- spdlog::error(
- "Failed reading masterserver authentication response: encountered parse error \"{}\"",
- rapidjson::GetParseError_En(serverAddedJson.GetParseError()));
- goto REQUEST_END_CLEANUP;
- }
+ curl_easy_cleanup(curl);
- if (!serverAddedJson.IsObject())
- {
- spdlog::error("Failed reading masterserver authentication response: root object is not an object");
- goto REQUEST_END_CLEANUP;
- }
+ m_bSavingPersistentData = false;
+ });
- if (serverAddedJson.HasMember("error"))
- {
- spdlog::error("Failed reading masterserver response: got fastify error response");
- spdlog::error(readBuffer);
+ requestThread.detach();
+}
- // Check for enum member in JSON
- if (serverAddedJson["error"].HasMember("enum"))
- {
- // Check for DUPLICATE_SERVER error response, stop if we tried 10 times
- if (strncmp(serverAddedJson["error"]["enum"].GetString(), "DUPLICATE_SERVER", 17) == 0 && retry_counter < 10)
- {
+class MasterServerPresenceReporter : public ServerPresenceReporter
+{
+ const int MAX_REGISTRATION_ATTEMPTS = 5;
- spdlog::info("Retrying request in 10 seconds.");
- // Incremement retry counter
- retry_counter++;
+ double m_bShouldTryRegisterServer;
+ int m_nNumRegistrationAttempts;
- // Sleep for 10 seconds
- std::this_thread::sleep_for(std::chrono::seconds(10));
+ void CreatePresence(const ServerPresence* pServerPresence) override
+ {
+ m_bShouldTryRegisterServer = true;
+ m_nNumRegistrationAttempts = 0;
+ }
- // curl cleanup to retry request
- curl_easy_cleanup(curl);
- curl_mime_free(mime);
+ void ReportPresence(const ServerPresence* pServerPresence) override
+ {
+ // make a copy of presence for multithreading purposes
- // go to beginning and retry
- goto START_REQUEST;
- }
- }
- goto REQUEST_END_CLEANUP;
- }
+ ServerPresence threadedPresence(pServerPresence);
- if (!serverAddedJson["success"].IsTrue())
- {
- spdlog::error("Adding server to masterserver failed: \"success\" is not true");
- goto REQUEST_END_CLEANUP;
- }
-
- if (!serverAddedJson.HasMember("id") || !serverAddedJson["id"].IsString() ||
- !serverAddedJson.HasMember("serverAuthToken") || !serverAddedJson["serverAuthToken"].IsString())
+ if (!*g_pMasterServerManager->m_sOwnServerId || m_bShouldTryRegisterServer)
+ {
+ // add server
+ std::thread addServerThread(
+ [this, threadedPresence]
{
- spdlog::error("Failed reading masterserver response: malformed json object");
- goto REQUEST_END_CLEANUP;
- }
-
- strncpy(m_ownServerId, serverAddedJson["id"].GetString(), sizeof(m_ownServerId));
- m_ownServerId[sizeof(m_ownServerId) - 1] = 0;
+ g_pMasterServerManager->m_sOwnServerId[0] = 0;
+ g_pMasterServerManager->m_sOwnServerAuthToken[0] = 0;
- strncpy(m_ownServerAuthToken, serverAddedJson["serverAuthToken"].GetString(), sizeof(m_ownServerAuthToken));
- m_ownServerAuthToken[sizeof(m_ownServerAuthToken) - 1] = 0;
+ CURL* curl = curl_easy_init();
+ SetCommonHttpClientOptions(curl);
- spdlog::info("Succesfully added server to the master server.");
+ std::string readBuffer;
+ curl_easy_setopt(curl, CURLOPT_POST, 1L);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
- // heartbeat thread
- // ideally this should actually be done in main thread, rather than on it's own thread, so it'd stop if server freezes
- std::thread heartbeatThread(
- [this]
- {
- Sleep(5000);
+ curl_mime* mime = curl_mime_init(curl);
+ curl_mimepart* part = curl_mime_addpart(mime);
- // defensive check, as m_ownServer could be set to null during the Sleep(5000) above
- if (!*m_ownServerId)
- return;
+ curl_mime_data(
+ part, g_pMasterServerManager->m_sOwnModInfoJson.c_str(), g_pMasterServerManager->m_sOwnModInfoJson.size());
+ curl_mime_name(part, "modinfo");
+ curl_mime_filename(part, "modinfo.json");
+ curl_mime_type(part, "application/json");
- do
- {
- CURL* curl = curl_easy_init();
- SetCommonHttpClientOptions(curl);
+ curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);
- std::string readBuffer;
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
- curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);
+ // format every paramter because computers hate me
+ {
+ char* nameEscaped = curl_easy_escape(curl, threadedPresence.m_sServerName.c_str(), NULL);
+ char* descEscaped = curl_easy_escape(curl, threadedPresence.m_sServerDesc.c_str(), NULL);
+ char* mapEscaped = curl_easy_escape(curl, threadedPresence.m_MapName, NULL);
+ char* playlistEscaped = curl_easy_escape(curl, threadedPresence.m_PlaylistName, NULL);
+ char* passwordEscaped = curl_easy_escape(curl, threadedPresence.m_Password, NULL);
+
+ curl_easy_setopt(
+ curl,
+ CURLOPT_URL,
+ fmt::format(
+ "{}/server/"
+ "add_server?port={}&authPort={}&name={}&description={}&map={}&playlist={}&maxPlayers={}&password={}",
+ Cvar_ns_masterserver_hostname->GetString(),
+ threadedPresence.m_iPort,
+ threadedPresence.m_iAuthPort,
+ nameEscaped,
+ descEscaped,
+ mapEscaped,
+ playlistEscaped,
+ threadedPresence.m_iMaxPlayers,
+ passwordEscaped)
+ .c_str());
+
+ curl_free(nameEscaped);
+ curl_free(descEscaped);
+ curl_free(mapEscaped);
+ curl_free(playlistEscaped);
+ curl_free(passwordEscaped);
+ }
- // send all registration info so we have all necessary info to reregister our server if masterserver goes down,
- // without a restart this isn't threadsafe :terror:
- {
- char* escapedNameNew = curl_easy_escape(curl, g_MasterServerManager->ns_auth_srvName.c_str(), NULL);
- char* escapedDescNew = curl_easy_escape(curl, g_MasterServerManager->ns_auth_srvDesc.c_str(), NULL);
- char* escapedMapNew = curl_easy_escape(curl, g_pHostState->m_levelName, NULL);
- char* escapedPlaylistNew = curl_easy_escape(curl, GetCurrentPlaylistName(), NULL);
- char* escapedPasswordNew = curl_easy_escape(curl, Cvar_ns_server_password->GetString(), NULL);
-
- int maxPlayers = 6;
- char* maxPlayersVar = GetCurrentPlaylistVar("max_players", false);
- if (maxPlayersVar) // GetCurrentPlaylistVar can return null so protect against this
- maxPlayers = std::stoi(maxPlayersVar);
-
- curl_easy_setopt(
- curl,
- CURLOPT_URL,
- fmt::format(
- "{}/server/"
- "update_values?id={}&port={}&authPort={}&name={}&description={}&map={}&playlist={}&playerCount={}&"
- "maxPlayers={}&password={}",
- Cvar_ns_masterserver_hostname->GetString(),
- m_ownServerId,
- Cvar_hostport->GetInt(),
- Cvar_ns_player_auth_port->GetInt(),
- escapedNameNew,
- escapedDescNew,
- escapedMapNew,
- escapedPlaylistNew,
- g_ServerAuthenticationManager->m_additionalPlayerData.size(),
- maxPlayers,
- escapedPasswordNew)
- .c_str());
-
- curl_free(escapedNameNew);
- curl_free(escapedDescNew);
- curl_free(escapedMapNew);
- curl_free(escapedPlaylistNew);
- curl_free(escapedPasswordNew);
- }
+ CURLcode result = curl_easy_perform(curl);
- curl_mime* mime = curl_mime_init(curl);
- curl_mimepart* part = curl_mime_addpart(mime);
+ if (result == CURLcode::CURLE_OK)
+ {
+ g_pMasterServerManager->m_bSuccessfullyConnected = true;
- curl_mime_data(part, m_ownModInfoJson.c_str(), m_ownModInfoJson.size());
- curl_mime_name(part, "modinfo");
- curl_mime_filename(part, "modinfo.json");
- curl_mime_type(part, "application/json");
+ rapidjson_document serverAddedJson;
+ serverAddedJson.Parse(readBuffer.c_str());
- curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);
+ if (serverAddedJson.HasParseError())
+ {
+ spdlog::error(
+ "Failed reading masterserver authentication response: encountered parse error \"{}\"",
+ rapidjson::GetParseError_En(serverAddedJson.GetParseError()));
+ goto REQUEST_END_CLEANUP;
+ }
- CURLcode result = curl_easy_perform(curl);
+ if (!serverAddedJson.IsObject())
+ {
+ spdlog::error("Failed reading masterserver authentication response: root object is not an object");
+ goto REQUEST_END_CLEANUP;
+ }
- // defensive check, as m_ownServerId could be set to null before this request gets processed
- if (!*m_ownServerId)
- return;
+ if (serverAddedJson.HasMember("error"))
+ {
+ spdlog::error("Failed reading masterserver response: got fastify error response");
+ spdlog::error(readBuffer);
- if (result == CURLcode::CURLE_OK)
+ if (serverAddedJson["error"].HasMember("enum") && !strcmp(serverAddedJson["error"]["enum"].GetString(), "DUPLICATE_SERVER"))
{
- rapidjson_document serverAddedJson;
- serverAddedJson.Parse(readBuffer.c_str());
-
- if (!serverAddedJson.HasParseError() && serverAddedJson.IsObject())
- {
- if (serverAddedJson.HasMember("id") && serverAddedJson["id"].IsString())
- {
- strncpy(m_ownServerId, serverAddedJson["id"].GetString(), sizeof(m_ownServerId));
- m_ownServerId[sizeof(m_ownServerId) - 1] = 0;
- }
-
- if (serverAddedJson.HasMember("serverAuthToken") && serverAddedJson["serverAuthToken"].IsString())
- {
- strncpy(
- m_ownServerAuthToken,
- serverAddedJson["serverAuthToken"].GetString(),
- sizeof(m_ownServerAuthToken));
- m_ownServerAuthToken[sizeof(m_ownServerAuthToken) - 1] = 0;
- }
- }
- }
- else
- spdlog::warn("Heartbeat failed with error {}", curl_easy_strerror(result));
+ if (++m_nNumRegistrationAttempts == MAX_REGISTRATION_ATTEMPTS)
+ m_bShouldTryRegisterServer = false;
- curl_easy_cleanup(curl);
- Sleep(10000);
- } while (*m_ownServerId);
- });
+ goto REQUEST_END_CLEANUP_RETRY;
+ }
- heartbeatThread.detach();
- }
- else
- {
- spdlog::error("Failed adding self to server list: error {}", curl_easy_strerror(result));
- m_successfullyConnected = false;
- }
+ goto REQUEST_END_CLEANUP;
+ }
- REQUEST_END_CLEANUP:
- curl_easy_cleanup(curl);
- curl_mime_free(mime);
- });
+ if (!serverAddedJson["success"].IsTrue())
+ {
+ spdlog::error("Adding server to masterserver failed: \"success\" is not true");
+ goto REQUEST_END_CLEANUP;
+ }
- requestThread.detach();
-}
+ if (!serverAddedJson.HasMember("id") || !serverAddedJson["id"].IsString() ||
+ !serverAddedJson.HasMember("serverAuthToken") || !serverAddedJson["serverAuthToken"].IsString())
+ {
+ spdlog::error("Failed reading masterserver response: malformed json object");
+ goto REQUEST_END_CLEANUP;
+ }
-void MasterServerManager::UpdateServerMapAndPlaylist(char* map, char* playlist, int maxPlayers)
-{
- // dont call this if we don't have a server id
- if (!*m_ownServerId)
- return;
+ strncpy_s(
+ g_pMasterServerManager->m_sOwnServerId,
+ sizeof(g_pMasterServerManager->m_sOwnServerId),
+ serverAddedJson["id"].GetString(),
+ sizeof(g_pMasterServerManager->m_sOwnServerId) - 1);
+
+ strncpy_s(
+ g_pMasterServerManager->m_sOwnServerAuthToken,
+ sizeof(g_pMasterServerManager->m_sOwnServerAuthToken),
+ serverAddedJson["serverAuthToken"].GetString(),
+ sizeof(g_pMasterServerManager->m_sOwnServerAuthToken) - 1);
+ }
+ else
+ {
+ spdlog::error("Failed adding self to server list: error {}", curl_easy_strerror(result));
+ g_pMasterServerManager->m_bSuccessfullyConnected = false;
+ }
- std::string strMap(map);
- std::string strPlaylist(playlist);
+ REQUEST_END_CLEANUP:
+ m_bShouldTryRegisterServer = false;
- std::thread requestThread(
- [this, strMap, strPlaylist, maxPlayers]
+ REQUEST_END_CLEANUP_RETRY:
+ curl_easy_cleanup(curl);
+ curl_mime_free(mime);
+ });
+ addServerThread.detach();
+ }
+ else
{
- CURL* curl = curl_easy_init();
- SetCommonHttpClientOptions(curl);
-
- std::string readBuffer;
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
-
- // escape params
- {
- char* mapEscaped = curl_easy_escape(curl, strMap.c_str(), strMap.length());
- char* playlistEscaped = curl_easy_escape(curl, strPlaylist.c_str(), strPlaylist.length());
-
- curl_easy_setopt(
- curl,
- CURLOPT_URL,
- fmt::format(
- "{}/server/update_values?id={}&map={}&playlist={}&maxPlayers={}",
- Cvar_ns_masterserver_hostname->GetString(),
- m_ownServerId,
- mapEscaped,
- playlistEscaped,
- maxPlayers)
- .c_str());
-
- curl_free(mapEscaped);
- curl_free(playlistEscaped);
- }
+ // update server
+ std::thread updateServerThread(
+ [threadedPresence]
+ {
+ CURL* curl = curl_easy_init();
+ SetCommonHttpClientOptions(curl);
- CURLcode result = curl_easy_perform(curl);
+ std::string readBuffer;
+ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);
- if (result == CURLcode::CURLE_OK)
- m_successfullyConnected = true;
- else
- m_successfullyConnected = false;
+ // send all registration info so we have all necessary info to reregister our server if masterserver goes down,
+ // without a restart this isn't threadsafe :terror:
+ {
+ char* nameEscaped = curl_easy_escape(curl, threadedPresence.m_sServerName.c_str(), NULL);
+ char* descEscaped = curl_easy_escape(curl, threadedPresence.m_sServerDesc.c_str(), NULL);
+ char* mapEscaped = curl_easy_escape(curl, threadedPresence.m_MapName, NULL);
+ char* playlistEscaped = curl_easy_escape(curl, threadedPresence.m_PlaylistName, NULL);
+ char* passwordEscaped = curl_easy_escape(curl, threadedPresence.m_Password, NULL);
+
+ curl_easy_setopt(
+ curl,
+ CURLOPT_URL,
+ fmt::format(
+ "{}/server/"
+ "update_values?id={}&port={}&authPort={}&name={}&description={}&map={}&playlist={}&playerCount={}&"
+ "maxPlayers={}&password={}",
+ Cvar_ns_masterserver_hostname->GetString(),
+ g_pMasterServerManager->m_sOwnServerId,
+ threadedPresence.m_iPort,
+ threadedPresence.m_iAuthPort,
+ nameEscaped,
+ descEscaped,
+ mapEscaped,
+ playlistEscaped,
+ threadedPresence.m_iPlayerCount,
+ threadedPresence.m_iMaxPlayers,
+ passwordEscaped)
+ .c_str());
+
+ curl_free(nameEscaped);
+ curl_free(descEscaped);
+ curl_free(mapEscaped);
+ curl_free(playlistEscaped);
+ curl_free(passwordEscaped);
+ }
- curl_easy_cleanup(curl);
- });
+ curl_mime* mime = curl_mime_init(curl);
+ curl_mimepart* part = curl_mime_addpart(mime);
- requestThread.detach();
-}
+ curl_mime_data(
+ part, g_pMasterServerManager->m_sOwnModInfoJson.c_str(), g_pMasterServerManager->m_sOwnModInfoJson.size());
+ curl_mime_name(part, "modinfo");
+ curl_mime_filename(part, "modinfo.json");
+ curl_mime_type(part, "application/json");
-void MasterServerManager::UpdateServerPlayerCount(int playerCount)
-{
- // dont call this if we don't have a server id
- if (!*m_ownServerId)
- return;
+ curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);
- std::thread requestThread(
- [this, playerCount]
- {
- CURL* curl = curl_easy_init();
- SetCommonHttpClientOptions(curl);
+ CURLcode result = curl_easy_perform(curl);
- std::string readBuffer;
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
- curl_easy_setopt(
- curl,
- CURLOPT_URL,
- fmt::format(
- "{}/server/update_values?id={}&playerCount={}", Cvar_ns_masterserver_hostname->GetString(), m_ownServerId, playerCount)
- .c_str());
-
- CURLcode result = curl_easy_perform(curl);
+ if (result == CURLcode::CURLE_OK)
+ {
+ rapidjson_document serverAddedJson;
+ serverAddedJson.Parse(readBuffer.c_str());
- if (result == CURLcode::CURLE_OK)
- m_successfullyConnected = true;
- else
- m_successfullyConnected = false;
+ if (!serverAddedJson.HasParseError() && serverAddedJson.IsObject())
+ {
+ if (serverAddedJson.HasMember("id") && serverAddedJson["id"].IsString())
+ {
+ strncpy_s(
+ g_pMasterServerManager->m_sOwnServerId,
+ sizeof(g_pMasterServerManager->m_sOwnServerId),
+ serverAddedJson["id"].GetString(),
+ sizeof(g_pMasterServerManager->m_sOwnServerId) - 1);
+ }
- curl_easy_cleanup(curl);
- });
+ if (serverAddedJson.HasMember("serverAuthToken") && serverAddedJson["serverAuthToken"].IsString())
+ {
- requestThread.detach();
-}
+ strncpy_s(
+ g_pMasterServerManager->m_sOwnServerAuthToken,
+ sizeof(g_pMasterServerManager->m_sOwnServerAuthToken),
+ serverAddedJson["serverAuthToken"].GetString(),
+ sizeof(g_pMasterServerManager->m_sOwnServerAuthToken) - 1);
+ }
+ }
+ }
+ else
+ spdlog::warn("Heartbeat failed with error {}", curl_easy_strerror(result));
-void MasterServerManager::WritePlayerPersistentData(char* playerId, char* pdata, size_t pdataSize)
-{
- // still call this if we don't have a server id, since lobbies that aren't port forwarded need to be able to call it
- m_savingPersistentData = true;
- if (!pdataSize)
- {
- spdlog::warn("attempted to write pdata of size 0!");
- return;
+ curl_easy_cleanup(curl);
+ });
+ updateServerThread.detach();
+ }
}
- std::string strPlayerId(playerId);
- std::string strPdata(pdata, pdataSize);
-
- std::thread requestThread(
- [this, strPlayerId, strPdata, pdataSize]
- {
- CURL* curl = curl_easy_init();
- SetCommonHttpClientOptions(curl);
-
- std::string readBuffer;
- curl_easy_setopt(
- curl,
- CURLOPT_URL,
- fmt::format(
- "{}/accounts/write_persistence?id={}&serverId={}",
- Cvar_ns_masterserver_hostname->GetString(),
- strPlayerId,
- m_ownServerId)
- .c_str());
- curl_easy_setopt(curl, CURLOPT_POST, 1L);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
-
- curl_mime* mime = curl_mime_init(curl);
- curl_mimepart* part = curl_mime_addpart(mime);
-
- curl_mime_data(part, strPdata.c_str(), pdataSize);
- curl_mime_name(part, "pdata");
- curl_mime_filename(part, "file.pdata");
- curl_mime_type(part, "application/octet-stream");
-
- curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);
-
- CURLcode result = curl_easy_perform(curl);
-
- if (result == CURLcode::CURLE_OK)
- m_successfullyConnected = true;
- else
- m_successfullyConnected = false;
-
- curl_easy_cleanup(curl);
-
- m_savingPersistentData = false;
- });
-
- requestThread.detach();
-}
-
-void MasterServerManager::RemoveSelfFromServerList()
-{
- // dont call this if we don't have a server id
- if (!*m_ownServerId || !Cvar_ns_report_server_to_masterserver->GetBool())
- return;
-
- std::thread requestThread(
- [this]
- {
- CURL* curl = curl_easy_init();
- SetCommonHttpClientOptions(curl);
-
- std::string readBuffer;
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
- curl_easy_setopt(
- curl,
- CURLOPT_URL,
- fmt::format("{}/server/remove_server?id={}", Cvar_ns_masterserver_hostname->GetString(), m_ownServerId).c_str());
-
- *m_ownServerId = 0;
- CURLcode result = curl_easy_perform(curl);
-
- if (result == CURLcode::CURLE_OK)
- m_successfullyConnected = true;
- else
- m_successfullyConnected = false;
+ void DestroyPresence(const ServerPresence* pServerPresence) override
+ {
+ // dont call this if we don't have a server id
+ if (!*g_pMasterServerManager->m_sOwnServerId)
+ return;
- curl_easy_cleanup(curl);
- });
+ std::thread requestThread(
+ [this]
+ {
+ CURL* curl = curl_easy_init();
+ SetCommonHttpClientOptions(curl);
- requestThread.detach();
-}
+ std::string readBuffer;
+ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
+ curl_easy_setopt(
+ curl,
+ CURLOPT_URL,
+ fmt::format(
+ "{}/server/remove_server?id={}", Cvar_ns_masterserver_hostname->GetString(), g_pMasterServerManager->m_sOwnServerId)
+ .c_str());
-void ConCommand_ns_fetchservers(const CCommand& args)
-{
- g_MasterServerManager->RequestServerList();
-}
+ *g_pMasterServerManager->m_sOwnServerId = 0;
+ CURLcode result = curl_easy_perform(curl);
-void CHostState__State_NewGameHook(CHostState* hostState)
-{
- Cbuf_AddText(Cbuf_GetCurrentPlayer(), "exec autoexec_ns_server", cmd_source_t::kCommandSrcCode);
- Cbuf_Execute();
+ if (result == CURLcode::CURLE_OK)
+ g_pMasterServerManager->m_bSuccessfullyConnected = true;
+ else
+ g_pMasterServerManager->m_bSuccessfullyConnected = false;
- // need to do this to ensure we don't go to private match
- if (g_ServerAuthenticationManager->m_bNeedLocalAuthForNewgame)
- SetCurrentPlaylist("tdm");
+ curl_easy_cleanup(curl);
+ });
- // net_data_block_enabled is required for sp, force it if we're on an sp map
- // sucks for security but just how it be
- if (!strncmp(g_pHostState->m_levelName, "sp_", 3))
- {
- Cbuf_AddText(Cbuf_GetCurrentPlayer(), "net_data_block_enabled 1", cmd_source_t::kCommandSrcCode);
- Cbuf_Execute();
+ requestThread.detach();
}
+};
- CHostState__State_NewGame(hostState);
-
- int maxPlayers = 6;
- char* maxPlayersVar = GetCurrentPlaylistVar("max_players", false);
- if (maxPlayersVar) // GetCurrentPlaylistVar can return null so protect against this
- maxPlayers = std::stoi(maxPlayersVar);
-
- // Copy new server name cvar to source
- Cvar_hostname->SetValue(Cvar_ns_server_name->GetString());
- // This calls the function that converts unicode strings from servername and serverdesc to UTF-8
- UpdateServerInfoFromUnicodeToUTF8();
-
- g_MasterServerManager->AddSelfToServerList(
- Cvar_hostport->GetInt(),
- Cvar_ns_player_auth_port->GetInt(),
- (char*)Cvar_ns_server_name->GetString(),
- (char*)Cvar_ns_server_desc->GetString(),
- hostState->m_levelName,
- (char*)GetCurrentPlaylistName(),
- maxPlayers,
- (char*)Cvar_ns_server_password->GetString());
- g_ServerAuthenticationManager->StartPlayerAuthServer();
- g_ServerAuthenticationManager->m_bNeedLocalAuthForNewgame = false;
-}
-
-void CHostState__State_ChangeLevelMPHook(CHostState* hostState)
+void ConCommand_ns_fetchservers(const CCommand& args)
{
- int maxPlayers = 6;
- char* maxPlayersVar = GetCurrentPlaylistVar("max_players", false);
- if (maxPlayersVar) // GetCurrentPlaylistVar can return null so protect against this
- maxPlayers = std::stoi(maxPlayersVar);
-
- // net_data_block_enabled is required for sp, force it if we're on an sp map
- // sucks for security but just how it be
- if (!strncmp(g_pHostState->m_levelName, "sp_", 3))
- {
- Cbuf_AddText(Cbuf_GetCurrentPlayer(), "net_data_block_enabled 1", cmd_source_t::kCommandSrcCode);
- Cbuf_Execute();
- }
-
- g_MasterServerManager->UpdateServerMapAndPlaylist(hostState->m_levelName, (char*)GetCurrentPlaylistName(), maxPlayers);
- CHostState__State_ChangeLevelMP(hostState);
+ g_pMasterServerManager->RequestServerList();
}
-void CHostState__State_ChangeLevelSPHook(CHostState* hostState)
-{
- // is this even called? genuinely i don't think so
- // from what i can tell, it's not called on mp=>sp change or sp=>sp change
- // so idk it's fucked
-
- int maxPlayers = 6;
- char* maxPlayersVar = GetCurrentPlaylistVar("max_players", false);
- if (maxPlayersVar) // GetCurrentPlaylistVar can return null so protect against this
- maxPlayers = std::stoi(maxPlayersVar);
-
- g_MasterServerManager->UpdateServerMapAndPlaylist(hostState->m_levelName, (char*)GetCurrentPlaylistName(), maxPlayers);
- CHostState__State_ChangeLevelSP(hostState);
-}
+MasterServerManager::MasterServerManager() : m_pendingConnectionInfo {}, m_sOwnServerId {""}, m_sOwnClientAuthToken {""} {}
-void CHostState__State_GameShutdownHook(CHostState* hostState)
+ON_DLL_LOAD_RELIESON("engine.dll", MasterServer, (ConCommand, ServerPresence), (CModule module))
{
- g_MasterServerManager->RemoveSelfFromServerList();
- g_ServerAuthenticationManager->StopPlayerAuthServer();
+ g_pMasterServerManager = new MasterServerManager;
- CHostState__State_GameShutdown(hostState);
-}
+ Cvar_ns_masterserver_hostname = new ConVar("ns_masterserver_hostname", "127.0.0.1", FCVAR_NONE, "");
+ Cvar_ns_curl_log_enable = new ConVar("ns_curl_log_enable", "0", FCVAR_NONE, "Whether curl should log to the console");
-MasterServerManager::MasterServerManager() : m_pendingConnectionInfo {}, m_ownServerId {""}, m_ownClientAuthToken {""} {}
+ RegisterConCommand("ns_fetchservers", ConCommand_ns_fetchservers, "Fetch all servers from the masterserver", FCVAR_CLIENTDLL);
-void InitialiseSharedMasterServer(HMODULE baseAddress)
-{
- Cvar_ns_masterserver_hostname = new ConVar("ns_masterserver_hostname", "127.0.0.1", FCVAR_NONE, "");
- // unfortunately lib doesn't let us specify a port and still have https work
- // Cvar_ns_masterserver_port = new ConVar("ns_masterserver_port", "8080", FCVAR_NONE, "");
-
- Cvar_ns_server_name = new ConVar("ns_server_name", "Unnamed Northstar Server", FCVAR_GAMEDLL, "");
- Cvar_ns_server_desc = new ConVar("ns_server_desc", "Default server description", FCVAR_GAMEDLL, "");
- Cvar_ns_server_password = new ConVar("ns_server_password", "", FCVAR_GAMEDLL, "");
- Cvar_ns_report_server_to_masterserver = new ConVar("ns_report_server_to_masterserver", "1", FCVAR_GAMEDLL, "");
- Cvar_ns_report_sp_server_to_masterserver = new ConVar("ns_report_sp_server_to_masterserver", "0", FCVAR_GAMEDLL, "");
-
- Cvar_ns_curl_log_enable = new ConVar("ns_curl_log_enable", "0", FCVAR_NONE, "");
-
- Cvar_hostname = *(ConVar**)((char*)baseAddress + 0x1315bae8);
-
- g_MasterServerManager = new MasterServerManager;
-
- RegisterConCommand("ns_fetchservers", ConCommand_ns_fetchservers, "", FCVAR_CLIENTDLL);
-
- HookEnabler hook;
- ENABLER_CREATEHOOK(
- hook, (char*)baseAddress + 0x16E7D0, CHostState__State_NewGameHook, reinterpret_cast<LPVOID*>(&CHostState__State_NewGame));
- ENABLER_CREATEHOOK(
- hook,
- (char*)baseAddress + 0x16E520,
- CHostState__State_ChangeLevelMPHook,
- reinterpret_cast<LPVOID*>(&CHostState__State_ChangeLevelMP));
- ENABLER_CREATEHOOK(
- hook,
- (char*)baseAddress + 0x16E5D0,
- CHostState__State_ChangeLevelSPHook,
- reinterpret_cast<LPVOID*>(&CHostState__State_ChangeLevelSP));
- ENABLER_CREATEHOOK(
- hook,
- (char*)baseAddress + 0x16E640,
- CHostState__State_GameShutdownHook,
- reinterpret_cast<LPVOID*>(&CHostState__State_GameShutdown));
+ MasterServerPresenceReporter* presenceReporter = new MasterServerPresenceReporter;
+ g_pServerPresence->AddPresenceReporter(presenceReporter);
}