Add logic for new Settings type

Signed-off-by: Lucca Jiménez Könings <development@jimkoen.com>
This commit is contained in:
Lucca Jiménez Könings 2024-02-22 23:15:32 +01:00
parent 55f5437618
commit 89034a64e0
4 changed files with 97 additions and 18 deletions

View File

@ -103,7 +103,7 @@ public:
static void SetPPS(const std::string& NewPPS) { mPPS = NewPPS; } static void SetPPS(const std::string& NewPPS) { mPPS = NewPPS; }
static TSettings Settings; static TSettings Settings;
static struct Settings SettingsSingleton; static inline struct Settings SettingsSingleton { };
static std::vector<std::string> GetBackendUrlsInOrder() { static std::vector<std::string> GetBackendUrlsInOrder() {
return { return {

View File

@ -19,6 +19,7 @@
#pragma once #pragma once
#include "Common.h" #include "Common.h"
#include "TSettings.h"
#include <atomic> #include <atomic>
#include <filesystem> #include <filesystem>
@ -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, 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, 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, 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(); void ParseOldFormat();
std::string TagsAsPrettyArray() const; std::string TagsAsPrettyArray() const;

View File

@ -49,7 +49,21 @@ struct Settings {
General_Debug General_Debug
}; };
std::unordered_map<Key, SettingsTypeVariant> SettingsMap {}; std::unordered_map<Key, SettingsTypeVariant> 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) { std::string getAsString(Key key) {
if (!SettingsMap.contains(key)) { if (!SettingsMap.contains(key)) {
@ -72,6 +86,13 @@ struct Settings {
return std::get<bool>(SettingsMap.at(key)); return std::get<bool>(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) { void set(Key key, std::string value) {
if (!SettingsMap.contains(key)) { if (!SettingsMap.contains(key)) {
throw std::logic_error { "Undefined setting key accessed in Settings::getAsString" }; throw std::logic_error { "Undefined setting key accessed in Settings::getAsString" };

View File

@ -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) { 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 (!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) { if (const char* envp = std::getenv(Env.data()); envp != nullptr && std::strcmp(envp, "") != 0) {
OutValue = std::string(envp); OutValue = std::string(envp);
return; 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) { 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 (!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) { if (const char* envp = std::getenv(Env.data()); envp != nullptr && std::strcmp(envp, "") != 0) {
auto Str = std::string(envp); auto Str = std::string(envp);
OutValue = Str == "1" || Str == "true"; 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) { 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 (!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) { if (const char* envp = std::getenv(Env.data()); envp != nullptr && std::strcmp(envp, "") != 0) {
OutValue = int(std::strtol(envp, nullptr, 10)); OutValue = int(std::strtol(envp, nullptr, 10));
return; 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 <class... Ts>
struct overloaded : Ts... {
using Ts::operator()...;
};
template <class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
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) { void TConfig::ParseFromFile(std::string_view name) {
try { try {
toml::value data {}; toml::value data {};
@ -255,23 +312,22 @@ void TConfig::ParseFromFile(std::string_view name) {
TryReadValue(data, "Misc", StrSendErrorsMessageEnabled, "", Application::Settings.SendErrorsMessageEnabled); TryReadValue(data, "Misc", StrSendErrorsMessageEnabled, "", Application::Settings.SendErrorsMessageEnabled);
// Read into new Settings Singleton // Read into new Settings Singleton
TryReadValue(data, "General", StrDebug, EnvStrDebug, Application::Settings.DebugModeEnabled); TryReadValue(data, "General", StrDebug, EnvStrDebug, Settings::Key::General_Debug);
TryReadValue(data, "General", StrPrivate, EnvStrPrivate, Application::Settings.Private); TryReadValue(data, "General", StrPrivate, EnvStrPrivate, Settings::Key::General_Private);
TryReadValue(data, "General", StrPort, EnvStrPort, Application::Settings.Port); TryReadValue(data, "General", StrPort, EnvStrPort, Settings::Key::General_Port);
TryReadValue(data, "General", StrMaxCars, EnvStrMaxCars, Application::Settings.MaxCars); TryReadValue(data, "General", StrMaxCars, EnvStrMaxCars, Settings::Key::General_MaxCars);
TryReadValue(data, "General", StrMaxPlayers, EnvStrMaxPlayers, Application::Settings.MaxPlayers); TryReadValue(data, "General", StrMaxPlayers, EnvStrMaxPlayers, Settings::Key::General_MaxPlayers);
TryReadValue(data, "General", StrMap, EnvStrMap, Application::Settings.MapName); TryReadValue(data, "General", StrMap, EnvStrMap, Settings::Key::General_Map);
TryReadValue(data, "General", StrName, EnvStrName, Application::Settings.ServerName); TryReadValue(data, "General", StrName, EnvStrName, Settings::Key::General_Name);
TryReadValue(data, "General", StrDescription, EnvStrDescription, Application::Settings.ServerDesc); TryReadValue(data, "General", StrDescription, EnvStrDescription, Settings::Key::General_Description);
TryReadValue(data, "General", StrTags, EnvStrTags, Application::Settings.ServerTags); TryReadValue(data, "General", StrTags, EnvStrTags, Settings::Key::General_Tags);
TryReadValue(data, "General", StrResourceFolder, EnvStrResourceFolder, Application::Settings.Resource); TryReadValue(data, "General", StrResourceFolder, EnvStrResourceFolder, Settings::Key::General_ResourceFolder);
TryReadValue(data, "General", StrAuthKey, EnvStrAuthKey, Application::Settings.Key); TryReadValue(data, "General", StrAuthKey, EnvStrAuthKey, Settings::Key::General_AuthKey);
TryReadValue(data, "General", StrLogChat, EnvStrLogChat, Application::Settings.LogChat); TryReadValue(data, "General", StrLogChat, EnvStrLogChat, Settings::Key::General_LogChat);
TryReadValue(data, "General", StrPassword, "", Application::Settings.Password);
// Misc // Misc
TryReadValue(data, "Misc", StrSendErrors, "", Application::Settings.SendErrors); TryReadValue(data, "Misc", StrSendErrors, "", Settings::Key::Misc_SendErrors);
TryReadValue(data, "Misc", StrHideUpdateMessages, "", Application::Settings.HideUpdateMessages); TryReadValue(data, "Misc", StrHideUpdateMessages, "", Settings::Misc_ImScaredOfUpdates);
TryReadValue(data, "Misc", StrSendErrorsMessageEnabled, "", Application::Settings.SendErrorsMessageEnabled); TryReadValue(data, "Misc", StrSendErrorsMessageEnabled, "", Settings::Misc_SendErrorsShowMessage);
} catch (const std::exception& err) { } catch (const std::exception& err) {
beammp_error("Error parsing config file value: " + std::string(err.what())); beammp_error("Error parsing config file value: " + std::string(err.what()));