mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
NetworkSystem: significant performance improvements
Now uses new JSON member lookup utilities implemented in commit f6e93ee457876ce869ce04067fe9344ff81083ca. All lookups (typically 3 per member) have been reduced to 1 by caching and reusing all value pointers or iterators. Also added error strings for 2 missing error cases in CPylon::GetEULA(), previously it would only set the error string during fetch error, but not when the data is missing or the schema is incorrect.
This commit is contained in:
parent
f6e93ee457
commit
a5e05d3996
@ -7,6 +7,7 @@
|
||||
|
||||
#include "core/stdafx.h"
|
||||
#include "tier1/strtools.h"
|
||||
#include "tier2/jsonutils.h"
|
||||
#include "engine/net.h"
|
||||
#include "engine/server/server.h"
|
||||
#include "engine/client/client.h"
|
||||
@ -47,25 +48,33 @@ void CBanSystem::LoadList(void)
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t nTotalBans = 0;
|
||||
if (document.HasMember("totalBans") && document["totalBans"].IsUint64())
|
||||
int nTotalBans = 0;
|
||||
|
||||
if (!JSON_GetValue(document, "totalBans", JSONFieldType_e::kSint32, nTotalBans))
|
||||
{
|
||||
nTotalBans = document["totalBans"].GetUint64();
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint64_t i = 0; i < nTotalBans; i++)
|
||||
for (int i = 0; i < nTotalBans; i++)
|
||||
{
|
||||
char idx[64]; _ui64toa(i, idx, 10);
|
||||
char idx[12]; itoa(i, idx, 10);
|
||||
|
||||
if (document.HasMember(idx) && document[idx].IsObject())
|
||||
rapidjson::Value::ConstMemberIterator entryIt;
|
||||
|
||||
if (JSON_GetIterator(document, idx, JSONFieldType_e::kObject, entryIt))
|
||||
{
|
||||
const rapidjson::Value& entry = document[idx];
|
||||
if (entry.HasMember("ipAddress") && entry["ipAddress"].IsString() &&
|
||||
entry.HasMember("nucleusId") && entry["nucleusId"].IsUint64())
|
||||
const rapidjson::Value& entry = entryIt->value;
|
||||
|
||||
const char* ipAddress = nullptr;
|
||||
NucleusID_t nucleusId = NULL;
|
||||
|
||||
if (JSON_GetValue(entry, "ipAddress", JSONFieldType_e::kString, ipAddress) &&
|
||||
JSON_GetValue(entry, "nucleusId", JSONFieldType_e::kUint64, nucleusId))
|
||||
{
|
||||
Banned_t banned;
|
||||
banned.m_Address = entry["ipAddress"].GetString();
|
||||
banned.m_NucleusID = entry["nucleusId"].GetUint64();
|
||||
|
||||
banned.m_Address = ipAddress;
|
||||
banned.m_NucleusID = nucleusId;
|
||||
|
||||
m_BannedList.AddToTail(banned);
|
||||
}
|
||||
@ -93,9 +102,10 @@ void CBanSystem::SaveList(void) const
|
||||
FOR_EACH_VEC(m_BannedList, i)
|
||||
{
|
||||
const Banned_t& banned = m_BannedList[i];
|
||||
char idx[64]; _ui64toa(i, idx, 10);
|
||||
char idx[12]; itoa(i, idx, 10);
|
||||
|
||||
rapidjson::Value obj(rapidjson::kObjectType);
|
||||
|
||||
obj.AddMember("ipAddress", rapidjson::Value(banned.m_Address.String(), allocator), allocator);
|
||||
obj.AddMember("nucleusId", banned.m_NucleusID, allocator);
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <core/stdafx.h>
|
||||
#include <tier1/cvar.h>
|
||||
#include <tier2/curlutils.h>
|
||||
#include <tier2/jsonutils.h>
|
||||
#include <networksystem/pylon.h>
|
||||
#include <engine/server/server.h>
|
||||
|
||||
@ -19,23 +20,24 @@ ConVar pylon_host_update_interval("pylon_host_update_interval", "5", FCVAR_RELEA
|
||||
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.
|
||||
// Purpose: checks if server listing fields are valid, and sets outGameServer
|
||||
// Input : &value -
|
||||
// &outGameServer -
|
||||
// Output : true on success, false on failure.
|
||||
//-----------------------------------------------------------------------------
|
||||
static bool IsServerListingValid(const rapidjson::Value& value)
|
||||
static bool GetServerListingFromJSON(const rapidjson::Value& value, NetGameServer_t& outGameServer)
|
||||
{
|
||||
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("numPlayers") && value["numPlayers"].IsInt() &&
|
||||
value.HasMember("maxPlayers") && value["maxPlayers"].IsInt())
|
||||
if (JSON_GetValue(value, "name", JSONFieldType_e::kString, outGameServer.name) &&
|
||||
JSON_GetValue(value, "description", JSONFieldType_e::kString, outGameServer.description) &&
|
||||
JSON_GetValue(value, "hidden", JSONFieldType_e::kBool, outGameServer.hidden) &&
|
||||
JSON_GetValue(value, "map", JSONFieldType_e::kString, outGameServer.map) &&
|
||||
JSON_GetValue(value, "playlist", JSONFieldType_e::kString, outGameServer.playlist) &&
|
||||
JSON_GetValue(value, "ip", JSONFieldType_e::kString, outGameServer.address) &&
|
||||
JSON_GetValue(value, "port", JSONFieldType_e::kSint32, outGameServer.port) &&
|
||||
JSON_GetValue(value, "key", JSONFieldType_e::kString, outGameServer.netKey) &&
|
||||
JSON_GetValue(value, "checksum", JSONFieldType_e::kUint32, outGameServer.checksum) &&
|
||||
JSON_GetValue(value, "numPlayers", JSONFieldType_e::kSint32, outGameServer.numPlayers) &&
|
||||
JSON_GetValue(value, "maxPlayers", JSONFieldType_e::kSint32, outGameServer.maxPlayers))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -66,43 +68,27 @@ bool CPylon::GetServerList(vector<NetGameServer_t>& outServerList, string& outMe
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!responseJson.HasMember("servers"))
|
||||
rapidjson::Document::ConstMemberIterator serversIt;
|
||||
|
||||
if (!JSON_GetIterator(responseJson, "servers", JSONFieldType_e::kArray, serversIt))
|
||||
{
|
||||
outMessage = Format("Invalid response with status: %d", int(status));
|
||||
return false;
|
||||
}
|
||||
|
||||
const rapidjson::Value& servers = responseJson["servers"];
|
||||
const rapidjson::Value::ConstArray serverArray = serversIt->value.GetArray();
|
||||
|
||||
for (rapidjson::Value::ConstValueIterator itr = servers.Begin();
|
||||
itr != servers.End(); ++itr)
|
||||
for (const rapidjson::Value& obj : serverArray)
|
||||
{
|
||||
const rapidjson::Value& obj = *itr;
|
||||
NetGameServer_t gameServer;
|
||||
|
||||
if (!IsServerListingValid(obj))
|
||||
if (!GetServerListingFromJSON(obj, gameServer))
|
||||
{
|
||||
// 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["numPlayers"].GetInt(),
|
||||
obj["maxPlayers"].GetInt(),
|
||||
-1,
|
||||
}
|
||||
);
|
||||
outServerList.push_back(gameServer);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -134,37 +120,22 @@ bool CPylon::GetServerByToken(NetGameServer_t& outGameServer,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!responseJson.HasMember("server"))
|
||||
rapidjson::Document::ConstMemberIterator serversIt;
|
||||
|
||||
if (!JSON_GetIterator(responseJson, "servers", JSONFieldType_e::kArray, serversIt))
|
||||
{
|
||||
outMessage = Format("Invalid response with status: %d", int(status));
|
||||
return false;
|
||||
}
|
||||
|
||||
const rapidjson::Value& serverJson = responseJson["server"];
|
||||
const rapidjson::Value& serverJson = serversIt->value;
|
||||
|
||||
if (!IsServerListingValid(serverJson))
|
||||
if (!GetServerListingFromJSON(serverJson, outGameServer))
|
||||
{
|
||||
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["numPlayers"].GetInt(),
|
||||
serverJson["maxPlayers"].GetInt(),
|
||||
-1,
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -192,7 +163,7 @@ bool CPylon::PostServerHost(string& outMessage, string& outToken, string& outHos
|
||||
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("numPlayers", netGameServer.numPlayers, allocator);
|
||||
requestJson.AddMember("numPlayers", netGameServer.numPlayers, allocator);
|
||||
requestJson.AddMember("maxPlayers", netGameServer.maxPlayers, allocator);
|
||||
requestJson.AddMember("timeStamp", netGameServer.timeStamp, allocator);
|
||||
|
||||
@ -206,21 +177,25 @@ bool CPylon::PostServerHost(string& outMessage, string& outToken, string& outHos
|
||||
|
||||
if (netGameServer.hidden)
|
||||
{
|
||||
if (!responseJson.HasMember("token") || !responseJson["token"].IsString())
|
||||
const char* token = nullptr;
|
||||
|
||||
if (!JSON_GetValue(responseJson, "token", JSONFieldType_e::kString, token))
|
||||
{
|
||||
outMessage = Format("Invalid response with status: %d", int(status));
|
||||
outToken.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
outToken = responseJson["token"].GetString();
|
||||
outToken = token;
|
||||
}
|
||||
|
||||
if (responseJson.HasMember("ip") && responseJson["ip"].IsString() &&
|
||||
responseJson.HasMember("port") && responseJson["port"].IsInt())
|
||||
const char* ip = nullptr;
|
||||
int port = 0;
|
||||
|
||||
if (JSON_GetValue(responseJson, "ip", JSONFieldType_e::kString, ip) &&
|
||||
JSON_GetValue(responseJson, "port", JSONFieldType_e::kSint32, port))
|
||||
{
|
||||
outHostIp = Format("[%s]:%i",
|
||||
responseJson["ip"].GetString(), responseJson["port"].GetInt());
|
||||
outHostIp = Format("[%s]:%i", ip, port);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -244,10 +219,11 @@ bool CPylon::GetBannedList(const CBanSystem::BannedList_t& inBannedVec, CBanSyst
|
||||
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);
|
||||
}
|
||||
|
||||
@ -263,19 +239,25 @@ bool CPylon::GetBannedList(const CBanSystem::BannedList_t& inBannedVec, CBanSyst
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!responseJson.HasMember("bannedPlayers") || !responseJson["bannedPlayers"].IsArray())
|
||||
rapidjson::Value::ConstMemberIterator bannedPlayersIt;
|
||||
|
||||
if (!JSON_GetIterator(responseJson, "bannedPlayers", JSONFieldType_e::kArray, bannedPlayersIt))
|
||||
{
|
||||
outMessage = Format("Invalid response with status: %d", int(status));
|
||||
return false;
|
||||
}
|
||||
|
||||
const rapidjson::Value& bannedPlayers = responseJson["bannedPlayers"];
|
||||
for (const rapidjson::Value& obj : bannedPlayers.GetArray())
|
||||
const rapidjson::Value::ConstArray bannedPlayers = bannedPlayersIt->value.GetArray();
|
||||
|
||||
for (const rapidjson::Value& obj : bannedPlayers)
|
||||
{
|
||||
CBanSystem::Banned_t banned(
|
||||
obj.HasMember("reason") ? obj["reason"].GetString() : "#DISCONNECT_BANNED",
|
||||
obj.HasMember("id") && obj["id"].IsUint64() ? obj["id"].GetUint64() : NucleusID_t(NULL)
|
||||
);
|
||||
const char* reason = nullptr;
|
||||
JSON_GetValue(obj, "reason", JSONFieldType_e::kString, reason);
|
||||
|
||||
NucleusID_t nuc = NULL;
|
||||
JSON_GetValue(obj, "id", JSONFieldType_e::kUint64, nuc);
|
||||
|
||||
CBanSystem::Banned_t banned(reason ? reason : "#DISCONNECT_BANNED", nuc);
|
||||
outBannedVec.AddToTail(banned);
|
||||
}
|
||||
|
||||
@ -309,11 +291,18 @@ bool CPylon::CheckForBan(const string& ipAddress, const uint64_t nucleusId, cons
|
||||
return false;
|
||||
}
|
||||
|
||||
if (responseJson.HasMember("banned") && responseJson["banned"].IsBool())
|
||||
bool isBanned = false;
|
||||
|
||||
if (JSON_GetValue(responseJson, "banned", JSONFieldType_e::kBool, isBanned))
|
||||
{
|
||||
if (responseJson["banned"].GetBool())
|
||||
if (isBanned)
|
||||
{
|
||||
outReason = responseJson.HasMember("reason") ? responseJson["reason"].GetString() : "#DISCONNECT_BANNED";
|
||||
const char* reason = nullptr;
|
||||
|
||||
outReason = JSON_GetValue(responseJson, "reason", JSONFieldType_e::kString, reason)
|
||||
? reason
|
||||
: "#DISCONNECT_BANNED";
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -351,39 +340,17 @@ bool CPylon::AuthForConnection(const uint64_t nucleusId, const char* ipAddress,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (responseJson.HasMember("token") && responseJson["token"].IsString())
|
||||
const char* token = nullptr;
|
||||
|
||||
if (JSON_GetValue(responseJson, "token", JSONFieldType_e::kString, token))
|
||||
{
|
||||
outToken = responseJson["token"].GetString();
|
||||
outToken = token;
|
||||
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.
|
||||
@ -412,16 +379,32 @@ bool CPylon::GetEULA(MSEulaData_t& outData, string& outMessage) const
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ValidateEULAData(responseJson))
|
||||
rapidjson::Document::ConstMemberIterator serversIt;
|
||||
|
||||
if (!JSON_GetIterator(responseJson, "data", JSONFieldType_e::kObject, serversIt))
|
||||
{
|
||||
outMessage = "missing or invalid data";
|
||||
return false;
|
||||
}
|
||||
|
||||
const rapidjson::Value& data = responseJson["data"];
|
||||
const rapidjson::Value& data = serversIt->value;
|
||||
|
||||
outData.version = data["version"].GetInt();
|
||||
outData.language = data["lang"].GetString();
|
||||
outData.contents = data["contents"].GetString();
|
||||
int version = 0;
|
||||
const char* language = nullptr;
|
||||
const char* contents = nullptr;
|
||||
|
||||
// check if the EULA response fields are valid.
|
||||
if (!JSON_GetValue(data, "version", JSONFieldType_e::kSint32, version) ||
|
||||
!JSON_GetValue(data, "language", JSONFieldType_e::kString, language) ||
|
||||
!JSON_GetValue(data, "contents", JSONFieldType_e::kString, contents))
|
||||
{
|
||||
outMessage = "schema is invalid";
|
||||
return false;
|
||||
}
|
||||
|
||||
outData.version = version;
|
||||
outData.language = language;
|
||||
outData.contents = contents;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -478,9 +461,10 @@ bool CPylon::SendRequest(const char* endpoint, const rapidjson::Document& reques
|
||||
LogBody(responseJson);
|
||||
}
|
||||
|
||||
if (responseJson.HasMember("success") &&
|
||||
responseJson["success"].IsBool() &&
|
||||
responseJson["success"].GetBool())
|
||||
bool success = false;
|
||||
|
||||
if (JSON_GetValue(responseJson, "success", JSONFieldType_e::kBool, success)
|
||||
&& success)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -564,10 +548,12 @@ bool CPylon::QueryServer(const char* endpoint, const char* request,
|
||||
void CPylon::ExtractError(const rapidjson::Document& resultJson, string& outMessage,
|
||||
CURLINFO status, const char* errorText) const
|
||||
{
|
||||
if (resultJson.IsObject() && resultJson.HasMember("error") &&
|
||||
resultJson["error"].IsString())
|
||||
const char* error = nullptr;
|
||||
|
||||
if (resultJson.IsObject() &&
|
||||
JSON_GetValue(resultJson, "error", JSONFieldType_e::kString, error))
|
||||
{
|
||||
outMessage = resultJson["error"].GetString();
|
||||
outMessage = error;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -89,8 +89,6 @@ string FormatBytes(size_t nBytes);
|
||||
string FormatV(const char* szFormat, va_list args);
|
||||
string Format(const char* szFormat, ...);
|
||||
|
||||
void JSON_DocumentToBufferDeserialize(const rapidjson::Document& document, rapidjson::StringBuffer& buffer, unsigned int indent = 4);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Array
|
||||
template <typename Iter, typename Compare>
|
||||
|
@ -1119,16 +1119,6 @@ string Format(const char* szFormat, ...)
|
||||
return result;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// For dumping a json document to a string buffer.
|
||||
void JSON_DocumentToBufferDeserialize(const rapidjson::Document& document, rapidjson::StringBuffer& buffer, unsigned int indent)
|
||||
{
|
||||
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
|
||||
|
||||
writer.SetIndent(' ', indent);
|
||||
document.Accept(writer);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// For comparing two IPv6 addresses.
|
||||
int CompareIPv6(const IN6_ADDR& ipA, const IN6_ADDR& ipB)
|
||||
|
@ -7,6 +7,7 @@ add_sources( SOURCE_GROUP "Utility"
|
||||
"cryptutils.cpp"
|
||||
"curlutils.cpp"
|
||||
"fileutils.cpp"
|
||||
"jsonutils.cpp"
|
||||
"meshutils.cpp"
|
||||
"renderutils.cpp"
|
||||
"socketcreator.cpp"
|
||||
|
Loading…
x
Reference in New Issue
Block a user