From f0a561552ba19ab40c9458c465eec5454c31d581 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Mon, 24 Apr 2023 01:55:37 +0200 Subject: [PATCH] CPylon server query code deduplication Created another dedicated wrapper for handling API requests; significantly reduced duplicate code. --- r5dev/networksystem/pylon.cpp | 357 +++++++++++++++++----------------- r5dev/networksystem/pylon.h | 14 +- 2 files changed, 185 insertions(+), 186 deletions(-) diff --git a/r5dev/networksystem/pylon.cpp b/r5dev/networksystem/pylon.cpp index 367add68..9fbb594b 100644 --- a/r5dev/networksystem/pylon.cpp +++ b/r5dev/networksystem/pylon.cpp @@ -14,43 +14,35 @@ #endif // !CLIENT_DLL //----------------------------------------------------------------------------- -// Purpose: returns a vector of hosted servers. -// Input : &svOutMessage - +// Purpose: gets a vector of hosted servers. +// Input : &outMessage - // Output : vector //----------------------------------------------------------------------------- -vector CPylon::GetServerList(string& svOutMessage) const +vector CPylon::GetServerList(string& outMessage) const { - vector vslList; + vector vecServers; - nlohmann::json jsRequestBody = nlohmann::json::object(); - jsRequestBody["version"] = SDK_VERSION; + nlohmann::json requestJson = nlohmann::json::object(); + requestJson["version"] = SDK_VERSION; - const string svRequestBody = jsRequestBody.dump(4); - const bool bDebug = pylon_showdebuginfo->GetBool(); - - if (bDebug) - { - DevMsg(eDLL_T::ENGINE, "%s - Sending server list request to comp-server:\n%s\n", __FUNCTION__, svRequestBody.c_str()); - } - - string svResponse; + string responseBody; CURLINFO status; - if (!QueryMasterServer(pylon_matchmaking_hostname->GetString(), "/servers", svRequestBody, svResponse, svOutMessage, status)) + if (!QueryAPI("/servers", requestJson.dump(4), responseBody, outMessage, status)) { - return vslList; + return vecServers; } try { if (status == 200) // STATUS_OK { - nlohmann::json jsResultBody = nlohmann::json::parse(svResponse); - if (jsResultBody["success"].is_boolean() && jsResultBody["success"].get()) + nlohmann::json resultJson = nlohmann::json::parse(responseBody); + if (resultJson["success"].is_boolean() && resultJson["success"].get()) { - for (auto& obj : jsResultBody["servers"]) + for (auto& obj : resultJson["servers"]) { - vslList.push_back( + vecServers.push_back( NetGameServer_t { obj.value("name",""), @@ -74,13 +66,13 @@ vector CPylon::GetServerList(string& svOutMessage) const } else { - ExtractError(jsResultBody, svOutMessage, status); + ExtractError(resultJson, outMessage, status); } } else { - ExtractError(svResponse, svOutMessage, status, "Server list error"); - return vslList; + ExtractError(responseBody, outMessage, status, "Server list error"); + return vecServers; } } catch (const std::exception& ex) @@ -88,85 +80,74 @@ vector CPylon::GetServerList(string& svOutMessage) const Warning(eDLL_T::ENGINE, "%s - Exception while parsing comp-server response:\n%s\n", __FUNCTION__, ex.what()); } - return vslList; + return vecServers; } //----------------------------------------------------------------------------- // Purpose: Gets the server by token string. -// Input : &slOutServer - -// &svOutMessage - -// &svToken - +// Input : &outGameServer - +// &outMessage - +// &token - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- -bool CPylon::GetServerByToken(NetGameServer_t& slOutServer, string& svOutMessage, const string& svToken) const +bool CPylon::GetServerByToken(NetGameServer_t& outGameServer, + string& outMessage, const string& token) const { - nlohmann::json jsRequestBody = nlohmann::json::object(); - jsRequestBody["token"] = svToken; + nlohmann::json requestJson = nlohmann::json::object(); + requestJson["token"] = token; - const string svRequestBody = jsRequestBody.dump(4); - const bool bDebugLog = pylon_showdebuginfo->GetBool(); - - if (bDebugLog) - { - DevMsg(eDLL_T::ENGINE, "%s - Sending token connect request to comp-server:\n%s\n", __FUNCTION__, svRequestBody.c_str()); - } - - string svResponseBuf; + string responseBody; CURLINFO status; - if (!QueryMasterServer(pylon_matchmaking_hostname->GetString(), "/server/byToken", svRequestBody, svResponseBuf, svOutMessage, status)) + if (!QueryAPI("/server/byToken", requestJson.dump(4), responseBody, outMessage, status)) { return false; } - if (bDebugLog) - { - DevMsg(eDLL_T::ENGINE, "%s - Comp-server replied with status: '%d'\n", __FUNCTION__, status); - } - try { if (status == 200) // STATUS_OK { - nlohmann::json jsResultBody = nlohmann::json::parse(svResponseBuf); + nlohmann::json responseJson = nlohmann::json::parse(responseBody); - if (bDebugLog) + if (pylon_showdebuginfo->GetBool()) { - string svResultBody = jsResultBody.dump(4); - DevMsg(eDLL_T::ENGINE, "%s - Comp-server response body:\n%s\n", __FUNCTION__, svResultBody.c_str()); + responseBody = responseJson.dump(4); + DevMsg(eDLL_T::ENGINE, "%s: Response body:\n%s\n", + __FUNCTION__, responseBody.c_str()); } - if (jsResultBody["success"].is_boolean() && jsResultBody["success"]) + if (responseJson["success"].is_boolean() && responseJson["success"]) { - slOutServer = NetGameServer_t + outGameServer = NetGameServer_t { - jsResultBody["server"].value("name",""), - jsResultBody["server"].value("description",""), - jsResultBody["server"].value("hidden","false") == "true", - jsResultBody["server"].value("map",""), - jsResultBody["server"].value("playlist",""), - jsResultBody["server"].value("ip",""), - jsResultBody["server"].value("port", ""), - jsResultBody["server"].value("key",""), - jsResultBody["server"].value("checksum",""), - jsResultBody["server"].value("version", SDK_VERSION), - jsResultBody["server"].value("playerCount", ""), - jsResultBody["server"].value("maxPlayers", ""), - jsResultBody["server"].value("timeStamp", 0), - jsResultBody["server"].value("publicRef", ""), - jsResultBody["server"].value("cachedId", ""), + responseJson["server"].value("name",""), + responseJson["server"].value("description",""), + responseJson["server"].value("hidden","false") == "true", + responseJson["server"].value("map",""), + responseJson["server"].value("playlist",""), + responseJson["server"].value("ip",""), + responseJson["server"].value("port", ""), + responseJson["server"].value("key",""), + responseJson["server"].value("checksum",""), + responseJson["server"].value("version", SDK_VERSION), + responseJson["server"].value("playerCount", ""), + responseJson["server"].value("maxPlayers", ""), + responseJson["server"].value("timeStamp", 0), + responseJson["server"].value("publicRef", ""), + responseJson["server"].value("cachedId", ""), }; return true; } else { - ExtractError(jsResultBody, svOutMessage, status); + ExtractError(responseJson, outMessage, status); return false; } } else { - ExtractError(svResponseBuf, svOutMessage, status, "Server not found"); + ExtractError(responseBody, outMessage, status, "Server not found"); return false; } } @@ -180,88 +161,77 @@ bool CPylon::GetServerByToken(NetGameServer_t& slOutServer, string& svOutMessage //----------------------------------------------------------------------------- // Purpose: Sends host server POST request. -// Input : &svOutMessage - -// &svOutToken - +// Input : &outMessage - +// &outToken - // &netGameServer - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- -bool CPylon::PostServerHost(string& svOutMessage, string& svOutToken, const NetGameServer_t& netGameServer) const +bool CPylon::PostServerHost(string& outMessage, string& outToken, const NetGameServer_t& netGameServer) const { - nlohmann::json jsRequestBody = nlohmann::json::object(); - jsRequestBody["name"] = netGameServer.m_svHostName; - jsRequestBody["description"] = netGameServer.m_svDescription; - jsRequestBody["hidden"] = netGameServer.m_bHidden; - jsRequestBody["map"] = netGameServer.m_svHostMap; - jsRequestBody["playlist"] = netGameServer.m_svPlaylist; - jsRequestBody["ip"] = netGameServer.m_svIpAddress; - jsRequestBody["port"] = netGameServer.m_svGamePort; - jsRequestBody["key"] = netGameServer.m_svEncryptionKey; - jsRequestBody["checksum"] = netGameServer.m_svRemoteChecksum; - jsRequestBody["version"] = netGameServer.m_svSDKVersion; - jsRequestBody["playerCount"] = netGameServer.m_svPlayerCount; - jsRequestBody["maxPlayers"] = netGameServer.m_svMaxPlayers; - jsRequestBody["timeStamp"] = netGameServer.m_nTimeStamp; - jsRequestBody["publicRef"] = netGameServer.m_svPublicRef; - jsRequestBody["cachedId"] = netGameServer.m_svCachedId; - - string svRequestBody = jsRequestBody.dump(4); - string svResponseBuf; - - const bool bDebugLog = pylon_showdebuginfo->GetBool(); - if (bDebugLog) - { - DevMsg(eDLL_T::ENGINE, "%s - Sending post host request to comp-server:\n%s\n", __FUNCTION__, svRequestBody.c_str()); - } + nlohmann::json requestJson = nlohmann::json::object(); + requestJson["name"] = netGameServer.m_svHostName; + requestJson["description"] = netGameServer.m_svDescription; + requestJson["hidden"] = netGameServer.m_bHidden; + requestJson["map"] = netGameServer.m_svHostMap; + requestJson["playlist"] = netGameServer.m_svPlaylist; + requestJson["ip"] = netGameServer.m_svIpAddress; + requestJson["port"] = netGameServer.m_svGamePort; + requestJson["key"] = netGameServer.m_svEncryptionKey; + requestJson["checksum"] = netGameServer.m_svRemoteChecksum; + requestJson["version"] = netGameServer.m_svSDKVersion; + requestJson["playerCount"] = netGameServer.m_svPlayerCount; + requestJson["maxPlayers"] = netGameServer.m_svMaxPlayers; + requestJson["timeStamp"] = netGameServer.m_nTimeStamp; + requestJson["publicRef"] = netGameServer.m_svPublicRef; + requestJson["cachedId"] = netGameServer.m_svCachedId; + string responseBody; CURLINFO status; - if (!QueryMasterServer(pylon_matchmaking_hostname->GetString(), "/servers/add", svRequestBody, svResponseBuf, svOutMessage, status)) + + if (!QueryAPI("/servers/add", requestJson.dump(4), responseBody, outMessage, status)) { return false; } - if (bDebugLog) - { - DevMsg(eDLL_T::ENGINE, "%s - Comp-server replied with status: '%d'\n", __FUNCTION__, status); - } - try { if (status == 200) // STATUS_OK { - nlohmann::json jsResultBody = nlohmann::json::parse(svResponseBuf); + nlohmann::json responseJson = nlohmann::json::parse(responseBody); - if (bDebugLog) + if (pylon_showdebuginfo->GetBool()) { - string svResultBody = jsResultBody.dump(4); - DevMsg(eDLL_T::ENGINE, "%s - Comp-server response body:\n%s\n", __FUNCTION__, svResultBody.c_str()); + responseBody = responseJson.dump(4); + DevMsg(eDLL_T::ENGINE, "%s: Response body:\n%s\n", + __FUNCTION__, responseBody.c_str()); } - if (jsResultBody["success"].is_boolean() && jsResultBody["success"].get()) + if (responseJson["success"].is_boolean() && responseJson["success"].get()) { - if (jsResultBody["token"].is_string()) + if (responseJson["token"].is_string()) { - svOutToken = jsResultBody["token"].get(); + outToken = responseJson["token"].get(); } else { - svOutMessage = Format("Invalid response with status: %d", static_cast(status)); - svOutToken.clear(); + outMessage = Format("Invalid response with status: %d", static_cast(status)); + outToken.clear(); } return true; } else { - ExtractError(jsResultBody, svOutMessage, status); - svOutToken.clear(); + ExtractError(responseJson, outMessage, status); + outToken.clear(); return false; } } else { - ExtractError(svResponseBuf, svOutMessage, status, "Server host error"); - svOutToken.clear(); + ExtractError(responseBody, outMessage, status, "Server host error"); + outToken.clear(); return false; } @@ -287,26 +257,26 @@ bool CPylon::KeepAlive(const NetGameServer_t& netGameServer) return false; } - string svHostToken; - string svErrorMsg; + string errorMsg; + string hostToken; - bool result = PostServerHost(svErrorMsg, svHostToken, netGameServer); + const bool result = PostServerHost(errorMsg, hostToken, netGameServer); if (!result) { - if (!svErrorMsg.empty() && m_ErrorMsg.compare(svErrorMsg) != NULL) + if (!errorMsg.empty() && m_ErrorMsg.compare(errorMsg) != NULL) { - m_ErrorMsg = svErrorMsg; - Error(eDLL_T::SERVER, NO_ERROR, "%s\n", svErrorMsg.c_str()); + m_ErrorMsg = errorMsg; + Error(eDLL_T::SERVER, NO_ERROR, "%s\n", errorMsg.c_str()); } } else // Attempt to log the token, if there is one. { - if (!svHostToken.empty() && m_Token.compare(svHostToken) != NULL) + if (!hostToken.empty() && m_Token.compare(hostToken) != NULL) { - m_Token = svHostToken; + m_Token = hostToken; DevMsg(eDLL_T::SERVER, "Published server with token: %s'%s%s%s'\n", g_svReset.c_str(), g_svGreyB.c_str(), - svHostToken.c_str(), g_svReset.c_str()); + hostToken.c_str(), g_svReset.c_str()); } } @@ -316,39 +286,26 @@ bool CPylon::KeepAlive(const NetGameServer_t& netGameServer) //----------------------------------------------------------------------------- // Purpose: Checks if client is banned on the comp server. -// Input : &svIpAddress - -// nNucleusID - -// &svOutReason - +// Input : &ipAddress - +// nucleusId - +// &outReason - <- contains banned reason if any. // Output : Returns true if banned, false if not banned. //----------------------------------------------------------------------------- -bool CPylon::CheckForBan(const string& svIpAddress, const uint64_t nNucleusID, string& svOutReason) const +bool CPylon::CheckForBan(const string& ipAddress, const uint64_t nucleusId, string& outReason) const { - nlohmann::json jsRequestBody = nlohmann::json::object(); - jsRequestBody["id"] = nNucleusID; - jsRequestBody["ip"] = svIpAddress; + nlohmann::json requestJson = nlohmann::json::object(); + requestJson["id"] = nucleusId; + requestJson["ip"] = ipAddress; - string svRequestBody = jsRequestBody.dump(4); - string svResponseBuf; - string svOutMessage; + string responseBody; + string outMessage; CURLINFO status; - const bool bDebugLog = pylon_showdebuginfo->GetBool(); - - if (bDebugLog) - { - DevMsg(eDLL_T::ENGINE, "%s - Sending ban check request to comp-server:\n%s\n", __FUNCTION__, svRequestBody.c_str()); - } - - if (!QueryMasterServer(pylon_matchmaking_hostname->GetString(), "/banlist/isBanned", svRequestBody, svResponseBuf, svOutMessage, status)) + if (!QueryAPI("/banlist/isBanned", requestJson.dump(4), responseBody, outMessage, status)) { return false; } - if (bDebugLog) - { - DevMsg(eDLL_T::ENGINE, "%s - Comp-server replied with status: '%d'\n", __FUNCTION__, status); - } - if (status != 200) { Error(eDLL_T::ENGINE, NO_ERROR, "%s - Failed to query comp-server: status code = %d\n", __FUNCTION__, status); @@ -357,19 +314,20 @@ bool CPylon::CheckForBan(const string& svIpAddress, const uint64_t nNucleusID, s try { - nlohmann::json jsResultBody = nlohmann::json::parse(svResponseBuf); + nlohmann::json responseJson = nlohmann::json::parse(responseBody); - if (bDebugLog) + if (pylon_showdebuginfo->GetBool()) { - string svResultBody = jsResultBody.dump(4); - DevMsg(eDLL_T::ENGINE, "%s - Comp-server response body:\n%s\n", __FUNCTION__, svResultBody.c_str()); + responseBody = responseJson.dump(4); + DevMsg(eDLL_T::ENGINE, "%s: Response body:\n%s\n", + __FUNCTION__, responseBody.c_str()); } - if (jsResultBody["success"].is_boolean() && jsResultBody["success"].get()) + if (responseJson["success"].is_boolean() && responseJson["success"].get()) { - if (jsResultBody["banned"].is_boolean() && jsResultBody["banned"].get()) + if (responseJson["banned"].is_boolean() && responseJson["banned"].get()) { - svOutReason = jsResultBody.value("reason", "#DISCONNECT_BANNED"); + outReason = responseJson.value("reason", "#DISCONNECT_BANNED"); return true; } } @@ -384,29 +342,66 @@ bool CPylon::CheckForBan(const string& svIpAddress, const uint64_t nNucleusID, s //----------------------------------------------------------------------------- // Purpose: Sends query to master server. -// Input : &svHostName - -// &svApi - -// &svInRequest - -// &svResponse - -// &svOutMessage - -// &outStatus - +// Input : &apiName - +// &request - +// &outResponse - +// &outMessage - <- contains an error message if any. +// &outStatus - // Output : Returns true if successful, false otherwise. //----------------------------------------------------------------------------- -bool CPylon::QueryMasterServer(const string& svHostName, const string& svApi, const string& svInRequest, - string& svOutResponse, string& svOutMessage, CURLINFO& outStatus) const +bool CPylon::QueryAPI(const string& apiName, const string& request, + string& outResponse, string& outMessage, CURLINFO& outStatus) const { - string svUrl; - CURLFormatUrl(svUrl, svHostName, svApi); + const bool showDebug = pylon_showdebuginfo->GetBool(); + const char* hostName = pylon_matchmaking_hostname->GetString(); + + if (showDebug) + { + DevMsg(eDLL_T::ENGINE, "%s: Sending '%s' request to '%s':\n%s\n", + __FUNCTION__, apiName.c_str(), hostName, request.c_str()); + } + + if (!QueryMasterServer(pylon_matchmaking_hostname->GetString(), apiName, + request, outResponse, outMessage, outStatus)) + { + return false; + } + + if (showDebug) + { + DevMsg(eDLL_T::ENGINE, "%s: Host '%s' replied with status: '%d'\n", + __FUNCTION__, hostName, outStatus); + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Sends query to master server. +// Input : &hostName - +// &apiName - +// &request - +// &outResponse - +// &outMessage - <- contains an error message if any. +// &outStatus - +// Output : Returns true if successful, false otherwise. +//----------------------------------------------------------------------------- +bool CPylon::QueryMasterServer(const string& hostName, const string& apiName, + const string& request, string& outResponse, string& outMessage, + CURLINFO& outStatus) const +{ + string finalUrl; + CURLFormatUrl(finalUrl, hostName, apiName); curl_slist* sList = nullptr; - CURL* curl = CURLInitRequest(svUrl, svInRequest, svOutResponse, sList); + CURL* curl = CURLInitRequest(finalUrl, request, outResponse, sList); if (!curl) { return false; } CURLcode res = CURLSubmitRequest(curl, sList); - if (!CURLHandleError(curl, res, svOutMessage)) + if (!CURLHandleError(curl, res, outMessage)) { return false; } @@ -416,17 +411,18 @@ bool CPylon::QueryMasterServer(const string& svHostName, const string& svApi, co } //----------------------------------------------------------------------------- -// Purpose: Extracts the error from the result body. -// Input : &resultBody - +// Purpose: Extracts the error from the result json. +// Input : &resultJson - // &outMessage - -// status - -// *errorText - +// status - +// *errorText - //----------------------------------------------------------------------------- -void CPylon::ExtractError(const nlohmann::json& resultBody, string& outMessage, CURLINFO status, const char* errorText) const +void CPylon::ExtractError(const nlohmann::json& resultJson, string& outMessage, + CURLINFO status, const char* errorText) const { - if (resultBody["error"].is_string()) + if (resultJson["error"].is_string()) { - outMessage = resultBody["error"].get(); + outMessage = resultJson["error"].get(); } else { @@ -441,16 +437,17 @@ void CPylon::ExtractError(const nlohmann::json& resultBody, string& outMessage, //----------------------------------------------------------------------------- // Purpose: Extracts the error from the response buffer. -// Input : &resultBody - +// Input : &response - // &outMessage - -// status - -// *errorText - +// status - +// *errorText - //----------------------------------------------------------------------------- -void CPylon::ExtractError(const string& responseBuffer, string& outMessage, CURLINFO status, const char* errorText) const +void CPylon::ExtractError(const string& response, string& outMessage, + CURLINFO status, const char* errorText) const { - if (!responseBuffer.empty()) + if (!response.empty()) { - nlohmann::json resultBody = nlohmann::json::parse(responseBuffer); + nlohmann::json resultBody = nlohmann::json::parse(response); ExtractError(resultBody, outMessage, status, errorText); } else if (status) diff --git a/r5dev/networksystem/pylon.h b/r5dev/networksystem/pylon.h index d6565198..a6ded6bb 100644 --- a/r5dev/networksystem/pylon.h +++ b/r5dev/networksystem/pylon.h @@ -5,14 +5,16 @@ class CPylon { public: - vector GetServerList(string& svOutMessage) const; - bool GetServerByToken(NetGameServer_t& slOutServer, string& svOutMessage, const string& svToken) const; - bool PostServerHost(string& svOutMessage, string& svOutToken, const NetGameServer_t& netGameServer) const; - bool CheckForBan(const string& svIpAddress, const uint64_t nNucleusID, string& svOutReason) const; - bool QueryMasterServer(const string& svHostName, const string& svApi, const string& svRequest, string& svResponse, string& svOutMessage, CURLINFO& status) const; + vector GetServerList(string& outMessage) const; + bool GetServerByToken(NetGameServer_t& slOutServer, string& outMessage, const string& svToken) const; + bool PostServerHost(string& outMessage, string& svOutToken, const NetGameServer_t& netGameServer) const; + bool CheckForBan(const string& ipAddress, const uint64_t nucleusId, string& outReason) const; + + bool QueryAPI(const string& apiName, const string& request, string& outResponse, string& outMessage, CURLINFO& outStatus) const; + bool QueryMasterServer(const string& hostName, const string& apiName, const string& request, string& outResponse, string& outMessage, CURLINFO& outStatus) const; void ExtractError(const nlohmann::json& resultBody, string& outMessage, CURLINFO status, const char* errorText = nullptr) const; - void ExtractError(const string& responseBuffer, string& outMessage, CURLINFO status, const char* messageText = nullptr) const; + void ExtractError(const string& response, string& outMessage, CURLINFO status, const char* messageText = nullptr) const; #ifdef DEDICATED bool KeepAlive(const NetGameServer_t& netGameServer);