mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
This patch splits host logic from CServerListManager. CServerListManager is actually meant for the client to manage the server list to which the client could connect to. The hosting logic has been moved to the new CServerHostManager class. Previously, we stored all the hosting details in CServerListManager, with connection criteria in CPylon, this data has been moved over to CServerHostManager as well. Previously, we also needed a mutex to access the server host data, function HostState_KeepAlive() has been refactored to the point this mutex is no longer necessary as the only threaded process is the actual request, the rest is being applied in the main thread. We also now only construct a NetGameServer_t struct if we actually plan to host. Access to CPylon::m_Language is now also protected by a mutex, as the change callback of cvar 'language' and the threaded method 'CPylon::QueryServer()' are competing for access.
626 lines
22 KiB
C++
626 lines
22 KiB
C++
//=============================================================================//
|
|
//
|
|
// Purpose: Implementation of the pylon client.
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
|
|
#include <core/stdafx.h>
|
|
#include <tier1/cvar.h>
|
|
#include <tier2/curlutils.h>
|
|
#include <networksystem/pylon.h>
|
|
#include <engine/server/server.h>
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Console variables
|
|
//-----------------------------------------------------------------------------
|
|
ConVar pylon_matchmaking_hostname("pylon_matchmaking_hostname", "ms.r5reloaded.com", FCVAR_RELEASE | FCVAR_ACCESSIBLE_FROM_THREADS, "Holds the pylon matchmaking hostname");
|
|
ConVar pylon_host_update_interval("pylon_host_update_interval", "5", FCVAR_RELEASE | FCVAR_ACCESSIBLE_FROM_THREADS, "Length of time in seconds between each status update interval to master server", true, 5.f, false, 0.f);
|
|
ConVar pylon_showdebuginfo("pylon_showdebuginfo", "0", FCVAR_RELEASE | FCVAR_ACCESSIBLE_FROM_THREADS, "Shows debug output for pylon");
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: checks if the server listing fields are valid.
|
|
// Input : &value -
|
|
// Output : true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
static bool IsServerListingValid(const rapidjson::Value& value)
|
|
{
|
|
if (value.HasMember("name") && value["name"].IsString() &&
|
|
value.HasMember("description") && value["description"].IsString() &&
|
|
value.HasMember("hidden") && value["hidden"].IsBool() &&
|
|
value.HasMember("map") && value["map"].IsString() &&
|
|
value.HasMember("playlist") && value["playlist"].IsString() &&
|
|
value.HasMember("ip") && value["ip"].IsString() &&
|
|
value.HasMember("port") && value["port"].IsInt() &&
|
|
value.HasMember("key") && value["key"].IsString() &&
|
|
value.HasMember("checksum") && value["checksum"].IsUint() &&
|
|
value.HasMember("playerCount") && value["playerCount"].IsInt() &&
|
|
value.HasMember("maxPlayers") && value["maxPlayers"].IsInt())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: gets a vector of hosted servers.
|
|
// Input : &outMessage -
|
|
// Output : true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool CPylon::GetServerList(vector<NetGameServer_t>& outServerList, string& outMessage) const
|
|
{
|
|
rapidjson::Document requestJson;
|
|
requestJson.SetObject();
|
|
requestJson.AddMember("version", SDK_VERSION, requestJson.GetAllocator());
|
|
|
|
rapidjson::StringBuffer stringBuffer;
|
|
JSON_DocumentToBufferDeserialize(requestJson, stringBuffer);
|
|
|
|
rapidjson::Document responseJson;
|
|
CURLINFO status;
|
|
|
|
if (!SendRequest("/servers", requestJson, responseJson,
|
|
outMessage, status, "server list error"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!responseJson.HasMember("servers"))
|
|
{
|
|
outMessage = Format("Invalid response with status: %d", int(status));
|
|
return false;
|
|
}
|
|
|
|
const rapidjson::Value& servers = responseJson["servers"];
|
|
|
|
for (rapidjson::Value::ConstValueIterator itr = servers.Begin();
|
|
itr != servers.End(); ++itr)
|
|
{
|
|
const rapidjson::Value& obj = *itr;
|
|
|
|
if (!IsServerListingValid(obj))
|
|
{
|
|
// Missing details; skip this server listing.
|
|
continue;
|
|
}
|
|
|
|
outServerList.push_back(
|
|
NetGameServer_t
|
|
{
|
|
obj["name"].GetString(),
|
|
obj["description"].GetString(),
|
|
obj["hidden"].GetBool(),
|
|
obj["map"].GetString(),
|
|
obj["playlist"].GetString(),
|
|
obj["ip"].GetString(),
|
|
obj["port"].GetInt(),
|
|
obj["key"].GetString(),
|
|
obj["checksum"].GetUint(),
|
|
SDK_VERSION,
|
|
obj["playerCount"].GetInt(),
|
|
obj["maxPlayers"].GetInt(),
|
|
-1,
|
|
}
|
|
);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Gets the server by token string.
|
|
// Input : &outGameServer -
|
|
// &outMessage -
|
|
// &token -
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool CPylon::GetServerByToken(NetGameServer_t& outGameServer,
|
|
string& outMessage, const string& token) const
|
|
{
|
|
rapidjson::Document requestJson;
|
|
requestJson.SetObject();
|
|
|
|
rapidjson::Document::AllocatorType& allocator = requestJson.GetAllocator();
|
|
requestJson.AddMember("version", rapidjson::Value(SDK_VERSION, requestJson.GetAllocator()), allocator);
|
|
requestJson.AddMember("token", rapidjson::Value(token.c_str(), requestJson.GetAllocator()), allocator);
|
|
|
|
rapidjson::Document responseJson;
|
|
CURLINFO status;
|
|
|
|
if (!SendRequest("/server/byToken", requestJson, responseJson,
|
|
outMessage, status, "server not found"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!responseJson.HasMember("server"))
|
|
{
|
|
outMessage = Format("Invalid response with status: %d", int(status));
|
|
return false;
|
|
}
|
|
|
|
const rapidjson::Value& serverJson = responseJson["server"];
|
|
|
|
if (!IsServerListingValid(serverJson))
|
|
{
|
|
outMessage = Format("Invalid server listing data!");
|
|
return false;
|
|
}
|
|
|
|
outGameServer = NetGameServer_t
|
|
{
|
|
serverJson["name"].GetString(),
|
|
serverJson["description"].GetString(),
|
|
serverJson["hidden"].GetBool(),
|
|
serverJson["map"].GetString(),
|
|
serverJson["playlist"].GetString(),
|
|
serverJson["ip"].GetString(),
|
|
serverJson["port"].GetInt(),
|
|
serverJson["key"].GetString(),
|
|
serverJson["checksum"].GetUint(),
|
|
SDK_VERSION,
|
|
serverJson["playerCount"].GetInt(),
|
|
serverJson["maxPlayers"].GetInt(),
|
|
-1,
|
|
};
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Sends host server POST request.
|
|
// Input : &outMessage -
|
|
// &outToken -
|
|
// &netGameServer -
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool CPylon::PostServerHost(string& outMessage, string& outToken, string& outHostIp, const NetGameServer_t& netGameServer) const
|
|
{
|
|
rapidjson::Document requestJson;
|
|
requestJson.SetObject();
|
|
|
|
rapidjson::Document::AllocatorType& allocator = requestJson.GetAllocator();
|
|
|
|
requestJson.AddMember("name", rapidjson::Value(netGameServer.name.c_str(), allocator), allocator);
|
|
requestJson.AddMember("description", rapidjson::Value(netGameServer.description.c_str(), allocator), allocator);
|
|
requestJson.AddMember("hidden", netGameServer.hidden, allocator);
|
|
requestJson.AddMember("map", rapidjson::Value(netGameServer.map.c_str(), allocator), allocator);
|
|
requestJson.AddMember("playlist", rapidjson::Value(netGameServer.playlist.c_str(), allocator), allocator);
|
|
requestJson.AddMember("ip", rapidjson::Value(netGameServer.address.c_str(), allocator), allocator);
|
|
requestJson.AddMember("port", netGameServer.port, allocator);
|
|
requestJson.AddMember("key", rapidjson::Value(netGameServer.netKey.c_str(), allocator), allocator);
|
|
requestJson.AddMember("checksum", netGameServer.checksum, allocator);
|
|
requestJson.AddMember("version", rapidjson::Value(netGameServer.versionId.c_str(), allocator), allocator);
|
|
requestJson.AddMember("playerCount", netGameServer.numPlayers, allocator);
|
|
requestJson.AddMember("maxPlayers", netGameServer.maxPlayers, allocator);
|
|
requestJson.AddMember("timeStamp", netGameServer.timeStamp, allocator);
|
|
|
|
rapidjson::Document responseJson;
|
|
CURLINFO status;
|
|
|
|
if (!SendRequest("/servers/add", requestJson, responseJson, outMessage, status, "server host error"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (netGameServer.hidden)
|
|
{
|
|
if (!responseJson.HasMember("token") || !responseJson["token"].IsString())
|
|
{
|
|
outMessage = Format("Invalid response with status: %d", int(status));
|
|
outToken.clear();
|
|
return false;
|
|
}
|
|
|
|
outToken = responseJson["token"].GetString();
|
|
}
|
|
|
|
if (responseJson.HasMember("ip") && responseJson["ip"].IsString() &&
|
|
responseJson.HasMember("port") && responseJson["port"].IsInt())
|
|
{
|
|
outHostIp = Format("[%s]:%i",
|
|
responseJson["ip"].GetString(), responseJson["port"].GetInt());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Checks a list of clients for their banned status.
|
|
// Input : &inBannedVec -
|
|
// &outBannedVec -
|
|
// Output : True on success, false otherwise.
|
|
//-----------------------------------------------------------------------------
|
|
bool CPylon::GetBannedList(const CBanSystem::BannedList_t& inBannedVec, CBanSystem::BannedList_t& outBannedVec) const
|
|
{
|
|
rapidjson::Document requestJson;
|
|
requestJson.SetObject();
|
|
|
|
rapidjson::Value playersArray(rapidjson::kArrayType);
|
|
|
|
rapidjson::Document::AllocatorType& allocator = requestJson.GetAllocator();
|
|
|
|
FOR_EACH_VEC(inBannedVec, i)
|
|
{
|
|
const CBanSystem::Banned_t& banned = inBannedVec[i];
|
|
|
|
rapidjson::Value player(rapidjson::kObjectType);
|
|
player.AddMember("id", banned.m_NucleusID, allocator);
|
|
player.AddMember("ip", rapidjson::Value(banned.m_Address.String(), allocator), allocator);
|
|
playersArray.PushBack(player, allocator);
|
|
}
|
|
|
|
requestJson.AddMember("players", playersArray, allocator);
|
|
|
|
rapidjson::Document responseJson;
|
|
|
|
string outMessage;
|
|
CURLINFO status;
|
|
|
|
if (!SendRequest("/banlist/bulkCheck", requestJson, responseJson, outMessage, status, "banned bulk check error"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!responseJson.HasMember("bannedPlayers") || !responseJson["bannedPlayers"].IsArray())
|
|
{
|
|
outMessage = Format("Invalid response with status: %d", int(status));
|
|
return false;
|
|
}
|
|
|
|
const rapidjson::Value& bannedPlayers = responseJson["bannedPlayers"];
|
|
for (const rapidjson::Value& obj : bannedPlayers.GetArray())
|
|
{
|
|
CBanSystem::Banned_t banned(
|
|
obj.HasMember("reason") ? obj["reason"].GetString() : "#DISCONNECT_BANNED",
|
|
obj.HasMember("id") && obj["id"].IsUint64() ? obj["id"].GetUint64() : NucleusID_t(NULL)
|
|
);
|
|
outBannedVec.AddToTail(banned);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Checks if client is banned on the comp server.
|
|
// Input : &ipAddress -
|
|
// nucleusId -
|
|
// &outReason - <- contains banned reason if any.
|
|
// Output : True if banned, false if not banned.
|
|
//-----------------------------------------------------------------------------
|
|
bool CPylon::CheckForBan(const string& ipAddress, const uint64_t nucleusId, const string& personaName, string& outReason) const
|
|
{
|
|
rapidjson::Document requestJson;
|
|
requestJson.SetObject();
|
|
|
|
rapidjson::Document::AllocatorType& allocator = requestJson.GetAllocator();
|
|
|
|
requestJson.AddMember("name", rapidjson::Value(personaName.c_str(), allocator), allocator);
|
|
requestJson.AddMember("id", nucleusId, allocator);
|
|
requestJson.AddMember("ip", rapidjson::Value(ipAddress.c_str(), allocator), allocator);
|
|
|
|
rapidjson::Document responseJson;
|
|
string outMessage;
|
|
CURLINFO status;
|
|
|
|
if (!SendRequest("/banlist/isBanned", requestJson, responseJson, outMessage, status, "banned check error"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (responseJson.HasMember("banned") && responseJson["banned"].IsBool())
|
|
{
|
|
if (responseJson["banned"].GetBool())
|
|
{
|
|
outReason = responseJson.HasMember("reason") ? responseJson["reason"].GetString() : "#DISCONNECT_BANNED";
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: authenticate for 'this' particular connection.
|
|
// Input : nucleusId -
|
|
// *ipAddress -
|
|
// *authCode -
|
|
// &outToken -
|
|
// &outMessage -
|
|
// Output : true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool CPylon::AuthForConnection(const uint64_t nucleusId, const char* ipAddress,
|
|
const char* authCode, string& outToken, string& outMessage) const
|
|
{
|
|
rapidjson::Document requestJson;
|
|
requestJson.SetObject();
|
|
|
|
rapidjson::Document::AllocatorType& allocator = requestJson.GetAllocator();
|
|
|
|
requestJson.AddMember("id", nucleusId, allocator);
|
|
requestJson.AddMember("ip", rapidjson::Value(ipAddress, allocator), allocator);
|
|
requestJson.AddMember("code", rapidjson::Value(authCode, allocator), allocator);
|
|
|
|
rapidjson::Document responseJson;
|
|
|
|
CURLINFO status;
|
|
|
|
if (!SendRequest("/client/authenticate", requestJson, responseJson, outMessage, status, "origin auth error"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (responseJson.HasMember("token") && responseJson["token"].IsString())
|
|
{
|
|
outToken = responseJson["token"].GetString();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: checks if the EULA response fields are valid.
|
|
// Input : &doc -
|
|
// Output : true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
static bool ValidateEULAData(const rapidjson::Document& doc)
|
|
{
|
|
if (!doc.HasMember("data") || !doc["data"].IsObject())
|
|
return false;
|
|
|
|
const rapidjson::Value& data = doc["data"];
|
|
|
|
if (!data.HasMember("version") || !data["version"].IsInt())
|
|
return false;
|
|
|
|
if (!data.HasMember("lang") || !data["lang"].IsString())
|
|
return false;
|
|
|
|
if (!data.HasMember("contents") || !data["contents"].IsString())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: checks if the accepted EULA is up to date.
|
|
// Output : true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
static bool IsEULAUpToDate()
|
|
{
|
|
return (eula_version_accepted->GetInt() == eula_version->GetInt());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Gets the EULA from master server.
|
|
// Input : &outData -
|
|
// &outMessage -
|
|
// Output : True on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool CPylon::GetEULA(MSEulaData_t& outData, string& outMessage) const
|
|
{
|
|
rapidjson::Document requestJson;
|
|
requestJson.SetObject();
|
|
|
|
rapidjson::Document responseJson;
|
|
CURLINFO status;
|
|
|
|
if (!SendRequest("/eula", requestJson, responseJson, outMessage, status, "eula fetch error", false))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!ValidateEULAData(responseJson))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const rapidjson::Value& data = responseJson["data"];
|
|
|
|
outData.version = data["version"].GetInt();
|
|
outData.language = data["lang"].GetString();
|
|
outData.contents = data["contents"].GetString();
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Sends request to Pylon Master Server.
|
|
// Input : *endpoint -
|
|
// &requestJson -
|
|
// &responseJson -
|
|
// &outMessage -
|
|
// &status -
|
|
// checkEula -
|
|
// Output : True on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool CPylon::SendRequest(const char* endpoint, const rapidjson::Document& requestJson,
|
|
rapidjson::Document& responseJson, string& outMessage, CURLINFO& status,
|
|
const char* errorText, const bool checkEula) const
|
|
{
|
|
if (!IsDedicated() && !IsEULAUpToDate() && checkEula)
|
|
{
|
|
outMessage = "EULA not accepted";
|
|
return false;
|
|
}
|
|
|
|
rapidjson::StringBuffer stringBuffer;
|
|
JSON_DocumentToBufferDeserialize(requestJson, stringBuffer);
|
|
|
|
string responseBody;
|
|
if (!QueryServer(endpoint, stringBuffer.GetString(), responseBody, outMessage, status))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (status == 200) // STATUS_OK
|
|
{
|
|
responseJson.Parse(responseBody.c_str());
|
|
|
|
if (responseJson.HasParseError())
|
|
{
|
|
Warning(eDLL_T::ENGINE, "%s: JSON parse error at position %zu: %s\n", __FUNCTION__,
|
|
responseJson.GetErrorOffset(), rapidjson::GetParseError_En(responseJson.GetParseError()));
|
|
|
|
return false;
|
|
}
|
|
|
|
if (!responseJson.IsObject())
|
|
{
|
|
Warning(eDLL_T::ENGINE, "%s: JSON root was not an object\n", __FUNCTION__);
|
|
return false;
|
|
}
|
|
|
|
if (pylon_showdebuginfo.GetBool())
|
|
{
|
|
LogBody(responseJson);
|
|
}
|
|
|
|
if (responseJson.HasMember("success") &&
|
|
responseJson["success"].IsBool() &&
|
|
responseJson["success"].GetBool())
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
ExtractError(responseJson, outMessage, status);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ExtractError(responseBody, outMessage, status, errorText);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Sends query to master server.
|
|
// Input : *endpoint -
|
|
// *request -
|
|
// &outResponse -
|
|
// &outMessage - <- contains an error message on failure.
|
|
// &outStatus -
|
|
// Output : True on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool CPylon::QueryServer(const char* endpoint, const char* request,
|
|
string& outResponse, string& outMessage, CURLINFO& outStatus) const
|
|
{
|
|
const bool showDebug = pylon_showdebuginfo.GetBool();
|
|
const char* hostName = pylon_matchmaking_hostname.GetString();
|
|
|
|
if (showDebug)
|
|
{
|
|
Msg(eDLL_T::ENGINE, "Sending request to '%s' with endpoint '%s':\n%s\n",
|
|
hostName, endpoint, request);
|
|
}
|
|
|
|
string finalUrl;
|
|
CURLFormatUrl(finalUrl, hostName, endpoint);
|
|
finalUrl += Format("?language=%s", this->GetLanguage().c_str());
|
|
|
|
CURLParams params;
|
|
|
|
params.writeFunction = CURLWriteStringCallback;
|
|
params.timeout = curl_timeout.GetInt();
|
|
params.verifyPeer = ssl_verify_peer.GetBool();
|
|
params.verbose = curl_debug.GetBool();
|
|
|
|
curl_slist* sList = nullptr;
|
|
CURL* curl = CURLInitRequest(finalUrl.c_str(), request, outResponse, sList, params);
|
|
if (!curl)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
CURLcode res = CURLSubmitRequest(curl, sList);
|
|
if (!CURLHandleError(curl, res, outMessage,
|
|
!IsDedicated(/* Errors are already shown for dedicated! */)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
outStatus = CURLRetrieveInfo(curl);
|
|
|
|
if (showDebug)
|
|
{
|
|
Msg(eDLL_T::ENGINE, "Host '%s' replied with status: '%d'\n",
|
|
hostName, outStatus);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Extracts the error from the result json.
|
|
// Input : &resultJson -
|
|
// &outMessage -
|
|
// status -
|
|
// *errorText -
|
|
//-----------------------------------------------------------------------------
|
|
void CPylon::ExtractError(const rapidjson::Document& resultJson, string& outMessage,
|
|
CURLINFO status, const char* errorText) const
|
|
{
|
|
if (resultJson.IsObject() && resultJson.HasMember("error") &&
|
|
resultJson["error"].IsString())
|
|
{
|
|
outMessage = resultJson["error"].GetString();
|
|
}
|
|
else
|
|
{
|
|
if (!errorText)
|
|
{
|
|
errorText = "unknown error";
|
|
}
|
|
|
|
outMessage = Format("Failed with status: %d (%s)",
|
|
int(status), errorText);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Extracts the error from the response buffer.
|
|
// Input : &response -
|
|
// &outMessage -
|
|
// status -
|
|
// *errorText -
|
|
//-----------------------------------------------------------------------------
|
|
void CPylon::ExtractError(const string& response, string& outMessage,
|
|
CURLINFO status, const char* errorText) const
|
|
{
|
|
if (!response.empty())
|
|
{
|
|
rapidjson::Document resultBody;
|
|
resultBody.Parse(response.c_str());
|
|
|
|
ExtractError(resultBody, outMessage, status, errorText);
|
|
}
|
|
else if (status)
|
|
{
|
|
outMessage = Format("Failed server query: %d", int(status));
|
|
}
|
|
else
|
|
{
|
|
outMessage = Format("Failed to reach server: %s",
|
|
"connection timed out");
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Logs the response body if debug is enabled.
|
|
// Input : &responseJson -
|
|
//-----------------------------------------------------------------------------
|
|
void CPylon::LogBody(const rapidjson::Document& responseJson) const
|
|
{
|
|
rapidjson::StringBuffer stringBuffer;
|
|
|
|
JSON_DocumentToBufferDeserialize(responseJson, stringBuffer);
|
|
Msg(eDLL_T::ENGINE, "\n%s\n", stringBuffer.GetString());
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
CPylon g_MasterServer;
|