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] 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()));