From 1b821c10592c8cab05d47c518e9ee5f3242c5b94 Mon Sep 17 00:00:00 2001 From: Simon Jarrett Date: Wed, 12 Aug 2020 15:47:50 +0100 Subject: [PATCH] Refactor ServerList to use new JSON library --- src/openrct2/network/ServerList.cpp | 129 +++++++++++++++------------- src/openrct2/network/ServerList.h | 11 ++- 2 files changed, 76 insertions(+), 64 deletions(-) diff --git a/src/openrct2/network/ServerList.cpp b/src/openrct2/network/ServerList.cpp index e0b73491c6..d70c76a200 100644 --- a/src/openrct2/network/ServerList.cpp +++ b/src/openrct2/network/ServerList.cpp @@ -15,6 +15,7 @@ # include "../PlatformEnvironment.h" # include "../config/Config.h" # include "../core/FileStream.hpp" +# include "../core/Guard.hpp" # include "../core/Http.h" # include "../core/Json.hpp" # include "../core/Memory.hpp" @@ -70,35 +71,42 @@ bool ServerListEntry::IsVersionValid() const return Version.empty() || Version == network_get_version(); } -std::optional ServerListEntry::FromJson(const json_t* server) +std::optional ServerListEntry::FromJson(json_t& server) { - auto port = json_object_get(server, "port"); - auto name = json_object_get(server, "name"); - auto description = json_object_get(server, "description"); - auto requiresPassword = json_object_get(server, "requiresPassword"); - auto version = json_object_get(server, "version"); - auto players = json_object_get(server, "players"); - auto maxPlayers = json_object_get(server, "maxPlayers"); - auto ip = json_object_get(server, "ip"); - auto ip4 = json_object_get(ip, "v4"); - auto addressIp = json_array_get(ip4, 0); + Guard::Assert(server.is_object(), "ServerListEntry::FromJson expects parameter server to be object"); - if (name == nullptr || version == nullptr) + const auto port = Json::GetNumber(server["port"]); + const auto name = Json::GetString(server["name"]); + const auto description = Json::GetString(server["description"]); + const auto requiresPassword = Json::GetBoolean(server["requiresPassword"]); + const auto version = Json::GetString(server["version"]); + const auto players = Json::GetNumber(server["players"]); + const auto maxPlayers = Json::GetNumber(server["maxPlayers"]); + std::string ip; + // if server["ip"] or server["ip"]["v4"] are values, this will throw an exception, so check first + if (server["ip"].is_object() && server["ip"]["v4"].is_array()) + { + ip = Json::GetString(server["ip"]["v4"][0]); + } + + if (name.empty() || version.empty()) { log_verbose("Cowardly refusing to add server without name or version specified."); + return std::nullopt; } else { ServerListEntry entry; - entry.Address = String::StdFormat( - "%s:%d", json_string_value(addressIp), static_cast(json_integer_value(port))); - entry.Name = (name == nullptr ? "" : json_string_value(name)); - entry.Description = (description == nullptr ? "" : json_string_value(description)); - entry.Version = json_string_value(version); - entry.RequiresPassword = json_is_true(requiresPassword); - entry.Players = static_cast(json_integer_value(players)); - entry.MaxPlayers = static_cast(json_integer_value(maxPlayers)); + + entry.Address = ip + ":" + std::to_string(port); + entry.Name = name; + entry.Description = description; + entry.Version = version; + entry.RequiresPassword = requiresPassword; + entry.Players = players; + entry.MaxPlayers = maxPlayers; + return entry; } } @@ -263,20 +271,17 @@ std::future> ServerList::FetchLocalServerListAsync( log_verbose("Received %zu bytes back from %s", recievedLen, sender.c_str()); auto jinfo = Json::FromString(std::string_view(buffer)); - auto ip4 = json_array(); - json_array_append_new(ip4, json_string(sender.c_str())); - auto ip = json_object(); - json_object_set_new(ip, "v4", ip4); - json_object_set_new(jinfo, "ip", ip); - - auto entry = ServerListEntry::FromJson(jinfo); - if (entry.has_value()) + if (jinfo.is_object()) { - (*entry).Local = true; - entries.push_back(*entry); - } + jinfo["ip"] = { { "v4", { sender } } }; - json_decref(jinfo); + auto entry = ServerListEntry::FromJson(jinfo); + if (entry.has_value()) + { + (*entry).Local = true; + entries.push_back(*entry); + } + } } } catch (const std::exception& e) @@ -341,7 +346,7 @@ std::future> ServerList::FetchOnlineServerListAsync request.method = Http::Method::GET; request.header["Accept"] = "application/json"; Http::DoAsync(request, [p](Http::Response& response) -> void { - json_t* root{}; + json_t root; try { if (response.status != Http::Status::OK) @@ -350,46 +355,46 @@ std::future> ServerList::FetchOnlineServerListAsync } root = Json::FromString(response.body); - auto jsonStatus = json_object_get(root, "status"); - if (!json_is_number(jsonStatus)) + if (root.is_object()) { - throw MasterServerException(STR_SERVER_LIST_INVALID_RESPONSE_JSON_NUMBER); - } - - auto status = static_cast(json_integer_value(jsonStatus)); - if (status != 200) - { - throw MasterServerException(STR_SERVER_LIST_MASTER_SERVER_FAILED); - } - - auto jServers = json_object_get(root, "servers"); - if (!json_is_array(jServers)) - { - throw MasterServerException(STR_SERVER_LIST_INVALID_RESPONSE_JSON_ARRAY); - } - - std::vector entries; - auto count = json_array_size(jServers); - for (size_t i = 0; i < count; i++) - { - auto jServer = json_array_get(jServers, i); - if (json_is_object(jServer)) + auto jsonStatus = root["status"]; + if (!jsonStatus.is_number_integer()) { - auto entry = ServerListEntry::FromJson(jServer); - if (entry.has_value()) + throw MasterServerException(STR_SERVER_LIST_INVALID_RESPONSE_JSON_NUMBER); + } + + auto status = Json::GetNumber(jsonStatus); + if (status != 200) + { + throw MasterServerException(STR_SERVER_LIST_MASTER_SERVER_FAILED); + } + + auto jServers = root["servers"]; + if (!jServers.is_array()) + { + throw MasterServerException(STR_SERVER_LIST_INVALID_RESPONSE_JSON_ARRAY); + } + + std::vector entries; + for (auto& jServer : jServers) + { + if (jServer.is_object()) { - entries.push_back(*entry); + auto entry = ServerListEntry::FromJson(jServer); + if (entry.has_value()) + { + entries.push_back(*entry); + } } } - } - p->set_value(entries); + p->set_value(entries); + } } catch (...) { p->set_exception(std::current_exception()); } - json_decref(root); }); return f; # endif diff --git a/src/openrct2/network/ServerList.h b/src/openrct2/network/ServerList.h index 8eccdf783f..d4c1109557 100644 --- a/src/openrct2/network/ServerList.h +++ b/src/openrct2/network/ServerList.h @@ -10,6 +10,7 @@ #pragma once #include "../common.h" +#include "../core/Json.hpp" #include #include @@ -17,7 +18,6 @@ #include #include -struct json_t; struct INetworkEndpoint; struct ServerListEntry @@ -35,7 +35,14 @@ struct ServerListEntry int32_t CompareTo(const ServerListEntry& other) const; bool IsVersionValid() const; - static std::optional FromJson(const json_t* root); + /** + * Creates a ServerListEntry object from a JSON object + * + * @param json JSON data source - must be object type + * @return A NetworkGroup object + * @note json is deliberately left non-const: json_t behaviour changes when const + */ + static std::optional FromJson(json_t& server); }; class ServerList