From 55f543761840aa4fb416fbd48cc65f9229c0d1b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Sun, 18 Feb 2024 19:21:07 +0100 Subject: [PATCH 01/43] Add new type `Settings` & refactor TSettings behavior into Settings by adding getters/setters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lucca Jiménez Könings --- include/Common.h | 4 +- include/TSettings.h | 136 ++++++++++++++++++++++++++++++++++++++++++++ src/TConfig.cpp | 20 +++++++ src/main.cpp | 5 ++ 4 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 include/TSettings.h diff --git a/include/Common.h b/include/Common.h index bb28a64..415e566 100644 --- a/include/Common.h +++ b/include/Common.h @@ -37,6 +37,7 @@ namespace fs = std::filesystem; #include "TConsole.h" +#include "TSettings.h" struct Version { uint8_t major; @@ -65,7 +66,7 @@ public: std::string Resource { "Resources" }; std::string MapName { "/levels/gridmap_v2/info.json" }; std::string Key {}; - std::string Password{}; + std::string Password {}; std::string SSLKeyPath { "./.ssl/HttpServer/key.pem" }; std::string SSLCertPath { "./.ssl/HttpServer/cert.pem" }; bool HTTPServerEnabled { false }; @@ -102,6 +103,7 @@ public: static void SetPPS(const std::string& NewPPS) { mPPS = NewPPS; } static TSettings Settings; + static struct Settings SettingsSingleton; static std::vector GetBackendUrlsInOrder() { return { diff --git a/include/TSettings.h b/include/TSettings.h new file mode 100644 index 0000000..1baa2b6 --- /dev/null +++ b/include/TSettings.h @@ -0,0 +1,136 @@ +// BeamMP, the BeamNG.drive multiplayer mod. +// Copyright (C) 2024 BeamMP Ltd., BeamMP team and contributors. +// +// BeamMP Ltd. can be contacted by electronic mail via contact@beammp.com. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#pragma once +#include +#include +#include +#include + +struct Settings { + using SettingsTypeVariant = std::variant; + + enum Key { + // Keys that correspond to the keys set in TOML + // Keys have their TOML section name as prefix + + // [Misc] + Misc_SendErrorsShowMessage, + Misc_SendErrors, + Misc_ImScaredOfUpdates, + + // [General] + General_Description, + General_Tags, + General_MaxPlayers, + General_Name, + General_Map, + General_AuthKey, + General_Private, + General_Port, + General_MaxCars, + General_LogChat, + General_ResourceFolder, + General_Debug + }; + + std::unordered_map SettingsMap {}; + + std::string getAsString(Key key) { + if (!SettingsMap.contains(key)) { + throw std::logic_error { "Undefined key accessed in Settings::getAsString" }; + } + return std::get(SettingsMap.at(key)); + } + + int getAsInt(Key key) { + if (!SettingsMap.contains(key)) { + throw std::logic_error { "Undefined key accessed in Settings::getAsInt" }; + } + return std::get(SettingsMap.at(key)); + } + + bool getAsBool(Key key) { + if (!SettingsMap.contains(key)) { + throw std::logic_error { "Undefined key accessed in Settings::getAsBool" }; + } + return std::get(SettingsMap.at(key)); + } + + void set(Key key, std::string value) { + if (!SettingsMap.contains(key)) { + throw std::logic_error { "Undefined setting key accessed in Settings::getAsString" }; + } + if (!std::holds_alternative(SettingsMap.at(key))) { + throw std::logic_error { "Wrong value type in Settings::get: std::string" }; + } + SettingsMap.at(key) = value; + } + + void set(Key key, int value) { + if (!SettingsMap.contains(key)) { + throw std::logic_error { "Undefined setting key accessed in Settings::getAsString" }; + } + if (!std::holds_alternative(SettingsMap.at(key))) { + throw std::logic_error { "Wrong value type in Settings::get: std::string" }; + } + SettingsMap.at(key) = value; + } + + void set(Key key, bool value) { + if (!SettingsMap.contains(key)) { + throw std::logic_error { "Undefined setting key accessed in Settings::getAsString" }; + } + if (!std::holds_alternative(SettingsMap.at(key))) { + throw std::logic_error { "Wrong value type in Settings::get: std::string" }; + } + SettingsMap.at(key) = value; + } +}; + +/*struct TSettings { + + +std::string ServerName { "BeamMP Server" }; +std::string ServerDesc { "BeamMP Default Description" }; +std::string ServerTags { "Freeroam" }; +std::string Resource { "Resources" }; +std::string MapName { "/levels/gridmap_v2/info.json" }; +std::string Key {}; +std::string Password{}; +std::string SSLKeyPath { "./.ssl/HttpServer/key.pem" }; +std::string SSLCertPath { "./.ssl/HttpServer/cert.pem" }; +bool HTTPServerEnabled { false }; +int MaxPlayers { 8 }; +bool Private { true }; +int MaxCars { 1 }; +bool DebugModeEnabled { false }; +int Port { 30814 }; +std::string CustomIP {}; +bool LogChat { true }; +bool SendErrors { true }; +bool SendErrorsMessageEnabled { true }; +int HTTPServerPort { 8080 }; +std::string HTTPServerIP { "127.0.0.1" }; +bool HTTPServerUseSSL { false }; +bool HideUpdateMessages { false }; +[[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); } + }; + + +}*/ diff --git a/src/TConfig.cpp b/src/TConfig.cpp index 7192f87..46abe3f 100644 --- a/src/TConfig.cpp +++ b/src/TConfig.cpp @@ -253,6 +253,26 @@ void TConfig::ParseFromFile(std::string_view name) { TryReadValue(data, "Misc", StrSendErrors, "", Application::Settings.SendErrors); TryReadValue(data, "Misc", StrHideUpdateMessages, "", Application::Settings.HideUpdateMessages); TryReadValue(data, "Misc", StrSendErrorsMessageEnabled, "", Application::Settings.SendErrorsMessageEnabled); + + // Read into new Settings Singleton + TryReadValue(data, "General", StrDebug, EnvStrDebug, Application::Settings.DebugModeEnabled); + TryReadValue(data, "General", StrPrivate, EnvStrPrivate, Application::Settings.Private); + TryReadValue(data, "General", StrPort, EnvStrPort, Application::Settings.Port); + TryReadValue(data, "General", StrMaxCars, EnvStrMaxCars, Application::Settings.MaxCars); + TryReadValue(data, "General", StrMaxPlayers, EnvStrMaxPlayers, Application::Settings.MaxPlayers); + TryReadValue(data, "General", StrMap, EnvStrMap, Application::Settings.MapName); + TryReadValue(data, "General", StrName, EnvStrName, Application::Settings.ServerName); + TryReadValue(data, "General", StrDescription, EnvStrDescription, Application::Settings.ServerDesc); + TryReadValue(data, "General", StrTags, EnvStrTags, Application::Settings.ServerTags); + TryReadValue(data, "General", StrResourceFolder, EnvStrResourceFolder, Application::Settings.Resource); + TryReadValue(data, "General", StrAuthKey, EnvStrAuthKey, Application::Settings.Key); + TryReadValue(data, "General", StrLogChat, EnvStrLogChat, Application::Settings.LogChat); + TryReadValue(data, "General", StrPassword, "", Application::Settings.Password); + // Misc + TryReadValue(data, "Misc", StrSendErrors, "", Application::Settings.SendErrors); + TryReadValue(data, "Misc", StrHideUpdateMessages, "", Application::Settings.HideUpdateMessages); + TryReadValue(data, "Misc", StrSendErrorsMessageEnabled, "", Application::Settings.SendErrorsMessageEnabled); + } catch (const std::exception& err) { beammp_error("Error parsing config file value: " + std::string(err.what())); mFailed = true; diff --git a/src/main.cpp b/src/main.cpp index 309eef7..d3f1473 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,6 +29,7 @@ #include "TPluginMonitor.h" #include "TResourceManager.h" #include "TServer.h" +#include "TSettings.h" #include #include @@ -165,6 +166,10 @@ int BeamMPServerMain(MainArguments Arguments) { SetupSignalHandlers(); + Settings settings {}; + settings.SettingsMap.emplace(Settings::Key::General_Name, Settings::SettingsTypeVariant { "Your mom" }); + beammp_infof("Server name set in new impl: {}", settings.getAsString(Settings::Key::General_Name)); + bool Shutdown = false; Application::RegisterShutdownHandler([&Shutdown] { beammp_info("If this takes too long, you can press Ctrl+C repeatedly to force a shutdown."); From 89034a64e094b838cfd9edf5f454787e3d3ad647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Thu, 22 Feb 2024 23:15:32 +0100 Subject: [PATCH 02/43] Add logic for new Settings type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lucca Jiménez Könings --- include/Common.h | 2 +- include/TConfig.h | 2 ++ include/TSettings.h | 23 +++++++++++- src/TConfig.cpp | 88 ++++++++++++++++++++++++++++++++++++--------- 4 files changed, 97 insertions(+), 18 deletions(-) diff --git a/include/Common.h b/include/Common.h index 415e566..3127011 100644 --- a/include/Common.h +++ b/include/Common.h @@ -103,7 +103,7 @@ public: static void SetPPS(const std::string& NewPPS) { mPPS = NewPPS; } static TSettings Settings; - static struct Settings SettingsSingleton; + static inline struct Settings SettingsSingleton { }; static std::vector GetBackendUrlsInOrder() { return { diff --git a/include/TConfig.h b/include/TConfig.h index 0701076..f84a5bc 100644 --- a/include/TConfig.h +++ b/include/TConfig.h @@ -19,6 +19,7 @@ #pragma once #include "Common.h" +#include "TSettings.h" #include #include @@ -43,6 +44,7 @@ private: void TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, const std::string_view& Env, std::string& OutValue); void TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, const std::string_view& Env, bool& OutValue); void TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, const std::string_view& Env, int& OutValue); + void TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, const std::string_view& Env, Settings::Key key); void ParseOldFormat(); std::string TagsAsPrettyArray() const; diff --git a/include/TSettings.h b/include/TSettings.h index 1baa2b6..a2bd1d5 100644 --- a/include/TSettings.h +++ b/include/TSettings.h @@ -49,7 +49,21 @@ struct Settings { General_Debug }; - std::unordered_map SettingsMap {}; + std::unordered_map SettingsMap { + { General_Description, "BeamMP Default Description" }, + { General_Tags, "Freeroam" }, + { General_MaxPlayers, 8 }, + { General_Name, "BeamMP Server" }, + { General_Map, "/levels/gridmap_v2/info.json" }, + { General_AuthKey, "" }, + { General_Private, true }, + { General_Port, 30814 }, + { General_MaxCars, 1 }, + { General_LogChat, true }, + { General_ResourceFolder, "Resources" }, + { General_Debug, false } + + }; std::string getAsString(Key key) { if (!SettingsMap.contains(key)) { @@ -72,6 +86,13 @@ struct Settings { return std::get(SettingsMap.at(key)); } + SettingsTypeVariant get(Key key) { + if (!SettingsMap.contains(key)) { + throw std::logic_error { "Undefined setting key accessed in Settings::get" }; + } + return SettingsMap.at(key); + } + void set(Key key, std::string value) { if (!SettingsMap.contains(key)) { throw std::logic_error { "Undefined setting key accessed in Settings::getAsString" }; diff --git a/src/TConfig.cpp b/src/TConfig.cpp index 46abe3f..3bad985 100644 --- a/src/TConfig.cpp +++ b/src/TConfig.cpp @@ -181,6 +181,7 @@ void TConfig::CreateConfigFile() { void TConfig::TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, const std::string_view& Env, std::string& OutValue) { if (!Env.empty()) { + // If this environment variable exists, return a C-String and check if it's empty or not if (const char* envp = std::getenv(Env.data()); envp != nullptr && std::strcmp(envp, "") != 0) { OutValue = std::string(envp); return; @@ -196,6 +197,7 @@ void TConfig::TryReadValue(toml::value& Table, const std::string& Category, cons void TConfig::TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, const std::string_view& Env, bool& OutValue) { if (!Env.empty()) { + // If this environment variable exists, return a C-String and check if it's empty or not if (const char* envp = std::getenv(Env.data()); envp != nullptr && std::strcmp(envp, "") != 0) { auto Str = std::string(envp); OutValue = Str == "1" || Str == "true"; @@ -212,6 +214,7 @@ void TConfig::TryReadValue(toml::value& Table, const std::string& Category, cons void TConfig::TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, const std::string_view& Env, int& OutValue) { if (!Env.empty()) { + // If this environment variable exists, return a C-String and check if it's empty or not if (const char* envp = std::getenv(Env.data()); envp != nullptr && std::strcmp(envp, "") != 0) { OutValue = int(std::strtol(envp, nullptr, 10)); return; @@ -225,6 +228,60 @@ void TConfig::TryReadValue(toml::value& Table, const std::string& Category, cons } } +// This arcane template magic is needed for using lambdas as overloaded visitors +// See https://en.cppreference.com/w/cpp/utility/variant/visit for reference +template +struct overloaded : Ts... { + using Ts::operator()...; +}; +template +overloaded(Ts...) -> overloaded; + +void TConfig::TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, const std::string_view& Env, Settings::Key key) { + + if (!Env.empty()) { + if (const char* envp = std::getenv(Env.data()); envp != nullptr && std::strcmp(envp, "") != 0) { + + std::visit( + overloaded { + [&envp, &key](std::string) { + Application::SettingsSingleton.set(key, std::string(envp)); + }, + [&envp, &key](int) { + Application::SettingsSingleton.set(key, int(std::strtol(envp, nullptr, 10))); + }, + [&envp, &key](bool) { + auto Str = std::string(envp); + Application::SettingsSingleton.set(key, bool(Str == "1" || Str == "true")); + } }, + Application::SettingsSingleton.get(key)); + } + } else { + + std::visit( + overloaded { + [&Table, &Category, &Key, &key](std::string) { + if (Table[Category.c_str()][Key.data()].is_string()) + Application::SettingsSingleton.set(key, Table[Category.c_str()][Key.data()].as_string()); + else + beammp_warnf("Value '{}.{}' has unexpected type, expected type 'string'", Category, Key); + }, + [&Table, &Category, &Key, &key](int) { + if (Table[Category.c_str()][Key.data()].is_integer()) + Application::SettingsSingleton.set(key, int(Table[Category.c_str()][Key.data()].as_integer())); + else + beammp_warnf("Value '{}.{}' has unexpected type, expected type 'integer'", Category, Key); + }, + [&Table, &Category, &Key, &key](bool) { + if (Table[Category.c_str()][Key.data()].is_boolean()) + Application::SettingsSingleton.set(key, Table[Category.c_str()][Key.data()].as_boolean()); + else + beammp_warnf("Value '{}.{}' has unexpected type, expected type 'boolean'", Category, Key); + } }, + Application::SettingsSingleton.get(key)); + } +} + void TConfig::ParseFromFile(std::string_view name) { try { toml::value data {}; @@ -255,23 +312,22 @@ void TConfig::ParseFromFile(std::string_view name) { TryReadValue(data, "Misc", StrSendErrorsMessageEnabled, "", Application::Settings.SendErrorsMessageEnabled); // Read into new Settings Singleton - TryReadValue(data, "General", StrDebug, EnvStrDebug, Application::Settings.DebugModeEnabled); - TryReadValue(data, "General", StrPrivate, EnvStrPrivate, Application::Settings.Private); - TryReadValue(data, "General", StrPort, EnvStrPort, Application::Settings.Port); - TryReadValue(data, "General", StrMaxCars, EnvStrMaxCars, Application::Settings.MaxCars); - TryReadValue(data, "General", StrMaxPlayers, EnvStrMaxPlayers, Application::Settings.MaxPlayers); - TryReadValue(data, "General", StrMap, EnvStrMap, Application::Settings.MapName); - TryReadValue(data, "General", StrName, EnvStrName, Application::Settings.ServerName); - TryReadValue(data, "General", StrDescription, EnvStrDescription, Application::Settings.ServerDesc); - TryReadValue(data, "General", StrTags, EnvStrTags, Application::Settings.ServerTags); - TryReadValue(data, "General", StrResourceFolder, EnvStrResourceFolder, Application::Settings.Resource); - TryReadValue(data, "General", StrAuthKey, EnvStrAuthKey, Application::Settings.Key); - TryReadValue(data, "General", StrLogChat, EnvStrLogChat, Application::Settings.LogChat); - TryReadValue(data, "General", StrPassword, "", Application::Settings.Password); + TryReadValue(data, "General", StrDebug, EnvStrDebug, Settings::Key::General_Debug); + TryReadValue(data, "General", StrPrivate, EnvStrPrivate, Settings::Key::General_Private); + TryReadValue(data, "General", StrPort, EnvStrPort, Settings::Key::General_Port); + TryReadValue(data, "General", StrMaxCars, EnvStrMaxCars, Settings::Key::General_MaxCars); + TryReadValue(data, "General", StrMaxPlayers, EnvStrMaxPlayers, Settings::Key::General_MaxPlayers); + TryReadValue(data, "General", StrMap, EnvStrMap, Settings::Key::General_Map); + TryReadValue(data, "General", StrName, EnvStrName, Settings::Key::General_Name); + TryReadValue(data, "General", StrDescription, EnvStrDescription, Settings::Key::General_Description); + TryReadValue(data, "General", StrTags, EnvStrTags, Settings::Key::General_Tags); + TryReadValue(data, "General", StrResourceFolder, EnvStrResourceFolder, Settings::Key::General_ResourceFolder); + TryReadValue(data, "General", StrAuthKey, EnvStrAuthKey, Settings::Key::General_AuthKey); + TryReadValue(data, "General", StrLogChat, EnvStrLogChat, Settings::Key::General_LogChat); // Misc - TryReadValue(data, "Misc", StrSendErrors, "", Application::Settings.SendErrors); - TryReadValue(data, "Misc", StrHideUpdateMessages, "", Application::Settings.HideUpdateMessages); - TryReadValue(data, "Misc", StrSendErrorsMessageEnabled, "", Application::Settings.SendErrorsMessageEnabled); + TryReadValue(data, "Misc", StrSendErrors, "", Settings::Key::Misc_SendErrors); + TryReadValue(data, "Misc", StrHideUpdateMessages, "", Settings::Misc_ImScaredOfUpdates); + TryReadValue(data, "Misc", StrSendErrorsMessageEnabled, "", Settings::Misc_SendErrorsShowMessage); } catch (const std::exception& err) { beammp_error("Error parsing config file value: " + std::string(err.what())); From 86f0052e072eee5b21ffaf6f7d226d613b160380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Mon, 26 Feb 2024 03:13:01 +0100 Subject: [PATCH 03/43] Add rudimentary access control to type `Settings` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lucca Jiménez Könings --- include/TSettings.h | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/include/TSettings.h b/include/TSettings.h index a2bd1d5..da588fb 100644 --- a/include/TSettings.h +++ b/include/TSettings.h @@ -63,8 +63,53 @@ struct Settings { { General_ResourceFolder, "Resources" }, { General_Debug, false } + enum SettingsAccessMask { + read, // Value can be read from console + write, // Value can be read and written to from console + noaccess // Value is inaccessible from console (no read OR write) }; + using SettingsAccessControl = std::pair< + Key, // The Key's corresponding enum encoding + SettingsAccessMask // Console read/write permissions + >; + + std::unordered_map InputAccessMapping { + { "Description", { General_Description, write } }, + { "Tags", { General_Tags, write } }, + { "MaxPlayers", { General_MaxPlayers, write } }, + { "Name", { General_Name, write } }, + { "Map", { General_Map, read } }, + { "AuthKey", { General_AuthKey, noaccess } }, + { "Private", { General_Private, read } }, + { "Port", { General_Port, read } }, + { "MaxCars", { General_MaxCars, write } }, + { "LogChat", { General_LogChat, read } }, + { "Resourcefolder", { General_ResourceFolder, read } }, + { "Debug", { General_Debug, noaccess } }, + { "SendErrorsShowMessage", { Misc_SendErrorsShowMessage, noaccess } }, + { "SendErrors", { Misc_SendErrors, noaccess } }, + { "ImScaredOfUpdates", { Misc_ImScaredOfUpdates, noaccess } } + }; + /* + std::unordered_map InputKeyMapping{ + {"Description", General_Description}, + {"Tags", General_Tags}, + {"MaxPlayers", General_MaxPlayers}, + {"Name", General_Name}, + {"Map", General_Map}, + {"AuthKey", General_AuthKey}, + {"Private", General_Private}, + {"Port", General_Port}, + {"MaxCars", General_MaxCars}, + {"LogChat", General_LogChat}, + {"Resourcefolder", General_ResourceFolder}, + {"Debug", General_Debug}, + {"SendErrorsShowMessage", Misc_SendErrorsShowMessage}, + {"SendErrors", Misc_SendErrors}, + {"ImScaredOfUpdates", Misc_ImScaredOfUpdates} + } + */ std::string getAsString(Key key) { if (!SettingsMap.contains(key)) { throw std::logic_error { "Undefined key accessed in Settings::getAsString" }; From a357ff8ca3b11570506813a9f9d1919b2f0fbed4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Mon, 26 Feb 2024 03:16:19 +0100 Subject: [PATCH 04/43] Add missing options to defaults MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lucca Jiménez Könings --- include/TSettings.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/TSettings.h b/include/TSettings.h index da588fb..305ca76 100644 --- a/include/TSettings.h +++ b/include/TSettings.h @@ -61,7 +61,11 @@ struct Settings { { General_MaxCars, 1 }, { General_LogChat, true }, { General_ResourceFolder, "Resources" }, - { General_Debug, false } + { General_Debug, false }, + { Misc_SendErrorsShowMessage, true }, + { Misc_SendErrors, true }, + { Misc_ImScaredOfUpdates, true } + }; enum SettingsAccessMask { read, // Value can be read from console From 3989961ff97f941c730b0d804dfc1f894bce55b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Mon, 26 Feb 2024 03:16:57 +0100 Subject: [PATCH 05/43] Add `get` and `set` to console command `settings` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lucca Jiménez Könings --- include/TSettings.h | 64 +++++++++++++++++++++++++++ src/TConfig.cpp | 2 +- src/TConsole.cpp | 103 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 167 insertions(+), 2 deletions(-) diff --git a/include/TSettings.h b/include/TSettings.h index 305ca76..81857fe 100644 --- a/include/TSettings.h +++ b/include/TSettings.h @@ -171,6 +171,70 @@ struct Settings { } SettingsMap.at(key) = value; } + + const std::unordered_map& getACLMap() const { + return InputAccessMapping; + } + SettingsAccessControl getConsoleInputAccessMapping(const std::string& keyName) { + if (!InputAccessMapping.contains(keyName)) { + throw std::logic_error { "Unknown key name accessed in Settings::getConsoleInputAccessMapping" }; + } else if (InputAccessMapping.at(keyName).second == SettingsAccessMask::noaccess) { + throw std::logic_error { "Key " + keyName + " is not accessible from within the runtime!" }; + } + return InputAccessMapping.at(keyName); + } + + void setConsoleInputAccessMapping(const std::string& keyName, std::string value) { + if (!InputAccessMapping.contains(keyName)) { + throw std::logic_error { "Unknown key name accessed in Settings::setConsoleInputAccessMapping" }; + } else if (InputAccessMapping.at(keyName).second == SettingsAccessMask::noaccess) { + throw std::logic_error { "Key " + keyName + " is not accessible from within the runtime!" }; + } else if (InputAccessMapping.at(keyName).second == SettingsAccessMask::read) { + throw std::logic_error { "Key " + keyName + " is not writeable from within the runtime!" }; + } + + Key key = InputAccessMapping.at(keyName).first; + + if (!std::holds_alternative(SettingsMap.at(key))) { + throw std::logic_error { "Wrong value type in Settings::setConsoleInputAccessMapping: expected std::string" }; + } + + SettingsMap.at(key) = value; + } + void setConsoleInputAccessMapping(const std::string& keyName, int value) { + if (!InputAccessMapping.contains(keyName)) { + throw std::logic_error { "Unknown key name accessed in Settings::setConsoleInputAccessMapping" }; + } else if (InputAccessMapping.at(keyName).second == SettingsAccessMask::noaccess) { + throw std::logic_error { "Key " + keyName + " is not accessible from within the runtime!" }; + } else if (InputAccessMapping.at(keyName).second == SettingsAccessMask::read) { + throw std::logic_error { "Key " + keyName + " is not writeable from within the runtime!" }; + } + + Key key = InputAccessMapping.at(keyName).first; + + if (!std::holds_alternative(SettingsMap.at(key))) { + throw std::logic_error { "Wrong value type in Settings::setConsoleInputAccessMapping: expected int" }; + } + + SettingsMap.at(key) = value; + } + void setConsoleInputAccessMapping(const std::string& keyName, bool value) { + if (!InputAccessMapping.contains(keyName)) { + throw std::logic_error { "Unknown key name accessed in Settings::setConsoleInputAccessMapping" }; + } else if (InputAccessMapping.at(keyName).second == SettingsAccessMask::noaccess) { + throw std::logic_error { "Key " + keyName + " is not accessible from within the runtime!" }; + } else if (InputAccessMapping.at(keyName).second == SettingsAccessMask::read) { + throw std::logic_error { "Key " + keyName + " is not writeable from within the runtime!" }; + } + + Key key = InputAccessMapping.at(keyName).first; + + if (!std::holds_alternative(SettingsMap.at(key))) { + throw std::logic_error { "Wrong value type in Settings::setConsoleInputAccessMapping: expected bool" }; + } + + SettingsMap.at(key) = value; + } }; /*struct TSettings { diff --git a/src/TConfig.cpp b/src/TConfig.cpp index 3bad985..923b770 100644 --- a/src/TConfig.cpp +++ b/src/TConfig.cpp @@ -238,7 +238,7 @@ template overloaded(Ts...) -> overloaded; void TConfig::TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, const std::string_view& Env, Settings::Key key) { - + beammp_infof("SettingsSingletonMap Size: {}", Application::SettingsSingleton.SettingsMap.size()); if (!Env.empty()) { if (const char* envp = std::getenv(Env.data()); envp != nullptr && std::strcmp(envp, "") != 0) { diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 9c6561e..ff50760 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -28,6 +28,7 @@ #include #include #include +#include static inline bool StringStartsWith(const std::string& What, const std::string& StartsWith) { return What.size() >= StartsWith.size() && What.substr(0, StartsWith.size()) == StartsWith; @@ -366,8 +367,108 @@ std::tuple> TConsole::ParseCommand(const s return { Command, Args }; } +template +struct overloaded : Ts... { + using Ts::operator()...; +}; +template +overloaded(Ts...) -> overloaded; + void TConsole::Command_Settings(const std::string&, const std::vector& args) { - if (!EnsureArgsCount(args, 1, 2)) { + + for (const std::string& arg : args) + beammp_infof("Argument: {}", arg); + + static constexpr const char* sHelpString = R"( + Settings: + settings help displays this help + settings list lists all settings + settings get prints current value of specified setting + settings set sets specified setting to value + )"; + + if (args.size() == 0) { + beammp_errorf("No arguments specified for command 'settings'!"); + Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString)); + return; + } + + if (args.front() == "help") { + + Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString)); + return; + } else if (args.front() == "get") { + if (args.size() == 1) { + beammp_errorf("'settings get' needs at least one argument!"); + + Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString)); + return; + } + + try { + Settings::SettingsAccessControl acl = Application::SettingsSingleton.getConsoleInputAccessMapping(args.at(1)); + Settings::SettingsTypeVariant keyType = Application::SettingsSingleton.get(acl.first); + + std::visit( + overloaded { + [&args](std::string keyValue) { + Application::Console().WriteRaw(fmt::format("{} = {}", args.at(1), keyValue)); + }, + [&args](int keyValue) { + Application::Console().WriteRaw(fmt::format("{} = {}", args.at(1), keyValue)); + }, + [&args](bool keyValue) { + Application::Console().WriteRaw(fmt::format("{} = {}", args.at(1), keyValue)); + } + + }, + keyType); + + } catch (std::logic_error& e) { + beammp_errorf("Error when getting key: {}", e.what()); + return; + } + } else if (args.front() == "set") { + if (args.size() == 1) { + beammp_errorf("'settings set' needs at least two arguments!"); + + Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString)); + return; + } + + try { + + Settings::SettingsAccessControl acl = Application::SettingsSingleton.getConsoleInputAccessMapping(args.at(1)); + Settings::SettingsTypeVariant keyType = Application::SettingsSingleton.get(acl.first); + + std::visit( + overloaded { + [&args](std::string keyValue) { + Application::SettingsSingleton.setConsoleInputAccessMapping(args.at(1), std::string(args.at(2))); + Application::Console().WriteRaw(fmt::format("{} := {}", args.at(1), std::string(args.at(2)))); + }, + [&args](int keyValue) { + Application::SettingsSingleton.setConsoleInputAccessMapping(args.at(1), std::stoi(args.at(2))); + Application::Console().WriteRaw(fmt::format("{} := {}", args.at(1), std::stoi(args.at(2)))); + }, + [&args](bool keyValue) { + // todo: implement other way to convert from string to bool + Application::SettingsSingleton.setConsoleInputAccessMapping(args.at(1), std::stoi(args.at(2))); + Application::Console().WriteRaw(fmt::format("{} := {}", args.at(1), std::stoi(args.at(2)))); + } + + }, + keyType); + + } catch (std::logic_error& e) { + beammp_errorf("Error when setting key: {}", e.what()); + return; + } + + } else { + beammp_errorf("Unknown argument for cammand 'settings': {}", args.front()); + + Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString)); return; } } From 8693b8a2b0d7395027676e7ea1fd6d1ef58e1d8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Mon, 26 Feb 2024 03:54:01 +0100 Subject: [PATCH 06/43] Add `list` argument to command `settings` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lucca Jiménez Könings --- src/TConsole.cpp | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/TConsole.cpp b/src/TConsole.cpp index ff50760..d0ca3af 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -29,6 +29,7 @@ #include #include #include +#include static inline bool StringStartsWith(const std::string& What, const std::string& StartsWith) { return What.size() >= StartsWith.size() && What.substr(0, StartsWith.size()) == StartsWith; @@ -465,7 +466,36 @@ void TConsole::Command_Settings(const std::string&, const std::vector + for(const auto& [keyName, keyACL] : Application::SettingsSingleton.getACLMap()){ + // even though we have the value, we want to ignore it in order to make use of access + // control checks + + try{ + + Settings::SettingsAccessControl acl = Application::SettingsSingleton.getConsoleInputAccessMapping(keyName); + Settings::SettingsTypeVariant keyType = Application::SettingsSingleton.get(acl.first); + + std::visit( + overloaded { + [&keyName](std::string keyValue) { + Application::Console().WriteRaw(fmt::format("{} = {}", keyName, keyValue)); + }, + [&keyName](int keyValue) { + Application::Console().WriteRaw(fmt::format("{} = {}", keyName, keyValue)); + }, + [&keyName](bool keyValue) { + Application::Console().WriteRaw(fmt::format("{} = {}", keyName, keyValue)); + } + + }, + keyType); + }catch(std::logic_error& e){ + beammp_errorf("Error when getting key: {}", e.what()); + } + } + }else { beammp_errorf("Unknown argument for cammand 'settings': {}", args.front()); Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString)); From f5e2f7425f1d11e3c3656fb36d5ad8a24defebc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Tue, 7 May 2024 12:45:36 +0200 Subject: [PATCH 07/43] Fix `ComposedKey` metadata not printing to console MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make ComposedKey formattable by overloading fmt::format Signed-off-by: Lucca Jiménez Könings --- include/TSettings.h | 90 +++++++++++++++++++++++++++++++-------------- src/TConsole.cpp | 51 ++++++++++++------------- 2 files changed, 88 insertions(+), 53 deletions(-) diff --git a/include/TSettings.h b/include/TSettings.h index 81857fe..11433df 100644 --- a/include/TSettings.h +++ b/include/TSettings.h @@ -17,11 +17,44 @@ // along with this program. If not, see . #pragma once +#include +#include +#include #include #include #include #include +struct ComposedKey { + std::string Category; + std::string Key; + + bool operator==(const ComposedKey& rhs) const { + return (this->Category == rhs.Category && this->Key == rhs.Key); + } +}; + +template <> +struct fmt::formatter : formatter { + auto format(ComposedKey key, format_context& ctx) const; +}; + +inline auto fmt::formatter::format(ComposedKey key, fmt::format_context& ctx) const { + std::string key_metadata = fmt::format("{}::{}", key.Category, key.Key); + return formatter::format(key_metadata, ctx); +} + +namespace std { +template <> +class hash { +public: + std::uint64_t operator()(const ComposedKey& key) const { + std::hash hash_fn; + return hash_fn(key.Category + key.Key); + } +}; +} + struct Settings { using SettingsTypeVariant = std::variant; @@ -78,23 +111,24 @@ struct Settings { SettingsAccessMask // Console read/write permissions >; - std::unordered_map InputAccessMapping { - { "Description", { General_Description, write } }, - { "Tags", { General_Tags, write } }, - { "MaxPlayers", { General_MaxPlayers, write } }, - { "Name", { General_Name, write } }, - { "Map", { General_Map, read } }, - { "AuthKey", { General_AuthKey, noaccess } }, - { "Private", { General_Private, read } }, - { "Port", { General_Port, read } }, - { "MaxCars", { General_MaxCars, write } }, - { "LogChat", { General_LogChat, read } }, - { "Resourcefolder", { General_ResourceFolder, read } }, - { "Debug", { General_Debug, noaccess } }, - { "SendErrorsShowMessage", { Misc_SendErrorsShowMessage, noaccess } }, - { "SendErrors", { Misc_SendErrors, noaccess } }, - { "ImScaredOfUpdates", { Misc_ImScaredOfUpdates, noaccess } } + std::unordered_map InputAccessMapping { + { { "General", "Description" }, { General_Description, write } }, + { { "General", "Tags" }, { General_Tags, write } }, + { { "General", "MaxPlayers" }, { General_MaxPlayers, write } }, + { { "General", "Name" }, { General_Name, write } }, + { { "General", "Map" }, { General_Map, read } }, + { { "General", "AuthKey" }, { General_AuthKey, noaccess } }, + { { "General", "Private" }, { General_Private, read } }, + { { "General", "Port" }, { General_Port, read } }, + { { "General", "MaxCars" }, { General_MaxCars, write } }, + { { "General", "LogChat" }, { General_LogChat, read } }, + { { "General", "ResourceFolder" }, { General_ResourceFolder, read } }, + { { "General", "Debug" }, { General_Debug, noaccess } }, + { { "Misc", "SendErrorsShowMessage" }, { Misc_SendErrorsShowMessage, noaccess } }, + { { "Misc", "SendErrors" }, { Misc_SendErrors, noaccess } }, + { { "Misc", "ImScaredOfUpdates" }, { Misc_ImScaredOfUpdates, noaccess } } }; + /* std::unordered_map InputKeyMapping{ {"Description", General_Description}, @@ -172,25 +206,25 @@ struct Settings { SettingsMap.at(key) = value; } - const std::unordered_map& getACLMap() const { + const std::unordered_map& getACLMap() const { return InputAccessMapping; } - SettingsAccessControl getConsoleInputAccessMapping(const std::string& keyName) { + SettingsAccessControl getConsoleInputAccessMapping(const ComposedKey& keyName) { if (!InputAccessMapping.contains(keyName)) { throw std::logic_error { "Unknown key name accessed in Settings::getConsoleInputAccessMapping" }; } else if (InputAccessMapping.at(keyName).second == SettingsAccessMask::noaccess) { - throw std::logic_error { "Key " + keyName + " is not accessible from within the runtime!" }; + throw std::logic_error { "Setting '" + keyName.Category + " > " + keyName.Key + "' is not accessible from within the runtime!" }; } return InputAccessMapping.at(keyName); } - void setConsoleInputAccessMapping(const std::string& keyName, std::string value) { + void setConsoleInputAccessMapping(const ComposedKey& keyName, std::string value) { if (!InputAccessMapping.contains(keyName)) { throw std::logic_error { "Unknown key name accessed in Settings::setConsoleInputAccessMapping" }; } else if (InputAccessMapping.at(keyName).second == SettingsAccessMask::noaccess) { - throw std::logic_error { "Key " + keyName + " is not accessible from within the runtime!" }; + throw std::logic_error { "Setting '" + keyName.Category + " > " + keyName.Key + "' is not accessible from within the runtime!" }; } else if (InputAccessMapping.at(keyName).second == SettingsAccessMask::read) { - throw std::logic_error { "Key " + keyName + " is not writeable from within the runtime!" }; + throw std::logic_error { "Setting '" + keyName.Category + " > " + keyName.Key + "' is not writeable from within the runtime!" }; } Key key = InputAccessMapping.at(keyName).first; @@ -201,13 +235,13 @@ struct Settings { SettingsMap.at(key) = value; } - void setConsoleInputAccessMapping(const std::string& keyName, int value) { + void setConsoleInputAccessMapping(const ComposedKey& keyName, int value) { if (!InputAccessMapping.contains(keyName)) { throw std::logic_error { "Unknown key name accessed in Settings::setConsoleInputAccessMapping" }; } else if (InputAccessMapping.at(keyName).second == SettingsAccessMask::noaccess) { - throw std::logic_error { "Key " + keyName + " is not accessible from within the runtime!" }; + throw std::logic_error { "Key '" + keyName.Category + " > " + keyName.Key + "' is not accessible from within the runtime!" }; } else if (InputAccessMapping.at(keyName).second == SettingsAccessMask::read) { - throw std::logic_error { "Key " + keyName + " is not writeable from within the runtime!" }; + throw std::logic_error { "Key '" + keyName.Category + " > " + keyName.Key + "' is not writeable from within the runtime!" }; } Key key = InputAccessMapping.at(keyName).first; @@ -218,13 +252,13 @@ struct Settings { SettingsMap.at(key) = value; } - void setConsoleInputAccessMapping(const std::string& keyName, bool value) { + void setConsoleInputAccessMapping(const ComposedKey& keyName, bool value) { if (!InputAccessMapping.contains(keyName)) { throw std::logic_error { "Unknown key name accessed in Settings::setConsoleInputAccessMapping" }; } else if (InputAccessMapping.at(keyName).second == SettingsAccessMask::noaccess) { - throw std::logic_error { "Key " + keyName + " is not accessible from within the runtime!" }; + throw std::logic_error { "Key '" + keyName.Category + " > " + keyName.Key + "' is not accessible from within the runtime!" }; } else if (InputAccessMapping.at(keyName).second == SettingsAccessMask::read) { - throw std::logic_error { "Key " + keyName + " is not writeable from within the runtime!" }; + throw std::logic_error { "Key '" + keyName.Category + " > " + keyName.Key + "' is not writeable from within the runtime!" }; } Key key = InputAccessMapping.at(keyName).first; diff --git a/src/TConsole.cpp b/src/TConsole.cpp index d0ca3af..64eb176 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -382,10 +382,10 @@ void TConsole::Command_Settings(const std::string&, const std::vector prints current value of specified setting - settings set sets specified setting to value + settings help displays this help + settings list lists all settings + settings get prints current value of specified setting + settings set sets specified setting to value )"; if (args.size() == 0) { @@ -399,27 +399,28 @@ void TConsole::Command_Settings(const std::string&, const std::vector {}' = {}", args.at(1), args.at(2), keyValue)); }, [&args](int keyValue) { - Application::Console().WriteRaw(fmt::format("{} = {}", args.at(1), keyValue)); + Application::Console().WriteRaw(fmt::format("'{} > {}' = {}", args.at(1), args.at(2), keyValue)); }, [&args](bool keyValue) { - Application::Console().WriteRaw(fmt::format("{} = {}", args.at(1), keyValue)); + Application::Console().WriteRaw(fmt::format("'{} > {}' = {}", args.at(1), args.at(2), keyValue)); } }, @@ -439,23 +440,23 @@ void TConsole::Command_Settings(const std::string&, const std::vector {} := {}", args.at(1), args.at(2), std::string(args.at(2)))); }, [&args](int keyValue) { - Application::SettingsSingleton.setConsoleInputAccessMapping(args.at(1), std::stoi(args.at(2))); - Application::Console().WriteRaw(fmt::format("{} := {}", args.at(1), std::stoi(args.at(2)))); + Application::SettingsSingleton.setConsoleInputAccessMapping(ComposedKey(args.at(1), args.at(2)), std::stoi(args.at(2))); + Application::Console().WriteRaw(fmt::format("{} > {} := {}", args.at(1),args.at(2), std::stoi(args.at(2)))); }, [&args](bool keyValue) { // todo: implement other way to convert from string to bool - Application::SettingsSingleton.setConsoleInputAccessMapping(args.at(1), std::stoi(args.at(2))); - Application::Console().WriteRaw(fmt::format("{} := {}", args.at(1), std::stoi(args.at(2)))); + Application::SettingsSingleton.setConsoleInputAccessMapping(ComposedKey(args.at(1), args.at(2)), std::stoi(args.at(2))); + Application::Console().WriteRaw(fmt::format("{} > {} := {}", args.at(1), args.at(2), std::stoi(args.at(2)))); } }, @@ -468,25 +469,25 @@ void TConsole::Command_Settings(const std::string&, const std::vector - for(const auto& [keyName, keyACL] : Application::SettingsSingleton.getACLMap()){ + for(const auto& [composedKey, keyACL] : Application::SettingsSingleton.getACLMap()){ // even though we have the value, we want to ignore it in order to make use of access // control checks try{ - Settings::SettingsAccessControl acl = Application::SettingsSingleton.getConsoleInputAccessMapping(keyName); + Settings::SettingsAccessControl acl = Application::SettingsSingleton.getConsoleInputAccessMapping(composedKey); Settings::SettingsTypeVariant keyType = Application::SettingsSingleton.get(acl.first); std::visit( overloaded { - [&keyName](std::string keyValue) { - Application::Console().WriteRaw(fmt::format("{} = {}", keyName, keyValue)); + [&composedKey](std::string keyValue) { + Application::Console().WriteRaw(fmt::format("{} = {}", composedKey, keyValue)); }, - [&keyName](int keyValue) { - Application::Console().WriteRaw(fmt::format("{} = {}", keyName, keyValue)); + [&composedKey](int keyValue) { + Application::Console().WriteRaw(fmt::format("{} = {}", composedKey, keyValue)); }, - [&keyName](bool keyValue) { - Application::Console().WriteRaw(fmt::format("{} = {}", keyName, keyValue)); + [&composedKey](bool keyValue) { + Application::Console().WriteRaw(fmt::format("{} = {}", composedKey, keyValue)); } }, From d6b78b9683c5b979e2df46438ff9f37bda09a3a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Tue, 7 May 2024 13:04:11 +0200 Subject: [PATCH 08/43] Fix argument parsing at wrong index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lucca Jiménez Könings --- src/TConsole.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 64eb176..10be958 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -446,17 +446,17 @@ void TConsole::Command_Settings(const std::string&, const std::vector {} := {}", args.at(1), args.at(2), std::string(args.at(2)))); + Application::SettingsSingleton.setConsoleInputAccessMapping(ComposedKey(args.at(1), args.at(2)), std::string(args.at(3))); + Application::Console().WriteRaw(fmt::format("{}::{} := {}", args.at(1), args.at(2), std::string(args.at(3)))); }, [&args](int keyValue) { - Application::SettingsSingleton.setConsoleInputAccessMapping(ComposedKey(args.at(1), args.at(2)), std::stoi(args.at(2))); - Application::Console().WriteRaw(fmt::format("{} > {} := {}", args.at(1),args.at(2), std::stoi(args.at(2)))); + Application::SettingsSingleton.setConsoleInputAccessMapping(ComposedKey(args.at(1), args.at(2)), std::stoi(args.at(3))); + Application::Console().WriteRaw(fmt::format("{}::{} := {}", args.at(1),args.at(2), std::stoi(args.at(3)))); }, [&args](bool keyValue) { // todo: implement other way to convert from string to bool - Application::SettingsSingleton.setConsoleInputAccessMapping(ComposedKey(args.at(1), args.at(2)), std::stoi(args.at(2))); - Application::Console().WriteRaw(fmt::format("{} > {} := {}", args.at(1), args.at(2), std::stoi(args.at(2)))); + Application::SettingsSingleton.setConsoleInputAccessMapping(ComposedKey(args.at(1), args.at(2)), std::stoi(args.at(3))); + Application::Console().WriteRaw(fmt::format("{}::{} := {}", args.at(1), args.at(2), std::stoi(args.at(3)))); } }, From 37109ae3f165a10f05c7a243c57e10ed77619a4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Tue, 7 May 2024 13:04:34 +0200 Subject: [PATCH 09/43] Make 'General::Debug' setting r/w from runtime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lucca Jiménez Könings --- include/TSettings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/TSettings.h b/include/TSettings.h index 11433df..263ba2a 100644 --- a/include/TSettings.h +++ b/include/TSettings.h @@ -123,7 +123,7 @@ struct Settings { { { "General", "MaxCars" }, { General_MaxCars, write } }, { { "General", "LogChat" }, { General_LogChat, read } }, { { "General", "ResourceFolder" }, { General_ResourceFolder, read } }, - { { "General", "Debug" }, { General_Debug, noaccess } }, + { { "General", "Debug" }, { General_Debug, write } }, { { "Misc", "SendErrorsShowMessage" }, { Misc_SendErrorsShowMessage, noaccess } }, { { "Misc", "SendErrors" }, { Misc_SendErrors, noaccess } }, { { "Misc", "ImScaredOfUpdates" }, { Misc_ImScaredOfUpdates, noaccess } } From c6dac5567918eafdb5dc012fe468ab3bd1e78d57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Tue, 7 May 2024 17:24:45 +0200 Subject: [PATCH 10/43] Wrap Settings into synchronization primitives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lucca Jiménez Könings --- include/TSettings.h | 96 +++++++++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 42 deletions(-) diff --git a/include/TSettings.h b/include/TSettings.h index 263ba2a..aec0b79 100644 --- a/include/TSettings.h +++ b/include/TSettings.h @@ -24,6 +24,7 @@ #include #include #include +#include "Sync.h" struct ComposedKey { std::string Category; @@ -82,7 +83,7 @@ struct Settings { General_Debug }; - std::unordered_map SettingsMap { + Sync> SettingsMap = std::unordered_map{ { General_Description, "BeamMP Default Description" }, { General_Tags, "Freeroam" }, { General_MaxPlayers, 8 }, @@ -111,7 +112,7 @@ struct Settings { SettingsAccessMask // Console read/write permissions >; - std::unordered_map InputAccessMapping { + Sync> InputAccessMapping = std::unordered_map{ { { "General", "Description" }, { General_Description, write } }, { { "General", "Tags" }, { General_Tags, write } }, { { "General", "MaxPlayers" }, { General_MaxPlayers, write } }, @@ -149,125 +150,136 @@ struct Settings { } */ std::string getAsString(Key key) { - if (!SettingsMap.contains(key)) { + auto map = SettingsMap.synchronize(); + if (!map->contains(key)) { throw std::logic_error { "Undefined key accessed in Settings::getAsString" }; } - return std::get(SettingsMap.at(key)); + return std::get(map->at(key)); } int getAsInt(Key key) { - if (!SettingsMap.contains(key)) { + auto map = SettingsMap.synchronize(); + if (!map->contains(key)) { throw std::logic_error { "Undefined key accessed in Settings::getAsInt" }; } - return std::get(SettingsMap.at(key)); + return std::get(map->at(key)); } bool getAsBool(Key key) { - if (!SettingsMap.contains(key)) { + auto map = SettingsMap.synchronize(); + if (!map->contains(key)) { throw std::logic_error { "Undefined key accessed in Settings::getAsBool" }; } - return std::get(SettingsMap.at(key)); + return std::get(map->at(key)); } SettingsTypeVariant get(Key key) { - if (!SettingsMap.contains(key)) { + auto map = SettingsMap.synchronize(); + if (!map->contains(key)) { throw std::logic_error { "Undefined setting key accessed in Settings::get" }; } - return SettingsMap.at(key); + return map->at(key); } void set(Key key, std::string value) { - if (!SettingsMap.contains(key)) { + auto map = SettingsMap.synchronize(); + if (!map->contains(key)) { throw std::logic_error { "Undefined setting key accessed in Settings::getAsString" }; } - if (!std::holds_alternative(SettingsMap.at(key))) { + if (!std::holds_alternative(map->at(key))) { throw std::logic_error { "Wrong value type in Settings::get: std::string" }; } - SettingsMap.at(key) = value; + map->at(key) = value; } void set(Key key, int value) { - if (!SettingsMap.contains(key)) { + auto map = SettingsMap.synchronize(); + if (!map->contains(key)) { throw std::logic_error { "Undefined setting key accessed in Settings::getAsString" }; } - if (!std::holds_alternative(SettingsMap.at(key))) { + if (!std::holds_alternative(map->at(key))) { throw std::logic_error { "Wrong value type in Settings::get: std::string" }; } - SettingsMap.at(key) = value; + map->at(key) = value; } void set(Key key, bool value) { - if (!SettingsMap.contains(key)) { + auto map = SettingsMap.synchronize(); + if (!map->contains(key)) { throw std::logic_error { "Undefined setting key accessed in Settings::getAsString" }; } - if (!std::holds_alternative(SettingsMap.at(key))) { + if (!std::holds_alternative(map->at(key))) { throw std::logic_error { "Wrong value type in Settings::get: std::string" }; } - SettingsMap.at(key) = value; + map->at(key) = value; } - const std::unordered_map& getACLMap() const { - return InputAccessMapping; + const std::unordered_map getACLMap() const { + return *InputAccessMapping; } SettingsAccessControl getConsoleInputAccessMapping(const ComposedKey& keyName) { - if (!InputAccessMapping.contains(keyName)) { + auto acl_map = InputAccessMapping.synchronize(); + if (!acl_map->contains(keyName)) { throw std::logic_error { "Unknown key name accessed in Settings::getConsoleInputAccessMapping" }; - } else if (InputAccessMapping.at(keyName).second == SettingsAccessMask::noaccess) { + } else if (acl_map->at(keyName).second == SettingsAccessMask::noaccess) { throw std::logic_error { "Setting '" + keyName.Category + " > " + keyName.Key + "' is not accessible from within the runtime!" }; } - return InputAccessMapping.at(keyName); + return acl_map->at(keyName); } void setConsoleInputAccessMapping(const ComposedKey& keyName, std::string value) { - if (!InputAccessMapping.contains(keyName)) { + auto [map, acl_map] = boost::synchronize(SettingsMap, InputAccessMapping); + if (!acl_map->contains(keyName)) { throw std::logic_error { "Unknown key name accessed in Settings::setConsoleInputAccessMapping" }; - } else if (InputAccessMapping.at(keyName).second == SettingsAccessMask::noaccess) { + } else if (acl_map->at(keyName).second == SettingsAccessMask::noaccess) { throw std::logic_error { "Setting '" + keyName.Category + " > " + keyName.Key + "' is not accessible from within the runtime!" }; - } else if (InputAccessMapping.at(keyName).second == SettingsAccessMask::read) { + } else if (acl_map->at(keyName).second == SettingsAccessMask::read) { throw std::logic_error { "Setting '" + keyName.Category + " > " + keyName.Key + "' is not writeable from within the runtime!" }; } - Key key = InputAccessMapping.at(keyName).first; + Key key = acl_map->at(keyName).first; - if (!std::holds_alternative(SettingsMap.at(key))) { + if (!std::holds_alternative(map->at(key))) { throw std::logic_error { "Wrong value type in Settings::setConsoleInputAccessMapping: expected std::string" }; } - SettingsMap.at(key) = value; + map->at(key) = value; } void setConsoleInputAccessMapping(const ComposedKey& keyName, int value) { - if (!InputAccessMapping.contains(keyName)) { + auto [map, acl_map] = boost::synchronize(SettingsMap, InputAccessMapping); + if (!acl_map->contains(keyName)) { throw std::logic_error { "Unknown key name accessed in Settings::setConsoleInputAccessMapping" }; - } else if (InputAccessMapping.at(keyName).second == SettingsAccessMask::noaccess) { + } else if (acl_map->at(keyName).second == SettingsAccessMask::noaccess) { throw std::logic_error { "Key '" + keyName.Category + " > " + keyName.Key + "' is not accessible from within the runtime!" }; - } else if (InputAccessMapping.at(keyName).second == SettingsAccessMask::read) { + } else if (acl_map->at(keyName).second == SettingsAccessMask::read) { throw std::logic_error { "Key '" + keyName.Category + " > " + keyName.Key + "' is not writeable from within the runtime!" }; } - Key key = InputAccessMapping.at(keyName).first; + Key key = acl_map->at(keyName).first; - if (!std::holds_alternative(SettingsMap.at(key))) { + if (!std::holds_alternative(map->at(key))) { throw std::logic_error { "Wrong value type in Settings::setConsoleInputAccessMapping: expected int" }; } - SettingsMap.at(key) = value; + map->at(key) = value; } void setConsoleInputAccessMapping(const ComposedKey& keyName, bool value) { - if (!InputAccessMapping.contains(keyName)) { + auto [map, acl_map] = boost::synchronize(SettingsMap, InputAccessMapping); + if (!acl_map->contains(keyName)) { throw std::logic_error { "Unknown key name accessed in Settings::setConsoleInputAccessMapping" }; - } else if (InputAccessMapping.at(keyName).second == SettingsAccessMask::noaccess) { + } else if (acl_map->at(keyName).second == SettingsAccessMask::noaccess) { throw std::logic_error { "Key '" + keyName.Category + " > " + keyName.Key + "' is not accessible from within the runtime!" }; - } else if (InputAccessMapping.at(keyName).second == SettingsAccessMask::read) { + } else if (acl_map->at(keyName).second == SettingsAccessMask::read) { throw std::logic_error { "Key '" + keyName.Category + " > " + keyName.Key + "' is not writeable from within the runtime!" }; } - Key key = InputAccessMapping.at(keyName).first; + Key key = acl_map->at(keyName).first; - if (!std::holds_alternative(SettingsMap.at(key))) { + if (!std::holds_alternative(map->at(key))) { throw std::logic_error { "Wrong value type in Settings::setConsoleInputAccessMapping: expected bool" }; } - SettingsMap.at(key) = value; + map->at(key) = value; } }; From 158875a962a185f66ceb0bf459b93aae0a30091f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Tue, 7 May 2024 17:25:43 +0200 Subject: [PATCH 11/43] Remove debug code for new Settings implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lucca Jiménez Könings --- src/TConfig.cpp | 1 - src/main.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/src/TConfig.cpp b/src/TConfig.cpp index 923b770..c3d4b90 100644 --- a/src/TConfig.cpp +++ b/src/TConfig.cpp @@ -238,7 +238,6 @@ template overloaded(Ts...) -> overloaded; void TConfig::TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, const std::string_view& Env, Settings::Key key) { - beammp_infof("SettingsSingletonMap Size: {}", Application::SettingsSingleton.SettingsMap.size()); if (!Env.empty()) { if (const char* envp = std::getenv(Env.data()); envp != nullptr && std::strcmp(envp, "") != 0) { diff --git a/src/main.cpp b/src/main.cpp index d3f1473..cb5003d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -167,7 +167,6 @@ int BeamMPServerMain(MainArguments Arguments) { SetupSignalHandlers(); Settings settings {}; - settings.SettingsMap.emplace(Settings::Key::General_Name, Settings::SettingsTypeVariant { "Your mom" }); beammp_infof("Server name set in new impl: {}", settings.getAsString(Settings::Key::General_Name)); bool Shutdown = false; From 639c46ad3654a328318e9cd3b1bd836e29941b37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Tue, 7 May 2024 18:10:14 +0200 Subject: [PATCH 12/43] Remove additional debug calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lucca Jiménez Könings --- src/TConsole.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 10be958..aaa0f13 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -377,9 +377,6 @@ overloaded(Ts...) -> overloaded; void TConsole::Command_Settings(const std::string&, const std::vector& args) { - for (const std::string& arg : args) - beammp_infof("Argument: {}", arg); - static constexpr const char* sHelpString = R"( Settings: settings help displays this help @@ -473,6 +470,8 @@ void TConsole::Command_Settings(const std::string&, const std::vector Date: Tue, 7 May 2024 18:11:49 +0200 Subject: [PATCH 13/43] Fix `settings set` not accepting boolean literals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lucca Jiménez Könings --- src/TConsole.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/TConsole.cpp b/src/TConsole.cpp index aaa0f13..ad7f4d8 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -452,8 +452,15 @@ void TConsole::Command_Settings(const std::string&, const std::vector Date: Wed, 15 May 2024 12:45:19 +0200 Subject: [PATCH 14/43] Remove interfering legacy code (http,password,etc) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lucca Jiménez Könings --- src/Http.cpp | 5 ---- src/TConfig.cpp | 51 +--------------------------------------- src/THeartbeatThread.cpp | 3 --- src/TNetwork.cpp | 16 ------------- src/TServer.cpp | 11 --------- 5 files changed, 1 insertion(+), 85 deletions(-) diff --git a/src/Http.cpp b/src/Http.cpp index 2af767b..a20173d 100644 --- a/src/Http.cpp +++ b/src/Http.cpp @@ -172,7 +172,6 @@ Http::Server::THttpServerInstance::THttpServerInstance() { } void Http::Server::THttpServerInstance::operator()() try { - beammp_info("HTTP(S) Server started on port " + std::to_string(Application::Settings.HTTPServerPort)); std::unique_ptr HttpLibServerInstance; HttpLibServerInstance = std::make_unique(); // todo: make this IP agnostic so people can set their own IP @@ -214,10 +213,6 @@ void Http::Server::THttpServerInstance::operator()() try { beammp_debug("Http Server: " + Req.method + " " + Req.target + " -> " + std::to_string(Res.status)); }); Application::SetSubsystemStatus("HTTPServer", Application::Status::Good); - auto ret = HttpLibServerInstance->listen(Application::Settings.HTTPServerIP.c_str(), Application::Settings.HTTPServerPort); - if (!ret) { - beammp_error("Failed to start http server (failed to listen). Please ensure the http server is configured properly in the ServerConfig.toml, or turn it off if you don't need it."); - } } catch (const std::exception& e) { beammp_error("Failed to start http server. Please ensure the http server is configured properly in the ServerConfig.toml, or turn it off if you don't need it. Error: " + std::string(e.what())); } diff --git a/src/TConfig.cpp b/src/TConfig.cpp index c3d4b90..f0722c3 100644 --- a/src/TConfig.cpp +++ b/src/TConfig.cpp @@ -170,7 +170,7 @@ void TConfig::CreateConfigFile() { try { if (fs::exists("Server.cfg")) { // parse it (this is weird and bad and should be removed in some future version) - ParseOldFormat(); + // ParseOldFormat(); } } catch (const std::exception& e) { beammp_error("an error occurred and was ignored during config transfer: " + std::string(e.what())); @@ -179,55 +179,6 @@ void TConfig::CreateConfigFile() { FlushToFile(); } -void TConfig::TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, const std::string_view& Env, std::string& OutValue) { - if (!Env.empty()) { - // If this environment variable exists, return a C-String and check if it's empty or not - if (const char* envp = std::getenv(Env.data()); envp != nullptr && std::strcmp(envp, "") != 0) { - OutValue = std::string(envp); - return; - } - } - if (mDisableConfig) { - return; - } - if (Table[Category.c_str()][Key.data()].is_string()) { - OutValue = Table[Category.c_str()][Key.data()].as_string(); - } -} - -void TConfig::TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, const std::string_view& Env, bool& OutValue) { - if (!Env.empty()) { - // If this environment variable exists, return a C-String and check if it's empty or not - if (const char* envp = std::getenv(Env.data()); envp != nullptr && std::strcmp(envp, "") != 0) { - auto Str = std::string(envp); - OutValue = Str == "1" || Str == "true"; - return; - } - } - if (mDisableConfig) { - return; - } - if (Table[Category.c_str()][Key.data()].is_boolean()) { - OutValue = Table[Category.c_str()][Key.data()].as_boolean(); - } -} - -void TConfig::TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, const std::string_view& Env, int& OutValue) { - if (!Env.empty()) { - // If this environment variable exists, return a C-String and check if it's empty or not - if (const char* envp = std::getenv(Env.data()); envp != nullptr && std::strcmp(envp, "") != 0) { - OutValue = int(std::strtol(envp, nullptr, 10)); - return; - } - } - if (mDisableConfig) { - return; - } - if (Table[Category.c_str()][Key.data()].is_integer()) { - OutValue = int(Table[Category.c_str()][Key.data()].as_integer()); - } -} - // This arcane template magic is needed for using lambdas as overloaded visitors // See https://en.cppreference.com/w/cpp/utility/variant/visit for reference template diff --git a/src/THeartbeatThread.cpp b/src/THeartbeatThread.cpp index 2e282a4..77a3348 100644 --- a/src/THeartbeatThread.cpp +++ b/src/THeartbeatThread.cpp @@ -54,9 +54,6 @@ void THeartbeatThread::operator()() { Last = Body; LastNormalUpdateTime = Now; - if (!Application::Settings.CustomIP.empty()) { - Body += "&ip=" + Application::Settings.CustomIP; - } auto Target = "/heartbeat"; unsigned int ResponseCode = 0; diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index 9b17bce..a18b506 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -334,22 +334,6 @@ std::shared_ptr TNetwork::Authentication(TConnection&& RawConnection) { return nullptr; } - if(!Application::Settings.Password.empty()) { // ask password - if(!TCPSend(*Client, StringToVector("S"))) { - // TODO: handle - } - beammp_info("Waiting for password"); - Data = TCPRcv(*Client); - std::string Pass = std::string(reinterpret_cast(Data.data()), Data.size()); - if(Pass != HashPassword(Application::Settings.Password)) { - beammp_debug(Client->GetName() + " attempted to connect with a wrong password"); - ClientKick(*Client, "Wrong password!"); - return {}; - } else { - beammp_debug(Client->GetName() + " used the correct password"); - } - } - beammp_debug("Name -> " + Client->GetName() + ", Guest -> " + std::to_string(Client->IsGuest()) + ", Roles -> " + Client->GetRoles()); mServer.ForEachClient([&](const std::weak_ptr& ClientPtr) -> bool { std::shared_ptr Cl; diff --git a/src/TServer.cpp b/src/TServer.cpp index db1541a..9b9138d 100644 --- a/src/TServer.cpp +++ b/src/TServer.cpp @@ -123,17 +123,6 @@ TEST_CASE("GetPidVid") { TServer::TServer(const std::vector& Arguments) { beammp_info("BeamMP Server v" + Application::ServerVersionString()); Application::SetSubsystemStatus("Server", Application::Status::Starting); - if (Arguments.size() > 1) { - Application::Settings.CustomIP = Arguments[0]; - size_t n = std::count(Application::Settings.CustomIP.begin(), Application::Settings.CustomIP.end(), '.'); - auto p = Application::Settings.CustomIP.find_first_not_of(".0123456789"); - if (p != std::string::npos || n != 3 || Application::Settings.CustomIP.substr(0, 3) == "127") { - Application::Settings.CustomIP.clear(); - beammp_warn("IP Specified is invalid! Ignoring"); - } else { - beammp_info("server started with custom IP"); - } - } Application::SetSubsystemStatus("Server", Application::Status::Good); } From 8c15b87628e674e467679f2a257f2e0356ae5956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Wed, 15 May 2024 12:52:09 +0200 Subject: [PATCH 15/43] Refactor all references to settings to use new `Settings` type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lucca Jiménez Könings --- include/Common.h | 9 +- include/TSettings.h | 10 +- src/Common.cpp | 16 ++-- src/Compat.cpp | 2 +- src/LuaAPI.cpp | 29 +++--- src/TConfig.cpp | 197 +++++++++++++-------------------------- src/TConsole.cpp | 83 ++++++++--------- src/THeartbeatThread.cpp | 27 +++--- src/TLuaEngine.cpp | 10 +- src/TNetwork.cpp | 16 ++-- src/TResourceManager.cpp | 2 +- src/TServer.cpp | 2 +- src/main.cpp | 8 +- 13 files changed, 168 insertions(+), 243 deletions(-) diff --git a/include/Common.h b/include/Common.h index 3127011..56565e3 100644 --- a/include/Common.h +++ b/include/Common.h @@ -102,8 +102,7 @@ public: static std::string PPS() { return mPPS; } static void SetPPS(const std::string& NewPPS) { mPPS = NewPPS; } - static TSettings Settings; - static inline struct Settings SettingsSingleton { }; + static inline struct Settings Settings { }; static std::vector GetBackendUrlsInOrder() { return { @@ -225,13 +224,13 @@ void RegisterThread(const std::string& str); #define luaprint(x) Application::Console().Write(_this_location + std::string("[LUA] ") + (x)) #define beammp_debug(x) \ do { \ - if (Application::Settings.DebugModeEnabled) { \ + if (Application::Settings.getAsBool(Settings::Key::General_Debug)) { \ Application::Console().Write(_this_location + std::string("[DEBUG] ") + (x)); \ } \ } while (false) #define beammp_event(x) \ do { \ - if (Application::Settings.DebugModeEnabled) { \ + if (Application::Settings.getAsBool(Settings::Key::General_Debug)) { \ Application::Console().Write(_this_location + std::string("[EVENT] ") + (x)); \ } \ } while (false) @@ -239,7 +238,7 @@ void RegisterThread(const std::string& str); #if defined(DEBUG) #define beammp_trace(x) \ do { \ - if (Application::Settings.DebugModeEnabled) { \ + if (Application::Settings.getAsBool(Settings::Key::General_Debug)) { \ Application::Console().Write(_this_location + std::string("[TRACE] ") + (x)); \ } \ } while (false) diff --git a/include/TSettings.h b/include/TSettings.h index aec0b79..e6e191b 100644 --- a/include/TSettings.h +++ b/include/TSettings.h @@ -17,6 +17,7 @@ // along with this program. If not, see . #pragma once +#include "Sync.h" #include #include #include @@ -24,7 +25,6 @@ #include #include #include -#include "Sync.h" struct ComposedKey { std::string Category; @@ -83,9 +83,9 @@ struct Settings { General_Debug }; - Sync> SettingsMap = std::unordered_map{ - { General_Description, "BeamMP Default Description" }, - { General_Tags, "Freeroam" }, + Sync> SettingsMap = std::unordered_map { + { General_Description, std::string("BeamMP Default Description") }, + { General_Tags, std::string("Freeroam") }, { General_MaxPlayers, 8 }, { General_Name, "BeamMP Server" }, { General_Map, "/levels/gridmap_v2/info.json" }, @@ -112,7 +112,7 @@ struct Settings { SettingsAccessMask // Console read/write permissions >; - Sync> InputAccessMapping = std::unordered_map{ + Sync> InputAccessMapping = std::unordered_map { { { "General", "Description" }, { General_Description, write } }, { { "General", "Tags" }, { General_Tags, write } }, { { "General", "MaxPlayers" }, { General_MaxPlayers, write } }, diff --git a/src/Common.cpp b/src/Common.cpp index c2ac242..f4a4319 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -33,8 +33,6 @@ #include "CustomAssert.h" #include "Http.h" -Application::TSettings Application::Settings = {}; - void Application::RegisterShutdownHandler(const TShutdownHandler& Handler) { std::unique_lock Lock(mShutdownHandlersMutex); if (Handler) { @@ -256,7 +254,7 @@ static std::mutex ThreadNameMapMutex {}; std::string ThreadName(bool DebugModeOverride) { auto Lock = std::unique_lock(ThreadNameMapMutex); - if (DebugModeOverride || Application::Settings.DebugModeEnabled) { + if (DebugModeOverride || Application::Settings.getAsBool(Settings::Key::General_Debug)) { auto id = std::this_thread::get_id(); if (threadNameMap.find(id) != threadNameMap.end()) { // found @@ -268,21 +266,21 @@ std::string ThreadName(bool DebugModeOverride) { TEST_CASE("ThreadName") { RegisterThread("MyThread"); - auto OrigDebug = Application::Settings.DebugModeEnabled; + auto OrigDebug = Application::Settings.getAsBool(Settings::Key::General_Debug); // ThreadName adds a space at the end, legacy but we need it still SUBCASE("Debug mode enabled") { - Application::Settings.DebugModeEnabled = true; + Application::Settings.set(Settings::Key::General_Debug, true); CHECK(ThreadName(true) == "MyThread "); CHECK(ThreadName(false) == "MyThread "); } SUBCASE("Debug mode disabled") { - Application::Settings.DebugModeEnabled = false; + Application::Settings.set(Settings::Key::General_Debug, false); CHECK(ThreadName(true) == "MyThread "); CHECK(ThreadName(false) == ""); } // cleanup - Application::Settings.DebugModeEnabled = OrigDebug; + Application::Settings.set(Settings::Key::General_Debug, OrigDebug); } void RegisterThread(const std::string& str) { @@ -296,7 +294,7 @@ void RegisterThread(const std::string& str) { #elif defined(BEAMMP_FREEBSD) ThreadId = std::to_string(getpid()); #endif - if (Application::Settings.DebugModeEnabled) { + if (Application::Settings.getAsBool(Settings::Key::General_Debug)) { std::ofstream ThreadFile(".Threads.log", std::ios::app); ThreadFile << ("Thread \"" + str + "\" is TID " + ThreadId) << std::endl; } @@ -329,7 +327,7 @@ TEST_CASE("Version::AsString") { } void LogChatMessage(const std::string& name, int id, const std::string& msg) { - if (Application::Settings.LogChat) { + if (Application::Settings.getAsBool(Settings::Key::General_LogChat)) { std::stringstream ss; ss << ThreadName(); ss << "[CHAT] "; diff --git a/src/Compat.cpp b/src/Compat.cpp index 5f95a16..145828f 100644 --- a/src/Compat.cpp +++ b/src/Compat.cpp @@ -61,7 +61,7 @@ TEST_CASE("init and reset termios") { CHECK_EQ(current.c_lflag, original.c_lflag); #ifndef BEAMMP_FREEBSD // The 'c_line' attribute seems to only exist on Linux, so we need to omit it on other platforms CHECK_EQ(current.c_line, original.c_line); -#endif +#endif CHECK_EQ(current.c_oflag, original.c_oflag); CHECK_EQ(current.c_ospeed, original.c_ospeed); } diff --git a/src/LuaAPI.cpp b/src/LuaAPI.cpp index a395f64..2e99655 100644 --- a/src/LuaAPI.cpp +++ b/src/LuaAPI.cpp @@ -21,6 +21,7 @@ #include "Common.h" #include "CustomAssert.h" #include "TLuaEngine.h" +#include "TSettings.h" #include @@ -226,56 +227,56 @@ void LuaAPI::MP::Set(int ConfigID, sol::object NewValue) { switch (ConfigID) { case 0: // debug if (NewValue.is()) { - Application::Settings.DebugModeEnabled = NewValue.as(); - beammp_info(std::string("Set `Debug` to ") + (Application::Settings.DebugModeEnabled ? "true" : "false")); + Application::Settings.set(Settings::Key::General_Debug, NewValue.as()); + beammp_info(std::string("Set `Debug` to ") + (Application::Settings.getAsBool(Settings::Key::General_Debug) ? "true" : "false")); } else { beammp_lua_error("set invalid argument [2] expected boolean"); } break; case 1: // private if (NewValue.is()) { - Application::Settings.Private = NewValue.as(); - beammp_info(std::string("Set `Private` to ") + (Application::Settings.Private ? "true" : "false")); + Application::Settings.set(Settings::Key::General_Private, NewValue.as()); + beammp_info(std::string("Set `Private` to ") + (Application::Settings.getAsBool(Settings::Key::General_Private) ? "true" : "false")); } else { beammp_lua_error("set invalid argument [2] expected boolean"); } break; case 2: // max cars if (NewValue.is()) { - Application::Settings.MaxCars = NewValue.as(); - beammp_info(std::string("Set `MaxCars` to ") + std::to_string(Application::Settings.MaxCars)); + Application::Settings.set(Settings::Key::General_MaxCars, NewValue.as()); + beammp_info(std::string("Set `MaxCars` to ") + std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxCars))); } else { beammp_lua_error("set invalid argument [2] expected integer"); } break; case 3: // max players if (NewValue.is()) { - Application::Settings.MaxPlayers = NewValue.as(); - beammp_info(std::string("Set `MaxPlayers` to ") + std::to_string(Application::Settings.MaxPlayers)); + Application::Settings.set(Settings::Key::General_MaxPlayers, NewValue.as()); + beammp_info(std::string("Set `MaxPlayers` to ") + std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxPlayers))); } else { beammp_lua_error("set invalid argument [2] expected integer"); } break; case 4: // Map if (NewValue.is()) { - Application::Settings.MapName = NewValue.as(); - beammp_info(std::string("Set `Map` to ") + Application::Settings.MapName); + Application::Settings.set(Settings::Key::General_Map, NewValue.as()); + beammp_info(std::string("Set `Map` to ") + Application::Settings.getAsString(Settings::Key::General_Map)); } else { beammp_lua_error("set invalid argument [2] expected string"); } break; case 5: // Name if (NewValue.is()) { - Application::Settings.ServerName = NewValue.as(); - beammp_info(std::string("Set `Name` to ") + Application::Settings.ServerName); + Application::Settings.set(Settings::Key::General_Name, NewValue.as()); + beammp_info(std::string("Set `Name` to ") + Application::Settings.getAsString(Settings::Key::General_Name)); } else { beammp_lua_error("set invalid argument [2] expected string"); } break; case 6: // Desc if (NewValue.is()) { - Application::Settings.ServerDesc = NewValue.as(); - beammp_info(std::string("Set `Description` to ") + Application::Settings.ServerDesc); + Application::Settings.set(Settings::Key::General_Description, NewValue.as()); + beammp_info(std::string("Set `Description` to ") + Application::Settings.getAsString(Settings::Key::General_Description)); } else { beammp_lua_error("set invalid argument [2] expected string"); } diff --git a/src/TConfig.cpp b/src/TConfig.cpp index f0722c3..daf8b7e 100644 --- a/src/TConfig.cpp +++ b/src/TConfig.cpp @@ -20,11 +20,14 @@ #include "Env.h" #include "TConfig.h" +#include "TSettings.h" #include +#include #include #include #include #include +#include // General static constexpr std::string_view StrDebug = "Debug"; @@ -120,29 +123,29 @@ void SetComment(CommentsT& Comments, const std::string& Comment) { void TConfig::FlushToFile() { // auto data = toml::parse(mConfigFileName); auto data = toml::value {}; - data["General"][StrAuthKey.data()] = Application::Settings.Key; + data["General"][StrAuthKey.data()] = Application::Settings.getAsString(Settings::Key::General_AuthKey); SetComment(data["General"][StrAuthKey.data()].comments(), " AuthKey has to be filled out in order to run the server"); - data["General"][StrLogChat.data()] = Application::Settings.LogChat; + data["General"][StrLogChat.data()] = Application::Settings.getAsBool(Settings::Key::General_LogChat); SetComment(data["General"][StrLogChat.data()].comments(), " Whether to log chat messages in the console / log"); - data["General"][StrDebug.data()] = Application::Settings.DebugModeEnabled; - data["General"][StrPrivate.data()] = Application::Settings.Private; - data["General"][StrPort.data()] = Application::Settings.Port; - data["General"][StrName.data()] = Application::Settings.ServerName; + data["General"][StrDebug.data()] = Application::Settings.getAsBool(Settings::Key::General_Debug); + data["General"][StrPrivate.data()] = Application::Settings.getAsBool(Settings::Key::General_Private); + data["General"][StrPort.data()] = Application::Settings.getAsInt(Settings::Key::General_Port); + data["General"][StrName.data()] = Application::Settings.getAsString(Settings::Key::General_Name); SetComment(data["General"][StrTags.data()].comments(), " Add custom identifying tags to your server to make it easier to find. Format should be TagA,TagB,TagC. Note the comma seperation."); - data["General"][StrTags.data()] = Application::Settings.ServerTags; - data["General"][StrMaxCars.data()] = Application::Settings.MaxCars; - data["General"][StrMaxPlayers.data()] = Application::Settings.MaxPlayers; - data["General"][StrMap.data()] = Application::Settings.MapName; - data["General"][StrDescription.data()] = Application::Settings.ServerDesc; - data["General"][StrResourceFolder.data()] = Application::Settings.Resource; + data["General"][StrTags.data()] = Application::Settings.getAsString(Settings::Key::General_Tags); + data["General"][StrMaxCars.data()] = Application::Settings.getAsInt(Settings::Key::General_MaxCars); + data["General"][StrMaxPlayers.data()] = Application::Settings.getAsInt(Settings::Key::General_MaxCars); + data["General"][StrMap.data()] = Application::Settings.getAsString(Settings::Key::General_Map); + data["General"][StrDescription.data()] = Application::Settings.getAsString(Settings::Key::General_Description); + data["General"][StrResourceFolder.data()] = Application::Settings.getAsString(Settings::Key::General_ResourceFolder); // data["General"][StrPassword.data()] = Application::Settings.Password; // SetComment(data["General"][StrPassword.data()].comments(), " Sets a password on this server, which restricts people from joining. To join, a player must enter this exact password. Leave empty ("") to disable the password."); // Misc - data["Misc"][StrHideUpdateMessages.data()] = Application::Settings.HideUpdateMessages; + data["Misc"][StrHideUpdateMessages.data()] = Application::Settings.getAsBool(Settings::Key::Misc_ImScaredOfUpdates); SetComment(data["Misc"][StrHideUpdateMessages.data()].comments(), " Hides the periodic update message which notifies you of a new server version. You should really keep this on and always update as soon as possible. For more information visit https://wiki.beammp.com/en/home/server-maintenance#updating-the-server. An update message will always appear at startup regardless."); - data["Misc"][StrSendErrors.data()] = Application::Settings.SendErrors; + data["Misc"][StrSendErrors.data()] = Application::Settings.getAsBool(Settings::Key::Misc_SendErrors); SetComment(data["Misc"][StrSendErrors.data()].comments(), " If SendErrors is `true`, the server will send helpful info about crashes and other issues back to the BeamMP developers. This info may include your config, who is on your server at the time of the error, and similar general information. This kind of data is vital in helping us diagnose and fix issues faster. This has no impact on server performance. You can opt-out of this system by setting this to `false`"); - data["Misc"][StrSendErrorsMessageEnabled.data()] = Application::Settings.SendErrorsMessageEnabled; + data["Misc"][StrSendErrorsMessageEnabled.data()] = Application::Settings.getAsBool(Settings::Key::Misc_SendErrorsShowMessage); SetComment(data["Misc"][StrSendErrorsMessageEnabled.data()].comments(), " You can turn on/off the SendErrors message you get on startup here"); std::stringstream Ss; Ss << "# This is the BeamMP-Server config file.\n" @@ -190,76 +193,59 @@ overloaded(Ts...) -> overloaded; void TConfig::TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, const std::string_view& Env, Settings::Key key) { if (!Env.empty()) { - if (const char* envp = std::getenv(Env.data()); envp != nullptr && std::strcmp(envp, "") != 0) { + if (const char* envp = std::getenv(Env.data()); + envp != nullptr && std::strcmp(envp, "") != 0) { std::visit( overloaded { [&envp, &key](std::string) { - Application::SettingsSingleton.set(key, std::string(envp)); + Application::Settings.set(key, std::string(envp)); }, [&envp, &key](int) { - Application::SettingsSingleton.set(key, int(std::strtol(envp, nullptr, 10))); + Application::Settings.set(key, int(std::strtol(envp, nullptr, 10))); }, [&envp, &key](bool) { auto Str = std::string(envp); - Application::SettingsSingleton.set(key, bool(Str == "1" || Str == "true")); + Application::Settings.set(key, bool(Str == "1" || Str == "true")); } }, - Application::SettingsSingleton.get(key)); - } - } else { - std::visit( - overloaded { - [&Table, &Category, &Key, &key](std::string) { - if (Table[Category.c_str()][Key.data()].is_string()) - Application::SettingsSingleton.set(key, Table[Category.c_str()][Key.data()].as_string()); - else - beammp_warnf("Value '{}.{}' has unexpected type, expected type 'string'", Category, Key); - }, - [&Table, &Category, &Key, &key](int) { - if (Table[Category.c_str()][Key.data()].is_integer()) - Application::SettingsSingleton.set(key, int(Table[Category.c_str()][Key.data()].as_integer())); - else - beammp_warnf("Value '{}.{}' has unexpected type, expected type 'integer'", Category, Key); - }, - [&Table, &Category, &Key, &key](bool) { - if (Table[Category.c_str()][Key.data()].is_boolean()) - Application::SettingsSingleton.set(key, Table[Category.c_str()][Key.data()].as_boolean()); - else - beammp_warnf("Value '{}.{}' has unexpected type, expected type 'boolean'", Category, Key); - } }, - Application::SettingsSingleton.get(key)); + Application::Settings.get(key)); + return; + } } + + std::visit([&Table, &Category, &Key, &key](auto&& arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + if (Table[Category.c_str()][Key.data()].is_string()) + Application::Settings.set(key, Table[Category.c_str()][Key.data()].as_string()); + else + beammp_warnf("Value '{}.{}' has unexpected type, expected type 'string'", Category, Key); + } else if constexpr (std::is_same_v) { + if (Table[Category.c_str()][Key.data()].is_integer()) + Application::Settings.set(key, int(Table[Category.c_str()][Key.data()].as_integer())); + else + beammp_warnf("Value '{}.{}' has unexpected type, expected type 'integer'", Category, Key); + } else if constexpr (std::is_same_v) { + if (Table[Category.c_str()][Key.data()].is_boolean()) + Application::Settings.set(key, Table[Category.c_str()][Key.data()].as_boolean()); + else + beammp_warnf("Value '{}.{}' has unexpected type, expected type 'boolean'", Category, Key); + } else { + throw std::logic_error { "Invalid type for config value during read attempt" }; + } + }, + Application::Settings.get(key)); } void TConfig::ParseFromFile(std::string_view name) { try { toml::value data {}; - if (!mDisableConfig) { + if (!mDisableConfig) { // todo: rename mDisableCofig to configEnabled data = toml::parse(name.data()); } + // GENERAL - TryReadValue(data, "General", StrDebug, EnvStrDebug, Application::Settings.DebugModeEnabled); - TryReadValue(data, "General", StrPrivate, EnvStrPrivate, Application::Settings.Private); - if (Env::Get(Env::Key::PROVIDER_PORT_ENV).has_value()) { - TryReadValue(data, "General", StrPort, Env::Get(Env::Key::PROVIDER_PORT_ENV).value(), Application::Settings.Port); - } else { - TryReadValue(data, "General", StrPort, EnvStrPort, Application::Settings.Port); - } - TryReadValue(data, "General", StrMaxCars, EnvStrMaxCars, Application::Settings.MaxCars); - TryReadValue(data, "General", StrMaxPlayers, EnvStrMaxPlayers, Application::Settings.MaxPlayers); - TryReadValue(data, "General", StrMap, EnvStrMap, Application::Settings.MapName); - TryReadValue(data, "General", StrName, EnvStrName, Application::Settings.ServerName); - TryReadValue(data, "General", StrDescription, EnvStrDescription, Application::Settings.ServerDesc); - TryReadValue(data, "General", StrTags, EnvStrTags, Application::Settings.ServerTags); - TryReadValue(data, "General", StrResourceFolder, EnvStrResourceFolder, Application::Settings.Resource); - TryReadValue(data, "General", StrAuthKey, EnvStrAuthKey, Application::Settings.Key); - TryReadValue(data, "General", StrLogChat, EnvStrLogChat, Application::Settings.LogChat); - TryReadValue(data, "General", StrPassword, "", Application::Settings.Password); - // Misc - TryReadValue(data, "Misc", StrSendErrors, "", Application::Settings.SendErrors); - TryReadValue(data, "Misc", StrHideUpdateMessages, "", Application::Settings.HideUpdateMessages); - TryReadValue(data, "Misc", StrSendErrorsMessageEnabled, "", Application::Settings.SendErrorsMessageEnabled); // Read into new Settings Singleton TryReadValue(data, "General", StrDebug, EnvStrDebug, Settings::Key::General_Debug); @@ -291,7 +277,7 @@ void TConfig::ParseFromFile(std::string_view name) { FlushToFile(); } // all good so far, let's check if there's a key - if (Application::Settings.Key.empty()) { + if (Application::Settings.getAsString(Settings::Key::General_AuthKey).empty()) { if (mDisableConfig) { beammp_error("No AuthKey specified in the environment."); } else { @@ -302,7 +288,7 @@ void TConfig::ParseFromFile(std::string_view name) { return; } Application::SetSubsystemStatus("Config", Application::Status::Good); - if (Application::Settings.Key.size() != 36) { + if (Application::Settings.getAsString(Settings::Key::General_AuthKey).size() != 36) { beammp_warn("AuthKey specified is the wrong length and likely isn't valid."); } } @@ -311,77 +297,24 @@ void TConfig::PrintDebug() { if (mDisableConfig) { beammp_debug("Provider turned off the generation and parsing of the ServerConfig.toml"); } - beammp_debug(std::string(StrDebug) + ": " + std::string(Application::Settings.DebugModeEnabled ? "true" : "false")); - beammp_debug(std::string(StrPrivate) + ": " + std::string(Application::Settings.Private ? "true" : "false")); - beammp_debug(std::string(StrPort) + ": " + std::to_string(Application::Settings.Port)); - beammp_debug(std::string(StrMaxCars) + ": " + std::to_string(Application::Settings.MaxCars)); - beammp_debug(std::string(StrMaxPlayers) + ": " + std::to_string(Application::Settings.MaxPlayers)); - beammp_debug(std::string(StrMap) + ": \"" + Application::Settings.MapName + "\""); - beammp_debug(std::string(StrName) + ": \"" + Application::Settings.ServerName + "\""); - beammp_debug(std::string(StrDescription) + ": \"" + Application::Settings.ServerDesc + "\""); + beammp_debug(std::string(StrDebug) + ": " + std::string(Application::Settings.getAsBool(Settings::Key::General_Debug) ? "true" : "false")); + beammp_debug(std::string(StrPrivate) + ": " + std::string(Application::Settings.getAsBool(Settings::Key::General_Private) ? "true" : "false")); + beammp_debug(std::string(StrPort) + ": " + std::to_string(Application::Settings.getAsInt(Settings::Key::General_Port))); + beammp_debug(std::string(StrMaxCars) + ": " + std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxCars))); + beammp_debug(std::string(StrMaxPlayers) + ": " + std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxPlayers))); + beammp_debug(std::string(StrMap) + ": \"" + Application::Settings.getAsString(Settings::Key::General_Map) + "\""); + beammp_debug(std::string(StrName) + ": \"" + Application::Settings.getAsString(Settings::Key::General_Name) + "\""); + beammp_debug(std::string(StrDescription) + ": \"" + Application::Settings.getAsString(Settings::Key::General_Description) + "\""); beammp_debug(std::string(StrTags) + ": " + TagsAsPrettyArray()); - beammp_debug(std::string(StrLogChat) + ": \"" + (Application::Settings.LogChat ? "true" : "false") + "\""); - beammp_debug(std::string(StrResourceFolder) + ": \"" + Application::Settings.Resource + "\""); + beammp_debug(std::string(StrLogChat) + ": \"" + (Application::Settings.getAsBool(Settings::Key::General_LogChat) ? "true" : "false") + "\""); + beammp_debug(std::string(StrResourceFolder) + ": \"" + Application::Settings.getAsString(Settings::Key::General_ResourceFolder) + "\""); // special! - beammp_debug("Key Length: " + std::to_string(Application::Settings.Key.length()) + ""); - beammp_debug("Password Protected: " + std::string(Application::Settings.Password.empty() ? "false" : "true")); + beammp_debug("Key Length: " + std::to_string(Application::Settings.getAsString(Settings::Key::General_AuthKey).length()) + ""); } -void TConfig::ParseOldFormat() { - std::ifstream File("Server.cfg"); - // read all, strip comments - std::string Content; - for (;;) { - std::string Line; - std::getline(File, Line); - if (!Line.empty() && Line.at(0) != '#') { - Line = Line.substr(0, Line.find_first_of('#')); - Content += Line + "\n"; - } - if (!File.good()) { - break; - } - } - std::stringstream Str(Content); - std::string Key, Ignore, Value; - for (;;) { - Str >> Key >> std::ws >> Ignore >> std::ws; - std::getline(Str, Value); - if (Str.eof()) { - break; - } - std::stringstream ValueStream(Value); - ValueStream >> std::ws; // strip leading whitespace if any - Value = ValueStream.str(); - if (Key == "Debug") { - Application::Settings.DebugModeEnabled = Value.find("true") != std::string::npos; - } else if (Key == "Private") { - Application::Settings.Private = Value.find("true") != std::string::npos; - } else if (Key == "Port") { - ValueStream >> Application::Settings.Port; - } else if (Key == "Cars") { - ValueStream >> Application::Settings.MaxCars; - } else if (Key == "MaxPlayers") { - ValueStream >> Application::Settings.MaxPlayers; - } else if (Key == "Map") { - Application::Settings.MapName = Value.substr(1, Value.size() - 3); - } else if (Key == "Name") { - Application::Settings.ServerName = Value.substr(1, Value.size() - 3); - } else if (Key == "Desc") { - Application::Settings.ServerDesc = Value.substr(1, Value.size() - 3); - } else if (Key == "use") { - Application::Settings.Resource = Value.substr(1, Value.size() - 3); - } else if (Key == "AuthKey") { - Application::Settings.Key = Value.substr(1, Value.size() - 3); - } else { - beammp_warn("unknown key in old auth file (ignored): " + Key); - } - Str >> std::ws; - } -} std::string TConfig::TagsAsPrettyArray() const { std::vector TagsArray = {}; - SplitString(Application::Settings.ServerTags, ',', TagsArray); + SplitString(Application::Settings.getAsString(Settings::General_Tags), ',', TagsArray); std::string Pretty = {}; for (size_t i = 0; i < TagsArray.size() - 1; ++i) { Pretty += '\"' + TagsArray[i] + "\", "; diff --git a/src/TConsole.cpp b/src/TConsole.cpp index ad7f4d8..679bca3 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -78,7 +78,7 @@ static std::string GetDate() { auto local_tm = std::localtime(&tt); char buf[30]; std::string date; - if (Application::Settings.DebugModeEnabled) { + if (Application::Settings.getAsBool(Settings::Key::General_Debug)) { std::strftime(buf, sizeof(buf), "[%d/%m/%y %T.", local_tm); date += buf; auto seconds = std::chrono::time_point_cast(now); @@ -402,11 +402,10 @@ void TConsole::Command_Settings(const std::string&, const std::vector - for(const auto& [composedKey, keyACL] : Application::SettingsSingleton.getACLMap()){ + } else if (args.front() == "list") { + // std::unordered_map + for (const auto& [composedKey, keyACL] : Application::Settings.getACLMap()) { // even though we have the value, we want to ignore it in order to make use of access // control checks - - if(keyACL.second != Settings::SettingsAccessMask::noaccess){ - - try{ - - Settings::SettingsAccessControl acl = Application::SettingsSingleton.getConsoleInputAccessMapping(composedKey); - Settings::SettingsTypeVariant keyType = Application::SettingsSingleton.get(acl.first); - std::visit( - overloaded { - [&composedKey](std::string keyValue) { - Application::Console().WriteRaw(fmt::format("{} = {}", composedKey, keyValue)); - }, - [&composedKey](int keyValue) { - Application::Console().WriteRaw(fmt::format("{} = {}", composedKey, keyValue)); - }, - [&composedKey](bool keyValue) { - Application::Console().WriteRaw(fmt::format("{} = {}", composedKey, keyValue)); - } + if (keyACL.second != Settings::SettingsAccessMask::noaccess) { - }, - keyType); - }catch(std::logic_error& e){ - beammp_errorf("Error when getting key: {}", e.what()); - } + try { + + Settings::SettingsAccessControl acl = Application::Settings.getConsoleInputAccessMapping(composedKey); + Settings::SettingsTypeVariant keyType = Application::Settings.get(acl.first); + + std::visit( + overloaded { + [&composedKey](std::string keyValue) { + Application::Console().WriteRaw(fmt::format("{} = {}", composedKey, keyValue)); + }, + [&composedKey](int keyValue) { + Application::Console().WriteRaw(fmt::format("{} = {}", composedKey, keyValue)); + }, + [&composedKey](bool keyValue) { + Application::Console().WriteRaw(fmt::format("{} = {}", composedKey, keyValue)); + } + + }, + keyType); + } catch (std::logic_error& e) { + beammp_errorf("Error when getting key: {}", e.what()); + } } } - }else { + } else { beammp_errorf("Unknown argument for cammand 'settings': {}", args.front()); Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString)); @@ -515,7 +514,7 @@ void TConsole::Command_Say(const std::string& FullCmd) { if (FullCmd.size() > 3) { auto Message = FullCmd.substr(4); LuaAPI::MP::SendChatMessage(-1, Message); - if (!Application::Settings.LogChat) { + if (!Application::Settings.getAsBool(Settings::Key::General_LogChat)) { Application::Console().WriteRaw("Chat message sent!"); } } diff --git a/src/THeartbeatThread.cpp b/src/THeartbeatThread.cpp index 77a3348..4148439 100644 --- a/src/THeartbeatThread.cpp +++ b/src/THeartbeatThread.cpp @@ -64,7 +64,7 @@ void THeartbeatThread::operator()() { T = Http::POST(Url, 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode, { { "api-v", "2" } }); Doc.Parse(T.data(), T.size()); if (Doc.HasParseError() || !Doc.IsObject()) { - if (!Application::Settings.Private) { + if (!Application::Settings.getAsBool(Settings::Key::General_Private)) { beammp_trace("Backend response failed to parse as valid json"); beammp_trace("Response was: `" + T + "`"); } @@ -104,12 +104,12 @@ void THeartbeatThread::operator()() { beammp_error("Missing/invalid json members in backend response"); } } else { - if (!Application::Settings.Private) { + if (!Application::Settings.getAsBool(Settings::Key::General_Private)) { beammp_warn("Backend failed to respond to a heartbeat. Your server may temporarily disappear from the server list. This is not an error, and will likely resolve itself soon. Direct connect will still work."); } } - if (Ok && !isAuth && !Application::Settings.Private) { + if (Ok && !isAuth && !Application::Settings.getAsBool(Settings::Key::General_Private)) { if (Status == "2000") { beammp_info(("Authenticated! " + Message)); isAuth = true; @@ -123,10 +123,10 @@ void THeartbeatThread::operator()() { beammp_error("Backend REFUSED the auth key. Reason: " + Message); } } - if (isAuth || Application::Settings.Private) { + if (isAuth || Application::Settings.getAsBool(Settings::Key::General_Private)) { Application::SetSubsystemStatus("Heartbeat", Application::Status::Good); } - if (!Application::Settings.HideUpdateMessages && UpdateReminderCounter % 5) { + if (!Application::Settings.getAsBool(Settings::Key::Misc_ImScaredOfUpdates) && UpdateReminderCounter % 5) { Application::CheckForUpdates(); } } @@ -135,22 +135,21 @@ void THeartbeatThread::operator()() { std::string THeartbeatThread::GenerateCall() { std::stringstream Ret; - Ret << "uuid=" << Application::Settings.Key + Ret << "uuid=" << Application::Settings.getAsString(Settings::Key::General_AuthKey) << "&players=" << mServer.ClientCount() - << "&maxplayers=" << Application::Settings.MaxPlayers - << "&port=" << Application::Settings.Port - << "&map=" << Application::Settings.MapName - << "&private=" << (Application::Settings.Private ? "true" : "false") + << "&maxplayers=" << Application::Settings.getAsInt(Settings::Key::General_MaxPlayers) + << "&port=" << Application::Settings.getAsInt(Settings::Key::General_Port) + << "&map=" << Application::Settings.getAsString(Settings::Key::General_Map) + << "&private=" << (Application::Settings.getAsBool(Settings::Key::General_Private) ? "true" : "false") << "&version=" << Application::ServerVersionString() << "&clientversion=" << std::to_string(Application::ClientMajorVersion()) + ".0" // FIXME: Wtf. - << "&name=" << Application::Settings.ServerName - << "&tags=" << Application::Settings.ServerTags + << "&name=" << Application::Settings.getAsString(Settings::Key::General_Name) + << "&tags=" << Application::Settings.getAsString(Settings::Key::General_Tags) << "&modlist=" << mResourceManager.TrimmedList() << "&modstotalsize=" << mResourceManager.MaxModSize() << "&modstotal=" << mResourceManager.ModsLoaded() << "&playerslist=" << GetPlayers() - << "&desc=" << Application::Settings.ServerDesc - << "&pass=" << (Application::Settings.Password.empty() ? "false" : "true"); + << "&desc=" << Application::Settings.getAsString(Settings::Key::General_Description); return Ret.str(); } THeartbeatThread::THeartbeatThread(TResourceManager& ResourceManager, TServer& Server) diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 031e5cc..121044b 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -35,11 +35,11 @@ TLuaEngine* LuaAPI::MP::Engine; TLuaEngine::TLuaEngine() - : mResourceServerPath(fs::path(Application::Settings.Resource) / "Server") { + : mResourceServerPath(fs::path(Application::Settings.getAsString(Settings::Key::General_ResourceFolder)) / "Server") { Application::SetSubsystemStatus("LuaEngine", Application::Status::Starting); LuaAPI::MP::Engine = this; - if (!fs::exists(Application::Settings.Resource)) { - fs::create_directory(Application::Settings.Resource); + if (!fs::exists(Application::Settings.getAsString(Settings::Key::General_ResourceFolder))) { + fs::create_directory(Application::Settings.getAsString(Settings::Key::General_ResourceFolder)); } if (!fs::exists(mResourceServerPath)) { fs::create_directory(mResourceServerPath); @@ -55,7 +55,7 @@ TLuaEngine::TLuaEngine() } TEST_CASE("TLuaEngine ctor & dtor") { - Application::Settings.Resource = "beammp_server_test_resources"; + Application::Settings.set(Settings::Key::General_ResourceFolder, "beammp_server_test_resources"); TLuaEngine engine; Application::GracefullyShutdown(); } @@ -836,7 +836,7 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, TLuaStateI ToPrint += LuaAPI::LuaToString(static_cast(arg)); ToPrint += "\t"; } - if (Application::Settings.DebugModeEnabled) { + if (Application::Settings.getAsBool(Settings::Key::General_Debug)) { beammp_lua_log("DEBUG", mStateId, ToPrint); } }); diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index a18b506..114d59c 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -80,7 +80,7 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R void TNetwork::UDPServerMain() { RegisterThread("UDPServer"); - ip::udp::endpoint UdpListenEndpoint(ip::address::from_string("0.0.0.0"), Application::Settings.Port); + ip::udp::endpoint UdpListenEndpoint(ip::address::from_string("0.0.0.0"), Application::Settings.getAsInt(Settings::Key::General_Port)); boost::system::error_code ec; mUDPSock.open(UdpListenEndpoint.protocol(), ec); if (ec) { @@ -95,8 +95,8 @@ void TNetwork::UDPServerMain() { Application::GracefullyShutdown(); } Application::SetSubsystemStatus("UDPNetwork", Application::Status::Good); - beammp_info(("Vehicle data network online on port ") + std::to_string(Application::Settings.Port) + (" with a Max of ") - + std::to_string(Application::Settings.MaxPlayers) + (" Clients")); + beammp_info(("Vehicle data network online on port ") + std::to_string(Application::Settings.getAsInt(Settings::Key::General_Port)) + (" with a Max of ") + + std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxPlayers)) + (" Clients")); while (!Application::IsShuttingDown()) { try { ip::udp::endpoint client {}; @@ -133,7 +133,7 @@ void TNetwork::UDPServerMain() { void TNetwork::TCPServerMain() { RegisterThread("TCPServer"); - ip::tcp::endpoint ListenEp(ip::address::from_string("0.0.0.0"), Application::Settings.Port); + ip::tcp::endpoint ListenEp(ip::address::from_string("0.0.0.0"), Application::Settings.getAsInt(Settings::Key::General_Port)); ip::tcp::socket Listener(mServer.IoCtx()); boost::system::error_code ec; Listener.open(ListenEp.protocol(), ec); @@ -376,7 +376,7 @@ std::shared_ptr TNetwork::Authentication(TConnection&& RawConnection) { return {}; } - if (mServer.ClientCount() < size_t(Application::Settings.MaxPlayers)) { + if (mServer.ClientCount() < size_t(Application::Settings.getAsInt(Settings::Key::General_MaxPlayers))) { beammp_info("Identification success"); mServer.InsertClient(Client); TCPClient(Client); @@ -569,7 +569,7 @@ void TNetwork::TCPClient(const std::weak_ptr& c) { } void TNetwork::UpdatePlayer(TClient& Client) { - std::string Packet = ("Ss") + std::to_string(mServer.ClientCount()) + "/" + std::to_string(Application::Settings.MaxPlayers) + ":"; + std::string Packet = ("Ss") + std::to_string(mServer.ClientCount()) + "/" + std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxPlayers)) + ":"; mServer.ForEachClient([&](const std::weak_ptr& ClientPtr) -> bool { ReadLock Lock(mServer.GetClientMutex()); if (!ClientPtr.expired()) { @@ -644,7 +644,7 @@ void TNetwork::OnConnect(const std::weak_ptr& c) { SyncResources(*LockedClient); if (LockedClient->IsDisconnected()) return; - (void)Respond(*LockedClient, StringToVector("M" + Application::Settings.MapName), true); // Send the Map on connect + (void)Respond(*LockedClient, StringToVector("M" + Application::Settings.getAsString(Settings::Key::General_Map)), true); // Send the Map on connect beammp_info(LockedClient->GetName() + " : Connected"); LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onPlayerJoining", "", LockedClient->GetID())); } @@ -703,7 +703,7 @@ void TNetwork::SendFile(TClient& c, const std::string& UnsafeName) { return; } auto FileName = fs::path(UnsafeName).filename().string(); - FileName = Application::Settings.Resource + "/Client/" + FileName; + FileName = Application::Settings.getAsString(Settings::Key::General_ResourceFolder) + "/Client/" + FileName; if (!std::filesystem::exists(FileName)) { if (!TCPSend(c, StringToVector("CO"))) { diff --git a/src/TResourceManager.cpp b/src/TResourceManager.cpp index 00fff82..5582af3 100644 --- a/src/TResourceManager.cpp +++ b/src/TResourceManager.cpp @@ -25,7 +25,7 @@ namespace fs = std::filesystem; TResourceManager::TResourceManager() { Application::SetSubsystemStatus("ResourceManager", Application::Status::Starting); - std::string Path = Application::Settings.Resource + "/Client"; + std::string Path = Application::Settings.getAsString(Settings::Key::General_ResourceFolder) + "/Client"; if (!fs::exists(Path)) fs::create_directories(Path); for (const auto& entry : fs::directory_iterator(Path)) { diff --git a/src/TServer.cpp b/src/TServer.cpp index 9b9138d..215ae0a 100644 --- a/src/TServer.cpp +++ b/src/TServer.cpp @@ -286,7 +286,7 @@ bool TServer::ShouldSpawn(TClient& c, const std::string& CarJson, int ID) { c.SetUnicycleID(ID); return true; } else { - return c.GetCarCount() < Application::Settings.MaxCars; + return c.GetCarCount() < Application::Settings.getAsInt(Settings::Key::General_MaxCars); } } diff --git a/src/main.cpp b/src/main.cpp index cb5003d..b93ff77 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -151,7 +151,7 @@ int BeamMPServerMain(MainArguments Arguments) { beammp_errorf("Custom port requested via --port is invalid: '{}'", Port.value()); return 1; } else { - Application::Settings.Port = P; + Application::Settings.set(Settings::Key::General_Port, P); beammp_info("Custom port requested via commandline arguments: " + Port.value()); } } @@ -197,11 +197,7 @@ int BeamMPServerMain(MainArguments Arguments) { PPSMonitor.SetNetwork(Network); Application::CheckForUpdates(); - TPluginMonitor PluginMonitor(fs::path(Application::Settings.Resource) / "Server", LuaEngine); - - if (Application::Settings.HTTPServerEnabled) { - Http::Server::THttpServerInstance HttpServerInstance {}; - } + TPluginMonitor PluginMonitor(fs::path(Application::Settings.getAsString(Settings::Key::General_ResourceFolder)) / "Server", LuaEngine); Application::SetSubsystemStatus("Main", Application::Status::Good); RegisterThread("Main(Waiting)"); From bcd4b5a2355d74385f3fd7e613418dce7f42a857 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Wed, 15 May 2024 12:54:50 +0200 Subject: [PATCH 16/43] Fix Debug asserts on FreeBSD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lucca Jiménez Könings --- include/CustomAssert.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/CustomAssert.h b/include/CustomAssert.h index 71094e6..d45a8c1 100644 --- a/include/CustomAssert.h +++ b/include/CustomAssert.h @@ -32,6 +32,7 @@ #include #include "Common.h" +#include "Compat.h" static const char* const ANSI_RESET = "\u001b[0m"; @@ -56,7 +57,7 @@ static const char* const ANSI_WHITE_BOLD = "\u001b[37;1m"; static const char* const ANSI_BOLD = "\u001b[1m"; static const char* const ANSI_UNDERLINE = "\u001b[4m"; -#ifdef DEBUG +#if defined(DEBUG) && !defined(BEAMMP_FREEBSD) #include inline void _assert([[maybe_unused]] const char* file, [[maybe_unused]] const char* function, [[maybe_unused]] unsigned line, [[maybe_unused]] const char* condition_string, [[maybe_unused]] bool result) { @@ -81,8 +82,8 @@ inline void _assert([[maybe_unused]] const char* file, [[maybe_unused]] const ch beammp_errorf("Assertion failed in '{}:{}': {}.", __func__, _line, #cond); \ } \ } while (false) -#define beammp_assert_not_reachable() \ - do { \ +#define beammp_assert_not_reachable() \ + do { \ beammp_errorf("Assertion failed in '{}:{}': Unreachable code reached. This may result in a crash or undefined state of the program.", __func__, _line); \ } while (false) #endif // DEBUG From a5e3fc8fb93c261d1c0d1854f192b088a8dacaa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Wed, 15 May 2024 12:57:08 +0200 Subject: [PATCH 17/43] Add Sync.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lucca Jiménez Könings --- include/Sync.h | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 include/Sync.h diff --git a/include/Sync.h b/include/Sync.h new file mode 100644 index 0000000..82c8b57 --- /dev/null +++ b/include/Sync.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include + +/// This header provides convenience aliases for synchronization primitives. + +template +using Sync = boost::synchronized_value; From 3609fd77ecd6af567f283817d1800401e54a6628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Wed, 15 May 2024 13:15:54 +0200 Subject: [PATCH 18/43] Potential fix for compiler issues on Ubuntu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lucca Jiménez Könings --- src/TConsole.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 679bca3..2079b8d 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -404,7 +404,7 @@ void TConsole::Command_Settings(const std::string&, const std::vector Date: Wed, 15 May 2024 13:40:25 +0200 Subject: [PATCH 19/43] Add issue with implicit argument type conversions for `Settings.set()` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lucca Jiménez Könings --- include/TSettings.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/TSettings.h b/include/TSettings.h index e6e191b..bf8e522 100644 --- a/include/TSettings.h +++ b/include/TSettings.h @@ -213,6 +213,11 @@ struct Settings { } map->at(key) = value; } + // Additional set overload for const char*, to avoid implicit conversions when set is + // invoked with string literals rather than std::strings + void set(Key key, const char* value){ + set(key, std::string(value)); + } const std::unordered_map getACLMap() const { return *InputAccessMapping; From 3a8f4ded29600d1ef3f75506328395ad17b0fe60 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Fri, 17 May 2024 10:50:50 +0200 Subject: [PATCH 20/43] fix errors in Settings::set --- include/TSettings.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/TSettings.h b/include/TSettings.h index bf8e522..48adf54 100644 --- a/include/TSettings.h +++ b/include/TSettings.h @@ -184,10 +184,10 @@ struct Settings { void set(Key key, std::string value) { auto map = SettingsMap.synchronize(); if (!map->contains(key)) { - throw std::logic_error { "Undefined setting key accessed in Settings::getAsString" }; + throw std::logic_error { "Undefined setting key accessed in Settings::set(std::string)" }; } if (!std::holds_alternative(map->at(key))) { - throw std::logic_error { "Wrong value type in Settings::get: std::string" }; + throw std::logic_error { fmt::format("Wrong value type in Settings::set(std::string): index {}", map->at(key).index()) }; } map->at(key) = value; } @@ -195,10 +195,10 @@ struct Settings { void set(Key key, int value) { auto map = SettingsMap.synchronize(); if (!map->contains(key)) { - throw std::logic_error { "Undefined setting key accessed in Settings::getAsString" }; + throw std::logic_error { "Undefined setting key accessed in Settings::set(int)" }; } if (!std::holds_alternative(map->at(key))) { - throw std::logic_error { "Wrong value type in Settings::get: std::string" }; + throw std::logic_error { fmt::format("Wrong value type in Settings::set(int): index {}", map->at(key).index()) }; } map->at(key) = value; } @@ -206,10 +206,10 @@ struct Settings { void set(Key key, bool value) { auto map = SettingsMap.synchronize(); if (!map->contains(key)) { - throw std::logic_error { "Undefined setting key accessed in Settings::getAsString" }; + throw std::logic_error { "Undefined setting key accessed in Settings::set(bool)" }; } if (!std::holds_alternative(map->at(key))) { - throw std::logic_error { "Wrong value type in Settings::get: std::string" }; + throw std::logic_error { fmt::format("Wrong value type in Settings::set(bool): index {}", map->at(key).index()) }; } map->at(key) = value; } From 31ce0cc7de39a196a1ea7d8decb798c8f4069601 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Fri, 17 May 2024 11:08:05 +0200 Subject: [PATCH 21/43] add unit test + concept constraints for set(string) --- include/TSettings.h | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/include/TSettings.h b/include/TSettings.h index 48adf54..89adc65 100644 --- a/include/TSettings.h +++ b/include/TSettings.h @@ -18,7 +18,9 @@ #pragma once #include "Sync.h" +#include #include +#include #include #include #include @@ -84,8 +86,8 @@ struct Settings { }; Sync> SettingsMap = std::unordered_map { - { General_Description, std::string("BeamMP Default Description") }, - { General_Tags, std::string("Freeroam") }, + { General_Description, "BeamMP Default Description" }, + { General_Tags, "Freeroam" }, { General_MaxPlayers, 8 }, { General_Name, "BeamMP Server" }, { General_Map, "/levels/gridmap_v2/info.json" }, @@ -181,7 +183,8 @@ struct Settings { return map->at(key); } - void set(Key key, std::string value) { + template StringLike> + void set(Key key, const StringLike& value) { auto map = SettingsMap.synchronize(); if (!map->contains(key)) { throw std::logic_error { "Undefined setting key accessed in Settings::set(std::string)" }; @@ -215,7 +218,7 @@ struct Settings { } // Additional set overload for const char*, to avoid implicit conversions when set is // invoked with string literals rather than std::strings - void set(Key key, const char* value){ + void set(Key key, const char* value) { set(key, std::string(value)); } @@ -319,3 +322,14 @@ bool HideUpdateMessages { false }; }*/ + +TEST_CASE("settings variant functions") { + Settings settings; + settings.set(Settings::General_Name, "hello, world"); + CHECK_EQ(settings.getAsString(Settings::General_Name), "hello, world"); + settings.set(Settings::General_Name, std::string("hello, world")); + CHECK_EQ(settings.getAsString(Settings::General_Name), "hello, world"); + + CHECK_THROWS(settings.set(Settings::General_Debug, "hello, world")); + CHECK_NOTHROW(settings.set(Settings::General_Debug, false)); +} From 4c9fbc250a307f65829f947a4dbca7b246585012 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Fri, 17 May 2024 11:53:51 +0200 Subject: [PATCH 22/43] Change concepts in Settings.set + add test for remaining set overloads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lucca Jiménez Könings --- include/TSettings.h | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/include/TSettings.h b/include/TSettings.h index 89adc65..e8c0e36 100644 --- a/include/TSettings.h +++ b/include/TSettings.h @@ -183,8 +183,7 @@ struct Settings { return map->at(key); } - template StringLike> - void set(Key key, const StringLike& value) { + void set(Key key, const std::string& value) { auto map = SettingsMap.synchronize(); if (!map->contains(key)) { throw std::logic_error { "Undefined setting key accessed in Settings::set(std::string)" }; @@ -194,8 +193,8 @@ struct Settings { } map->at(key) = value; } - - void set(Key key, int value) { + template T> + void set(Key key, T value) { auto map = SettingsMap.synchronize(); if (!map->contains(key)) { throw std::logic_error { "Undefined setting key accessed in Settings::set(int)" }; @@ -205,8 +204,8 @@ struct Settings { } map->at(key) = value; } - - void set(Key key, bool value) { + template T> + void set(Key key, T value) { auto map = SettingsMap.synchronize(); if (!map->contains(key)) { throw std::logic_error { "Undefined setting key accessed in Settings::set(bool)" }; @@ -216,11 +215,6 @@ struct Settings { } map->at(key) = value; } - // Additional set overload for const char*, to avoid implicit conversions when set is - // invoked with string literals rather than std::strings - void set(Key key, const char* value) { - set(key, std::string(value)); - } const std::unordered_map getACLMap() const { return *InputAccessMapping; @@ -329,6 +323,8 @@ TEST_CASE("settings variant functions") { CHECK_EQ(settings.getAsString(Settings::General_Name), "hello, world"); settings.set(Settings::General_Name, std::string("hello, world")); CHECK_EQ(settings.getAsString(Settings::General_Name), "hello, world"); + settings.set(Settings::General_MaxPlayers, 12); + CHECK_EQ(settings.getAsInt(Settings::General_MaxPlayers), 12); CHECK_THROWS(settings.set(Settings::General_Debug, "hello, world")); CHECK_NOTHROW(settings.set(Settings::General_Debug, false)); From 67db9358e1fe8862184c425635e31e5767bc2f04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Tue, 21 May 2024 11:50:46 +0200 Subject: [PATCH 23/43] Fix concepts related error (for compat with gcc9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lucca Jiménez Könings --- include/TSettings.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/include/TSettings.h b/include/TSettings.h index e8c0e36..b9c74c4 100644 --- a/include/TSettings.h +++ b/include/TSettings.h @@ -193,8 +193,9 @@ struct Settings { } map->at(key) = value; } - template T> - void set(Key key, T value) { + + template, bool> = true> + void set(Key key, Integer value) { auto map = SettingsMap.synchronize(); if (!map->contains(key)) { throw std::logic_error { "Undefined setting key accessed in Settings::set(int)" }; @@ -204,8 +205,8 @@ struct Settings { } map->at(key) = value; } - template T> - void set(Key key, T value) { + template, bool> = true> + void set(Key key, Boolean value) { auto map = SettingsMap.synchronize(); if (!map->contains(key)) { throw std::logic_error { "Undefined setting key accessed in Settings::set(bool)" }; From 84f5f95e54fb99f5ed92c0afdd2956327deff532 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Tue, 21 May 2024 13:38:37 +0200 Subject: [PATCH 24/43] Refactor: feedback from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lucca Jiménez Könings --- CMakeLists.txt | 2 + include/Common.h | 29 +--- include/Settings.h | 187 +++++++++++++++++++++++++ include/TConfig.h | 5 +- include/TSettings.h | 332 -------------------------------------------- src/LuaAPI.cpp | 2 +- src/Settings.cpp | 132 ++++++++++++++++++ src/TConfig.cpp | 2 +- src/TConsole.cpp | 6 +- src/main.cpp | 2 +- 10 files changed, 330 insertions(+), 369 deletions(-) create mode 100644 include/Settings.h delete mode 100644 include/TSettings.h create mode 100644 src/Settings.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f4e273..0527453 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,7 @@ set(PRJ_HEADERS include/TServer.h include/VehicleData.h include/Env.h + include/Settings.h ) # add all source files (.cpp) to this, except the one with main() set(PRJ_SOURCES @@ -72,6 +73,7 @@ set(PRJ_SOURCES src/TServer.cpp src/VehicleData.cpp src/Env.cpp + src/Settings.cpp ) find_package(Lua REQUIRED) diff --git a/include/Common.h b/include/Common.h index 56565e3..259cb1d 100644 --- a/include/Common.h +++ b/include/Common.h @@ -37,7 +37,7 @@ namespace fs = std::filesystem; #include "TConsole.h" -#include "TSettings.h" +#include "Settings.h" struct Version { uint8_t major; @@ -59,32 +59,7 @@ using SparseArray = std::unordered_map; class Application final { public: // types - struct TSettings { - std::string ServerName { "BeamMP Server" }; - std::string ServerDesc { "BeamMP Default Description" }; - std::string ServerTags { "Freeroam" }; - std::string Resource { "Resources" }; - std::string MapName { "/levels/gridmap_v2/info.json" }; - std::string Key {}; - std::string Password {}; - std::string SSLKeyPath { "./.ssl/HttpServer/key.pem" }; - std::string SSLCertPath { "./.ssl/HttpServer/cert.pem" }; - bool HTTPServerEnabled { false }; - int MaxPlayers { 8 }; - bool Private { true }; - int MaxCars { 1 }; - bool DebugModeEnabled { false }; - int Port { 30814 }; - std::string CustomIP {}; - bool LogChat { true }; - bool SendErrors { true }; - bool SendErrorsMessageEnabled { true }; - int HTTPServerPort { 8080 }; - std::string HTTPServerIP { "127.0.0.1" }; - bool HTTPServerUseSSL { false }; - bool HideUpdateMessages { false }; - [[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); } - }; + using TShutdownHandler = std::function; diff --git a/include/Settings.h b/include/Settings.h new file mode 100644 index 0000000..2f9565c --- /dev/null +++ b/include/Settings.h @@ -0,0 +1,187 @@ +// BeamMP, the BeamNG.drive multiplayer mod. +// Copyright (C) 2024 BeamMP Ltd., BeamMP team and contributors. +// +// BeamMP Ltd. can be contacted by electronic mail via contact@beammp.com. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#pragma once +#include "Sync.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ComposedKey { + std::string Category; + std::string Key; + + bool operator==(const ComposedKey& rhs) const { + return (this->Category == rhs.Category && this->Key == rhs.Key); + } +}; + +template <> +struct fmt::formatter : formatter { + auto format(ComposedKey key, format_context& ctx) const; +}; + +inline auto fmt::formatter::format(ComposedKey key, fmt::format_context& ctx) const { + std::string key_metadata = fmt::format("{}::{}", key.Category, key.Key); + return formatter::format(key_metadata, ctx); +} + +namespace std { +template <> +class hash { +public: + std::uint64_t operator()(const ComposedKey& key) const { + std::hash hash_fn; + return hash_fn(key.Category + key.Key); + } +}; +} + +struct Settings { + using SettingsTypeVariant = std::variant; + + enum Key { + // Keys that correspond to the keys set in TOML + // Keys have their TOML section name as prefix + + // [Misc] + Misc_SendErrorsShowMessage, + Misc_SendErrors, + Misc_ImScaredOfUpdates, + + // [General] + General_Description, + General_Tags, + General_MaxPlayers, + General_Name, + General_Map, + General_AuthKey, + General_Private, + General_Port, + General_MaxCars, + General_LogChat, + General_ResourceFolder, + General_Debug + }; + + Sync> SettingsMap = std::unordered_map { + { General_Description, "BeamMP Default Description" }, + { General_Tags, "Freeroam" }, + { General_MaxPlayers, 8 }, + { General_Name, "BeamMP Server" }, + { General_Map, "/levels/gridmap_v2/info.json" }, + { General_AuthKey, "" }, + { General_Private, true }, + { General_Port, 30814 }, + { General_MaxCars, 1 }, + { General_LogChat, true }, + { General_ResourceFolder, "Resources" }, + { General_Debug, false }, + { Misc_SendErrorsShowMessage, true }, + { Misc_SendErrors, true }, + { Misc_ImScaredOfUpdates, true } + }; + + enum SettingsAccessMask { + READ_ONLY, // Value can be read from console + READ_WRITE, // Value can be read and written to from console + NO_ACCESS // Value is inaccessible from console (no read OR write) + }; + + using SettingsAccessControl = std::pair< + Key, // The Key's corresponding enum encoding + SettingsAccessMask // Console read/write permissions + >; + + Sync> InputAccessMapping = std::unordered_map { + { { "General", "Description" }, { General_Description, READ_WRITE } }, + { { "General", "Tags" }, { General_Tags, READ_WRITE } }, + { { "General", "MaxPlayers" }, { General_MaxPlayers, READ_WRITE } }, + { { "General", "Name" }, { General_Name, READ_WRITE } }, + { { "General", "Map" }, { General_Map, READ_WRITE } }, + { { "General", "AuthKey" }, { General_AuthKey, READ_WRITE } }, + { { "General", "Private" }, { General_Private, READ_ONLY } }, + { { "General", "Port" }, { General_Port, READ_ONLY } }, + { { "General", "MaxCars" }, { General_MaxCars, READ_WRITE } }, + { { "General", "LogChat" }, { General_LogChat, READ_ONLY } }, + { { "General", "ResourceFolder" }, { General_ResourceFolder, READ_ONLY } }, + { { "General", "Debug" }, { General_Debug, READ_WRITE } }, + { { "Misc", "SendErrorsShowMessage" }, { Misc_SendErrorsShowMessage, READ_WRITE } }, + { { "Misc", "SendErrors" }, { Misc_SendErrors, READ_WRITE } }, + { { "Misc", "ImScaredOfUpdates" }, { Misc_ImScaredOfUpdates, READ_WRITE } } + }; + + std::string getAsString(Key key); + + int getAsInt(Key key); + + bool getAsBool(Key key); + + SettingsTypeVariant get(Key key); + + void set(Key key, const std::string& value); + + template , bool> = true> + void set(Key key, Integer value) { + auto map = SettingsMap.synchronize(); + if (!map->contains(key)) { + throw std::logic_error { "Undefined setting key accessed in Settings::set(int)" }; + } + if (!std::holds_alternative(map->at(key))) { + throw std::logic_error { fmt::format("Wrong value type in Settings::set(int): index {}", map->at(key).index()) }; + } + map->at(key) = value; + } + template , bool> = true> + void set(Key key, Boolean value) { + auto map = SettingsMap.synchronize(); + if (!map->contains(key)) { + throw std::logic_error { "Undefined setting key accessed in Settings::set(bool)" }; + } + if (!std::holds_alternative(map->at(key))) { + throw std::logic_error { fmt::format("Wrong value type in Settings::set(bool): index {}", map->at(key).index()) }; + } + map->at(key) = value; + } + + const std::unordered_map getAccessControlMap() const; + SettingsAccessControl getConsoleInputAccessMapping(const ComposedKey& keyName); + + void setConsoleInputAccessMapping(const ComposedKey& keyName, const std::string& value); + void setConsoleInputAccessMapping(const ComposedKey& keyName, int value); + void setConsoleInputAccessMapping(const ComposedKey& keyName, bool value); +}; + +TEST_CASE("settings variant functions") { + Settings settings; + settings.set(Settings::General_Name, "hello, world"); + CHECK_EQ(settings.getAsString(Settings::General_Name), "hello, world"); + settings.set(Settings::General_Name, std::string("hello, world")); + CHECK_EQ(settings.getAsString(Settings::General_Name), "hello, world"); + settings.set(Settings::General_MaxPlayers, 12); + CHECK_EQ(settings.getAsInt(Settings::General_MaxPlayers), 12); + + CHECK_THROWS(settings.set(Settings::General_Debug, "hello, world")); + CHECK_NOTHROW(settings.set(Settings::General_Debug, false)); +} diff --git a/include/TConfig.h b/include/TConfig.h index f84a5bc..c510fb5 100644 --- a/include/TConfig.h +++ b/include/TConfig.h @@ -19,7 +19,7 @@ #pragma once #include "Common.h" -#include "TSettings.h" +#include "Settings.h" #include #include @@ -41,9 +41,6 @@ public: private: void CreateConfigFile(); void ParseFromFile(std::string_view name); - void TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, const std::string_view& Env, std::string& OutValue); - void TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, const std::string_view& Env, bool& OutValue); - void TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, const std::string_view& Env, int& OutValue); void TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, const std::string_view& Env, Settings::Key key); void ParseOldFormat(); diff --git a/include/TSettings.h b/include/TSettings.h deleted file mode 100644 index b9c74c4..0000000 --- a/include/TSettings.h +++ /dev/null @@ -1,332 +0,0 @@ -// BeamMP, the BeamNG.drive multiplayer mod. -// Copyright (C) 2024 BeamMP Ltd., BeamMP team and contributors. -// -// BeamMP Ltd. can be contacted by electronic mail via contact@beammp.com. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -#pragma once -#include "Sync.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct ComposedKey { - std::string Category; - std::string Key; - - bool operator==(const ComposedKey& rhs) const { - return (this->Category == rhs.Category && this->Key == rhs.Key); - } -}; - -template <> -struct fmt::formatter : formatter { - auto format(ComposedKey key, format_context& ctx) const; -}; - -inline auto fmt::formatter::format(ComposedKey key, fmt::format_context& ctx) const { - std::string key_metadata = fmt::format("{}::{}", key.Category, key.Key); - return formatter::format(key_metadata, ctx); -} - -namespace std { -template <> -class hash { -public: - std::uint64_t operator()(const ComposedKey& key) const { - std::hash hash_fn; - return hash_fn(key.Category + key.Key); - } -}; -} - -struct Settings { - using SettingsTypeVariant = std::variant; - - enum Key { - // Keys that correspond to the keys set in TOML - // Keys have their TOML section name as prefix - - // [Misc] - Misc_SendErrorsShowMessage, - Misc_SendErrors, - Misc_ImScaredOfUpdates, - - // [General] - General_Description, - General_Tags, - General_MaxPlayers, - General_Name, - General_Map, - General_AuthKey, - General_Private, - General_Port, - General_MaxCars, - General_LogChat, - General_ResourceFolder, - General_Debug - }; - - Sync> SettingsMap = std::unordered_map { - { General_Description, "BeamMP Default Description" }, - { General_Tags, "Freeroam" }, - { General_MaxPlayers, 8 }, - { General_Name, "BeamMP Server" }, - { General_Map, "/levels/gridmap_v2/info.json" }, - { General_AuthKey, "" }, - { General_Private, true }, - { General_Port, 30814 }, - { General_MaxCars, 1 }, - { General_LogChat, true }, - { General_ResourceFolder, "Resources" }, - { General_Debug, false }, - { Misc_SendErrorsShowMessage, true }, - { Misc_SendErrors, true }, - { Misc_ImScaredOfUpdates, true } - }; - - enum SettingsAccessMask { - read, // Value can be read from console - write, // Value can be read and written to from console - noaccess // Value is inaccessible from console (no read OR write) - }; - - using SettingsAccessControl = std::pair< - Key, // The Key's corresponding enum encoding - SettingsAccessMask // Console read/write permissions - >; - - Sync> InputAccessMapping = std::unordered_map { - { { "General", "Description" }, { General_Description, write } }, - { { "General", "Tags" }, { General_Tags, write } }, - { { "General", "MaxPlayers" }, { General_MaxPlayers, write } }, - { { "General", "Name" }, { General_Name, write } }, - { { "General", "Map" }, { General_Map, read } }, - { { "General", "AuthKey" }, { General_AuthKey, noaccess } }, - { { "General", "Private" }, { General_Private, read } }, - { { "General", "Port" }, { General_Port, read } }, - { { "General", "MaxCars" }, { General_MaxCars, write } }, - { { "General", "LogChat" }, { General_LogChat, read } }, - { { "General", "ResourceFolder" }, { General_ResourceFolder, read } }, - { { "General", "Debug" }, { General_Debug, write } }, - { { "Misc", "SendErrorsShowMessage" }, { Misc_SendErrorsShowMessage, noaccess } }, - { { "Misc", "SendErrors" }, { Misc_SendErrors, noaccess } }, - { { "Misc", "ImScaredOfUpdates" }, { Misc_ImScaredOfUpdates, noaccess } } - }; - - /* - std::unordered_map InputKeyMapping{ - {"Description", General_Description}, - {"Tags", General_Tags}, - {"MaxPlayers", General_MaxPlayers}, - {"Name", General_Name}, - {"Map", General_Map}, - {"AuthKey", General_AuthKey}, - {"Private", General_Private}, - {"Port", General_Port}, - {"MaxCars", General_MaxCars}, - {"LogChat", General_LogChat}, - {"Resourcefolder", General_ResourceFolder}, - {"Debug", General_Debug}, - {"SendErrorsShowMessage", Misc_SendErrorsShowMessage}, - {"SendErrors", Misc_SendErrors}, - {"ImScaredOfUpdates", Misc_ImScaredOfUpdates} - } - */ - std::string getAsString(Key key) { - auto map = SettingsMap.synchronize(); - if (!map->contains(key)) { - throw std::logic_error { "Undefined key accessed in Settings::getAsString" }; - } - return std::get(map->at(key)); - } - - int getAsInt(Key key) { - auto map = SettingsMap.synchronize(); - if (!map->contains(key)) { - throw std::logic_error { "Undefined key accessed in Settings::getAsInt" }; - } - return std::get(map->at(key)); - } - - bool getAsBool(Key key) { - auto map = SettingsMap.synchronize(); - if (!map->contains(key)) { - throw std::logic_error { "Undefined key accessed in Settings::getAsBool" }; - } - return std::get(map->at(key)); - } - - SettingsTypeVariant get(Key key) { - auto map = SettingsMap.synchronize(); - if (!map->contains(key)) { - throw std::logic_error { "Undefined setting key accessed in Settings::get" }; - } - return map->at(key); - } - - void set(Key key, const std::string& value) { - auto map = SettingsMap.synchronize(); - if (!map->contains(key)) { - throw std::logic_error { "Undefined setting key accessed in Settings::set(std::string)" }; - } - if (!std::holds_alternative(map->at(key))) { - throw std::logic_error { fmt::format("Wrong value type in Settings::set(std::string): index {}", map->at(key).index()) }; - } - map->at(key) = value; - } - - template, bool> = true> - void set(Key key, Integer value) { - auto map = SettingsMap.synchronize(); - if (!map->contains(key)) { - throw std::logic_error { "Undefined setting key accessed in Settings::set(int)" }; - } - if (!std::holds_alternative(map->at(key))) { - throw std::logic_error { fmt::format("Wrong value type in Settings::set(int): index {}", map->at(key).index()) }; - } - map->at(key) = value; - } - template, bool> = true> - void set(Key key, Boolean value) { - auto map = SettingsMap.synchronize(); - if (!map->contains(key)) { - throw std::logic_error { "Undefined setting key accessed in Settings::set(bool)" }; - } - if (!std::holds_alternative(map->at(key))) { - throw std::logic_error { fmt::format("Wrong value type in Settings::set(bool): index {}", map->at(key).index()) }; - } - map->at(key) = value; - } - - const std::unordered_map getACLMap() const { - return *InputAccessMapping; - } - SettingsAccessControl getConsoleInputAccessMapping(const ComposedKey& keyName) { - auto acl_map = InputAccessMapping.synchronize(); - if (!acl_map->contains(keyName)) { - throw std::logic_error { "Unknown key name accessed in Settings::getConsoleInputAccessMapping" }; - } else if (acl_map->at(keyName).second == SettingsAccessMask::noaccess) { - throw std::logic_error { "Setting '" + keyName.Category + " > " + keyName.Key + "' is not accessible from within the runtime!" }; - } - return acl_map->at(keyName); - } - - void setConsoleInputAccessMapping(const ComposedKey& keyName, std::string value) { - auto [map, acl_map] = boost::synchronize(SettingsMap, InputAccessMapping); - if (!acl_map->contains(keyName)) { - throw std::logic_error { "Unknown key name accessed in Settings::setConsoleInputAccessMapping" }; - } else if (acl_map->at(keyName).second == SettingsAccessMask::noaccess) { - throw std::logic_error { "Setting '" + keyName.Category + " > " + keyName.Key + "' is not accessible from within the runtime!" }; - } else if (acl_map->at(keyName).second == SettingsAccessMask::read) { - throw std::logic_error { "Setting '" + keyName.Category + " > " + keyName.Key + "' is not writeable from within the runtime!" }; - } - - Key key = acl_map->at(keyName).first; - - if (!std::holds_alternative(map->at(key))) { - throw std::logic_error { "Wrong value type in Settings::setConsoleInputAccessMapping: expected std::string" }; - } - - map->at(key) = value; - } - void setConsoleInputAccessMapping(const ComposedKey& keyName, int value) { - auto [map, acl_map] = boost::synchronize(SettingsMap, InputAccessMapping); - if (!acl_map->contains(keyName)) { - throw std::logic_error { "Unknown key name accessed in Settings::setConsoleInputAccessMapping" }; - } else if (acl_map->at(keyName).second == SettingsAccessMask::noaccess) { - throw std::logic_error { "Key '" + keyName.Category + " > " + keyName.Key + "' is not accessible from within the runtime!" }; - } else if (acl_map->at(keyName).second == SettingsAccessMask::read) { - throw std::logic_error { "Key '" + keyName.Category + " > " + keyName.Key + "' is not writeable from within the runtime!" }; - } - - Key key = acl_map->at(keyName).first; - - if (!std::holds_alternative(map->at(key))) { - throw std::logic_error { "Wrong value type in Settings::setConsoleInputAccessMapping: expected int" }; - } - - map->at(key) = value; - } - void setConsoleInputAccessMapping(const ComposedKey& keyName, bool value) { - auto [map, acl_map] = boost::synchronize(SettingsMap, InputAccessMapping); - if (!acl_map->contains(keyName)) { - throw std::logic_error { "Unknown key name accessed in Settings::setConsoleInputAccessMapping" }; - } else if (acl_map->at(keyName).second == SettingsAccessMask::noaccess) { - throw std::logic_error { "Key '" + keyName.Category + " > " + keyName.Key + "' is not accessible from within the runtime!" }; - } else if (acl_map->at(keyName).second == SettingsAccessMask::read) { - throw std::logic_error { "Key '" + keyName.Category + " > " + keyName.Key + "' is not writeable from within the runtime!" }; - } - - Key key = acl_map->at(keyName).first; - - if (!std::holds_alternative(map->at(key))) { - throw std::logic_error { "Wrong value type in Settings::setConsoleInputAccessMapping: expected bool" }; - } - - map->at(key) = value; - } -}; - -/*struct TSettings { - - -std::string ServerName { "BeamMP Server" }; -std::string ServerDesc { "BeamMP Default Description" }; -std::string ServerTags { "Freeroam" }; -std::string Resource { "Resources" }; -std::string MapName { "/levels/gridmap_v2/info.json" }; -std::string Key {}; -std::string Password{}; -std::string SSLKeyPath { "./.ssl/HttpServer/key.pem" }; -std::string SSLCertPath { "./.ssl/HttpServer/cert.pem" }; -bool HTTPServerEnabled { false }; -int MaxPlayers { 8 }; -bool Private { true }; -int MaxCars { 1 }; -bool DebugModeEnabled { false }; -int Port { 30814 }; -std::string CustomIP {}; -bool LogChat { true }; -bool SendErrors { true }; -bool SendErrorsMessageEnabled { true }; -int HTTPServerPort { 8080 }; -std::string HTTPServerIP { "127.0.0.1" }; -bool HTTPServerUseSSL { false }; -bool HideUpdateMessages { false }; -[[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); } - }; - - -}*/ - -TEST_CASE("settings variant functions") { - Settings settings; - settings.set(Settings::General_Name, "hello, world"); - CHECK_EQ(settings.getAsString(Settings::General_Name), "hello, world"); - settings.set(Settings::General_Name, std::string("hello, world")); - CHECK_EQ(settings.getAsString(Settings::General_Name), "hello, world"); - settings.set(Settings::General_MaxPlayers, 12); - CHECK_EQ(settings.getAsInt(Settings::General_MaxPlayers), 12); - - CHECK_THROWS(settings.set(Settings::General_Debug, "hello, world")); - CHECK_NOTHROW(settings.set(Settings::General_Debug, false)); -} diff --git a/src/LuaAPI.cpp b/src/LuaAPI.cpp index 2e99655..2d1906f 100644 --- a/src/LuaAPI.cpp +++ b/src/LuaAPI.cpp @@ -21,7 +21,7 @@ #include "Common.h" #include "CustomAssert.h" #include "TLuaEngine.h" -#include "TSettings.h" +#include "Settings.h" #include diff --git a/src/Settings.cpp b/src/Settings.cpp new file mode 100644 index 0000000..9c6753c --- /dev/null +++ b/src/Settings.cpp @@ -0,0 +1,132 @@ +// BeamMP, the BeamNG.drive multiplayer mod. +// Copyright (C) 2024 BeamMP Ltd., BeamMP team and contributors. +// +// BeamMP Ltd. can be contacted by electronic mail via contact@beammp.com. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "Settings.h" + +std::string Settings::getAsString(Key key) { + auto map = SettingsMap.synchronize(); + if (!map->contains(key)) { + throw std::logic_error { "Undefined key accessed in Settings::getAsString" }; + } + return std::get(map->at(key)); +} +int Settings::getAsInt(Key key) { + auto map = SettingsMap.synchronize(); + if (!map->contains(key)) { + throw std::logic_error { "Undefined key accessed in Settings::getAsInt" }; + } + return std::get(map->at(key)); +} + +bool Settings::getAsBool(Key key) { + auto map = SettingsMap.synchronize(); + if (!map->contains(key)) { + throw std::logic_error { "Undefined key accessed in Settings::getAsBool" }; + } + return std::get(map->at(key)); +} + +Settings::SettingsTypeVariant Settings::get(Key key) { + auto map = SettingsMap.synchronize(); + if (!map->contains(key)) { + throw std::logic_error { "Undefined setting key accessed in Settings::get" }; + } + return map->at(key); +} + +void Settings::set(Key key, const std::string& value) { + auto map = SettingsMap.synchronize(); + if (!map->contains(key)) { + throw std::logic_error { "Undefined setting key accessed in Settings::set(std::string)" }; + } + if (!std::holds_alternative(map->at(key))) { + throw std::logic_error { fmt::format("Wrong value type in Settings::set(std::string): index {}", map->at(key).index()) }; + } + map->at(key) = value; +} + +const std::unordered_map Settings::getAccessControlMap() const { + return *InputAccessMapping; +} + +Settings::SettingsAccessControl Settings::getConsoleInputAccessMapping(const ComposedKey& keyName) { + auto acl_map = InputAccessMapping.synchronize(); + if (!acl_map->contains(keyName)) { + throw std::logic_error { "Unknown key name accessed in Settings::getConsoleInputAccessMapping" }; + } else if (acl_map->at(keyName).second == SettingsAccessMask::NO_ACCESS) { + throw std::logic_error { "Setting '" + keyName.Category + " > " + keyName.Key + "' is not accessible from within the runtime!" }; + } + return acl_map->at(keyName); +} + +void Settings::setConsoleInputAccessMapping(const ComposedKey& keyName, const std::string& value) { + auto [map, acl_map] = boost::synchronize(SettingsMap, InputAccessMapping); + if (!acl_map->contains(keyName)) { + throw std::logic_error { "Unknown key name accessed in Settings::setConsoleInputAccessMapping" }; + } else if (acl_map->at(keyName).second == SettingsAccessMask::NO_ACCESS) { + throw std::logic_error { "Setting '" + keyName.Category + " > " + keyName.Key + "' is not accessible from within the runtime!" }; + } else if (acl_map->at(keyName).second == SettingsAccessMask::READ_ONLY) { + throw std::logic_error { "Setting '" + keyName.Category + " > " + keyName.Key + "' is not writeable from within the runtime!" }; + } + + Key key = acl_map->at(keyName).first; + + if (!std::holds_alternative(map->at(key))) { + throw std::logic_error { "Wrong value type in Settings::setConsoleInputAccessMapping: expected std::string" }; + } + + map->at(key) = value; +} + +void Settings::setConsoleInputAccessMapping(const ComposedKey& keyName, int value) { + auto [map, acl_map] = boost::synchronize(SettingsMap, InputAccessMapping); + if (!acl_map->contains(keyName)) { + throw std::logic_error { "Unknown key name accessed in Settings::setConsoleInputAccessMapping" }; + } else if (acl_map->at(keyName).second == SettingsAccessMask::NO_ACCESS) { + throw std::logic_error { "Key '" + keyName.Category + " > " + keyName.Key + "' is not accessible from within the runtime!" }; + } else if (acl_map->at(keyName).second == SettingsAccessMask::READ_ONLY) { + throw std::logic_error { "Key '" + keyName.Category + " > " + keyName.Key + "' is not writeable from within the runtime!" }; + } + + Key key = acl_map->at(keyName).first; + + if (!std::holds_alternative(map->at(key))) { + throw std::logic_error { "Wrong value type in Settings::setConsoleInputAccessMapping: expected int" }; + } + + map->at(key) = value; +} + +void Settings::setConsoleInputAccessMapping(const ComposedKey& keyName, bool value) { + auto [map, acl_map] = boost::synchronize(SettingsMap, InputAccessMapping); + if (!acl_map->contains(keyName)) { + throw std::logic_error { "Unknown key name accessed in Settings::setConsoleInputAccessMapping" }; + } else if (acl_map->at(keyName).second == SettingsAccessMask::NO_ACCESS) { + throw std::logic_error { "Key '" + keyName.Category + " > " + keyName.Key + "' is not accessible from within the runtime!" }; + } else if (acl_map->at(keyName).second == SettingsAccessMask::READ_ONLY) { + throw std::logic_error { "Key '" + keyName.Category + " > " + keyName.Key + "' is not writeable from within the runtime!" }; + } + + Key key = acl_map->at(keyName).first; + + if (!std::holds_alternative(map->at(key))) { + throw std::logic_error { "Wrong value type in Settings::setConsoleInputAccessMapping: expected bool" }; + } + + map->at(key) = value; +} diff --git a/src/TConfig.cpp b/src/TConfig.cpp index daf8b7e..a41cf18 100644 --- a/src/TConfig.cpp +++ b/src/TConfig.cpp @@ -20,7 +20,7 @@ #include "Env.h" #include "TConfig.h" -#include "TSettings.h" +#include "Settings.h" #include #include #include diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 2079b8d..636ae17 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -385,7 +385,7 @@ void TConsole::Command_Settings(const std::string&, const std::vector sets specified setting to value )"; - if (args.size() == 0) { + if (EnsureArgsCount(args, 0)) { beammp_errorf("No arguments specified for command 'settings'!"); Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString)); return; @@ -472,11 +472,11 @@ void TConsole::Command_Settings(const std::string&, const std::vector - for (const auto& [composedKey, keyACL] : Application::Settings.getACLMap()) { + for (const auto& [composedKey, keyACL] : Application::Settings.getAccessControlMap()) { // even though we have the value, we want to ignore it in order to make use of access // control checks - if (keyACL.second != Settings::SettingsAccessMask::noaccess) { + if (keyACL.second != Settings::SettingsAccessMask::NO_ACCESS) { try { diff --git a/src/main.cpp b/src/main.cpp index b93ff77..59c1469 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,7 +29,7 @@ #include "TPluginMonitor.h" #include "TResourceManager.h" #include "TServer.h" -#include "TSettings.h" +#include "Settings.h" #include #include From d30dccc94ae9922b75fb80c758881a465f5a07ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Tue, 21 May 2024 14:24:44 +0200 Subject: [PATCH 25/43] Separate settings tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lucca Jiménez Könings --- include/Settings.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/Settings.h b/include/Settings.h index 2f9565c..071088c 100644 --- a/include/Settings.h +++ b/include/Settings.h @@ -173,7 +173,7 @@ struct Settings { void setConsoleInputAccessMapping(const ComposedKey& keyName, bool value); }; -TEST_CASE("settings variant functions") { +TEST_CASE("settings get/set") { Settings settings; settings.set(Settings::General_Name, "hello, world"); CHECK_EQ(settings.getAsString(Settings::General_Name), "hello, world"); @@ -181,7 +181,10 @@ TEST_CASE("settings variant functions") { CHECK_EQ(settings.getAsString(Settings::General_Name), "hello, world"); settings.set(Settings::General_MaxPlayers, 12); CHECK_EQ(settings.getAsInt(Settings::General_MaxPlayers), 12); +} +TEST_CASE("settings check for exception on wrong input type"){ + Settings settings; CHECK_THROWS(settings.set(Settings::General_Debug, "hello, world")); CHECK_NOTHROW(settings.set(Settings::General_Debug, false)); } From 04e8d00daa794f18f1c04f705f5559930d23dfdf Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Fri, 24 May 2024 09:07:54 +0200 Subject: [PATCH 26/43] fix invalid default initialization in SettingsMap --- include/Settings.h | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/include/Settings.h b/include/Settings.h index 071088c..5c39e5e 100644 --- a/include/Settings.h +++ b/include/Settings.h @@ -86,17 +86,18 @@ struct Settings { }; Sync> SettingsMap = std::unordered_map { - { General_Description, "BeamMP Default Description" }, - { General_Tags, "Freeroam" }, + // All entries which contain std::strings must be explicitly constructed, otherwise they become 'bool' + { General_Description, std::string("BeamMP Default Description") }, + { General_Tags, std::string("Freeroam") }, { General_MaxPlayers, 8 }, - { General_Name, "BeamMP Server" }, - { General_Map, "/levels/gridmap_v2/info.json" }, - { General_AuthKey, "" }, + { General_Name, std::string("BeamMP Server") }, + { General_Map, std::string("/levels/gridmap_v2/info.json") }, + { General_AuthKey, std::string("") }, { General_Private, true }, { General_Port, 30814 }, { General_MaxCars, 1 }, { General_LogChat, true }, - { General_ResourceFolder, "Resources" }, + { General_ResourceFolder, std::string("Resources") }, { General_Debug, false }, { Misc_SendErrorsShowMessage, true }, { Misc_SendErrors, true }, @@ -183,7 +184,7 @@ TEST_CASE("settings get/set") { CHECK_EQ(settings.getAsInt(Settings::General_MaxPlayers), 12); } -TEST_CASE("settings check for exception on wrong input type"){ +TEST_CASE("settings check for exception on wrong input type") { Settings settings; CHECK_THROWS(settings.set(Settings::General_Debug, "hello, world")); CHECK_NOTHROW(settings.set(Settings::General_Debug, false)); From 73ecef1a87828ef9eeb9a118b431339fd15fa067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Wed, 26 Jun 2024 11:07:14 +0200 Subject: [PATCH 27/43] Move map declarations in Settings.h into .cpp --- include/Settings.h | 41 ++++------------------------------------- src/Settings.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 37 deletions(-) diff --git a/include/Settings.h b/include/Settings.h index 5c39e5e..984454a 100644 --- a/include/Settings.h +++ b/include/Settings.h @@ -61,6 +61,8 @@ public: struct Settings { using SettingsTypeVariant = std::variant; + Settings(); + enum Key { // Keys that correspond to the keys set in TOML // Keys have their TOML section name as prefix @@ -85,25 +87,7 @@ struct Settings { General_Debug }; - Sync> SettingsMap = std::unordered_map { - // All entries which contain std::strings must be explicitly constructed, otherwise they become 'bool' - { General_Description, std::string("BeamMP Default Description") }, - { General_Tags, std::string("Freeroam") }, - { General_MaxPlayers, 8 }, - { General_Name, std::string("BeamMP Server") }, - { General_Map, std::string("/levels/gridmap_v2/info.json") }, - { General_AuthKey, std::string("") }, - { General_Private, true }, - { General_Port, 30814 }, - { General_MaxCars, 1 }, - { General_LogChat, true }, - { General_ResourceFolder, std::string("Resources") }, - { General_Debug, false }, - { Misc_SendErrorsShowMessage, true }, - { Misc_SendErrors, true }, - { Misc_ImScaredOfUpdates, true } - }; - + Sync> SettingsMap; enum SettingsAccessMask { READ_ONLY, // Value can be read from console READ_WRITE, // Value can be read and written to from console @@ -115,24 +99,7 @@ struct Settings { SettingsAccessMask // Console read/write permissions >; - Sync> InputAccessMapping = std::unordered_map { - { { "General", "Description" }, { General_Description, READ_WRITE } }, - { { "General", "Tags" }, { General_Tags, READ_WRITE } }, - { { "General", "MaxPlayers" }, { General_MaxPlayers, READ_WRITE } }, - { { "General", "Name" }, { General_Name, READ_WRITE } }, - { { "General", "Map" }, { General_Map, READ_WRITE } }, - { { "General", "AuthKey" }, { General_AuthKey, READ_WRITE } }, - { { "General", "Private" }, { General_Private, READ_ONLY } }, - { { "General", "Port" }, { General_Port, READ_ONLY } }, - { { "General", "MaxCars" }, { General_MaxCars, READ_WRITE } }, - { { "General", "LogChat" }, { General_LogChat, READ_ONLY } }, - { { "General", "ResourceFolder" }, { General_ResourceFolder, READ_ONLY } }, - { { "General", "Debug" }, { General_Debug, READ_WRITE } }, - { { "Misc", "SendErrorsShowMessage" }, { Misc_SendErrorsShowMessage, READ_WRITE } }, - { { "Misc", "SendErrors" }, { Misc_SendErrors, READ_WRITE } }, - { { "Misc", "ImScaredOfUpdates" }, { Misc_ImScaredOfUpdates, READ_WRITE } } - }; - + Sync> InputAccessMapping; std::string getAsString(Key key); int getAsInt(Key key); diff --git a/src/Settings.cpp b/src/Settings.cpp index 9c6753c..8eec79b 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -18,6 +18,49 @@ #include "Settings.h" + + +Settings::Settings(){ + SettingsMap = std::unordered_map { + // All entries which contain std::strings must be explicitly constructed, otherwise they become 'bool' + { General_Description, std::string("BeamMP Default Description") }, + { General_Tags, std::string("Freeroam") }, + { General_MaxPlayers, 8 }, + { General_Name, std::string("BeamMP Server") }, + { General_Map, std::string("/levels/gridmap_v2/info.json") }, + { General_AuthKey, std::string("") }, + { General_Private, true }, + { General_Port, 30814 }, + { General_MaxCars, 1 }, + { General_LogChat, true }, + { General_ResourceFolder, std::string("Resources") }, + { General_Debug, false }, + { Misc_SendErrorsShowMessage, true }, + { Misc_SendErrors, true }, + { Misc_ImScaredOfUpdates, true } + }; + + InputAccessMapping = std::unordered_map { + { { "General", "Description" }, { General_Description, READ_WRITE } }, + { { "General", "Tags" }, { General_Tags, READ_WRITE } }, + { { "General", "MaxPlayers" }, { General_MaxPlayers, READ_WRITE } }, + { { "General", "Name" }, { General_Name, READ_WRITE } }, + { { "General", "Map" }, { General_Map, READ_WRITE } }, + { { "General", "AuthKey" }, { General_AuthKey, READ_WRITE } }, + { { "General", "Private" }, { General_Private, READ_ONLY } }, + { { "General", "Port" }, { General_Port, READ_ONLY } }, + { { "General", "MaxCars" }, { General_MaxCars, READ_WRITE } }, + { { "General", "LogChat" }, { General_LogChat, READ_ONLY } }, + { { "General", "ResourceFolder" }, { General_ResourceFolder, READ_ONLY } }, + { { "General", "Debug" }, { General_Debug, READ_WRITE } }, + { { "Misc", "SendErrorsShowMessage" }, { Misc_SendErrorsShowMessage, READ_WRITE } }, + { { "Misc", "SendErrors" }, { Misc_SendErrors, READ_WRITE } }, + { { "Misc", "ImScaredOfUpdates" }, { Misc_ImScaredOfUpdates, READ_WRITE } } + }; + + +} + std::string Settings::getAsString(Key key) { auto map = SettingsMap.synchronize(); if (!map->contains(key)) { From 509225f1515fb77fe25f5949893c72718438bfdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Wed, 26 Jun 2024 11:07:46 +0200 Subject: [PATCH 28/43] Move tests from .h to .cpp --- include/Settings.h | 14 -------------- src/Settings.cpp | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/include/Settings.h b/include/Settings.h index 984454a..461b491 100644 --- a/include/Settings.h +++ b/include/Settings.h @@ -141,18 +141,4 @@ struct Settings { void setConsoleInputAccessMapping(const ComposedKey& keyName, bool value); }; -TEST_CASE("settings get/set") { - Settings settings; - settings.set(Settings::General_Name, "hello, world"); - CHECK_EQ(settings.getAsString(Settings::General_Name), "hello, world"); - settings.set(Settings::General_Name, std::string("hello, world")); - CHECK_EQ(settings.getAsString(Settings::General_Name), "hello, world"); - settings.set(Settings::General_MaxPlayers, 12); - CHECK_EQ(settings.getAsInt(Settings::General_MaxPlayers), 12); -} -TEST_CASE("settings check for exception on wrong input type") { - Settings settings; - CHECK_THROWS(settings.set(Settings::General_Debug, "hello, world")); - CHECK_NOTHROW(settings.set(Settings::General_Debug, false)); -} diff --git a/src/Settings.cpp b/src/Settings.cpp index 8eec79b..af33786 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -173,3 +173,19 @@ void Settings::setConsoleInputAccessMapping(const ComposedKey& keyName, bool val map->at(key) = value; } + +TEST_CASE("settings get/set") { + Settings settings; + settings.set(Settings::General_Name, "hello, world"); + CHECK_EQ(settings.getAsString(Settings::General_Name), "hello, world"); + settings.set(Settings::General_Name, std::string("hello, world")); + CHECK_EQ(settings.getAsString(Settings::General_Name), "hello, world"); + settings.set(Settings::General_MaxPlayers, 12); + CHECK_EQ(settings.getAsInt(Settings::General_MaxPlayers), 12); +} + +TEST_CASE("settings check for exception on wrong input type") { + Settings settings; + CHECK_THROWS(settings.set(Settings::General_Debug, "hello, world")); + CHECK_NOTHROW(settings.set(Settings::General_Debug, false)); +} From 0d3256c429282b2da973ca15ab1b0f62a23a6af4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Wed, 26 Jun 2024 11:08:57 +0200 Subject: [PATCH 29/43] Remove todo in accordance with review --- src/TConfig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TConfig.cpp b/src/TConfig.cpp index a41cf18..66bcd2b 100644 --- a/src/TConfig.cpp +++ b/src/TConfig.cpp @@ -241,7 +241,7 @@ void TConfig::TryReadValue(toml::value& Table, const std::string& Category, cons void TConfig::ParseFromFile(std::string_view name) { try { toml::value data {}; - if (!mDisableConfig) { // todo: rename mDisableCofig to configEnabled + if (!mDisableConfig) { data = toml::parse(name.data()); } From 6c0a8d1d6236828bafe2bf55494d1cca63b528a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Wed, 26 Jun 2024 11:10:27 +0200 Subject: [PATCH 30/43] remove superflous code --- src/TConsole.cpp | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 636ae17..ac72c3e 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -106,41 +106,6 @@ void TConsole::BackupOldLog() { } catch (const std::exception& e) { beammp_warn(e.what()); } - /* - int err = 0; - zip* z = zip_open("ServerLogs.zip", ZIP_CREATE, &err); - if (!z) { - std::cerr << GetPlatformAgnosticErrorString() << std::endl; - return; - } - FILE* File = std::fopen(Path.string().c_str(), "r"); - if (!File) { - std::cerr << GetPlatformAgnosticErrorString() << std::endl; - return; - } - std::vector Buffer; - Buffer.resize(fs::file_size(Path)); - std::fread(Buffer.data(), 1, Buffer.size(), File); - std::fclose(File); - - auto s = zip_source_buffer(z, Buffer.data(), Buffer.size(), 0); - - auto TimePoint = fs::last_write_time(Path); - auto Secs = TimePoint.time_since_epoch().count(); - auto MyTimeT = std::time(&Secs); - - std::string NewName = Path.stem().string(); - NewName += "_"; - std::string Time; - Time.resize(32); - size_t n = strftime(Time.data(), Time.size(), "%F_%H.%M.%S", localtime(&MyTimeT)); - Time.resize(n); - NewName += Time; - NewName += ".log"; - - zip_file_add(z, NewName.c_str(), s, 0); - zip_close(z); - */ } } From 814927d0a1bf957ea6d8b9287960f903a9c09d4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Wed, 26 Jun 2024 11:11:13 +0200 Subject: [PATCH 31/43] change log output for consistency --- src/TConsole.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/TConsole.cpp b/src/TConsole.cpp index ac72c3e..68abaf7 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -375,13 +375,13 @@ void TConsole::Command_Settings(const std::string&, const std::vector {}' = {}", args.at(1), args.at(2), keyValue)); + Application::Console().WriteRaw(fmt::format("'{}::{}' = {}", args.at(1), args.at(2), keyValue)); }, [&args](int keyValue) { - Application::Console().WriteRaw(fmt::format("'{} > {}' = {}", args.at(1), args.at(2), keyValue)); + Application::Console().WriteRaw(fmt::format("'{}::{}' = {}", args.at(1), args.at(2), keyValue)); }, [&args](bool keyValue) { - Application::Console().WriteRaw(fmt::format("'{} > {}' = {}", args.at(1), args.at(2), keyValue)); + Application::Console().WriteRaw(fmt::format("'{}::{}' = {}", args.at(1), args.at(2), keyValue)); } }, From 2451e08b01b7b94a8bf965ae5e414db2b7ef6395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Wed, 26 Jun 2024 12:31:47 +0200 Subject: [PATCH 32/43] update remaining sections of code after merge --- include/Settings.h | 4 +++- src/Settings.cpp | 8 ++++++-- src/TConfig.cpp | 14 +++++++------- src/THeartbeatThread.cpp | 6 +++--- src/TNetwork.cpp | 4 ++-- 5 files changed, 21 insertions(+), 15 deletions(-) diff --git a/include/Settings.h b/include/Settings.h index 461b491..8ee3ffc 100644 --- a/include/Settings.h +++ b/include/Settings.h @@ -71,6 +71,7 @@ struct Settings { Misc_SendErrorsShowMessage, Misc_SendErrors, Misc_ImScaredOfUpdates, + Misc_UpdateReminderTime, // [General] General_Description, @@ -84,7 +85,8 @@ struct Settings { General_MaxCars, General_LogChat, General_ResourceFolder, - General_Debug + General_Debug, + General_AllowGuests }; Sync> SettingsMap; diff --git a/src/Settings.cpp b/src/Settings.cpp index af33786..191d4fc 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -35,9 +35,11 @@ Settings::Settings(){ { General_LogChat, true }, { General_ResourceFolder, std::string("Resources") }, { General_Debug, false }, + { General_AllowGuests, true }, { Misc_SendErrorsShowMessage, true }, { Misc_SendErrors, true }, - { Misc_ImScaredOfUpdates, true } + { Misc_ImScaredOfUpdates, true }, + { Misc_UpdateReminderTime, "30s"} }; InputAccessMapping = std::unordered_map { @@ -53,9 +55,11 @@ Settings::Settings(){ { { "General", "LogChat" }, { General_LogChat, READ_ONLY } }, { { "General", "ResourceFolder" }, { General_ResourceFolder, READ_ONLY } }, { { "General", "Debug" }, { General_Debug, READ_WRITE } }, + { { "General", "AllowGuests"}, { General_AllowGuests, READ_WRITE } }, { { "Misc", "SendErrorsShowMessage" }, { Misc_SendErrorsShowMessage, READ_WRITE } }, { { "Misc", "SendErrors" }, { Misc_SendErrors, READ_WRITE } }, - { { "Misc", "ImScaredOfUpdates" }, { Misc_ImScaredOfUpdates, READ_WRITE } } + { { "Misc", "ImScaredOfUpdates" }, { Misc_ImScaredOfUpdates, READ_WRITE } }, + { { "Misc", "UpdateReminderTime" }, {Misc_UpdateReminderTime, READ_WRITE} } }; diff --git a/src/TConfig.cpp b/src/TConfig.cpp index d144989..959ad82 100644 --- a/src/TConfig.cpp +++ b/src/TConfig.cpp @@ -132,7 +132,7 @@ void TConfig::FlushToFile() { SetComment(data["General"][StrLogChat.data()].comments(), " Whether to log chat messages in the console / log"); data["General"][StrDebug.data()] = Application::Settings.getAsBool(Settings::Key::General_Debug); data["General"][StrPrivate.data()] = Application::Settings.getAsBool(Settings::Key::General_Private); - data["General"][StrAllowGuests.data()] = Application::Settings.AllowGuests; + data["General"][StrAllowGuests.data()] = Application::Settings.getAsBool(Settings::Key::General_AllowGuests); SetComment(data["General"][StrAllowGuests.data()].comments(), " Whether to allow guests"); data["General"][StrPort.data()] = Application::Settings.getAsInt(Settings::Key::General_Port); data["General"][StrName.data()] = Application::Settings.getAsString(Settings::Key::General_Name); @@ -149,7 +149,7 @@ void TConfig::FlushToFile() { data["Misc"][StrHideUpdateMessages.data()] = Application::Settings.getAsBool(Settings::Key::Misc_ImScaredOfUpdates); SetComment(data["Misc"][StrHideUpdateMessages.data()].comments(), " Hides the periodic update message which notifies you of a new server version. You should really keep this on and always update as soon as possible. For more information visit https://wiki.beammp.com/en/home/server-maintenance#updating-the-server. An update message will always appear at startup regardless."); data["Misc"][StrSendErrors.data()] = Application::Settings.getAsBool(Settings::Key::Misc_SendErrors); - data["Misc"][StrUpdateReminderTime.data()] = Application::Settings.UpdateReminderTime; + data["Misc"][StrUpdateReminderTime.data()] = Application::Settings.getAsString(Settings::Key::Misc_UpdateReminderTime); SetComment(data["Misc"][StrUpdateReminderTime.data()].comments(), " Specifies the time between update reminders. You can use any of \"s, min, h, d\" at the end to specify the units seconds, minutes, hours or days. So 30d or 0.5min will print the update message every 30 days or half a minute."); SetComment(data["Misc"][StrSendErrors.data()].comments(), " If SendErrors is `true`, the server will send helpful info about crashes and other issues back to the BeamMP developers. This info may include your config, who is on your server at the time of the error, and similar general information. This kind of data is vital in helping us diagnose and fix issues faster. This has no impact on server performance. You can opt-out of this system by setting this to `false`"); data["Misc"][StrSendErrorsMessageEnabled.data()] = Application::Settings.getAsBool(Settings::Key::Misc_SendErrorsShowMessage); @@ -267,12 +267,12 @@ void TConfig::ParseFromFile(std::string_view name) { TryReadValue(data, "General", StrResourceFolder, EnvStrResourceFolder, Settings::Key::General_ResourceFolder); TryReadValue(data, "General", StrAuthKey, EnvStrAuthKey, Settings::Key::General_AuthKey); TryReadValue(data, "General", StrLogChat, EnvStrLogChat, Settings::Key::General_LogChat); - TryReadValue(data, "General", StrAllowGuests, EnvStrAllowGuests, Application::Settings.AllowGuests); + TryReadValue(data, "General", StrAllowGuests, EnvStrAllowGuests, Settings::Key::General_AllowGuests); // Misc TryReadValue(data, "Misc", StrSendErrors, "", Settings::Key::Misc_SendErrors); - TryReadValue(data, "Misc", StrHideUpdateMessages, "", Settings::Misc_ImScaredOfUpdates); - TryReadValue(data, "Misc", StrSendErrorsMessageEnabled, "", Settings::Misc_SendErrorsShowMessage); - TryReadValue(data, "Misc", StrUpdateReminderTime, "", Application::Settings.UpdateReminderTime); + TryReadValue(data, "Misc", StrHideUpdateMessages, "", Settings::Key::Misc_ImScaredOfUpdates); + TryReadValue(data, "Misc", StrSendErrorsMessageEnabled, "", Settings::Key::Misc_SendErrorsShowMessage); + TryReadValue(data, "Misc", StrUpdateReminderTime, "", Settings::Key::Misc_UpdateReminderTime); } catch (const std::exception& err) { beammp_error("Error parsing config file value: " + std::string(err.what())); @@ -317,7 +317,7 @@ void TConfig::PrintDebug() { beammp_debug(std::string(StrTags) + ": " + TagsAsPrettyArray()); beammp_debug(std::string(StrLogChat) + ": \"" + (Application::Settings.getAsBool(Settings::Key::General_LogChat) ? "true" : "false") + "\""); beammp_debug(std::string(StrResourceFolder) + ": \"" + Application::Settings.getAsString(Settings::Key::General_ResourceFolder) + "\""); - beammp_debug(std::string(StrAllowGuests) + ": \"" + (Application::Settings.AllowGuests ? "true" : "false") + "\""); + beammp_debug(std::string(StrAllowGuests) + ": \"" + (Application::Settings.getAsBool(Settings::Key::General_AllowGuests) ? "true" : "false") + "\""); // special! beammp_debug("Key Length: " + std::to_string(Application::Settings.getAsString(Settings::Key::General_AuthKey).length()) + ""); } diff --git a/src/THeartbeatThread.cpp b/src/THeartbeatThread.cpp index fb40669..a7f37c6 100644 --- a/src/THeartbeatThread.cpp +++ b/src/THeartbeatThread.cpp @@ -40,7 +40,7 @@ void THeartbeatThread::operator()() { static std::chrono::high_resolution_clock::time_point LastUpdateReminderTime = std::chrono::high_resolution_clock::now(); bool isAuth = false; std::chrono::high_resolution_clock::duration UpdateReminderTimePassed; - auto UpdateReminderTimeout = ChronoWrapper::TimeFromStringWithLiteral(Application::Settings.UpdateReminderTime); + auto UpdateReminderTimeout = ChronoWrapper::TimeFromStringWithLiteral(Application::Settings.getAsString(Settings::Key::Misc_UpdateReminderTime)); while (!Application::IsShuttingDown()) { Body = GenerateCall(); // a hot-change occurs when a setting has changed, to update the backend of that change. @@ -129,7 +129,7 @@ void THeartbeatThread::operator()() { if (isAuth || Application::Settings.getAsBool(Settings::Key::General_Private)) { Application::SetSubsystemStatus("Heartbeat", Application::Status::Good); } - if (!Application::Settings.HideUpdateMessages && UpdateReminderTimePassed.count() > UpdateReminderTimeout.count()) { + if (!Application::Settings.getAsBool(Settings::Key::Misc_ImScaredOfUpdates) && UpdateReminderTimePassed.count() > UpdateReminderTimeout.count()) { LastUpdateReminderTime = std::chrono::high_resolution_clock::now(); Application::CheckForUpdates(); } @@ -149,7 +149,7 @@ std::string THeartbeatThread::GenerateCall() { << "&clientversion=" << std::to_string(Application::ClientMajorVersion()) + ".0" // FIXME: Wtf. << "&name=" << Application::Settings.getAsString(Settings::Key::General_Name) << "&tags=" << Application::Settings.getAsString(Settings::Key::General_Tags) - << "&guests=" << (Application::Settings.AllowGuests ? "true" : "false") + << "&guests=" << (Application::Settings.getAsBool(Settings::Key::General_AllowGuests) ? "true" : "false") << "&modlist=" << mResourceManager.TrimmedList() << "&modstotalsize=" << mResourceManager.MaxModSize() << "&modstotal=" << mResourceManager.ModsLoaded() diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index a142f75..65f0c97 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -290,7 +290,7 @@ std::shared_ptr TNetwork::Authentication(TConnection&& RawConnection) { } std::string Key(reinterpret_cast(Data.data()), Data.size()); - std::string AuthKey = Application::Settings.Key; + std::string AuthKey = Application::Settings.getAsString(Settings::Key::General_AuthKey); std::string ClientIp = Client->GetIdentifiers().at("ip"); nlohmann::json AuthReq {}; @@ -373,7 +373,7 @@ std::shared_ptr TNetwork::Authentication(TConnection&& RawConnection) { return false; }); - if (!NotAllowedWithReason && !Application::Settings.AllowGuests && Client->IsGuest()) { //!NotAllowedWithReason because this message has the lowest priority + if (!NotAllowedWithReason && !Application::Settings.getAsBool(Settings::Key::General_AllowGuests) && Client->IsGuest()) { //!NotAllowedWithReason because this message has the lowest priority NotAllowedWithReason = true; Reason = "No guests are allowed on this server! To join, sign up at: forum.beammp.com."; } From 26ef39827ee5947a0edc945281ce7ae07691c0ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Wed, 26 Jun 2024 13:07:58 +0200 Subject: [PATCH 33/43] fix AuthKey being writable from console --- src/Settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Settings.cpp b/src/Settings.cpp index 191d4fc..7ab0c2d 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -48,7 +48,7 @@ Settings::Settings(){ { { "General", "MaxPlayers" }, { General_MaxPlayers, READ_WRITE } }, { { "General", "Name" }, { General_Name, READ_WRITE } }, { { "General", "Map" }, { General_Map, READ_WRITE } }, - { { "General", "AuthKey" }, { General_AuthKey, READ_WRITE } }, + { { "General", "AuthKey" }, { General_AuthKey, NO_ACCESS } }, { { "General", "Private" }, { General_Private, READ_ONLY } }, { { "General", "Port" }, { General_Port, READ_ONLY } }, { { "General", "MaxCars" }, { General_MaxCars, READ_WRITE } }, From 7919f81927c8b70c473047eb55a6b4d9495c4982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Wed, 26 Jun 2024 13:08:26 +0200 Subject: [PATCH 34/43] remove dead code for deprecated config format --- src/TConfig.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/TConfig.cpp b/src/TConfig.cpp index 959ad82..c3a0a8d 100644 --- a/src/TConfig.cpp +++ b/src/TConfig.cpp @@ -177,15 +177,6 @@ void TConfig::CreateConfigFile() { if (mDisableConfig) { return; } - try { - if (fs::exists("Server.cfg")) { - // parse it (this is weird and bad and should be removed in some future version) - // ParseOldFormat(); - } - } catch (const std::exception& e) { - beammp_error("an error occurred and was ignored during config transfer: " + std::string(e.what())); - } - FlushToFile(); } From 8c32d760be31219310c446823a5355f6bfc5eed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Wed, 26 Jun 2024 13:09:18 +0200 Subject: [PATCH 35/43] fix confusing error when setting wrong key --- src/TConsole.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 356f3a4..4d3cd5a 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -370,7 +370,7 @@ void TConsole::Command_Settings(const std::string&, const std::vector sets specified setting to value )"; - if (EnsureArgsCount(args, 0)) { + if (args.size() == 0) { beammp_errorf("No arguments specified for command 'settings'!"); Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString)); return; @@ -451,7 +451,8 @@ void TConsole::Command_Settings(const std::string&, const std::vector Date: Wed, 26 Jun 2024 13:10:34 +0200 Subject: [PATCH 36/43] remove superflous comments --- src/TConsole.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 4d3cd5a..8196207 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -435,7 +435,6 @@ void TConsole::Command_Settings(const std::string&, const std::vector for (const auto& [composedKey, keyACL] : Application::Settings.getAccessControlMap()) { // even though we have the value, we want to ignore it in order to make use of access // control checks From e7c7f450390207b811aadae4113964983d0ab013 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Wed, 26 Jun 2024 13:10:46 +0200 Subject: [PATCH 37/43] fix chrono wrapper --- src/THeartbeatThread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/THeartbeatThread.cpp b/src/THeartbeatThread.cpp index a7f37c6..3e6699c 100644 --- a/src/THeartbeatThread.cpp +++ b/src/THeartbeatThread.cpp @@ -40,8 +40,8 @@ void THeartbeatThread::operator()() { static std::chrono::high_resolution_clock::time_point LastUpdateReminderTime = std::chrono::high_resolution_clock::now(); bool isAuth = false; std::chrono::high_resolution_clock::duration UpdateReminderTimePassed; - auto UpdateReminderTimeout = ChronoWrapper::TimeFromStringWithLiteral(Application::Settings.getAsString(Settings::Key::Misc_UpdateReminderTime)); while (!Application::IsShuttingDown()) { + auto UpdateReminderTimeout = ChronoWrapper::TimeFromStringWithLiteral(Application::Settings.getAsString(Settings::Key::Misc_UpdateReminderTime)); Body = GenerateCall(); // a hot-change occurs when a setting has changed, to update the backend of that change. auto Now = std::chrono::high_resolution_clock::now(); From 6731b3e9776699d757e499a4e7caa06923af65c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Wed, 26 Jun 2024 13:12:10 +0200 Subject: [PATCH 38/43] fix typo --- src/TConsole.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 8196207..38231cf 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -487,7 +487,7 @@ void TConsole::Command_Settings(const std::string&, const std::vector Date: Wed, 26 Jun 2024 13:34:32 +0200 Subject: [PATCH 39/43] improve error messages --- src/TConsole.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 38231cf..b9ae153 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -367,7 +367,7 @@ void TConsole::Command_Settings(const std::string&, const std::vector prints current value of specified setting - settings set sets specified setting to value + settings set sets specified setting to value )"; if (args.size() == 0) { @@ -412,8 +412,8 @@ void TConsole::Command_Settings(const std::string&, const std::vector Date: Wed, 26 Jun 2024 13:38:07 +0200 Subject: [PATCH 40/43] improve acl error message consistency --- src/Settings.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Settings.cpp b/src/Settings.cpp index 7ab0c2d..b5692a7 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -116,7 +116,7 @@ Settings::SettingsAccessControl Settings::getConsoleInputAccessMapping(const Com if (!acl_map->contains(keyName)) { throw std::logic_error { "Unknown key name accessed in Settings::getConsoleInputAccessMapping" }; } else if (acl_map->at(keyName).second == SettingsAccessMask::NO_ACCESS) { - throw std::logic_error { "Setting '" + keyName.Category + " > " + keyName.Key + "' is not accessible from within the runtime!" }; + throw std::logic_error { "Setting '" + keyName.Category + "::" + keyName.Key + "' is not accessible from within the runtime!" }; } return acl_map->at(keyName); } @@ -126,9 +126,9 @@ void Settings::setConsoleInputAccessMapping(const ComposedKey& keyName, const st if (!acl_map->contains(keyName)) { throw std::logic_error { "Unknown key name accessed in Settings::setConsoleInputAccessMapping" }; } else if (acl_map->at(keyName).second == SettingsAccessMask::NO_ACCESS) { - throw std::logic_error { "Setting '" + keyName.Category + " > " + keyName.Key + "' is not accessible from within the runtime!" }; + throw std::logic_error { "Setting '" + keyName.Category + "::" + keyName.Key + "' is not accessible from within the runtime!" }; } else if (acl_map->at(keyName).second == SettingsAccessMask::READ_ONLY) { - throw std::logic_error { "Setting '" + keyName.Category + " > " + keyName.Key + "' is not writeable from within the runtime!" }; + throw std::logic_error { "Setting '" + keyName.Category + "::" + keyName.Key + "' is not writeable from within the runtime!" }; } Key key = acl_map->at(keyName).first; @@ -145,9 +145,9 @@ void Settings::setConsoleInputAccessMapping(const ComposedKey& keyName, int valu if (!acl_map->contains(keyName)) { throw std::logic_error { "Unknown key name accessed in Settings::setConsoleInputAccessMapping" }; } else if (acl_map->at(keyName).second == SettingsAccessMask::NO_ACCESS) { - throw std::logic_error { "Key '" + keyName.Category + " > " + keyName.Key + "' is not accessible from within the runtime!" }; + throw std::logic_error { "Key '" + keyName.Category + "::" + keyName.Key + "' is not accessible from within the runtime!" }; } else if (acl_map->at(keyName).second == SettingsAccessMask::READ_ONLY) { - throw std::logic_error { "Key '" + keyName.Category + " > " + keyName.Key + "' is not writeable from within the runtime!" }; + throw std::logic_error { "Key '" + keyName.Category + "::" + keyName.Key + "' is not writeable from within the runtime!" }; } Key key = acl_map->at(keyName).first; @@ -164,9 +164,9 @@ void Settings::setConsoleInputAccessMapping(const ComposedKey& keyName, bool val if (!acl_map->contains(keyName)) { throw std::logic_error { "Unknown key name accessed in Settings::setConsoleInputAccessMapping" }; } else if (acl_map->at(keyName).second == SettingsAccessMask::NO_ACCESS) { - throw std::logic_error { "Key '" + keyName.Category + " > " + keyName.Key + "' is not accessible from within the runtime!" }; + throw std::logic_error { "Key '" + keyName.Category + "::" + keyName.Key + "' is not accessible from within the runtime!" }; } else if (acl_map->at(keyName).second == SettingsAccessMask::READ_ONLY) { - throw std::logic_error { "Key '" + keyName.Category + " > " + keyName.Key + "' is not writeable from within the runtime!" }; + throw std::logic_error { "Key '" + keyName.Category + "::" + keyName.Key + "' is not writeable from within the runtime!" }; } Key key = acl_map->at(keyName).first; From 3c80bcbf0184bcf6b440ddde020e9f944bda6026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Wed, 26 Jun 2024 13:40:39 +0200 Subject: [PATCH 41/43] remove line ChronoWrapper.cpp:13 as discussed in review --- src/ChronoWrapper.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ChronoWrapper.cpp b/src/ChronoWrapper.cpp index 8b2b917..9a2c50b 100644 --- a/src/ChronoWrapper.cpp +++ b/src/ChronoWrapper.cpp @@ -10,7 +10,6 @@ std::chrono::high_resolution_clock::duration ChronoWrapper::TimeFromStringWithLi float time_value; if (!std::regex_search(time_str, match, time_regex)) return std::chrono::nanoseconds(0); time_value = stof(match.str(1)); - beammp_debugf("Parsed time was: {}{}", time_value, match.str(2)); if (match.str(2) == "d") { return std::chrono::seconds((uint64_t)(time_value * 86400)); //86400 seconds in a day } From 29f4d0d286503906766e7467f4995ca4a1d27c00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Wed, 26 Jun 2024 14:06:06 +0200 Subject: [PATCH 42/43] run clang-format --- include/ChronoWrapper.h | 2 +- include/Common.h | 4 +--- include/Json.h | 4 ++-- include/RWMutex.h | 2 +- include/Settings.h | 6 ++---- include/TPPSMonitor.h | 2 +- src/ChronoWrapper.cpp | 21 +++++++++------------ src/Common.cpp | 3 +-- src/Http.cpp | 10 +++++----- src/LuaAPI.cpp | 2 +- src/Profiling.cpp | 1 - src/Settings.cpp | 12 ++++-------- src/TConfig.cpp | 4 ++-- src/TConsole.cpp | 12 ++++++------ src/THeartbeatThread.cpp | 4 ++-- src/TLuaEngine.cpp | 2 +- src/TNetwork.cpp | 4 ++-- src/TPPSMonitor.cpp | 4 ++-- src/main.cpp | 2 +- 19 files changed, 44 insertions(+), 57 deletions(-) diff --git a/include/ChronoWrapper.h b/include/ChronoWrapper.h index 009e911..906ba26 100644 --- a/include/ChronoWrapper.h +++ b/include/ChronoWrapper.h @@ -4,5 +4,5 @@ #include namespace ChronoWrapper { - std::chrono::high_resolution_clock::duration TimeFromStringWithLiteral(const std::string& time_str); +std::chrono::high_resolution_clock::duration TimeFromStringWithLiteral(const std::string& time_str); } diff --git a/include/Common.h b/include/Common.h index 64d7194..4960077 100644 --- a/include/Common.h +++ b/include/Common.h @@ -37,8 +37,8 @@ #include namespace fs = std::filesystem; -#include "TConsole.h" #include "Settings.h" +#include "TConsole.h" struct Version { uint8_t major; @@ -61,8 +61,6 @@ class Application final { public: // types - - using TShutdownHandler = std::function; // methods diff --git a/include/Json.h b/include/Json.h index 4034ad0..fbb90eb 100644 --- a/include/Json.h +++ b/include/Json.h @@ -17,7 +17,7 @@ // along with this program. If not, see . #pragma once -#include "rapidjson/stringbuffer.h" -#include "rapidjson/prettywriter.h" #include "rapidjson/document.h" +#include "rapidjson/prettywriter.h" +#include "rapidjson/stringbuffer.h" #include "rapidjson/writer.h" diff --git a/include/RWMutex.h b/include/RWMutex.h index f18e69d..82d0504 100644 --- a/include/RWMutex.h +++ b/include/RWMutex.h @@ -23,8 +23,8 @@ * and write locks and read locks are mutually exclusive. */ -#include #include +#include // Use ReadLock(m) and WriteLock(m) to lock it. using RWMutex = std::shared_mutex; diff --git a/include/Settings.h b/include/Settings.h index 8ee3ffc..1ef8ea1 100644 --- a/include/Settings.h +++ b/include/Settings.h @@ -89,7 +89,7 @@ struct Settings { General_AllowGuests }; - Sync> SettingsMap; + Sync> SettingsMap; enum SettingsAccessMask { READ_ONLY, // Value can be read from console READ_WRITE, // Value can be read and written to from console @@ -101,7 +101,7 @@ struct Settings { SettingsAccessMask // Console read/write permissions >; - Sync> InputAccessMapping; + Sync> InputAccessMapping; std::string getAsString(Key key); int getAsInt(Key key); @@ -142,5 +142,3 @@ struct Settings { void setConsoleInputAccessMapping(const ComposedKey& keyName, int value); void setConsoleInputAccessMapping(const ComposedKey& keyName, bool value); }; - - diff --git a/include/TPPSMonitor.h b/include/TPPSMonitor.h index bb5e802..aaeb417 100644 --- a/include/TPPSMonitor.h +++ b/include/TPPSMonitor.h @@ -27,7 +27,7 @@ class TNetwork; class TPPSMonitor : public IThreaded { public: explicit TPPSMonitor(TServer& Server); - virtual ~TPPSMonitor() {} + virtual ~TPPSMonitor() { } void operator()() override; diff --git a/src/ChronoWrapper.cpp b/src/ChronoWrapper.cpp index 9a2c50b..99ad5cc 100644 --- a/src/ChronoWrapper.cpp +++ b/src/ChronoWrapper.cpp @@ -2,24 +2,21 @@ #include "Common.h" #include -std::chrono::high_resolution_clock::duration ChronoWrapper::TimeFromStringWithLiteral(const std::string& time_str) -{ +std::chrono::high_resolution_clock::duration ChronoWrapper::TimeFromStringWithLiteral(const std::string& time_str) { // const std::regex time_regex(R"((\d+\.{0,1}\d*)(min|ms|us|ns|[dhs]))"); //i.e one of: "25ns, 6us, 256ms, 2s, 13min, 69h, 356d" will get matched (only available in newer C++ versions) - const std::regex time_regex(R"((\d+\.{0,1}\d*)(min|[dhs]))"); //i.e one of: "2.01s, 13min, 69h, 356.69d" will get matched + const std::regex time_regex(R"((\d+\.{0,1}\d*)(min|[dhs]))"); // i.e one of: "2.01s, 13min, 69h, 356.69d" will get matched std::smatch match; float time_value; - if (!std::regex_search(time_str, match, time_regex)) return std::chrono::nanoseconds(0); + if (!std::regex_search(time_str, match, time_regex)) + return std::chrono::nanoseconds(0); time_value = stof(match.str(1)); if (match.str(2) == "d") { - return std::chrono::seconds((uint64_t)(time_value * 86400)); //86400 seconds in a day - } - else if (match.str(2) == "h") { - return std::chrono::seconds((uint64_t)(time_value * 3600)); //3600 seconds in an hour - } - else if (match.str(2) == "min") { + return std::chrono::seconds((uint64_t)(time_value * 86400)); // 86400 seconds in a day + } else if (match.str(2) == "h") { + return std::chrono::seconds((uint64_t)(time_value * 3600)); // 3600 seconds in an hour + } else if (match.str(2) == "min") { return std::chrono::seconds((uint64_t)(time_value * 60)); - } - else if (match.str(2) == "s") { + } else if (match.str(2) == "s") { return std::chrono::seconds((uint64_t)time_value); } return std::chrono::nanoseconds(0); diff --git a/src/Common.cpp b/src/Common.cpp index e412a6f..4988fc4 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -22,13 +22,13 @@ #include "TConsole.h" #include #include +#include #include #include #include #include #include #include -#include #include "Compat.h" #include "CustomAssert.h" @@ -383,4 +383,3 @@ void SplitString(const std::string& str, const char delim, std::vector, CONNECTION_A write_index++; write_index %= CONNECTION_AMOUNT; clients[i] = std::make_shared(connectionInfo.host, connectionInfo.port); - connections[i] = {connectionInfo.host, connectionInfo.port}; + connections[i] = { connectionInfo.host, connectionInfo.port }; beammp_tracef("New client connected, with ip {} and port {}", connectionInfo.host, connectionInfo.port); return clients[i]; } std::string Http::GET(const std::string& host, int port, const std::string& target, unsigned int* status) { - std::shared_ptr client = getClient({host, port}); + std::shared_ptr client = getClient({ host, port }); client->enable_server_certificate_verification(false); client->set_address_family(AF_INET); auto res = client->Get(target.c_str()); @@ -75,7 +75,7 @@ std::string Http::GET(const std::string& host, int port, const std::string& targ } std::string Http::POST(const std::string& host, int port, const std::string& target, const std::string& body, const std::string& ContentType, unsigned int* status, const httplib::Headers& headers) { - std::shared_ptr client = getClient({host, port}); + std::shared_ptr client = getClient({ host, port }); client->set_read_timeout(std::chrono::seconds(10)); beammp_assert(client->is_valid()); client->enable_server_certificate_verification(false); diff --git a/src/LuaAPI.cpp b/src/LuaAPI.cpp index 8ec0ec8..2189080 100644 --- a/src/LuaAPI.cpp +++ b/src/LuaAPI.cpp @@ -20,8 +20,8 @@ #include "Client.h" #include "Common.h" #include "CustomAssert.h" -#include "TLuaEngine.h" #include "Settings.h" +#include "TLuaEngine.h" #include diff --git a/src/Profiling.cpp b/src/Profiling.cpp index f6a41d8..ef8c405 100644 --- a/src/Profiling.cpp +++ b/src/Profiling.cpp @@ -57,4 +57,3 @@ size_t prof::UnitExecutionTime::measurement_count() const { std::unique_lock lock(m_mtx); return m_total_calls; } - diff --git a/src/Settings.cpp b/src/Settings.cpp index b5692a7..a82a66b 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -18,9 +18,7 @@ #include "Settings.h" - - -Settings::Settings(){ +Settings::Settings() { SettingsMap = std::unordered_map { // All entries which contain std::strings must be explicitly constructed, otherwise they become 'bool' { General_Description, std::string("BeamMP Default Description") }, @@ -39,7 +37,7 @@ Settings::Settings(){ { Misc_SendErrorsShowMessage, true }, { Misc_SendErrors, true }, { Misc_ImScaredOfUpdates, true }, - { Misc_UpdateReminderTime, "30s"} + { Misc_UpdateReminderTime, "30s" } }; InputAccessMapping = std::unordered_map { @@ -55,14 +53,12 @@ Settings::Settings(){ { { "General", "LogChat" }, { General_LogChat, READ_ONLY } }, { { "General", "ResourceFolder" }, { General_ResourceFolder, READ_ONLY } }, { { "General", "Debug" }, { General_Debug, READ_WRITE } }, - { { "General", "AllowGuests"}, { General_AllowGuests, READ_WRITE } }, + { { "General", "AllowGuests" }, { General_AllowGuests, READ_WRITE } }, { { "Misc", "SendErrorsShowMessage" }, { Misc_SendErrorsShowMessage, READ_WRITE } }, { { "Misc", "SendErrors" }, { Misc_SendErrors, READ_WRITE } }, { { "Misc", "ImScaredOfUpdates" }, { Misc_ImScaredOfUpdates, READ_WRITE } }, - { { "Misc", "UpdateReminderTime" }, {Misc_UpdateReminderTime, READ_WRITE} } + { { "Misc", "UpdateReminderTime" }, { Misc_UpdateReminderTime, READ_WRITE } } }; - - } std::string Settings::getAsString(Key key) { diff --git a/src/TConfig.cpp b/src/TConfig.cpp index c3a0a8d..95044a7 100644 --- a/src/TConfig.cpp +++ b/src/TConfig.cpp @@ -19,8 +19,8 @@ #include "Common.h" #include "Env.h" -#include "TConfig.h" #include "Settings.h" +#include "TConfig.h" #include #include #include @@ -239,7 +239,7 @@ void TConfig::TryReadValue(toml::value& Table, const std::string& Category, cons void TConfig::ParseFromFile(std::string_view name) { try { toml::value data {}; - if (!mDisableConfig) { + if (!mDisableConfig) { data = toml::parse(name.data()); } diff --git a/src/TConsole.cpp b/src/TConsole.cpp index b9ae153..9882c12 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -389,7 +389,7 @@ void TConsole::Command_Settings(const std::string&, const std::vector #include #include diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 32ad0e5..a48c848 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -1135,7 +1135,7 @@ void TLuaEngine::StateThreadData::operator()() { case TLuaType::Bool: LuaArgs.push_back(sol::make_object(StateView, std::get(Arg))); break; - case TLuaType::StringStringMap: { + case TLuaType::StringStringMap: { auto Map = std::get>(Arg); auto Table = StateView.create_table(); for (const auto& [k, v] : Map) { diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index 65f0c97..ddecf69 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -292,7 +292,7 @@ std::shared_ptr TNetwork::Authentication(TConnection&& RawConnection) { std::string Key(reinterpret_cast(Data.data()), Data.size()); std::string AuthKey = Application::Settings.getAsString(Settings::Key::General_AuthKey); std::string ClientIp = Client->GetIdentifiers().at("ip"); - + nlohmann::json AuthReq {}; std::string AuthResStr {}; try { @@ -373,7 +373,7 @@ std::shared_ptr TNetwork::Authentication(TConnection&& RawConnection) { return false; }); - if (!NotAllowedWithReason && !Application::Settings.getAsBool(Settings::Key::General_AllowGuests) && Client->IsGuest()) { //!NotAllowedWithReason because this message has the lowest priority + if (!NotAllowedWithReason && !Application::Settings.getAsBool(Settings::Key::General_AllowGuests) && Client->IsGuest()) { //! NotAllowedWithReason because this message has the lowest priority NotAllowedWithReason = true; Reason = "No guests are allowed on this server! To join, sign up at: forum.beammp.com."; } diff --git a/src/TPPSMonitor.cpp b/src/TPPSMonitor.cpp index 461dfbf..ccababf 100644 --- a/src/TPPSMonitor.cpp +++ b/src/TPPSMonitor.cpp @@ -65,13 +65,13 @@ void TPPSMonitor::operator()() { V += c->GetCarCount(); } // kick on "no ping" - if (c->SecondsSinceLastPing() > (20 * 60) ){ + if (c->SecondsSinceLastPing() > (20 * 60)) { beammp_debugf("client {} ({}) timing out: {}", c->GetID(), c->GetName(), c->SecondsSinceLastPing()); TimedOutClients.push_back(c); } else if (c->IsSynced() && c->SecondsSinceLastPing() > (1 * 60)) { beammp_debugf("client {} ({}) timing out: {}", c->GetName(), c->GetID(), c->SecondsSinceLastPing()); TimedOutClients.push_back(c); - } + } return true; }); diff --git a/src/main.cpp b/src/main.cpp index 59c1469..2b36e5a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,6 +20,7 @@ #include "Common.h" #include "Http.h" #include "LuaAPI.h" +#include "Settings.h" #include "SignalHandling.h" #include "TConfig.h" #include "THeartbeatThread.h" @@ -29,7 +30,6 @@ #include "TPluginMonitor.h" #include "TResourceManager.h" #include "TServer.h" -#include "Settings.h" #include #include From 08374b13986c61ade322a9cc9e4cdc1e3f0b39ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucca=20Jim=C3=A9nez=20K=C3=B6nings?= Date: Wed, 26 Jun 2024 14:10:59 +0200 Subject: [PATCH 43/43] deprecate Ubuntu 20.04 --- .github/workflows/linux.yml | 4 ++-- scripts/{ubuntu-20.04 => ubuntu-24.04}/1-install-deps.sh | 0 scripts/{ubuntu-20.04 => ubuntu-24.04}/1.5-git-safe.sh | 0 scripts/{ubuntu-20.04 => ubuntu-24.04}/2-configure.sh | 0 scripts/{ubuntu-20.04 => ubuntu-24.04}/3-build-tests.sh | 0 scripts/{ubuntu-20.04 => ubuntu-24.04}/3-build.sh | 0 .../{ubuntu-20.04 => ubuntu-24.04}/4-install-runtime-deps.sh | 0 7 files changed, 2 insertions(+), 2 deletions(-) rename scripts/{ubuntu-20.04 => ubuntu-24.04}/1-install-deps.sh (100%) rename scripts/{ubuntu-20.04 => ubuntu-24.04}/1.5-git-safe.sh (100%) rename scripts/{ubuntu-20.04 => ubuntu-24.04}/2-configure.sh (100%) rename scripts/{ubuntu-20.04 => ubuntu-24.04}/3-build-tests.sh (100%) rename scripts/{ubuntu-20.04 => ubuntu-24.04}/3-build.sh (100%) rename scripts/{ubuntu-20.04 => ubuntu-24.04}/4-install-runtime-deps.sh (100%) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 9881f63..a560375 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -26,7 +26,7 @@ jobs: - distro: ubuntu version: 22.04 - distro: ubuntu - version: 20.04 + version: 24.04 container: image: ${{ matrix.distro }}:${{ matrix.version }} steps: @@ -97,7 +97,7 @@ jobs: - distro: ubuntu version: 22.04 - distro: ubuntu - version: 20.04 + version: 24.04 container: image: ${{ matrix.distro }}:${{ matrix.version }} steps: diff --git a/scripts/ubuntu-20.04/1-install-deps.sh b/scripts/ubuntu-24.04/1-install-deps.sh similarity index 100% rename from scripts/ubuntu-20.04/1-install-deps.sh rename to scripts/ubuntu-24.04/1-install-deps.sh diff --git a/scripts/ubuntu-20.04/1.5-git-safe.sh b/scripts/ubuntu-24.04/1.5-git-safe.sh similarity index 100% rename from scripts/ubuntu-20.04/1.5-git-safe.sh rename to scripts/ubuntu-24.04/1.5-git-safe.sh diff --git a/scripts/ubuntu-20.04/2-configure.sh b/scripts/ubuntu-24.04/2-configure.sh similarity index 100% rename from scripts/ubuntu-20.04/2-configure.sh rename to scripts/ubuntu-24.04/2-configure.sh diff --git a/scripts/ubuntu-20.04/3-build-tests.sh b/scripts/ubuntu-24.04/3-build-tests.sh similarity index 100% rename from scripts/ubuntu-20.04/3-build-tests.sh rename to scripts/ubuntu-24.04/3-build-tests.sh diff --git a/scripts/ubuntu-20.04/3-build.sh b/scripts/ubuntu-24.04/3-build.sh similarity index 100% rename from scripts/ubuntu-20.04/3-build.sh rename to scripts/ubuntu-24.04/3-build.sh diff --git a/scripts/ubuntu-20.04/4-install-runtime-deps.sh b/scripts/ubuntu-24.04/4-install-runtime-deps.sh similarity index 100% rename from scripts/ubuntu-20.04/4-install-runtime-deps.sh rename to scripts/ubuntu-24.04/4-install-runtime-deps.sh