From aff4b6125ed98e53bb7e47f991d02db614023f2e Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Mon, 24 Apr 2023 00:32:27 +0200 Subject: [PATCH 01/19] CPylon error handling code deduplication Deduplicate code. --- r5dev/networksystem/pylon.cpp | 167 +++++++++++++--------------------- r5dev/networksystem/pylon.h | 3 + 2 files changed, 68 insertions(+), 102 deletions(-) diff --git a/r5dev/networksystem/pylon.cpp b/r5dev/networksystem/pylon.cpp index fd9dfc67..367add68 100644 --- a/r5dev/networksystem/pylon.cpp +++ b/r5dev/networksystem/pylon.cpp @@ -25,15 +25,17 @@ vector CPylon::GetServerList(string& svOutMessage) const nlohmann::json jsRequestBody = nlohmann::json::object(); jsRequestBody["version"] = SDK_VERSION; - string svRequestBody = jsRequestBody.dump(4); - string svResponse; + const string svRequestBody = jsRequestBody.dump(4); + const bool bDebug = pylon_showdebuginfo->GetBool(); - if (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; CURLINFO status; + if (!QueryMasterServer(pylon_matchmaking_hostname->GetString(), "/servers", svRequestBody, svResponse, svOutMessage, status)) { return vslList; @@ -72,41 +74,12 @@ vector CPylon::GetServerList(string& svOutMessage) const } else { - if (jsResultBody["error"].is_string()) - { - svOutMessage = jsResultBody["error"].get(); - } - else - { - svOutMessage = Format("Unknown error with status: %d", static_cast(status)); - } + ExtractError(jsResultBody, svOutMessage, status); } } else { - if (status) - { - if (!svResponse.empty()) - { - nlohmann::json jsResultBody = nlohmann::json::parse(svResponse); - - if (jsResultBody["error"].is_string()) - { - svOutMessage = jsResultBody["error"].get(); - } - else - { - svOutMessage = Format("Server list error: %d", static_cast(status)); - } - - return vslList; - } - - svOutMessage = Format("Failed comp-server query: %d", static_cast(status)); - return vslList; - } - - svOutMessage = Format("Failed to reach comp-server: %s", "connection timed-out"); + ExtractError(svResponse, svOutMessage, status, "Server list error"); return vslList; } } @@ -130,9 +103,7 @@ bool CPylon::GetServerByToken(NetGameServer_t& slOutServer, string& svOutMessage nlohmann::json jsRequestBody = nlohmann::json::object(); jsRequestBody["token"] = svToken; - string svRequestBody = jsRequestBody.dump(4); - string svResponseBuf; - + const string svRequestBody = jsRequestBody.dump(4); const bool bDebugLog = pylon_showdebuginfo->GetBool(); if (bDebugLog) @@ -140,7 +111,9 @@ bool CPylon::GetServerByToken(NetGameServer_t& slOutServer, string& svOutMessage DevMsg(eDLL_T::ENGINE, "%s - Sending token connect request to comp-server:\n%s\n", __FUNCTION__, svRequestBody.c_str()); } + string svResponseBuf; CURLINFO status; + if (!QueryMasterServer(pylon_matchmaking_hostname->GetString(), "/server/byToken", svRequestBody, svResponseBuf, svOutMessage, status)) { return false; @@ -187,43 +160,13 @@ bool CPylon::GetServerByToken(NetGameServer_t& slOutServer, string& svOutMessage } else { - if (jsResultBody["error"].is_string()) - { - svOutMessage = jsResultBody["error"].get(); - } - else - { - svOutMessage = Format("Unknown error with status: %d", static_cast(status)); - } - + ExtractError(jsResultBody, svOutMessage, status); return false; } } else { - if (status) - { - if (!svResponseBuf.empty()) - { - nlohmann::json jsResultBody = nlohmann::json::parse(svResponseBuf); - - if (jsResultBody["error"].is_string()) - { - svOutMessage = jsResultBody["error"].get(); - } - else - { - svOutMessage = Format("Server not found: %d", static_cast(status)); - } - - return false; - } - - svOutMessage = Format("Failed comp-server query: %d", static_cast(status)); - return false; - } - - svOutMessage = Format("Failed to reach comp-server: %s", "connection timed-out"); + ExtractError(svResponseBuf, svOutMessage, status, "Server not found"); return false; } } @@ -309,45 +252,17 @@ bool CPylon::PostServerHost(string& svOutMessage, string& svOutToken, const NetG } else { - if (jsResultBody["error"].is_string()) - { - svOutMessage = jsResultBody["error"].get(); - } - else - { - svOutMessage = Format("Unknown error with status: %d", static_cast(status)); - } + ExtractError(jsResultBody, svOutMessage, status); + svOutToken.clear(); + return false; } } else { - if (status) - { - if (!svResponseBuf.empty()) - { - nlohmann::json jsResultBody = nlohmann::json::parse(svResponseBuf); - - if (jsResultBody["error"].is_string()) - { - svOutMessage = jsResultBody["error"].get(); - } - else - { - svOutMessage = Format("Server host error: %d", static_cast(status)); - } - - svOutToken.clear(); - return false; - } - - svOutMessage = Format("Failed comp-server query: %d", static_cast(status)); - svOutToken.clear(); - return false; - } - - svOutMessage = Format("Failed to reach comp-server: %s", "connection timed-out"); + ExtractError(svResponseBuf, svOutMessage, status, "Server host error"); svOutToken.clear(); + return false; } } @@ -500,5 +415,53 @@ bool CPylon::QueryMasterServer(const string& svHostName, const string& svApi, co return true; } +//----------------------------------------------------------------------------- +// Purpose: Extracts the error from the result body. +// Input : &resultBody - +// &outMessage - +// status - +// *errorText - +//----------------------------------------------------------------------------- +void CPylon::ExtractError(const nlohmann::json& resultBody, string& outMessage, CURLINFO status, const char* errorText) const +{ + if (resultBody["error"].is_string()) + { + outMessage = resultBody["error"].get(); + } + else + { + if (!errorText) + { + errorText = "Unknown error with status"; + } + + outMessage = Format("%s: %d", errorText, static_cast(status)); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Extracts the error from the response buffer. +// Input : &resultBody - +// &outMessage - +// status - +// *errorText - +//----------------------------------------------------------------------------- +void CPylon::ExtractError(const string& responseBuffer, string& outMessage, CURLINFO status, const char* errorText) const +{ + if (!responseBuffer.empty()) + { + nlohmann::json resultBody = nlohmann::json::parse(responseBuffer); + ExtractError(resultBody, outMessage, status, errorText); + } + else if (status) + { + outMessage = Format("Failed comp-server query: %d", static_cast(status)); + } + else + { + outMessage = Format("Failed to reach comp-server: %s", "connection timed-out"); + } +} + /////////////////////////////////////////////////////////////////////////////// CPylon* g_pMasterServer(new CPylon()); diff --git a/r5dev/networksystem/pylon.h b/r5dev/networksystem/pylon.h index e9d7c918..d6565198 100644 --- a/r5dev/networksystem/pylon.h +++ b/r5dev/networksystem/pylon.h @@ -11,6 +11,9 @@ public: 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; + 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; + #ifdef DEDICATED bool KeepAlive(const NetGameServer_t& netGameServer); 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 02/19] 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); From 000f7c7f8243e3909f694eb664bdbb29f55cfeea Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Tue, 25 Apr 2023 00:45:39 +0200 Subject: [PATCH 03/19] CPylon parsing/logging code deduplication Deduplicated parsing and logging code. Results are a lot more consistent and comprehensive. --- r5dev/networksystem/pylon.cpp | 357 ++++++++++++++-------------------- r5dev/networksystem/pylon.h | 7 +- 2 files changed, 152 insertions(+), 212 deletions(-) diff --git a/r5dev/networksystem/pylon.cpp b/r5dev/networksystem/pylon.cpp index 9fbb594b..840526d4 100644 --- a/r5dev/networksystem/pylon.cpp +++ b/r5dev/networksystem/pylon.cpp @@ -25,59 +25,42 @@ vector CPylon::GetServerList(string& outMessage) const nlohmann::json requestJson = nlohmann::json::object(); requestJson["version"] = SDK_VERSION; - string responseBody; + nlohmann::json responseJson; CURLINFO status; - if (!QueryAPI("/servers", requestJson.dump(4), responseBody, outMessage, status)) + if (!SendRequest("/servers", requestJson, responseJson, outMessage, status, "Server list error")) { return vecServers; } - try + if (!responseJson.contains("servers")) { - if (status == 200) // STATUS_OK - { - nlohmann::json resultJson = nlohmann::json::parse(responseBody); - if (resultJson["success"].is_boolean() && resultJson["success"].get()) - { - for (auto& obj : resultJson["servers"]) - { - vecServers.push_back( - NetGameServer_t - { - obj.value("name",""), - obj.value("description",""), - obj.value("hidden","false") == "true", - obj.value("map",""), - obj.value("playlist",""), - obj.value("ip",""), - obj.value("port", ""), - obj.value("key",""), - obj.value("checksum",""), - obj.value("version", SDK_VERSION), - obj.value("playerCount", ""), - obj.value("maxPlayers", ""), - obj.value("timeStamp", 0), - obj.value("publicRef", ""), - obj.value("cachedId", ""), - } - ); - } - } - else - { - ExtractError(resultJson, outMessage, status); - } - } - else - { - ExtractError(responseBody, outMessage, status, "Server list error"); - return vecServers; - } + outMessage = Format("Invalid response with status: %d", static_cast(status)); + return vecServers; } - catch (const std::exception& ex) + + for (auto& obj : responseJson["servers"]) { - Warning(eDLL_T::ENGINE, "%s - Exception while parsing comp-server response:\n%s\n", __FUNCTION__, ex.what()); + vecServers.push_back( + NetGameServer_t + { + obj.value("name",""), + obj.value("description",""), + obj.value("hidden","false") == "true", + obj.value("map",""), + obj.value("playlist",""), + obj.value("ip",""), + obj.value("port", ""), + obj.value("key",""), + obj.value("checksum",""), + obj.value("version", SDK_VERSION), + obj.value("playerCount", ""), + obj.value("maxPlayers", ""), + obj.value("timeStamp", 0), + obj.value("publicRef", ""), + obj.value("cachedId", ""), + } + ); } return vecServers; @@ -96,67 +79,42 @@ bool CPylon::GetServerByToken(NetGameServer_t& outGameServer, nlohmann::json requestJson = nlohmann::json::object(); requestJson["token"] = token; - string responseBody; + nlohmann::json responseJson; CURLINFO status; - if (!QueryAPI("/server/byToken", requestJson.dump(4), responseBody, outMessage, status)) + if (!SendRequest("/server/byToken", requestJson, responseJson, outMessage, status, "Server not found")) { return false; } - try + if (!responseJson.contains("server")) { - if (status == 200) // STATUS_OK - { - nlohmann::json responseJson = nlohmann::json::parse(responseBody); - - if (pylon_showdebuginfo->GetBool()) - { - responseBody = responseJson.dump(4); - DevMsg(eDLL_T::ENGINE, "%s: Response body:\n%s\n", - __FUNCTION__, responseBody.c_str()); - } - - if (responseJson["success"].is_boolean() && responseJson["success"]) - { - outGameServer = NetGameServer_t - { - 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(responseJson, outMessage, status); - return false; - } - } - else - { - ExtractError(responseBody, outMessage, status, "Server not found"); - return false; - } - } - catch (const std::exception& ex) - { - Warning(eDLL_T::ENGINE, "%s - Exception while parsing comp-server response:\n%s\n", __FUNCTION__, ex.what()); + outMessage = Format("Invalid response with status: %d", static_cast(status)); + return false; } - return false; + nlohmann::json& serverJson = responseJson["server"]; + + outGameServer = NetGameServer_t + { + serverJson.value("name",""), + serverJson.value("description",""), + serverJson.value("hidden","false") == "true", + serverJson.value("map",""), + serverJson.value("playlist",""), + serverJson.value("ip",""), + serverJson.value("port", ""), + serverJson.value("key",""), + serverJson.value("checksum",""), + serverJson.value("version", SDK_VERSION), + serverJson.value("playerCount", ""), + serverJson.value("maxPlayers", ""), + serverJson.value("timeStamp", 0), + serverJson.value("publicRef", ""), + serverJson.value("cachedId", ""), + }; + + return true; } //----------------------------------------------------------------------------- @@ -185,63 +143,26 @@ bool CPylon::PostServerHost(string& outMessage, string& outToken, const NetGameS requestJson["publicRef"] = netGameServer.m_svPublicRef; requestJson["cachedId"] = netGameServer.m_svCachedId; - string responseBody; + nlohmann::json responseJson; CURLINFO status; - if (!QueryAPI("/servers/add", requestJson.dump(4), responseBody, outMessage, status)) + if (!SendRequest("/servers/add", requestJson, responseJson, outMessage, status, "Server host error")) { return false; } - try + nlohmann::json& tokenJson = responseJson["token"]; + + if (!tokenJson.is_string()) { - if (status == 200) // STATUS_OK - { - nlohmann::json responseJson = nlohmann::json::parse(responseBody); + outMessage = Format("Invalid response with status: %d", static_cast(status)); + outToken.clear(); - if (pylon_showdebuginfo->GetBool()) - { - responseBody = responseJson.dump(4); - DevMsg(eDLL_T::ENGINE, "%s: Response body:\n%s\n", - __FUNCTION__, responseBody.c_str()); - } - - if (responseJson["success"].is_boolean() && responseJson["success"].get()) - { - if (responseJson["token"].is_string()) - { - outToken = responseJson["token"].get(); - } - else - { - outMessage = Format("Invalid response with status: %d", static_cast(status)); - outToken.clear(); - } - - return true; - } - else - { - ExtractError(responseJson, outMessage, status); - outToken.clear(); - - return false; - } - } - else - { - ExtractError(responseBody, outMessage, status, "Server host error"); - outToken.clear(); - - return false; - } - } - catch (const std::exception& ex) - { - Warning(eDLL_T::ENGINE, "%s - Exception while parsing comp-server response:\n%s\n", __FUNCTION__, ex.what()); + return false; } - return false; + outToken = tokenJson.get(); + return true; } #ifdef DEDICATED @@ -297,59 +218,86 @@ bool CPylon::CheckForBan(const string& ipAddress, const uint64_t nucleusId, stri requestJson["id"] = nucleusId; requestJson["ip"] = ipAddress; - string responseBody; + nlohmann::json responseJson; string outMessage; CURLINFO status; - if (!QueryAPI("/banlist/isBanned", requestJson.dump(4), responseBody, outMessage, status)) + if (!SendRequest("/banlist/isBanned", requestJson, + responseJson, outMessage, status, "Banned check error")) { return false; } - if (status != 200) + if (responseJson["banned"].is_boolean() && + responseJson["banned"].get()) { - Error(eDLL_T::ENGINE, NO_ERROR, "%s - Failed to query comp-server: status code = %d\n", __FUNCTION__, status); - return false; - } - - try - { - nlohmann::json responseJson = nlohmann::json::parse(responseBody); - - if (pylon_showdebuginfo->GetBool()) - { - responseBody = responseJson.dump(4); - DevMsg(eDLL_T::ENGINE, "%s: Response body:\n%s\n", - __FUNCTION__, responseBody.c_str()); - } - - if (responseJson["success"].is_boolean() && responseJson["success"].get()) - { - if (responseJson["banned"].is_boolean() && responseJson["banned"].get()) - { - outReason = responseJson.value("reason", "#DISCONNECT_BANNED"); - return true; - } - } - } - catch (const std::exception& ex) - { - Warning(eDLL_T::ENGINE, "%s - Exception while parsing comp-server response:\n%s\n", __FUNCTION__, ex.what()); + outReason = responseJson.value("reason", "#DISCONNECT_BANNED"); + return true; } return false; } +//----------------------------------------------------------------------------- +// Purpose: Sends request to Pylon Master Server. +// Input : *endpoint - +// &requestBody - +// &responseBody - +// &outMessage - +// &status - +// Output : True on success, false on failure. +//----------------------------------------------------------------------------- +bool CPylon::SendRequest(const char* endpoint, const nlohmann::json& requestJson, + nlohmann::json& responseJson, string& outMessage, CURLINFO& status, const char* errorText) const +{ + string responseBody; + + if (!QueryServer(endpoint, requestJson.dump(4).c_str(), responseBody, outMessage, status)) + { + return false; + } + + try + { + if (status == 200) // STATUS_OK + { + responseJson = nlohmann::json::parse(responseBody); + LogBody(responseJson); + + if (responseJson["success"].is_boolean() && + responseJson["success"].get()) + { + return true; + } + else + { + ExtractError(responseJson, outMessage, status); + return false; + } + } + else + { + ExtractError(responseBody, outMessage, status, errorText); + return false; + } + } + catch (const std::exception& ex) + { + Warning(eDLL_T::ENGINE, "%s - Exception while parsing response:\n%s\n", __FUNCTION__, ex.what()); + return false; + } +} + //----------------------------------------------------------------------------- // Purpose: Sends query to master server. -// Input : &apiName - -// &request - +// Input : *endpoint - +// *request - // &outResponse - // &outMessage - <- contains an error message if any. // &outStatus - -// Output : Returns true if successful, false otherwise. +// Output : True on success, false on failure. //----------------------------------------------------------------------------- -bool CPylon::QueryAPI(const string& apiName, const string& request, +bool CPylon::QueryServer(const char* endpoint, const char* request, string& outResponse, string& outMessage, CURLINFO& outStatus) const { const bool showDebug = pylon_showdebuginfo->GetBool(); @@ -357,41 +305,12 @@ bool CPylon::QueryAPI(const string& apiName, const string& request, if (showDebug) { - DevMsg(eDLL_T::ENGINE, "%s: Sending '%s' request to '%s':\n%s\n", - __FUNCTION__, apiName.c_str(), hostName, request.c_str()); + DevMsg(eDLL_T::ENGINE, "Sending request to '%s' with endpoint '%s':\n%s\n", + hostName, endpoint, request); } - 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); + CURLFormatUrl(finalUrl, hostName, endpoint); curl_slist* sList = nullptr; CURL* curl = CURLInitRequest(finalUrl, request, outResponse, sList); @@ -407,6 +326,13 @@ bool CPylon::QueryMasterServer(const string& hostName, const string& apiName, } outStatus = CURLRetrieveInfo(curl); + + if (showDebug) + { + DevMsg(eDLL_T::ENGINE, "Host '%s' replied with status: '%d'\n", + hostName, outStatus); + } + return true; } @@ -460,5 +386,18 @@ void CPylon::ExtractError(const string& response, string& outMessage, } } +//----------------------------------------------------------------------------- +// Purpose: Logs the response body if debug is enabled. +// Input : &responseJson - +//----------------------------------------------------------------------------- +void CPylon::LogBody(const nlohmann::json& responseJson) const +{ + if (pylon_showdebuginfo->GetBool()) + { + const string responseBody = responseJson.dump(4); + DevMsg(eDLL_T::ENGINE, "\n%s\n", responseBody.c_str()); + } +} + /////////////////////////////////////////////////////////////////////////////// CPylon* g_pMasterServer(new CPylon()); diff --git a/r5dev/networksystem/pylon.h b/r5dev/networksystem/pylon.h index a6ded6bb..39b3f279 100644 --- a/r5dev/networksystem/pylon.h +++ b/r5dev/networksystem/pylon.h @@ -10,12 +10,13 @@ public: 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& response, string& outMessage, CURLINFO status, const char* messageText = nullptr) const; + void LogBody(const nlohmann::json& responseJson) const; + bool SendRequest(const char* endpoint, const nlohmann::json& requestJson, nlohmann::json& responseJson, string& outMessage, CURLINFO& status, const char* errorText = nullptr) const; + bool QueryServer(const char* endpoint, const char* request, string& outResponse, string& outMessage, CURLINFO& outStatus) const; + #ifdef DEDICATED bool KeepAlive(const NetGameServer_t& netGameServer); From d9fa61141890bc8f2f86283ed7553bb76281a7a9 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Tue, 25 Apr 2023 00:46:45 +0200 Subject: [PATCH 04/19] Add new callbacks to server functions A few new callback hooks which are perhaps useful in the future. --- r5dev/engine/server/sv_main.cpp | 47 ++++++++++++++++++++++++++++++++- r5dev/engine/server/sv_main.h | 25 ++++++++++++------ 2 files changed, 63 insertions(+), 9 deletions(-) diff --git a/r5dev/engine/server/sv_main.cpp b/r5dev/engine/server/sv_main.cpp index f01d88b5..05913471 100644 --- a/r5dev/engine/server/sv_main.cpp +++ b/r5dev/engine/server/sv_main.cpp @@ -1,6 +1,12 @@ +//===========================================================================// +// +// Purpose: +// +//===========================================================================// #include "core/stdafx.h" #include "tier0/threadtools.h" #include "tier0/frametask.h" +#include "tier1/cvar.h" #include "engine/server/sv_main.h" #include "networksystem/pylon.h" #include "networksystem/bansystem.h" @@ -26,4 +32,43 @@ void SV_IsClientBanned(const string& svIPAddr, const uint64_t nNucleusID) } } -/////////////////////////////////////////////////////////////////////////////// \ No newline at end of file +//----------------------------------------------------------------------------- +// Purpose: loads the game .dll +//----------------------------------------------------------------------------- +void SV_InitGameDLL() +{ + v_SV_InitGameDLL(); +} + +//----------------------------------------------------------------------------- +// Purpose: release resources associated with extension DLLs. +//----------------------------------------------------------------------------- +void SV_ShutdownGameDLL() +{ + v_SV_ShutdownGameDLL(); +} + +//----------------------------------------------------------------------------- +// Purpose: activates the server +// Output : true on success, false on failure +//----------------------------------------------------------------------------- +bool SV_ActivateServer() +{ + return v_SV_ActivateServer(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void HSV_Main::Attach() const +{ + //DetourAttach(&v_SV_InitGameDLL, SV_InitGameDLL); + //DetourAttach(&v_SV_ShutdownGameDLL, SV_ShutdownGameDLL); + //DetourAttach(&v_SV_ActivateServer, SV_ActivateServer); +} + +void HSV_Main::Detach() const +{ + //DetourDetach(&v_SV_InitGameDLL, SV_InitGameDLL); + //DetourDetach(&v_SV_ShutdownGameDLL, SV_ShutdownGameDLL); + //DetourDetach(&v_SV_ActivateServer, SV_ActivateServer); +} \ No newline at end of file diff --git a/r5dev/engine/server/sv_main.h b/r5dev/engine/server/sv_main.h index 0beff5d5..7428c2c5 100644 --- a/r5dev/engine/server/sv_main.h +++ b/r5dev/engine/server/sv_main.h @@ -6,13 +6,16 @@ /* ==== SV_MAIN ======================================================================================================================================================= */ inline CMemory p_SV_InitGameDLL; -inline auto SV_InitGameDLL = p_SV_InitGameDLL.RCast(); +inline auto v_SV_InitGameDLL = p_SV_InitGameDLL.RCast(); inline CMemory p_SV_ShutdownGameDLL; -inline auto SV_ShutdownGameDLL = p_SV_ShutdownGameDLL.RCast(); +inline auto v_SV_ShutdownGameDLL = p_SV_ShutdownGameDLL.RCast(); + +inline CMemory p_SV_ActivateServer; +inline auto v_SV_ActivateServer = p_SV_ActivateServer.RCast(); inline CMemory p_SV_CreateBaseline; -inline auto SV_CreateBaseline = p_SV_CreateBaseline.RCast(); +inline auto v_SV_CreateBaseline = p_SV_CreateBaseline.RCast(); inline CMemory p_CGameServer__SpawnServer; inline auto CGameServer__SpawnServer = p_CGameServer__SpawnServer.RCast(); @@ -21,6 +24,9 @@ inline bool* s_bIsDedicated = nullptr; /////////////////////////////////////////////////////////////////////////////// +void SV_InitGameDLL(); +void SV_ShutdownGameDLL(); +bool SV_ActivateServer(); void SV_IsClientBanned(const string& svIPAddr, const uint64_t nNucleusID); /////////////////////////////////////////////////////////////////////////////// @@ -32,6 +38,7 @@ class HSV_Main : public IDetour LogFunAdr("CGameServer::SpawnServer", p_CGameServer__SpawnServer.GetPtr()); LogFunAdr("SV_InitGameDLL", p_SV_InitGameDLL.GetPtr()); LogFunAdr("SV_ShutdownGameDLL", p_SV_ShutdownGameDLL.GetPtr()); + LogFunAdr("SV_ActivateServer", p_SV_ActivateServer.GetPtr()); LogFunAdr("SV_CreateBaseline", p_SV_CreateBaseline.GetPtr()); LogVarAdr("s_bIsDedicated", reinterpret_cast(s_bIsDedicated)); } @@ -39,6 +46,7 @@ class HSV_Main : public IDetour { p_SV_InitGameDLL = g_GameDll.FindPatternSIMD("48 81 EC ?? ?? ?? ?? E8 ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 0F 85 ?? ?? ?? ??"); p_SV_ShutdownGameDLL = g_GameDll.FindPatternSIMD("48 83 EC 28 80 3D ?? ?? ?? ?? ?? 0F 84 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48"); + p_SV_CreateBaseline = g_GameDll.FindPatternSIMD("48 8B C4 56 48 81 EC ?? ?? ?? ?? 48 89 ?? ?? 48 8D"); p_SV_CreateBaseline = g_GameDll.FindPatternSIMD("48 83 EC 28 48 8B 0D ?? ?? ?? ?? 48 85 C9 75 07"); #if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) p_CGameServer__SpawnServer = g_GameDll.FindPatternSIMD("40 53 55 56 57 41 55 41 56 41 57 48 81 EC ?? ?? ?? ??"); @@ -46,9 +54,10 @@ class HSV_Main : public IDetour p_CGameServer__SpawnServer = g_GameDll.FindPatternSIMD("48 8B C4 53 55 56 57 41 54 41 55 41 57"); // 0x140312D80 // 48 8B C4 53 55 56 57 41 54 41 55 41 57 // #endif - SV_InitGameDLL = p_SV_InitGameDLL.RCast(); - SV_ShutdownGameDLL = p_SV_ShutdownGameDLL.RCast(); - SV_CreateBaseline = p_SV_CreateBaseline.RCast(); + v_SV_InitGameDLL = p_SV_InitGameDLL.RCast(); + v_SV_ShutdownGameDLL = p_SV_ShutdownGameDLL.RCast(); + v_SV_ActivateServer = p_SV_CreateBaseline.RCast(); + v_SV_CreateBaseline = p_SV_CreateBaseline.RCast(); CGameServer__SpawnServer = p_CGameServer__SpawnServer.RCast(); } virtual void GetVar(void) const @@ -57,7 +66,7 @@ class HSV_Main : public IDetour .FindPatternSelf("40 38 3D", CMemory::Direction::DOWN).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Attach(void) const; + virtual void Detach(void) const; }; /////////////////////////////////////////////////////////////////////////////// From 0399d753f4d0dceffbca43de9f5050b34fa47875 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Tue, 25 Apr 2023 00:47:44 +0200 Subject: [PATCH 05/19] Properly toggle cvars in sdk launcher Toggle them on or off instead of just on. Else disabling the toggle wouldn't disable then in the runtime as the default value of these cvars is '1'. --- r5dev/sdklauncher/basepanel.cpp | 29 ++++++++++++----------------- r5dev/sdklauncher/basepanel.h | 2 +- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/r5dev/sdklauncher/basepanel.cpp b/r5dev/sdklauncher/basepanel.cpp index 0334b48d..8708b628 100644 --- a/r5dev/sdklauncher/basepanel.cpp +++ b/r5dev/sdklauncher/basepanel.cpp @@ -347,13 +347,13 @@ void CSurface::Init() this->m_NetRandomKeyToggle->SetAnchor(Forms::AnchorStyles::Top | Forms::AnchorStyles::Left); this->m_EngineNetworkGroup->AddControl(this->m_NetRandomKeyToggle); - this->m_NoQueuedPacketThread = new UIX::UIXCheckBox(); - this->m_NoQueuedPacketThread->SetSize({ 125, 18 }); - this->m_NoQueuedPacketThread->SetLocation({ 15, 30 }); - this->m_NoQueuedPacketThread->SetTabIndex(2); - this->m_NoQueuedPacketThread->SetText("No queued packets"); - this->m_NoQueuedPacketThread->SetAnchor(Forms::AnchorStyles::Top | Forms::AnchorStyles::Left); - this->m_EngineNetworkGroup->AddControl(this->m_NoQueuedPacketThread); + this->m_QueuedPacketThread = new UIX::UIXCheckBox(); + this->m_QueuedPacketThread->SetSize({ 125, 18 }); + this->m_QueuedPacketThread->SetLocation({ 15, 30 }); + this->m_QueuedPacketThread->SetTabIndex(2); + this->m_QueuedPacketThread->SetText("Queued packets"); + this->m_QueuedPacketThread->SetAnchor(Forms::AnchorStyles::Top | Forms::AnchorStyles::Left); + this->m_EngineNetworkGroup->AddControl(this->m_QueuedPacketThread); this->m_NoTimeOutToggle = new UIX::UIXCheckBox(); this->m_NoTimeOutToggle->SetSize({ 125, 18 }); @@ -566,7 +566,7 @@ void CSurface::LoadSettings() this->m_NetRandomKeyToggle->SetChecked(attributeView != "0"); attributeView = vRoot.attribs["noQueuedPackets"]; - this->m_NoQueuedPacketThread->SetChecked(attributeView != "0"); + this->m_QueuedPacketThread->SetChecked(attributeView != "0"); attributeView = vRoot.attribs["noTimeOut"]; this->m_NoTimeOutToggle->SetChecked(attributeView != "0"); @@ -635,7 +635,7 @@ void CSurface::SaveSettings() // Network. vRoot.add_attribute("encryptionEnable", GetControlValue(this->m_NetEncryptionToggle)); vRoot.add_attribute("randomNetKey", GetControlValue(this->m_NetRandomKeyToggle)); - vRoot.add_attribute("noQueuedPackets", GetControlValue(this->m_NoQueuedPacketThread)); + vRoot.add_attribute("noQueuedPackets", GetControlValue(this->m_QueuedPacketThread)); vRoot.add_attribute("noTimeOut", GetControlValue(this->m_NoTimeOutToggle)); // Video. @@ -1031,14 +1031,9 @@ void CSurface::AppendHostParameters(string& svParameters) //----------------------------------------------------------------------------- void CSurface::AppendNetParameters(string& svParameters) { - if (this->m_NetEncryptionToggle->Checked()) - AppendParameterInternal(svParameters, "+net_encryptionEnable", "1"); - - if (this->m_NetRandomKeyToggle->Checked()) - AppendParameterInternal(svParameters, "+net_useRandomKey", "1"); - - if (this->m_NoQueuedPacketThread->Checked()) - AppendParameterInternal(svParameters, "+net_queued_packet_thread", "0"); + AppendParameterInternal(svParameters, "+net_encryptionEnable", this->m_NetEncryptionToggle->Checked() ? "1" : "0"); + AppendParameterInternal(svParameters, "+net_useRandomKey", this->m_NetRandomKeyToggle->Checked() ? "1" : "0"); + AppendParameterInternal(svParameters, "+net_queued_packet_thread", this->m_QueuedPacketThread->Checked() ? "1" : "0"); if (this->m_NoTimeOutToggle->Checked()) AppendParameterInternal(svParameters, "-notimeout"); diff --git a/r5dev/sdklauncher/basepanel.h b/r5dev/sdklauncher/basepanel.h index 3b26405b..1d6b7616 100644 --- a/r5dev/sdklauncher/basepanel.h +++ b/r5dev/sdklauncher/basepanel.h @@ -106,7 +106,7 @@ private: UIX::UIXCheckBox* m_NoAsyncJobsToggle; UIX::UIXCheckBox* m_NetEncryptionToggle; UIX::UIXCheckBox* m_NetRandomKeyToggle; - UIX::UIXCheckBox* m_NoQueuedPacketThread; + UIX::UIXCheckBox* m_QueuedPacketThread; UIX::UIXCheckBox* m_NoTimeOutToggle; UIX::UIXCheckBox* m_WindowedToggle; UIX::UIXCheckBox* m_NoBorderToggle; From 73fe590f99ad183c3e3a80fa3147fe111703c8e5 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Tue, 25 Apr 2023 22:51:06 +0200 Subject: [PATCH 06/19] Fix small bug Fixed bug caused by const char* to const string& conversion, resulting in the request message not always getting sent properly. --- r5dev/networksystem/pylon.cpp | 14 ++++++++------ r5dev/public/tier2/curlutils.h | 6 +++--- r5dev/tier2/curlutils.cpp | 14 +++++++------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/r5dev/networksystem/pylon.cpp b/r5dev/networksystem/pylon.cpp index 840526d4..9f913473 100644 --- a/r5dev/networksystem/pylon.cpp +++ b/r5dev/networksystem/pylon.cpp @@ -28,14 +28,15 @@ vector CPylon::GetServerList(string& outMessage) const nlohmann::json responseJson; CURLINFO status; - if (!SendRequest("/servers", requestJson, responseJson, outMessage, status, "Server list error")) + if (!SendRequest("/servers", requestJson, responseJson, + outMessage, status, "Server list error")) { return vecServers; } if (!responseJson.contains("servers")) { - outMessage = Format("Invalid response with status: %d", static_cast(status)); + outMessage = Format("Invalid response with status: %d", int(status)); return vecServers; } @@ -313,7 +314,7 @@ bool CPylon::QueryServer(const char* endpoint, const char* request, CURLFormatUrl(finalUrl, hostName, endpoint); curl_slist* sList = nullptr; - CURL* curl = CURLInitRequest(finalUrl, request, outResponse, sList); + CURL* curl = CURLInitRequest(finalUrl.c_str(), request, outResponse, sList); if (!curl) { return false; @@ -357,7 +358,7 @@ void CPylon::ExtractError(const nlohmann::json& resultJson, string& outMessage, errorText = "Unknown error with status"; } - outMessage = Format("%s: %d", errorText, static_cast(status)); + outMessage = Format("%s: %d", errorText, int(status)); } } @@ -378,11 +379,12 @@ void CPylon::ExtractError(const string& response, string& outMessage, } else if (status) { - outMessage = Format("Failed comp-server query: %d", static_cast(status)); + outMessage = Format("Failed comp-server query: %d", int(status)); } else { - outMessage = Format("Failed to reach comp-server: %s", "connection timed-out"); + outMessage = Format("Failed to reach comp-server: %s", + "connection timed out"); } } diff --git a/r5dev/public/tier2/curlutils.h b/r5dev/public/tier2/curlutils.h index a53aab83..8980c9ed 100644 --- a/r5dev/public/tier2/curlutils.h +++ b/r5dev/public/tier2/curlutils.h @@ -1,13 +1,13 @@ #ifndef TIER2_CURLUTILS_H #define TIER2_CURLUTILS_H -size_t CURLWriteStringCallback(char* contents, size_t size, size_t nmemb, void* userp); +size_t CURLWriteStringCallback(char* contents, const size_t size, const size_t nmemb, void* userp); -CURL* CURLInitRequest(const string& remote, const string& request, string& response, curl_slist*& slist); +CURL* CURLInitRequest(const char* remote, const char* request, string& outResponse, curl_slist*& slist); CURLcode CURLSubmitRequest(CURL* curl, curl_slist*& slist); CURLINFO CURLRetrieveInfo(CURL* curl); bool CURLHandleError(CURL* curl, CURLcode res, string& outMessage); -void CURLFormatUrl(string& url, const string& host, const string& api); +void CURLFormatUrl(string& outUrl, const char* host, const char* api); #endif // !TIER2_CURLUTILS_H \ No newline at end of file diff --git a/r5dev/tier2/curlutils.cpp b/r5dev/tier2/curlutils.cpp index 8139e4c2..d606e6c4 100644 --- a/r5dev/tier2/curlutils.cpp +++ b/r5dev/tier2/curlutils.cpp @@ -7,13 +7,13 @@ #include "tier1/cvar.h" #include "tier2/curlutils.h" -size_t CURLWriteStringCallback(char* contents, size_t size, size_t nmemb, void* userp) +size_t CURLWriteStringCallback(char* contents, const size_t size, const size_t nmemb, void* userp) { reinterpret_cast(userp)->append(contents, size * nmemb); return size * nmemb; } -CURL* CURLInitRequest(const string& remote, const string& request, string& response, curl_slist*& slist) +CURL* CURLInitRequest(const char* remote, const char* request, string& outResponse, curl_slist*& slist) { std::function fnError = [&](const char* errorMsg) { @@ -36,14 +36,14 @@ CURL* CURLInitRequest(const string& remote, const string& request, string& respo } curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist); - curl_easy_setopt(curl, CURLOPT_URL, remote.c_str()); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request.c_str()); + curl_easy_setopt(curl, CURLOPT_URL, remote); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request); curl_easy_setopt(curl, CURLOPT_POST, 1L); curl_easy_setopt(curl, CURLOPT_TIMEOUT, curl_timeout->GetInt()); curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CURLWriteStringCallback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &outResponse); curl_easy_setopt(curl, CURLOPT_VERBOSE, curl_debug->GetBool()); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); @@ -89,7 +89,7 @@ bool CURLHandleError(CURL* curl, CURLcode res, string& outMessage) return false; } -void CURLFormatUrl(string& url, const string& host, const string& api) +void CURLFormatUrl(string& outUrl, const char* host, const char* api) { - url = Format("%s%s%s", "https://", host.c_str(), api.c_str()); + outUrl = Format("%s%s%s", "https://", host, api); } From 9872b4042a7a752711edf58c6c40c83cb3aa534e Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Tue, 25 Apr 2023 22:57:06 +0200 Subject: [PATCH 07/19] Improve pylon error logging Improve error formatting. --- r5dev/networksystem/pylon.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/r5dev/networksystem/pylon.cpp b/r5dev/networksystem/pylon.cpp index 9f913473..24d86607 100644 --- a/r5dev/networksystem/pylon.cpp +++ b/r5dev/networksystem/pylon.cpp @@ -29,7 +29,7 @@ vector CPylon::GetServerList(string& outMessage) const CURLINFO status; if (!SendRequest("/servers", requestJson, responseJson, - outMessage, status, "Server list error")) + outMessage, status, "server list error")) { return vecServers; } @@ -83,7 +83,7 @@ bool CPylon::GetServerByToken(NetGameServer_t& outGameServer, nlohmann::json responseJson; CURLINFO status; - if (!SendRequest("/server/byToken", requestJson, responseJson, outMessage, status, "Server not found")) + if (!SendRequest("/server/byToken", requestJson, responseJson, outMessage, status, "server not found")) { return false; } @@ -147,7 +147,7 @@ bool CPylon::PostServerHost(string& outMessage, string& outToken, const NetGameS nlohmann::json responseJson; CURLINFO status; - if (!SendRequest("/servers/add", requestJson, responseJson, outMessage, status, "Server host error")) + if (!SendRequest("/servers/add", requestJson, responseJson, outMessage, status, "server host error")) { return false; } @@ -224,7 +224,7 @@ bool CPylon::CheckForBan(const string& ipAddress, const uint64_t nucleusId, stri CURLINFO status; if (!SendRequest("/banlist/isBanned", requestJson, - responseJson, outMessage, status, "Banned check error")) + responseJson, outMessage, status, "banned check error")) { return false; } @@ -355,10 +355,10 @@ void CPylon::ExtractError(const nlohmann::json& resultJson, string& outMessage, { if (!errorText) { - errorText = "Unknown error with status"; + errorText = "unknown error"; } - outMessage = Format("%s: %d", errorText, int(status)); + outMessage = Format("Failed with status: %d (%s)", int(status), errorText); } } @@ -379,11 +379,11 @@ void CPylon::ExtractError(const string& response, string& outMessage, } else if (status) { - outMessage = Format("Failed comp-server query: %d", int(status)); + outMessage = Format("Failed server query: %d", int(status)); } else { - outMessage = Format("Failed to reach comp-server: %s", + outMessage = Format("Failed to reach server: %s", "connection timed out"); } } From a1c97e1b393b454ef73c7249ef82ff8994f21ae9 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Wed, 26 Apr 2023 23:31:03 +0200 Subject: [PATCH 08/19] Fix assignment bug Sigscan results returned to the wrong var. --- r5dev/engine/server/sv_main.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/r5dev/engine/server/sv_main.h b/r5dev/engine/server/sv_main.h index 7428c2c5..6f15d7fa 100644 --- a/r5dev/engine/server/sv_main.h +++ b/r5dev/engine/server/sv_main.h @@ -46,7 +46,7 @@ class HSV_Main : public IDetour { p_SV_InitGameDLL = g_GameDll.FindPatternSIMD("48 81 EC ?? ?? ?? ?? E8 ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 0F 85 ?? ?? ?? ??"); p_SV_ShutdownGameDLL = g_GameDll.FindPatternSIMD("48 83 EC 28 80 3D ?? ?? ?? ?? ?? 0F 84 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48"); - p_SV_CreateBaseline = g_GameDll.FindPatternSIMD("48 8B C4 56 48 81 EC ?? ?? ?? ?? 48 89 ?? ?? 48 8D"); + p_SV_ActivateServer = g_GameDll.FindPatternSIMD("48 8B C4 56 48 81 EC ?? ?? ?? ?? 48 89 ?? ?? 48 8D"); p_SV_CreateBaseline = g_GameDll.FindPatternSIMD("48 83 EC 28 48 8B 0D ?? ?? ?? ?? 48 85 C9 75 07"); #if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) p_CGameServer__SpawnServer = g_GameDll.FindPatternSIMD("40 53 55 56 57 41 55 41 56 41 57 48 81 EC ?? ?? ?? ??"); @@ -56,7 +56,7 @@ class HSV_Main : public IDetour #endif v_SV_InitGameDLL = p_SV_InitGameDLL.RCast(); v_SV_ShutdownGameDLL = p_SV_ShutdownGameDLL.RCast(); - v_SV_ActivateServer = p_SV_CreateBaseline.RCast(); + v_SV_ActivateServer = p_SV_ActivateServer.RCast(); v_SV_CreateBaseline = p_SV_CreateBaseline.RCast(); CGameServer__SpawnServer = p_CGameServer__SpawnServer.RCast(); } From fcc4b410f3c2f720700d1a4d288dd2e422fcbdb8 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Wed, 26 Apr 2023 23:31:16 +0200 Subject: [PATCH 09/19] Improve formatting in pylon code --- r5dev/networksystem/pylon.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/r5dev/networksystem/pylon.cpp b/r5dev/networksystem/pylon.cpp index 24d86607..85a0000c 100644 --- a/r5dev/networksystem/pylon.cpp +++ b/r5dev/networksystem/pylon.cpp @@ -83,14 +83,15 @@ bool CPylon::GetServerByToken(NetGameServer_t& outGameServer, nlohmann::json responseJson; CURLINFO status; - if (!SendRequest("/server/byToken", requestJson, responseJson, outMessage, status, "server not found")) + if (!SendRequest("/server/byToken", requestJson, responseJson, + outMessage, status, "server not found")) { return false; } if (!responseJson.contains("server")) { - outMessage = Format("Invalid response with status: %d", static_cast(status)); + outMessage = Format("Invalid response with status: %d", int(status)); return false; } @@ -125,7 +126,8 @@ bool CPylon::GetServerByToken(NetGameServer_t& outGameServer, // &netGameServer - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- -bool CPylon::PostServerHost(string& outMessage, string& outToken, const NetGameServer_t& netGameServer) const +bool CPylon::PostServerHost(string& outMessage, string& outToken, + const NetGameServer_t& netGameServer) const { nlohmann::json requestJson = nlohmann::json::object(); requestJson["name"] = netGameServer.m_svHostName; @@ -147,7 +149,8 @@ bool CPylon::PostServerHost(string& outMessage, string& outToken, const NetGameS nlohmann::json responseJson; CURLINFO status; - if (!SendRequest("/servers/add", requestJson, responseJson, outMessage, status, "server host error")) + if (!SendRequest("/servers/add", requestJson, responseJson, + outMessage, status, "server host error")) { return false; } @@ -156,7 +159,7 @@ bool CPylon::PostServerHost(string& outMessage, string& outToken, const NetGameS if (!tokenJson.is_string()) { - outMessage = Format("Invalid response with status: %d", static_cast(status)); + outMessage = Format("Invalid response with status: %d", int(status)); outToken.clear(); return false; @@ -358,7 +361,8 @@ void CPylon::ExtractError(const nlohmann::json& resultJson, string& outMessage, errorText = "unknown error"; } - outMessage = Format("Failed with status: %d (%s)", int(status), errorText); + outMessage = Format("Failed with status: %d (%s)", + int(status), errorText); } } From 48bc69b028d997f31b678ddc2f485c66778b58ff Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Fri, 28 Apr 2023 23:47:58 +0200 Subject: [PATCH 10/19] Initial implementation of bulk ban checking Initial implementation of the bulk ban check system. This implementation takes a snapshot of the currently connected clients, sends it up to the master server, and the master server returns anything within this list that is marked 'banned'. The server would then kick the player from the server. This commit also removes the global banned list cache, as the bulk checking system offers a lot more freedom regarding banning specific players and have it sync across all available servers. --- r5dev/engine/host_state.cpp | 5 +- r5dev/engine/server/sv_main.cpp | 63 ++++++++++- r5dev/engine/server/sv_main.h | 1 + r5dev/networksystem/bansystem.cpp | 182 +++++++++--------------------- r5dev/networksystem/bansystem.h | 23 ++-- r5dev/networksystem/pylon.cpp | 51 ++++++++- r5dev/networksystem/pylon.h | 3 + 7 files changed, 178 insertions(+), 150 deletions(-) diff --git a/r5dev/engine/host_state.cpp b/r5dev/engine/host_state.cpp index 629bc452..e5969603 100644 --- a/r5dev/engine/host_state.cpp +++ b/r5dev/engine/host_state.cpp @@ -268,9 +268,10 @@ void CHostState::Think(void) const statsTimer.Start(); } - if (banListTimer.GetDurationInProgress().GetSeconds() > sv_banlistRefreshRate->GetDouble()) + if (sv_globalBanlist->GetBool() && + banListTimer.GetDurationInProgress().GetSeconds() > sv_banlistRefreshRate->GetDouble()) { - g_pBanSystem->BanListCheck(); + SV_CheckForBan(); banListTimer.Start(); } #ifdef DEDICATED diff --git a/r5dev/engine/server/sv_main.cpp b/r5dev/engine/server/sv_main.cpp index 05913471..ea7b72d7 100644 --- a/r5dev/engine/server/sv_main.cpp +++ b/r5dev/engine/server/sv_main.cpp @@ -8,6 +8,7 @@ #include "tier0/frametask.h" #include "tier1/cvar.h" #include "engine/server/sv_main.h" +#include "engine/client/client.h" #include "networksystem/pylon.h" #include "networksystem/bansystem.h" @@ -23,12 +24,68 @@ void SV_IsClientBanned(const string& svIPAddr, const uint64_t nNucleusID) { if (!ThreadInMainThread()) { - g_TaskScheduler->Dispatch([svError, nNucleusID] + g_TaskScheduler->Dispatch([svError, svIPAddr] { - g_pBanSystem->AddConnectionRefuse(svError, nNucleusID); // Add to the vector. + g_pBanSystem->KickPlayerById(svIPAddr.c_str(), svError.c_str()); }, 0); } - Warning(eDLL_T::SERVER, "Added '%s' to refused list ('%llu' is banned from the master server!)\n", svIPAddr.c_str(), nNucleusID); + + //Warning(eDLL_T::SERVER, "Added '%s' to refused list ('%llu' is banned from the master server!)\n", svIPAddr.c_str(), nNucleusID); + } +} + +void SV_ProcessBulkCheck(const BannedVec_t& bannedVec) +{ + BannedVec_t outBannedVec; + g_pMasterServer->GetBannedList(bannedVec, outBannedVec); + + if (!ThreadInMainThread()) + { + g_TaskScheduler->Dispatch([outBannedVec] + { + SV_CheckForBan(&outBannedVec); + }, 0); + } +} + +void SV_CheckForBan(const BannedVec_t* pBannedVec /*= nullptr*/) +{ + BannedVec_t bannedVec; + + for (int c = 0; c < MAX_PLAYERS; c++) // Loop through all possible client instances. + { + CClient* pClient = g_pClient->GetClient(c); + if (!pClient) + continue; + + CNetChan* pNetChan = pClient->GetNetChan(); + if (!pNetChan) + continue; + + if (!pClient->IsConnected()) + continue; + + const char* szIPAddr = pNetChan->GetAddress(); + const uint64_t nNucleusID = pClient->GetNucleusID(); + + if (!pBannedVec) + bannedVec.push_back(std::make_pair(szIPAddr, nNucleusID)); + else + { + for (auto& it : *pBannedVec) + { + if (it.second == pClient->GetNucleusID()) + { + Warning(eDLL_T::SERVER, "Removing client '%s' from slot '%i' ('%llu' is banned from this server!)\n", szIPAddr, c, nNucleusID); + pClient->Disconnect(Reputation_t::REP_MARK_BAD, "%s", it.first.c_str()); + } + } + } + } + + if (!pBannedVec && !bannedVec.empty()) + { + std::thread(&SV_ProcessBulkCheck, bannedVec).detach(); } } diff --git a/r5dev/engine/server/sv_main.h b/r5dev/engine/server/sv_main.h index 6f15d7fa..3d7bf8f3 100644 --- a/r5dev/engine/server/sv_main.h +++ b/r5dev/engine/server/sv_main.h @@ -28,6 +28,7 @@ void SV_InitGameDLL(); void SV_ShutdownGameDLL(); bool SV_ActivateServer(); void SV_IsClientBanned(const string& svIPAddr, const uint64_t nNucleusID); +void SV_CheckForBan(const BannedVec_t* pBannedVec = nullptr); /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/networksystem/bansystem.cpp b/r5dev/networksystem/bansystem.cpp index 4bc7ee8f..40d4c640 100644 --- a/r5dev/networksystem/bansystem.cpp +++ b/r5dev/networksystem/bansystem.cpp @@ -140,9 +140,7 @@ bool CBanSystem::DeleteEntry(const string& svIpAddress, const uint64_t nNucleusI if (it != m_vBanList.end()) { - DeleteConnectionRefuse(it->second); m_vBanList.erase(it); - return true; } } @@ -150,88 +148,6 @@ bool CBanSystem::DeleteEntry(const string& svIpAddress, const uint64_t nNucleusI return false; } -//----------------------------------------------------------------------------- -// Purpose: adds a connect refuse entry to the refused list -// Input : &svError - -// nNucleusID - -//----------------------------------------------------------------------------- -bool CBanSystem::AddConnectionRefuse(const string& svError, const uint64_t nNucleusID) -{ - if (IsRefuseListValid()) - { - auto it = std::find_if(m_vRefuseList.begin(), m_vRefuseList.end(), - [&](const pair& element) { return element.second == nNucleusID; }); - - if (it == m_vRefuseList.end()) - { - m_vRefuseList.push_back(std::make_pair(svError, nNucleusID)); - return true; - } - } - else - { - m_vRefuseList.push_back(std::make_pair(svError, nNucleusID)); - return true; - } - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: deletes an entry in the refused list -// Input : nNucleusID - -//----------------------------------------------------------------------------- -bool CBanSystem::DeleteConnectionRefuse(const uint64_t nNucleusID) -{ - if (IsRefuseListValid()) - { - auto it = std::find_if(m_vRefuseList.begin(), m_vRefuseList.end(), - [&](const pair& element) { return element.second == nNucleusID; }); - - if (it != m_vRefuseList.end()) - { - m_vRefuseList.erase(it); - return true; - } - } - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Check refuse list and kill netchan connection. -//----------------------------------------------------------------------------- -void CBanSystem::BanListCheck(void) -{ - if (!IsRefuseListValid()) - return; - - for (size_t i = 0; i < m_vRefuseList.size(); i++) - { - for (int c = 0; c < MAX_PLAYERS; c++) // Loop through all possible client instances. - { - CClient* pClient = g_pClient->GetClient(c); - if (!pClient) - continue; - - CNetChan* pNetChan = pClient->GetNetChan(); - if (!pNetChan) - continue; - - if (!pClient->IsConnected()) - continue; - - if (pClient->GetNucleusID() != m_vRefuseList[i].second) - continue; - - string svIpAddress = pNetChan->GetAddress(); - - Warning(eDLL_T::SERVER, "Removing client '%s' from slot '%i' ('%llu' is banned from this server!)\n", svIpAddress.c_str(), c, pClient->GetNucleusID()); - pClient->Disconnect(Reputation_t::REP_MARK_BAD, "%s", m_vRefuseList[i].first.c_str()); - } - } -} - //----------------------------------------------------------------------------- // Purpose: checks if specified ip address or nucleus id is banned // Input : &svIpAddress - @@ -261,14 +177,6 @@ bool CBanSystem::IsBanned(const string& svIpAddress, const uint64_t nNucleusID) return false; } -//----------------------------------------------------------------------------- -// Purpose: checks if refused list is valid -//----------------------------------------------------------------------------- -bool CBanSystem::IsRefuseListValid(void) const -{ - return !m_vRefuseList.empty(); -} - //----------------------------------------------------------------------------- // Purpose: checks if banned list is valid //----------------------------------------------------------------------------- @@ -279,50 +187,54 @@ bool CBanSystem::IsBanListValid(void) const //----------------------------------------------------------------------------- // Purpose: kicks a player by given name -// Input : &svPlayerName - +// Input : *playerName - +// *reason - //----------------------------------------------------------------------------- -void CBanSystem::KickPlayerByName(const string& svPlayerName) +void CBanSystem::KickPlayerByName(const char* playerName, const char* reason) { - if (svPlayerName.empty()) + if (!VALID_CHARSTAR(playerName)) return; - AuthorPlayerByName(svPlayerName, false); + AuthorPlayerByName(playerName, false); } //----------------------------------------------------------------------------- // Purpose: kicks a player by given handle or id -// Input : &svHandle - +// Input : *playerHandle - +// *reason - //----------------------------------------------------------------------------- -void CBanSystem::KickPlayerById(const string& svHandle) +void CBanSystem::KickPlayerById(const char* playerHandle, const char* reason) { - if (svHandle.empty()) + if (!VALID_CHARSTAR(playerHandle)) return; - AuthorPlayerById(svHandle, false); + AuthorPlayerById(playerHandle, false); } //----------------------------------------------------------------------------- // Purpose: bans a player by given name -// Input : &svPlayerName - +// Input : *playerName - +// *reason - //----------------------------------------------------------------------------- -void CBanSystem::BanPlayerByName(const string& svPlayerName) +void CBanSystem::BanPlayerByName(const char* playerName, const char* reason) { - if (svPlayerName.empty()) + if (!VALID_CHARSTAR(playerName)) return; - AuthorPlayerByName(svPlayerName, true); + AuthorPlayerByName(playerName, true); } //----------------------------------------------------------------------------- // Purpose: bans a player by given handle or id -// Input : &svHandle - +// Input : *playerHandle - +// *reason - //----------------------------------------------------------------------------- -void CBanSystem::BanPlayerById(const string& svHandle) +void CBanSystem::BanPlayerById(const char* playerHandle, const char* reason) { - if (svHandle.empty()) + if (!VALID_CHARSTAR(playerHandle)) return; - AuthorPlayerById(svHandle, true); + AuthorPlayerById(playerHandle, true); } //----------------------------------------------------------------------------- @@ -364,15 +276,19 @@ void CBanSystem::UnbanPlayer(const string& svCriteria) //----------------------------------------------------------------------------- // Purpose: authors player by given name -// Input : &svPlayerName - -// bBan - (only kicks if false) +// Input : *playerName - +// shouldBan - (only kicks if false) +// *reason - //----------------------------------------------------------------------------- -void CBanSystem::AuthorPlayerByName(const string& svPlayerName, const bool bBan) +void CBanSystem::AuthorPlayerByName(const char* playerName, const bool shouldBan, const char* reason) { - Assert(!svPlayerName.empty()); + Assert(VALID_CHARSTAR(playerName)); bool bDisconnect = false; bool bSave = false; + if (!reason) + reason = shouldBan ? "Banned from server" : "Kicked from server"; + for (int i = 0; i < MAX_PLAYERS; i++) { CClient* pClient = g_pClient->GetClient(i); @@ -385,12 +301,12 @@ void CBanSystem::AuthorPlayerByName(const string& svPlayerName, const bool bBan) if (strlen(pNetChan->GetName()) > 0) { - if (svPlayerName.compare(pNetChan->GetName()) == NULL) // Our wanted name? + if (strcmp(playerName, pNetChan->GetName()) == NULL) // Our wanted name? { - if (bBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave) + if (shouldBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave) bSave = true; - pClient->Disconnect(REP_MARK_BAD, bBan ? "Banned from server" : "Kicked from server"); + pClient->Disconnect(REP_MARK_BAD, reason); bDisconnect = true; } } @@ -399,29 +315,33 @@ void CBanSystem::AuthorPlayerByName(const string& svPlayerName, const bool bBan) if (bSave) { Save(); - DevMsg(eDLL_T::SERVER, "Added '%s' to banned list\n", svPlayerName.c_str()); + DevMsg(eDLL_T::SERVER, "Added '%s' to banned list\n", playerName); } else if (bDisconnect) { - DevMsg(eDLL_T::SERVER, "Kicked '%s' from server\n", svPlayerName.c_str()); + DevMsg(eDLL_T::SERVER, "Kicked '%s' from server\n", playerName); } } //----------------------------------------------------------------------------- // Purpose: authors player by given nucleus id or ip address -// Input : &svHandle - -// bBan - (only kicks if false) +// Input : *playerHandle - +// shouldBan - (only kicks if false) +// *reason - //----------------------------------------------------------------------------- -void CBanSystem::AuthorPlayerById(const string& svHandle, const bool bBan) +void CBanSystem::AuthorPlayerById(const char* playerHandle, const bool shouldBan, const char* reason) { - Assert(!svHandle.empty()); + Assert(VALID_CHARSTAR(playerHandle)); try { - bool bOnlyDigits = StringIsDigit(svHandle); + bool bOnlyDigits = StringIsDigit(playerHandle); bool bDisconnect = false; bool bSave = false; + if (!reason) + reason = shouldBan ? "Banned from server" : "Kicked from server"; + for (int i = 0; i < MAX_PLAYERS; i++) { CClient* pClient = g_pClient->GetClient(i); @@ -434,7 +354,7 @@ void CBanSystem::AuthorPlayerById(const string& svHandle, const bool bBan) if (bOnlyDigits) { - uint64_t nTargetID = static_cast(std::stoll(svHandle)); + uint64_t nTargetID = static_cast(std::stoll(playerHandle)); if (nTargetID > static_cast(MAX_PLAYERS)) // Is it a possible nucleusID? { uint64_t nNucleusID = pClient->GetNucleusID(); @@ -448,21 +368,23 @@ void CBanSystem::AuthorPlayerById(const string& svHandle, const bool bBan) continue; } - if (bBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave) + if (shouldBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave) bSave = true; - pClient->Disconnect(REP_MARK_BAD, bBan ? "Banned from server" : "Kicked from server"); + + + pClient->Disconnect(REP_MARK_BAD, reason); bDisconnect = true; } else { - if (svHandle.compare(pNetChan->GetAddress()) != NULL) + if (strcmp(playerHandle, pNetChan->GetAddress()) != NULL) continue; - if (bBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave) + if (shouldBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave) bSave = true; - pClient->Disconnect(REP_MARK_BAD, bBan ? "Banned from server" : "Kicked from server"); + pClient->Disconnect(REP_MARK_BAD, reason); bDisconnect = true; } } @@ -470,11 +392,11 @@ void CBanSystem::AuthorPlayerById(const string& svHandle, const bool bBan) if (bSave) { Save(); - DevMsg(eDLL_T::SERVER, "Added '%s' to banned list\n", svHandle.c_str()); + DevMsg(eDLL_T::SERVER, "Added '%s' to banned list\n", playerHandle); } else if (bDisconnect) { - DevMsg(eDLL_T::SERVER, "Kicked '%s' from server\n", svHandle.c_str()); + DevMsg(eDLL_T::SERVER, "Kicked '%s' from server\n", playerHandle); } } catch (const std::exception& e) diff --git a/r5dev/networksystem/bansystem.h b/r5dev/networksystem/bansystem.h index 3874ac5b..c266bb9c 100644 --- a/r5dev/networksystem/bansystem.h +++ b/r5dev/networksystem/bansystem.h @@ -1,5 +1,7 @@ #pragma once +typedef vector> BannedVec_t; + class CBanSystem { public: @@ -9,29 +11,22 @@ public: bool AddEntry(const string& svIpAddress, const uint64_t nNucleusID); bool DeleteEntry(const string& svIpAddress, const uint64_t nNucleusID); - bool AddConnectionRefuse(const string& svError, const uint64_t nNucleusID); - bool DeleteConnectionRefuse(const uint64_t nNucleusID); - - void BanListCheck(void); - bool IsBanned(const string& svIpAddress, const uint64_t nNucleusID) const; - bool IsRefuseListValid(void) const; bool IsBanListValid(void) const; - void KickPlayerByName(const string& svPlayerName); - void KickPlayerById(const string& svHandle); + void KickPlayerByName(const char* playerName, const char* reason = nullptr); + void KickPlayerById(const char* playerHandle, const char* reason = nullptr); - void BanPlayerByName(const string& svPlayerName); - void BanPlayerById(const string& svHandle); + void BanPlayerByName(const char* playerName, const char* reason = nullptr); + void BanPlayerById(const char* playerHandle, const char* reason = nullptr); void UnbanPlayer(const string& svCriteria); private: - void AuthorPlayerByName(const string& svPlayerName, const bool bBan); - void AuthorPlayerById(const string& svHandle, const bool bBan); + void AuthorPlayerByName(const char* playerName, const bool bBan, const char* reason = nullptr); + void AuthorPlayerById(const char* playerHandle, const bool bBan, const char* reason = nullptr); - vector> m_vRefuseList; - vector> m_vBanList; + BannedVec_t m_vBanList; }; extern CBanSystem* g_pBanSystem; diff --git a/r5dev/networksystem/pylon.cpp b/r5dev/networksystem/pylon.cpp index 85a0000c..842e1643 100644 --- a/r5dev/networksystem/pylon.cpp +++ b/r5dev/networksystem/pylon.cpp @@ -209,12 +209,61 @@ bool CPylon::KeepAlive(const NetGameServer_t& netGameServer) } #endif // DEDICATED +//----------------------------------------------------------------------------- +// Purpose: Checks a list of clients for their banned status. +// Input : &inBannedVec - +// &outBannedVec - +// Output : True on success, false otherwise. +//----------------------------------------------------------------------------- +bool CPylon::GetBannedList(const BannedVec_t& inBannedVec, BannedVec_t& outBannedVec) const +{ + nlohmann::json arrayJson = nlohmann::json::array(); + + for (const auto& bannedPair : inBannedVec) + { + nlohmann::json player; + player["id"] = bannedPair.second; + player["ip"] = bannedPair.first; + arrayJson.push_back(player); + } + + nlohmann::json playerArray; + playerArray["players"] = arrayJson; + + string outMessage; + CURLINFO status; + + if (!SendRequest("/banlist/bulkCheck", playerArray, + arrayJson, outMessage, status, "banned bulk check error")) + { + return false; + } + + if (!arrayJson.contains("bannedPlayers")) + { + outMessage = Format("Invalid response with status: %d", int(status)); + return false; + } + + for (auto& obj : arrayJson["bannedPlayers"]) + { + outBannedVec.push_back( + std::make_pair( + obj.value("reason", "#DISCONNECT_BANNED"), + obj.value("id", uint64_t(0)) + ) + ); + } + + return true; +} + //----------------------------------------------------------------------------- // Purpose: Checks if client is banned on the comp server. // Input : &ipAddress - // nucleusId - // &outReason - <- contains banned reason if any. -// Output : Returns true if banned, false if not banned. +// Output : True if banned, false if not banned. //----------------------------------------------------------------------------- bool CPylon::CheckForBan(const string& ipAddress, const uint64_t nucleusId, string& outReason) const { diff --git a/r5dev/networksystem/pylon.h b/r5dev/networksystem/pylon.h index 39b3f279..c73eb909 100644 --- a/r5dev/networksystem/pylon.h +++ b/r5dev/networksystem/pylon.h @@ -1,5 +1,6 @@ #pragma once #include "thirdparty/curl/include/curl/curl.h" +#include "bansystem.h" #include "serverlisting.h" class CPylon @@ -8,6 +9,8 @@ public: 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 GetBannedList(const BannedVec_t& inBannedVec, BannedVec_t& outBannedVec) const; bool CheckForBan(const string& ipAddress, const uint64_t nucleusId, string& outReason) const; void ExtractError(const nlohmann::json& resultBody, string& outMessage, CURLINFO status, const char* errorText = nullptr) const; From 7739e56b0d7731e4f81a671df748fdb5dbfef998 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Fri, 28 Apr 2023 23:48:30 +0200 Subject: [PATCH 11/19] Adjust default value of cvar 'sv_banlistRefreshRate' Set to 30 sec. --- r5dev/tier1/cvar.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/r5dev/tier1/cvar.cpp b/r5dev/tier1/cvar.cpp index acbf8d6e..9c3f31d0 100644 --- a/r5dev/tier1/cvar.cpp +++ b/r5dev/tier1/cvar.cpp @@ -339,7 +339,7 @@ void ConVar::StaticInit(void) sv_globalBanlist = ConVar::StaticCreate("sv_globalBanlist" , "1", FCVAR_RELEASE, "Determines whether or not to use the global banned list.", false, 0.f, false, 0.f, nullptr, "0 = Disable, 1 = Enable."); sv_pylonVisibility = ConVar::StaticCreate("sv_pylonVisibility", "0", FCVAR_RELEASE, "Determines the visibility to the Pylon master server.", false, 0.f, false, 0.f, nullptr, "0 = Offline, 1 = Hidden, 2 = Public."); sv_pylonRefreshRate = ConVar::StaticCreate("sv_pylonRefreshRate" , "5.0", FCVAR_RELEASE, "Pylon host refresh rate (seconds).", true, 2.f, true, 8.f, nullptr, nullptr); - sv_banlistRefreshRate = ConVar::StaticCreate("sv_banlistRefreshRate", "1.0", FCVAR_RELEASE, "Banned list refresh rate (seconds).", true, 1.f, false, 0.f, nullptr, nullptr); + sv_banlistRefreshRate = ConVar::StaticCreate("sv_banlistRefreshRate", "30.0", FCVAR_RELEASE, "Banned list refresh rate (seconds).", true, 1.f, false, 0.f, nullptr, nullptr); sv_statusRefreshRate = ConVar::StaticCreate("sv_statusRefreshRate" , "0.5", FCVAR_RELEASE, "Server status refresh rate (seconds).", true, 0.f, false, 0.f, nullptr, nullptr); sv_autoReloadRate = ConVar::StaticCreate("sv_autoReloadRate" , "0" , FCVAR_RELEASE, "Time in seconds between each server auto-reload (disabled if null).", true, 0.f, false, 0.f, nullptr, nullptr); sv_simulateBots = ConVar::StaticCreate("sv_simulateBots", "1", FCVAR_RELEASE, "Simulate user commands for bots on the server.", true, 0.f, false, 0.f, nullptr, nullptr); @@ -1237,13 +1237,13 @@ static void PrintCommand(const ConCommand* cmd, bool logging, FileHandle_t& f) Q_strncat(emptyflags, csvf, len); } // Names staring with +/- need to be wrapped in single quotes - char name[256]; - Q_snprintf(name, sizeof(name), "%s", cmd->GetName()); - if (name[0] == '+' || name[0] == '-') + char nameBuf[256]; + Q_snprintf(nameBuf, sizeof(nameBuf), "%s", cmd->GetName()); + if (nameBuf[0] == '+' || nameBuf[0] == '-') { - Q_snprintf(name, sizeof(name), "'%s'", cmd->GetName()); + Q_snprintf(nameBuf, sizeof(nameBuf), "'%s'", cmd->GetName()); } - FileSystem()->FPrintf(f, "\"%s\",\"%s\",%s,\"%s\"\n", name, "cmd", emptyflags, StripQuotes(cmd->GetHelpText(), tempbuff, sizeof(tempbuff))); + FileSystem()->FPrintf(f, "\"%s\",\"%s\",%s,\"%s\"\n", nameBuf, "cmd", emptyflags, StripQuotes(cmd->GetHelpText(), tempbuff, sizeof(tempbuff))); } } From ab9d36e0d86af052d24297e4de1056825120b91c Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Sat, 29 Apr 2023 00:05:47 +0200 Subject: [PATCH 12/19] Send client's persona name to the master server Will be used on the master server for additional checking. --- r5dev/engine/server/server.cpp | 2 +- r5dev/engine/server/sv_main.cpp | 5 +++-- r5dev/engine/server/sv_main.h | 2 +- r5dev/networksystem/pylon.cpp | 4 +++- r5dev/networksystem/pylon.h | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/r5dev/engine/server/server.cpp b/r5dev/engine/server/server.cpp index a41f3a19..2bb47c4e 100644 --- a/r5dev/engine/server/server.cpp +++ b/r5dev/engine/server/server.cpp @@ -119,7 +119,7 @@ bool CServer::AuthClient(user_creds_s* pChallenge) if (sv_globalBanlist->GetBool()) { - std::thread th(SV_IsClientBanned, string(pszAddresBuffer), nNucleusID); + std::thread th(SV_IsClientBanned, string(pszAddresBuffer), nNucleusID, string(pszPersonaName)); th.detach(); } diff --git a/r5dev/engine/server/sv_main.cpp b/r5dev/engine/server/sv_main.cpp index ea7b72d7..37ef5818 100644 --- a/r5dev/engine/server/sv_main.cpp +++ b/r5dev/engine/server/sv_main.cpp @@ -15,11 +15,11 @@ //----------------------------------------------------------------------------- // Purpose: checks if particular client is banned on the comp server //----------------------------------------------------------------------------- -void SV_IsClientBanned(const string& svIPAddr, const uint64_t nNucleusID) +void SV_IsClientBanned(const string& svIPAddr, const uint64_t nNucleusID, const string& svPersonaName) { string svError; + bool bCompBanned = g_pMasterServer->CheckForBan(svIPAddr, nNucleusID, svPersonaName, svError); - bool bCompBanned = g_pMasterServer->CheckForBan(svIPAddr, nNucleusID, svError); if (bCompBanned) { if (!ThreadInMainThread()) @@ -50,6 +50,7 @@ void SV_ProcessBulkCheck(const BannedVec_t& bannedVec) void SV_CheckForBan(const BannedVec_t* pBannedVec /*= nullptr*/) { + Assert(ThreadInMainThread()); BannedVec_t bannedVec; for (int c = 0; c < MAX_PLAYERS; c++) // Loop through all possible client instances. diff --git a/r5dev/engine/server/sv_main.h b/r5dev/engine/server/sv_main.h index 3d7bf8f3..941f8eb8 100644 --- a/r5dev/engine/server/sv_main.h +++ b/r5dev/engine/server/sv_main.h @@ -27,7 +27,7 @@ inline bool* s_bIsDedicated = nullptr; void SV_InitGameDLL(); void SV_ShutdownGameDLL(); bool SV_ActivateServer(); -void SV_IsClientBanned(const string& svIPAddr, const uint64_t nNucleusID); +void SV_IsClientBanned(const string& svIPAddr, const uint64_t nNucleusID, const string& svPersonaName); void SV_CheckForBan(const BannedVec_t* pBannedVec = nullptr); /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/networksystem/pylon.cpp b/r5dev/networksystem/pylon.cpp index 842e1643..e3e7e816 100644 --- a/r5dev/networksystem/pylon.cpp +++ b/r5dev/networksystem/pylon.cpp @@ -265,9 +265,11 @@ bool CPylon::GetBannedList(const BannedVec_t& inBannedVec, BannedVec_t& outBanne // &outReason - <- contains banned reason if any. // Output : True if banned, false if not banned. //----------------------------------------------------------------------------- -bool CPylon::CheckForBan(const string& ipAddress, const uint64_t nucleusId, string& outReason) const +bool CPylon::CheckForBan(const string& ipAddress, const uint64_t nucleusId, + const string& personaName, string& outReason) const { nlohmann::json requestJson = nlohmann::json::object(); + requestJson["name"] = personaName; requestJson["id"] = nucleusId; requestJson["ip"] = ipAddress; diff --git a/r5dev/networksystem/pylon.h b/r5dev/networksystem/pylon.h index c73eb909..3f72d963 100644 --- a/r5dev/networksystem/pylon.h +++ b/r5dev/networksystem/pylon.h @@ -11,7 +11,7 @@ public: bool PostServerHost(string& outMessage, string& svOutToken, const NetGameServer_t& netGameServer) const; bool GetBannedList(const BannedVec_t& inBannedVec, BannedVec_t& outBannedVec) const; - bool CheckForBan(const string& ipAddress, const uint64_t nucleusId, string& outReason) const; + bool CheckForBan(const string& ipAddress, const uint64_t nucleusId, const string& personaName, string& outReason) const; void ExtractError(const nlohmann::json& resultBody, string& outMessage, CURLINFO status, const char* errorText = nullptr) const; void ExtractError(const string& response, string& outMessage, CURLINFO status, const char* messageText = nullptr) const; From 3ea49766c6f8be476f812939e0bb68c4b50fc2b4 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Sat, 29 Apr 2023 12:15:51 +0200 Subject: [PATCH 13/19] Light banning code improvements * Removed extraneous std::string constructors (everything using the bansystem used raw char pointers). * Improved logging and function comments. --- r5dev/engine/server/sv_main.cpp | 15 ++++++-- r5dev/networksystem/bansystem.cpp | 61 ++++++++++++++++--------------- r5dev/networksystem/bansystem.h | 8 ++-- 3 files changed, 47 insertions(+), 37 deletions(-) diff --git a/r5dev/engine/server/sv_main.cpp b/r5dev/engine/server/sv_main.cpp index 37ef5818..685a8378 100644 --- a/r5dev/engine/server/sv_main.cpp +++ b/r5dev/engine/server/sv_main.cpp @@ -24,16 +24,19 @@ void SV_IsClientBanned(const string& svIPAddr, const uint64_t nNucleusID, const { if (!ThreadInMainThread()) { - g_TaskScheduler->Dispatch([svError, svIPAddr] + g_TaskScheduler->Dispatch([svError, svIPAddr, nNucleusID] { g_pBanSystem->KickPlayerById(svIPAddr.c_str(), svError.c_str()); + Warning(eDLL_T::SERVER, "Removed client '%s' ('%llu' is banned globally!)\n", + svIPAddr.c_str(), nNucleusID); }, 0); } - - //Warning(eDLL_T::SERVER, "Added '%s' to refused list ('%llu' is banned from the master server!)\n", svIPAddr.c_str(), nNucleusID); } } +//----------------------------------------------------------------------------- +// Purpose: checks if particular client is banned on the master server +//----------------------------------------------------------------------------- void SV_ProcessBulkCheck(const BannedVec_t& bannedVec) { BannedVec_t outBannedVec; @@ -48,6 +51,10 @@ void SV_ProcessBulkCheck(const BannedVec_t& bannedVec) } } +//----------------------------------------------------------------------------- +// Purpose: creates a snapshot of the currently connected clients +// Input : *pBannedVec - if passed, will check for bans and kick the clients +//----------------------------------------------------------------------------- void SV_CheckForBan(const BannedVec_t* pBannedVec /*= nullptr*/) { Assert(ThreadInMainThread()); @@ -77,7 +84,7 @@ void SV_CheckForBan(const BannedVec_t* pBannedVec /*= nullptr*/) { if (it.second == pClient->GetNucleusID()) { - Warning(eDLL_T::SERVER, "Removing client '%s' from slot '%i' ('%llu' is banned from this server!)\n", szIPAddr, c, nNucleusID); + Warning(eDLL_T::SERVER, "Removing client '%s' from slot '%i' ('%llu' is banned globally!)\n", szIPAddr, c, nNucleusID); pClient->Disconnect(Reputation_t::REP_MARK_BAD, "%s", it.first.c_str()); } } diff --git a/r5dev/networksystem/bansystem.cpp b/r5dev/networksystem/bansystem.cpp index 40d4c640..1dc7d4ff 100644 --- a/r5dev/networksystem/bansystem.cpp +++ b/r5dev/networksystem/bansystem.cpp @@ -98,25 +98,27 @@ void CBanSystem::Save(void) const //----------------------------------------------------------------------------- // Purpose: adds a banned player entry to the banned list -// Input : &svIpAddress - -// nNucleusID - +// Input : *ipAddress - +// nucleusId - //----------------------------------------------------------------------------- -bool CBanSystem::AddEntry(const string& svIpAddress, const uint64_t nNucleusID) +bool CBanSystem::AddEntry(const char* ipAddress, const uint64_t nucleusId) { - Assert(!svIpAddress.empty()); + Assert(VALID_CHARSTAR(ipAddress)); + const auto idPair = std::make_pair(string(ipAddress), nucleusId); if (IsBanListValid()) { - auto it = std::find(m_vBanList.begin(), m_vBanList.end(), std::make_pair(svIpAddress, nNucleusID)); + auto it = std::find(m_vBanList.begin(), m_vBanList.end(), idPair); + if (it == m_vBanList.end()) { - m_vBanList.push_back(std::make_pair(svIpAddress, nNucleusID)); + m_vBanList.push_back(idPair); return true; } } else { - m_vBanList.push_back(std::make_pair(svIpAddress, nNucleusID)); + m_vBanList.push_back(idPair); return true; } @@ -125,18 +127,21 @@ bool CBanSystem::AddEntry(const string& svIpAddress, const uint64_t nNucleusID) //----------------------------------------------------------------------------- // Purpose: deletes an entry in the banned list -// Input : &svIpAddress - -// nNucleusID - +// Input : *ipAddress - +// nucleusId - //----------------------------------------------------------------------------- -bool CBanSystem::DeleteEntry(const string& svIpAddress, const uint64_t nNucleusID) +bool CBanSystem::DeleteEntry(const char* ipAddress, const uint64_t nucleusId) { - Assert(!svIpAddress.empty()); + Assert(VALID_CHARSTAR(ipAddress)); if (IsBanListValid()) { auto it = std::find_if(m_vBanList.begin(), m_vBanList.end(), [&](const pair& element) - { return (svIpAddress.compare(element.first) == NULL || element.second == nNucleusID); }); + { + return (strcmp(ipAddress, element.first.c_str()) == NULL + || element.second == nucleusId); + }); if (it != m_vBanList.end()) { @@ -150,25 +155,25 @@ bool CBanSystem::DeleteEntry(const string& svIpAddress, const uint64_t nNucleusI //----------------------------------------------------------------------------- // Purpose: checks if specified ip address or nucleus id is banned -// Input : &svIpAddress - -// nNucleusID - +// Input : *ipAddress - +// nucleusId - // Output : true if banned, false if not banned //----------------------------------------------------------------------------- -bool CBanSystem::IsBanned(const string& svIpAddress, const uint64_t nNucleusID) const +bool CBanSystem::IsBanned(const char* ipAddress, const uint64_t nucleusId) const { for (size_t i = 0; i < m_vBanList.size(); i++) { - const string& ipAddress = m_vBanList[i].first; - const uint64_t nucleusID = m_vBanList[i].second; + const string& bannedIpAddress = m_vBanList[i].first; + const uint64_t bannedNucleusID = m_vBanList[i].second; - if (ipAddress.empty() || - !nucleusID) // Cannot be null. + if (bannedIpAddress.empty() + || !bannedNucleusID) // Cannot be null. { continue; } - if (ipAddress.compare(svIpAddress) == NULL || - nNucleusID == nucleusID) + if (bannedIpAddress.compare(ipAddress) == NULL + || nucleusId == bannedNucleusID) { return true; } @@ -239,23 +244,23 @@ void CBanSystem::BanPlayerById(const char* playerHandle, const char* reason) //----------------------------------------------------------------------------- // Purpose: unbans a player by given nucleus id or ip address -// Input : &svCriteria - +// Input : *criteria - //----------------------------------------------------------------------------- -void CBanSystem::UnbanPlayer(const string& svCriteria) +void CBanSystem::UnbanPlayer(const char* criteria) { try { bool bSave = false; - if (StringIsDigit(svCriteria)) // Check if we have an ip address or nucleus id. + if (StringIsDigit(criteria)) // Check if we have an ip address or nucleus id. { - if (DeleteEntry("<>", std::stoll(svCriteria))) // Delete ban entry. + if (DeleteEntry("<>", std::stoll(criteria))) // Delete ban entry. { bSave = true; } } else { - if (DeleteEntry(svCriteria, 0)) // Delete ban entry. + if (DeleteEntry(criteria, 0)) // Delete ban entry. { bSave = true; } @@ -264,7 +269,7 @@ void CBanSystem::UnbanPlayer(const string& svCriteria) if (bSave) { Save(); // Save modified vector to file. - DevMsg(eDLL_T::SERVER, "Removed '%s' from banned list\n", svCriteria.c_str()); + DevMsg(eDLL_T::SERVER, "Removed '%s' from banned list\n", criteria); } } catch (const std::exception& e) @@ -371,8 +376,6 @@ void CBanSystem::AuthorPlayerById(const char* playerHandle, const bool shouldBan if (shouldBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave) bSave = true; - - pClient->Disconnect(REP_MARK_BAD, reason); bDisconnect = true; } diff --git a/r5dev/networksystem/bansystem.h b/r5dev/networksystem/bansystem.h index c266bb9c..0820bf01 100644 --- a/r5dev/networksystem/bansystem.h +++ b/r5dev/networksystem/bansystem.h @@ -8,10 +8,10 @@ public: void Load(void); void Save(void) const; - bool AddEntry(const string& svIpAddress, const uint64_t nNucleusID); - bool DeleteEntry(const string& svIpAddress, const uint64_t nNucleusID); + bool AddEntry(const char* ipAddress, const uint64_t nucleusId); + bool DeleteEntry(const char* ipAddress, const uint64_t nucleusId); - bool IsBanned(const string& svIpAddress, const uint64_t nNucleusID) const; + bool IsBanned(const char* ipAddress, const uint64_t nucleusId) const; bool IsBanListValid(void) const; void KickPlayerByName(const char* playerName, const char* reason = nullptr); @@ -20,7 +20,7 @@ public: void BanPlayerByName(const char* playerName, const char* reason = nullptr); void BanPlayerById(const char* playerHandle, const char* reason = nullptr); - void UnbanPlayer(const string& svCriteria); + void UnbanPlayer(const char* criteria); private: void AuthorPlayerByName(const char* playerName, const bool bBan, const char* reason = nullptr); From d46061a55aaf86f61a02db9f4fb5301c7b4f4cad Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Sat, 29 Apr 2023 12:16:09 +0200 Subject: [PATCH 14/19] Don't log GPU device on dedicated --- r5dev/core/init.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/r5dev/core/init.cpp b/r5dev/core/init.cpp index bdaa2a71..592b0f30 100644 --- a/r5dev/core/init.cpp +++ b/r5dev/core/init.cpp @@ -250,6 +250,7 @@ void Winsock_Shutdown() } void QuerySystemInfo() { +#ifndef DEDICATED for (int i = 0; ; i++) { DISPLAY_DEVICE dd = { sizeof(dd), {0} }; @@ -266,6 +267,7 @@ void QuerySystemInfo() spdlog::info("{:25s}: '{:s}'\n", "GPU model identifier", szDeviceName); } } +#endif // !DEDICATED const CPUInformation& pi = GetCPUInformation(); From 145bc3797f9c4d9c740a2882678a19bc9f30d1d1 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Sat, 29 Apr 2023 13:15:18 +0200 Subject: [PATCH 15/19] Only check token field if the server is hidden --- r5dev/networksystem/pylon.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/r5dev/networksystem/pylon.cpp b/r5dev/networksystem/pylon.cpp index e3e7e816..13fec050 100644 --- a/r5dev/networksystem/pylon.cpp +++ b/r5dev/networksystem/pylon.cpp @@ -155,17 +155,21 @@ bool CPylon::PostServerHost(string& outMessage, string& outToken, return false; } - nlohmann::json& tokenJson = responseJson["token"]; - - if (!tokenJson.is_string()) + if (netGameServer.m_bHidden) { - outMessage = Format("Invalid response with status: %d", int(status)); - outToken.clear(); + nlohmann::json& tokenJson = responseJson["token"]; - return false; + if (!tokenJson.is_string()) + { + outMessage = Format("Invalid response with status: %d", int(status)); + outToken.clear(); + + return false; + } + + outToken = tokenJson.get(); } - outToken = tokenJson.get(); return true; } From 2748c61fe2db9042b98cf7581a80de7a78a33549 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Sat, 29 Apr 2023 14:58:46 +0200 Subject: [PATCH 16/19] Put json value getters in try catch Put 'nlohmann::json::value' operations in try catch, as these could raise an exception. --- r5dev/networksystem/pylon.cpp | 144 +++++++++++++++++++++------------- 1 file changed, 88 insertions(+), 56 deletions(-) diff --git a/r5dev/networksystem/pylon.cpp b/r5dev/networksystem/pylon.cpp index 13fec050..b6ed843d 100644 --- a/r5dev/networksystem/pylon.cpp +++ b/r5dev/networksystem/pylon.cpp @@ -40,28 +40,36 @@ vector CPylon::GetServerList(string& outMessage) const return vecServers; } - for (auto& obj : responseJson["servers"]) + try { - vecServers.push_back( - NetGameServer_t - { - obj.value("name",""), - obj.value("description",""), - obj.value("hidden","false") == "true", - obj.value("map",""), - obj.value("playlist",""), - obj.value("ip",""), - obj.value("port", ""), - obj.value("key",""), - obj.value("checksum",""), - obj.value("version", SDK_VERSION), - obj.value("playerCount", ""), - obj.value("maxPlayers", ""), - obj.value("timeStamp", 0), - obj.value("publicRef", ""), - obj.value("cachedId", ""), - } - ); + for (auto& obj : responseJson["servers"]) + { + vecServers.push_back( + NetGameServer_t + { + obj.value("name",""), + obj.value("description",""), + obj.value("hidden","false") == "true", + obj.value("map",""), + obj.value("playlist",""), + obj.value("ip",""), + obj.value("port", ""), + obj.value("key",""), + obj.value("checksum",""), + obj.value("version", SDK_VERSION), + obj.value("playerCount", ""), + obj.value("maxPlayers", ""), + obj.value("timeStamp", 0), + obj.value("publicRef", ""), + obj.value("cachedId", ""), + } + ); + } + } + catch (const std::exception& ex) + { + Warning(eDLL_T::ENGINE, "%s - %s\n", __FUNCTION__, ex.what()); + vecServers.clear(); // Clear as the vector may be partially filled. } return vecServers; @@ -95,28 +103,36 @@ bool CPylon::GetServerByToken(NetGameServer_t& outGameServer, return false; } - nlohmann::json& serverJson = responseJson["server"]; - - outGameServer = NetGameServer_t + try { - serverJson.value("name",""), - serverJson.value("description",""), - serverJson.value("hidden","false") == "true", - serverJson.value("map",""), - serverJson.value("playlist",""), - serverJson.value("ip",""), - serverJson.value("port", ""), - serverJson.value("key",""), - serverJson.value("checksum",""), - serverJson.value("version", SDK_VERSION), - serverJson.value("playerCount", ""), - serverJson.value("maxPlayers", ""), - serverJson.value("timeStamp", 0), - serverJson.value("publicRef", ""), - serverJson.value("cachedId", ""), - }; + nlohmann::json& serverJson = responseJson["server"]; + outGameServer = NetGameServer_t + { + serverJson.value("name",""), + serverJson.value("description",""), + serverJson.value("hidden","false") == "true", + serverJson.value("map",""), + serverJson.value("playlist",""), + serverJson.value("ip",""), + serverJson.value("port", ""), + serverJson.value("key",""), + serverJson.value("checksum",""), + serverJson.value("version", SDK_VERSION), + serverJson.value("playerCount", ""), + serverJson.value("maxPlayers", ""), + serverJson.value("timeStamp", 0), + serverJson.value("publicRef", ""), + serverJson.value("cachedId", ""), + }; - return true; + return true; + } + catch (const std::exception& ex) + { + Warning(eDLL_T::ENGINE, "%s - %s\n", __FUNCTION__, ex.what()); + } + + return false; } //----------------------------------------------------------------------------- @@ -249,17 +265,26 @@ bool CPylon::GetBannedList(const BannedVec_t& inBannedVec, BannedVec_t& outBanne return false; } - for (auto& obj : arrayJson["bannedPlayers"]) + try { - outBannedVec.push_back( - std::make_pair( - obj.value("reason", "#DISCONNECT_BANNED"), - obj.value("id", uint64_t(0)) - ) - ); + for (auto& obj : arrayJson["bannedPlayers"]) + { + outBannedVec.push_back( + std::make_pair( + obj.value("reason", "#DISCONNECT_BANNED"), + obj.value("id", uint64_t(0)) + ) + ); + } + + return true; + } + catch (const std::exception& ex) + { + Warning(eDLL_T::ENGINE, "%s - %s\n", __FUNCTION__, ex.what()); } - return true; + return false; } //----------------------------------------------------------------------------- @@ -287,11 +312,18 @@ bool CPylon::CheckForBan(const string& ipAddress, const uint64_t nucleusId, return false; } - if (responseJson["banned"].is_boolean() && - responseJson["banned"].get()) + try { - outReason = responseJson.value("reason", "#DISCONNECT_BANNED"); - return true; + if (responseJson["banned"].is_boolean() && + responseJson["banned"].get()) + { + outReason = responseJson.value("reason", "#DISCONNECT_BANNED"); + return true; + } + } + catch (const std::exception& ex) + { + Warning(eDLL_T::ENGINE, "%s - %s\n", __FUNCTION__, ex.what()); } return false; @@ -300,8 +332,8 @@ bool CPylon::CheckForBan(const string& ipAddress, const uint64_t nucleusId, //----------------------------------------------------------------------------- // Purpose: Sends request to Pylon Master Server. // Input : *endpoint - -// &requestBody - -// &responseBody - +// &requestJson - +// &responseJson - // &outMessage - // &status - // Output : True on success, false on failure. @@ -352,7 +384,7 @@ bool CPylon::SendRequest(const char* endpoint, const nlohmann::json& requestJson // Input : *endpoint - // *request - // &outResponse - -// &outMessage - <- contains an error message if any. +// &outMessage - <- contains an error message on failure. // &outStatus - // Output : True on success, false on failure. //----------------------------------------------------------------------------- From a5bc2f77cb33d33e65156275b487852b8426824c Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Sat, 29 Apr 2023 20:36:29 +0200 Subject: [PATCH 17/19] Fix broken 'SV_IsClientBanned' implementation SV_IsClientBanned is now checked after the creating of CClient, and thus 'CServer::AuthClient' has been inlined into 'CServer::ConnectClient'. Since we have to request the master server to check whether or not this particular client is globally banned, we have to detach it into a separate thread, and process the results back into the main thread once finished. This takes too much time, so we connect the client anyways and if the master server marked it as 'banned', we disconnect it right away. --- r5dev/engine/server/server.cpp | 43 +++++++++++++-------------------- r5dev/engine/server/server.h | 1 - r5dev/engine/server/sv_main.cpp | 25 +++++++++++++------ r5dev/engine/server/sv_main.h | 3 ++- 4 files changed, 37 insertions(+), 35 deletions(-) diff --git a/r5dev/engine/server/server.cpp b/r5dev/engine/server/server.cpp index 2bb47c4e..d77021ea 100644 --- a/r5dev/engine/server/server.cpp +++ b/r5dev/engine/server/server.cpp @@ -79,12 +79,17 @@ int CServer::GetNumClients(void) const } //--------------------------------------------------------------------------------- -// Purpose: client to server authentication -// Input : *pChallenge - -// Output : true if user isn't banned, false otherwise +// Purpose: Initializes a CSVClient for a new net connection. This will only be called +// once for a player each game, not once for each level change. +// Input : *pServer - +// *pInpacket - +// Output : pointer to client instance on success, nullptr on failure //--------------------------------------------------------------------------------- -bool CServer::AuthClient(user_creds_s* pChallenge) +CClient* CServer::ConnectClient(CServer* pServer, user_creds_s* pChallenge) { + if (pServer->m_State < server_state_t::ss_active) + return nullptr; + char* pszPersonaName = pChallenge->personaName; uint64_t nNucleusID = pChallenge->personaId; @@ -98,47 +103,33 @@ bool CServer::AuthClient(user_creds_s* pChallenge) // Only proceed connection if the client's name is valid and UTF-8 encoded. if (!VALID_CHARSTAR(pszPersonaName) || !IsValidUTF8(pszPersonaName) || !IsValidPersonaName(pszPersonaName)) { - RejectConnection(m_Socket, &pChallenge->netAdr, "#Valve_Reject_Invalid_Name"); + pServer->RejectConnection(pServer->m_Socket, &pChallenge->netAdr, "#Valve_Reject_Invalid_Name"); if (bEnableLogging) Warning(eDLL_T::SERVER, "Connection rejected for '%s' ('%llu' has an invalid name!)\n", pszAddresBuffer, nNucleusID); - return false; + return nullptr; } if (g_pBanSystem->IsBanListValid()) { if (g_pBanSystem->IsBanned(pszAddresBuffer, nNucleusID)) { - RejectConnection(m_Socket, &pChallenge->netAdr, "#Valve_Reject_Banned"); + pServer->RejectConnection(pServer->m_Socket, &pChallenge->netAdr, "#Valve_Reject_Banned"); if (bEnableLogging) Warning(eDLL_T::SERVER, "Connection rejected for '%s' ('%llu' is banned from this server!)\n", pszAddresBuffer, nNucleusID); - return false; + return nullptr; } } - if (sv_globalBanlist->GetBool()) + CClient* pClient = v_CServer_ConnectClient(pServer, pChallenge); + + if (pClient && sv_globalBanlist->GetBool()) { - std::thread th(SV_IsClientBanned, string(pszAddresBuffer), nNucleusID, string(pszPersonaName)); + std::thread th(SV_IsClientBanned, pClient, string(pszAddresBuffer), nNucleusID, string(pszPersonaName)); th.detach(); } - return true; -} - -//--------------------------------------------------------------------------------- -// Purpose: Initializes a CSVClient for a new net connection. This will only be called -// once for a player each game, not once for each level change. -// Input : *pServer - -// *pInpacket - -// Output : pointer to client instance on success, nullptr on failure -//--------------------------------------------------------------------------------- -CClient* CServer::ConnectClient(CServer* pServer, user_creds_s* pChallenge) -{ - if (pServer->m_State < server_state_t::ss_active || !pServer->AuthClient(pChallenge)) - return nullptr; - - CClient* pClient = v_CServer_ConnectClient(pServer, pChallenge); return pClient; } diff --git a/r5dev/engine/server/server.h b/r5dev/engine/server/server.h index a2681214..5483128c 100644 --- a/r5dev/engine/server/server.h +++ b/r5dev/engine/server/server.h @@ -42,7 +42,6 @@ public: bool IsActive(void) const { return m_State >= server_state_t::ss_active; } bool IsLoading(void) const { return m_State == server_state_t::ss_loading; } bool IsDedicated(void) const { return m_bIsDedicated; } - bool AuthClient(user_creds_s* pChallenge); void RejectConnection(int iSocket, netadr_t* pNetAdr, const char* szMessage); static CClient* ConnectClient(CServer* pServer, user_creds_s* pChallenge); static void RunFrame(CServer* pServer); diff --git a/r5dev/engine/server/sv_main.cpp b/r5dev/engine/server/sv_main.cpp index 685a8378..d538eda8 100644 --- a/r5dev/engine/server/sv_main.cpp +++ b/r5dev/engine/server/sv_main.cpp @@ -15,8 +15,10 @@ //----------------------------------------------------------------------------- // Purpose: checks if particular client is banned on the comp server //----------------------------------------------------------------------------- -void SV_IsClientBanned(const string& svIPAddr, const uint64_t nNucleusID, const string& svPersonaName) +void SV_IsClientBanned(CClient* pClient, const string& svIPAddr, const uint64_t nNucleusID, const string& svPersonaName) { + Assert(pClient != nullptr); + string svError; bool bCompBanned = g_pMasterServer->CheckForBan(svIPAddr, nNucleusID, svPersonaName, svError); @@ -24,11 +26,19 @@ void SV_IsClientBanned(const string& svIPAddr, const uint64_t nNucleusID, const { if (!ThreadInMainThread()) { - g_TaskScheduler->Dispatch([svError, svIPAddr, nNucleusID] + g_TaskScheduler->Dispatch([pClient, svError, svIPAddr, nNucleusID] { - g_pBanSystem->KickPlayerById(svIPAddr.c_str(), svError.c_str()); - Warning(eDLL_T::SERVER, "Removed client '%s' ('%llu' is banned globally!)\n", - svIPAddr.c_str(), nNucleusID); + // Make sure client isn't already disconnected, + // and that if there is a valid netchannel, that + // it hasn't been taken by a different client by + // the time this task is getting executed. + CNetChan* pChan = pClient->GetNetChan(); + if (pChan && pClient->GetNucleusID() == nNucleusID) + { + pClient->Disconnect(Reputation_t::REP_MARK_BAD, svError.c_str()); + Warning(eDLL_T::SERVER, "Removed client '%s' ('%llu' is banned globally!)\n", + svIPAddr.c_str(), nNucleusID); + } }, 0); } } @@ -77,15 +87,16 @@ void SV_CheckForBan(const BannedVec_t* pBannedVec /*= nullptr*/) const uint64_t nNucleusID = pClient->GetNucleusID(); if (!pBannedVec) - bannedVec.push_back(std::make_pair(szIPAddr, nNucleusID)); + bannedVec.push_back(std::make_pair(string(szIPAddr), nNucleusID)); else { for (auto& it : *pBannedVec) { if (it.second == pClient->GetNucleusID()) { - Warning(eDLL_T::SERVER, "Removing client '%s' from slot '%i' ('%llu' is banned globally!)\n", szIPAddr, c, nNucleusID); pClient->Disconnect(Reputation_t::REP_MARK_BAD, "%s", it.first.c_str()); + Warning(eDLL_T::SERVER, "Removed client '%s' from slot '%i' ('%llu' is banned globally!)\n", + szIPAddr, c, nNucleusID); } } } diff --git a/r5dev/engine/server/sv_main.h b/r5dev/engine/server/sv_main.h index 941f8eb8..4f31c323 100644 --- a/r5dev/engine/server/sv_main.h +++ b/r5dev/engine/server/sv_main.h @@ -3,6 +3,7 @@ #include "networksystem/bansystem.h" /////////////////////////////////////////////////////////////////////////////// +class CClient; /* ==== SV_MAIN ======================================================================================================================================================= */ inline CMemory p_SV_InitGameDLL; @@ -27,7 +28,7 @@ inline bool* s_bIsDedicated = nullptr; void SV_InitGameDLL(); void SV_ShutdownGameDLL(); bool SV_ActivateServer(); -void SV_IsClientBanned(const string& svIPAddr, const uint64_t nNucleusID, const string& svPersonaName); +void SV_IsClientBanned(CClient* pClient, const string& svIPAddr, const uint64_t nNucleusID, const string& svPersonaName); void SV_CheckForBan(const BannedVec_t* pBannedVec = nullptr); /////////////////////////////////////////////////////////////////////////////// From 7ecad819be903f3ef09387bfbcb6529de8321ca8 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Sat, 29 Apr 2023 20:36:57 +0200 Subject: [PATCH 18/19] Set dedicated nucleus id to '9990000' --- r5dev/ebisusdk/EbisuSDK.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/r5dev/ebisusdk/EbisuSDK.cpp b/r5dev/ebisusdk/EbisuSDK.cpp index dd41e952..6beac029 100644 --- a/r5dev/ebisusdk/EbisuSDK.cpp +++ b/r5dev/ebisusdk/EbisuSDK.cpp @@ -10,7 +10,7 @@ void HEbisuSDK_Init() #ifdef DEDICATED *g_EbisuSDKInit = true; // <- 1st EbisuSDK *g_EbisuProfileInit = true; // <- 2nd EbisuSDK - *g_NucleusID = true; // <- 3rd EbisuSDK + *g_NucleusID = 9990000; // <- 3rd EbisuSDK #endif // DEDICATED } From d52074238f81d937909c8a5b20a37dc797b1d888 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Sat, 29 Apr 2023 20:44:51 +0200 Subject: [PATCH 19/19] Mark pylon refresh cvars as devonly Marked 'sv_pylonRefreshRate' and 'sv_banlistRefreshRate' as development only. --- r5dev/tier1/cvar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/r5dev/tier1/cvar.cpp b/r5dev/tier1/cvar.cpp index 9c3f31d0..40b1e379 100644 --- a/r5dev/tier1/cvar.cpp +++ b/r5dev/tier1/cvar.cpp @@ -338,8 +338,8 @@ void ConVar::StaticInit(void) sv_showconnecting = ConVar::StaticCreate("sv_showconnecting" , "1", FCVAR_RELEASE, "Logs information about the connecting client to the console.", false, 0.f, false, 0.f, nullptr, nullptr); sv_globalBanlist = ConVar::StaticCreate("sv_globalBanlist" , "1", FCVAR_RELEASE, "Determines whether or not to use the global banned list.", false, 0.f, false, 0.f, nullptr, "0 = Disable, 1 = Enable."); sv_pylonVisibility = ConVar::StaticCreate("sv_pylonVisibility", "0", FCVAR_RELEASE, "Determines the visibility to the Pylon master server.", false, 0.f, false, 0.f, nullptr, "0 = Offline, 1 = Hidden, 2 = Public."); - sv_pylonRefreshRate = ConVar::StaticCreate("sv_pylonRefreshRate" , "5.0", FCVAR_RELEASE, "Pylon host refresh rate (seconds).", true, 2.f, true, 8.f, nullptr, nullptr); - sv_banlistRefreshRate = ConVar::StaticCreate("sv_banlistRefreshRate", "30.0", FCVAR_RELEASE, "Banned list refresh rate (seconds).", true, 1.f, false, 0.f, nullptr, nullptr); + sv_pylonRefreshRate = ConVar::StaticCreate("sv_pylonRefreshRate" , "5.0" , FCVAR_DEVELOPMENTONLY, "Pylon host refresh rate (seconds).", true, 2.f, true, 8.f, nullptr, nullptr); + sv_banlistRefreshRate = ConVar::StaticCreate("sv_banlistRefreshRate", "30.0", FCVAR_DEVELOPMENTONLY, "Banned list refresh rate (seconds).", true, 1.f, false, 0.f, nullptr, nullptr); sv_statusRefreshRate = ConVar::StaticCreate("sv_statusRefreshRate" , "0.5", FCVAR_RELEASE, "Server status refresh rate (seconds).", true, 0.f, false, 0.f, nullptr, nullptr); sv_autoReloadRate = ConVar::StaticCreate("sv_autoReloadRate" , "0" , FCVAR_RELEASE, "Time in seconds between each server auto-reload (disabled if null).", true, 0.f, false, 0.f, nullptr, nullptr); sv_simulateBots = ConVar::StaticCreate("sv_simulateBots", "1", FCVAR_RELEASE, "Simulate user commands for bots on the server.", true, 0.f, false, 0.f, nullptr, nullptr);