diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..897542b9 --- /dev/null +++ b/.clang-format @@ -0,0 +1,4 @@ +--- +BasedOnStyle: Microsoft + +... diff --git a/CMakeLists.txt b/CMakeLists.txt index 9464c6ee..cd420baf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,10 +13,10 @@ set(CMAKE_PREFIX_PATH ${ROR_DEPENDENCY_DIR} ${CMAKE_PREFIX_PATH}) set(CMAKE_THREAD_PREFER_PTHREAD YES) find_package(Threads REQUIRED) -find_package(Angelscript) -find_package(jsoncpp REQUIRED) +find_package(Poco REQUIRED COMPONENTS Foundation JSON) find_package(SocketW REQUIRED) find_package(CURL) +find_package(Angelscript) cmake_dependent_option(RORSERVER_WITH_ANGELSCRIPT "Adds scripting support" ON "TARGET Angelscript::angelscript" OFF) cmake_dependent_option(RORSERVER_WITH_CURL "Adds CURL request support (needs AngelScript)" ON "TARGET CURL::libcurl" OFF) diff --git a/conanfile.py b/conanfile.py index 2865c957..fc556acb 100644 --- a/conanfile.py +++ b/conanfile.py @@ -7,13 +7,17 @@ class RoRServer(ConanFile): name = "RoRServer" settings = "os", "compiler", "build_type", "arch" generators = "CMakeToolchain", "CMakeDeps" + default_options = { + "poco*:enable_pagecompiler": True, + "poco*:enable_data_mysql": False, + } def layout(self): self.folders.generators = os.path.join(self.folders.build, "generators") def requirements(self): self.requires("angelscript/2.37.0") - self.requires("jsoncpp/1.9.5") + self.requires("poco/1.13.3") self.requires("openssl/3.3.2", override=True) self.requires("socketw/3.11.0@anotherfoxguy/stable") self.requires("libcurl/8.10.1") \ No newline at end of file diff --git a/source/server/CMakeLists.txt b/source/server/CMakeLists.txt index f4e8348d..eb56ccb2 100644 --- a/source/server/CMakeLists.txt +++ b/source/server/CMakeLists.txt @@ -20,7 +20,12 @@ if (RORSERVER_WITH_CURL) target_link_libraries(${PROJECT_NAME} PRIVATE CURL::libcurl) endif () -target_link_libraries(${PROJECT_NAME} PRIVATE Threads::Threads SocketW::SocketW jsoncpp_lib) +target_link_libraries(${PROJECT_NAME} PRIVATE + Poco::Foundation + Poco::JSON + Threads::Threads + SocketW::SocketW +) IF (WIN32) target_compile_definitions(${PROJECT_NAME} PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX) diff --git a/source/server/blacklist.cpp b/source/server/blacklist.cpp index 474501d6..f7707c2c 100644 --- a/source/server/blacklist.cpp +++ b/source/server/blacklist.cpp @@ -24,11 +24,21 @@ along with Rigs of Rods Server. If not, see . #include "sequencer.h" #include "utils.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace Poco; +using namespace Poco::JSON; +using namespace Poco::Dynamic; + #include -#include -Blacklist::Blacklist(Sequencer* database) - : m_database(database) +Blacklist::Blacklist(Sequencer *database) : m_database(database) { } @@ -38,31 +48,29 @@ void Blacklist::SaveBlacklistToFile() f.open(Config::getBlacklistFile(), std::ios::out); if (!f.is_open() || !f.good()) { - Logger::Log(LogLevel::LOG_WARN, - "Couldn't open the local blacklist file ('%s'). Bans were not saved.", - Config::getBlacklistFile().c_str()); + Logger::Log(LogLevel::LOG_WARN, "Couldn't open the local blacklist file ('%s'). Bans were not saved.", + Config::getBlacklistFile().c_str()); return; } - Json::Value j_bans(Json::arrayValue); + JSON::Array j_bans; std::vector bans = m_database->GetBanListCopy(); - for (ban_t& ban : bans) + for (ban_t &ban : bans) { - Json::Value j_ban(Json::objectValue); - j_ban["bid"] = ban.bid; + DynamicStruct j_ban; + j_ban["bid"] = ban.bid; j_ban["ip"] = ban.ip; j_ban["nickname"] = ban.nickname; j_ban["banned_by_nickname"] = ban.bannedby_nick; j_ban["message"] = ban.banmsg; - j_bans.append(j_ban); + j_bans.add(j_ban); } - Json::Value j_doc(Json::objectValue); + DynamicStruct j_doc; j_doc["bans"] = j_bans; - Json::StyledStreamWriter j_writer; - j_writer.write(f, j_doc); + Poco::JSON::Stringifier::stringify(j_doc, f, 1); } bool Blacklist::LoadBlacklistFromFile() @@ -71,8 +79,7 @@ bool Blacklist::LoadBlacklistFromFile() f.open(Config::getBlacklistFile(), std::ios::in); if (!f.is_open() || !f.good()) { - Logger::Log(LogLevel::LOG_WARN, - "Couldn't open the local blacklist file ('%s'). No bans were loaded.", + Logger::Log(LogLevel::LOG_WARN, "Couldn't open the local blacklist file ('%s'). No bans were loaded.", Config::getBlacklistFile().c_str()); return false; } @@ -80,32 +87,41 @@ bool Blacklist::LoadBlacklistFromFile() if (Utils::IsEmptyFile(f)) { f.close(); - Logger::Log(LogLevel::LOG_WARN, - "Local blacklist file ('%s') is empty.", - Config::getBlacklistFile().c_str()); + Logger::Log(LogLevel::LOG_WARN, "Local blacklist file ('%s') is empty.", Config::getBlacklistFile().c_str()); return false; } - Json::Value j_doc; - Json::Reader j_reader; - j_reader.parse(f, j_doc); - if (!j_reader.good()) + Parser parser; + Var result; + + try { - Logger::Log(LogLevel::LOG_WARN, - "Couldn't parse blacklist file, messages:\n%s", - j_reader.getFormattedErrorMessages()); + result = parser.parse(f); + } + catch (JSONException &jsonException) + { + Logger::Log(LogLevel::LOG_WARN, "Couldn't parse blacklist file, messages:\n%s", + jsonException.message().c_str()); return false; } - for (Json::Value& j_ban: j_doc["bans"]) + try { - m_database->RecordBan( - // ban IDs are reset to start at 1 on every start/restart - j_ban["ip"].asString(), - j_ban["nickname"].asString(), - j_ban["banned_by_nickname"].asString(), - j_ban["message"].asString()); + Poco::JSON::Object::Ptr root = result.extract(); + JSON::Array::Ptr bans = root->getArray("bans"); + + for (int i = 0; i < bans->size(); ++i) + { + auto j_ban = bans->getObject(i); + m_database->RecordBan(j_ban->getValue("ip"), + j_ban->getValue("nickname"), + j_ban->getValue("banned_by_nickname"), + j_ban->getValue("message")); + } + } + catch (Exception e) + { + Logger::Log(LogLevel::LOG_WARN, "Couldn't parse blacklist file, messages:\n%s", e.message().c_str()); } - return true; } diff --git a/source/server/master-server.cpp b/source/server/master-server.cpp index 10e7e85c..d2826b72 100644 --- a/source/server/master-server.cpp +++ b/source/server/master-server.cpp @@ -24,10 +24,22 @@ along with Foobar. If not, see . #include "rornet.h" #include "logger.h" #include "http.h" -#include "json/json.h" +#include "utils.h" #include +#include +#include +#include +#include +#include +#include +#include + +using namespace Poco; +using namespace Poco::JSON; +using namespace Poco::Dynamic; + namespace MasterServer { Client::Client() : @@ -35,7 +47,7 @@ namespace MasterServer { m_is_registered(false) {} bool Client::Register() { - Json::Value data(Json::objectValue); + DynamicStruct data; data["ip"] = Config::getIPAddr(); data["port"] = Config::getListenPort(); data["name"] = Config::getServerName(); @@ -46,9 +58,11 @@ namespace MasterServer { m_server_path = "/" + Config::GetServerlistPath() + "/server-list"; + auto json_str = JsonToString(data); + Logger::Log(LOG_INFO, "Attempting to register on serverlist (%s)", m_server_path.c_str()); Http::Response response; - int result_code = this->HttpRequest(Http::METHOD_POST, data.toStyledString().c_str(), &response); + int result_code = this->HttpRequest(Http::METHOD_POST, json_str.c_str(), &response); if (result_code < 0) { Logger::Log(LOG_ERROR, "Registration failed, result code: %d", result_code); return false; @@ -58,33 +72,34 @@ namespace MasterServer { return false; } - Json::Value root; - Json::Reader reader; - if (!reader.parse(response.GetBody().c_str(), root)) { - Logger::Log(LOG_ERROR, "Registration failed, invalid server response (JSON parsing failed)"); - Logger::Log(LOG_DEBUG, "Raw response: %s", response.GetBody().c_str()); - return false; - } + Parser parser; + Var result; - Json::Value trust_level = root["verified-level"]; - Json::Value challenge = root["challenge"]; - if (!root.isObject() || !trust_level.isNumeric() || !challenge.isString()) { - Logger::Log(LOG_ERROR, "Registration failed, incorrect response from server"); + try + { + result = parser.parse(response.GetBody()); + } + catch(JSONException& jsonException) + { + Logger::Log(LOG_ERROR, "Registration failed, invalid server response (%s)", jsonException.message().c_str()); Logger::Log(LOG_DEBUG, "Raw response: %s", response.GetBody().c_str()); return false; } - m_token = challenge.asString(); - m_trust_level = trust_level.asInt(); + DynamicStruct root = result.extract(); + + m_trust_level = root["verified-level"]; + m_token = root["challenge"].toString(); m_is_registered = true; return true; } - bool Client::SendHeatbeat(Json::Value &user_list) { - Json::Value data(Json::objectValue); + bool Client::SendHeatbeat(Poco::JSON::Array &user_list) { + DynamicStruct data; data["challenge"] = m_token; data["users"] = user_list; - std::string json_str = data.toStyledString(); + + auto json_str = JsonToString(data); Logger::Log(LOG_DEBUG, "Heartbeat JSON:\n%s", json_str.c_str()); Http::Response response; @@ -100,9 +115,11 @@ namespace MasterServer { bool Client::UnRegister() { assert(m_is_registered == true); - Json::Value data(Json::objectValue); + DynamicStruct data; data["challenge"] = m_token; - std::string json_str = data.toStyledString(); + + auto json_str = JsonToString(data); + Logger::Log(LOG_DEBUG, "UnRegister JSON:\n%s", json_str.c_str()); Http::Response response; diff --git a/source/server/master-server.h b/source/server/master-server.h index d197b4d8..33aa14e5 100644 --- a/source/server/master-server.h +++ b/source/server/master-server.h @@ -22,8 +22,7 @@ along with Foobar. If not, see . #include "prerequisites.h" #include "UnicodeStrings.h" - -#include "json/json.h" +#include namespace MasterServer { @@ -33,7 +32,7 @@ namespace MasterServer { bool Register(); - bool SendHeatbeat(Json::Value &user_list); + bool SendHeatbeat(Poco::JSON::Array &user_list); bool UnRegister(); diff --git a/source/server/rorserver.cpp b/source/server/rorserver.cpp index 62de763f..e397e4be 100644 --- a/source/server/rorserver.cpp +++ b/source/server/rorserver.cpp @@ -39,6 +39,7 @@ along with Foobar. If not, see . #include #include +#include #ifdef _WIN32 # include "windows.h" @@ -343,7 +344,7 @@ int main(int argc, char *argv[]) { Utils::SleepSeconds(Config::GetHeartbeatIntervalSec()); Logger::Log(LOG_VERBOSE, "Sending heartbeat..."); - Json::Value user_list(Json::arrayValue); + Poco::JSON::Array user_list; s_sequencer.GetHeartbeatUserList(user_list); if (!s_master_server.SendHeatbeat(user_list)) { unsigned int timeout = Config::GetHeartbeatRetrySeconds(); diff --git a/source/server/sequencer.cpp b/source/server/sequencer.cpp index 2345e01a..76e3f18c 100644 --- a/source/server/sequencer.cpp +++ b/source/server/sequencer.cpp @@ -39,6 +39,10 @@ along with Foobar. If not, see . #include #include +#include +#include +#include + #ifdef __GNUC__ #include @@ -409,14 +413,15 @@ void Sequencer::broadcastUserInfo(int client_id) { } } -void Sequencer::GetHeartbeatUserList(Json::Value &out_array) { +void Sequencer::GetHeartbeatUserList(Poco::JSON::Array &out_array) +{ std::lock_guard scoped_lock(m_clients_mutex); auto itor = m_clients.begin(); auto endi = m_clients.end(); for (; itor != endi; ++itor) { Client *client = *itor; - Json::Value user_data(Json::objectValue); + Poco::DynamicStruct user_data; user_data["is_admin"] = (client->user.authstatus & RoRnet::AUTH_ADMIN); user_data["is_mod"] = (client->user.authstatus & RoRnet::AUTH_MOD); user_data["is_ranked"] = (client->user.authstatus & RoRnet::AUTH_RANKED); @@ -425,7 +430,7 @@ void Sequencer::GetHeartbeatUserList(Json::Value &out_array) { user_data["ip_address"] = client->GetIpAddress(); user_data["client_id"] = client->user.uniqueid; - out_array.append(user_data); + out_array.add(user_data); } } diff --git a/source/server/sequencer.h b/source/server/sequencer.h index d6395728..a8fce9dd 100644 --- a/source/server/sequencer.h +++ b/source/server/sequencer.h @@ -26,7 +26,7 @@ along with Foobar. If not, see . #include "broadcaster.h" #include "receiver.h" #include "spamfilter.h" -#include "json/json.h" +#include #ifdef WITH_ANGELSCRIPT @@ -44,6 +44,7 @@ along with Foobar. If not, see . #include #include + // How many not-vehicles streams has every user by default? (e.g.: "default" and "chat" are not-vehicles streams) // This is used for the vehicle-limit #define NON_VEHICLE_STREAMS 2 @@ -214,7 +215,7 @@ class Sequencer { void queueMessage(int uid, int type, unsigned int streamid, char *data, unsigned int len); void sendMOTDSynchronized(int uid); void frameStepScripts(float dt); - void GetHeartbeatUserList(Json::Value &out_array); + void GetHeartbeatUserList(Poco::JSON::Array &out_array); void UpdateMinuteStats(); int AuthorizeNick(std::string token, std::string &nickname); std::vector GetClientListCopy(); diff --git a/source/server/userauth.cpp b/source/server/userauth.cpp index 4f38fcfd..b36a20f8 100644 --- a/source/server/userauth.cpp +++ b/source/server/userauth.cpp @@ -24,10 +24,11 @@ along with Foobar. If not, see . #include "rornet.h" #include "logger.h" #include "http.h" -#include "json/json.h" +#include "utils.h" #include #include +#include #ifdef __GNUC__ @@ -158,24 +159,24 @@ int UserAuth::resolve(std::string user_token, std::string &user_nick, int client // contact the master server char url[512]; sprintf(url, "/%s/users", Config::GetServerlistPath().c_str()); - Logger::Log(LOG_INFO, "Attempting user authentication (%s)", url); + Logger::Log(LOG_INFO, "Attempting user authentication (%s%s)", Config::GetServerlistHostC(), url); - Json::Value data(Json::objectValue); + Poco::DynamicStruct data; data["username"] = user_nick; data["user_token"] = user_token; - std::string json_str = data.toStyledString(); + auto json_str = JsonToString(data); Http::Response resp; int result_code = Http::Request(Http::METHOD_GET, Config::GetServerlistHostC(), url, "application/json", - json_str.c_str(), &resp); + json_str, &resp); // 200 means success! if (result_code == 200) { Logger::Log(LOG_INFO, "User authentication success, result code: %d", result_code); authlevel = RoRnet::AUTH_RANKED; } else { - Logger::Log(LOG_INFO, "User authentication failed, result code: %d", result_code); + Logger::Log(LOG_INFO, "User authentication failed: %s, result code: %d",resp.GetBody().c_str(), result_code); } //then check for overrides in the authorizations file (server admins, etc) diff --git a/source/server/utils.cpp b/source/server/utils.cpp index 7f5e9dd6..f1857080 100644 --- a/source/server/utils.cpp +++ b/source/server/utils.cpp @@ -29,6 +29,7 @@ along with Foobar. If not, see . #include #include #include +#include #ifdef _WIN32 #include @@ -241,3 +242,11 @@ int intlen(int num) { } return length; } + + +std::string JsonToString(const Poco::DynamicStruct data) +{ + std::stringstream s; + Poco::JSON::Stringifier::stringify(data, s, 1); + return s.str(); +} diff --git a/source/server/utils.h b/source/server/utils.h index 32f905c7..20874d4b 100644 --- a/source/server/utils.h +++ b/source/server/utils.h @@ -26,6 +26,7 @@ along with Foobar. If not, see . #include #include #include +#include namespace Utils { @@ -74,3 +75,5 @@ std::string trim(const std::string &str); std::string hexdump(void *pAddressIn, long lSize); int intlen(int num); + +std::string JsonToString(const Poco::DynamicStruct data); \ No newline at end of file