From 1abf6d5adfe9a9d383dc7fd1e2fc53c0cbbc55ba Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Tue, 22 Jun 2021 00:20:12 +0200 Subject: [PATCH] Fully implement TOML config file, delete .idea folder The new config is called "ServerConfig.toml" and uses the TOML library. It's nice. --- .gitignore | 2 + .idea/.name | 1 - .idea/misc.xml | 4 - .idea/vcs.xml | 16 --- CMakeLists.txt | 2 +- include/TConfig.h | 16 +-- src/TConfig.cpp | 270 +++++++++++++++++++++++++++++++++------------- src/main.cpp | 9 +- 8 files changed, 217 insertions(+), 103 deletions(-) delete mode 100644 .idea/.name delete mode 100644 .idea/misc.xml delete mode 100644 .idea/vcs.xml diff --git a/.gitignore b/.gitignore index dc03c21..21661a1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.idea/ +*.toml boost_* Resources ## Ignore Visual Studio temporary files, build results, and diff --git a/.idea/.name b/.idea/.name deleted file mode 100644 index f083ce8..0000000 --- a/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -Server \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 79b3c94..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 4a40b5e..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a5a9f2..182749c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,7 +53,7 @@ add_executable(BeamMP-Server target_include_directories(BeamMP-Server PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/commandline") find_package(Lua REQUIRED) -target_include_directories(BeamMP-Server PUBLIC ${Boost_INCLUDE_DIRS} ${LUA_INCLUDE_DIR} "socket.io-client-cpp/src") +target_include_directories(BeamMP-Server PUBLIC ${Boost_INCLUDE_DIRS} ${LUA_INCLUDE_DIR} "socket.io-client-cpp/src" "include/tomlplusplus") find_package(OpenSSL REQUIRED) diff --git a/include/TConfig.h b/include/TConfig.h index bd1287f..02a5c5a 100644 --- a/include/TConfig.h +++ b/include/TConfig.h @@ -2,14 +2,18 @@ #include "Common.h" +#include + class TConfig { public: - explicit TConfig(const std::string& ConfigFile); + explicit TConfig(); + + bool Failed() const { return mFailed; } private: - static void ReadJson(); - static void PrintDebug(); - static void ManageJson(); - static std::string RemoveComments(const std::string& Line); - static void SetValues(const std::string& Line, int Index); + void CreateConfigFile(std::string_view name); + void ParseFromFile(std::string_view name); + void PrintDebug(); + + bool mFailed { false }; }; diff --git a/src/TConfig.cpp b/src/TConfig.cpp index 7adf0c1..f3c6efa 100644 --- a/src/TConfig.cpp +++ b/src/TConfig.cpp @@ -1,16 +1,34 @@ +#include // header-only version of TOML++ + #include "TConfig.h" #include -#include "Json.h" #include -TConfig::TConfig(const std::string& ConfigFile) { - if(fs::exists("Config.json")){ - info("New Config found updating values"); - ReadJson(); - return; +static const char* ConfigFileName = static_cast("ServerConfig.toml"); + +static constexpr std::string_view StrDebug = "Debug"; +static constexpr std::string_view StrPrivate = "Private"; +static constexpr std::string_view StrPort = "Port"; +static constexpr std::string_view StrMaxCars = "MaxCars"; +static constexpr std::string_view StrMaxPlayers = "MaxPlayers"; +static constexpr std::string_view StrMap = "Map"; +static constexpr std::string_view StrName = "Name"; +static constexpr std::string_view StrDescription = "Description"; +static constexpr std::string_view StrResourceFolder = "ResourceFolder"; +static constexpr std::string_view StrAuthKey = "AuthKey"; + +TConfig::TConfig() { + if (!fs::exists(ConfigFileName) || !fs::is_regular_file(ConfigFileName)) { + info("No config file found! Generating one..."); + CreateConfigFile(ConfigFileName); } - - if(!fs::exists("Server.cfg")){ + if (!mFailed) { + if (fs::exists("Server.cfg")) { + warn("An old \"Server.cfg\" file still exists. Please note that this is no longer used. Instead, \"" + std::string(ConfigFileName) + "\" is used. You can safely delete the \"Server.cfg\"."); + } + ParseFromFile(ConfigFileName); + } + /*if (!fs::exists("Server.cfg")) { info("Config not found generating default"); ManageJson(); error("AuthKey cannot be empty check Config.json!"); @@ -48,41 +66,145 @@ TConfig::TConfig(const std::string& ConfigFile) { FileStream.open(ConfigFile); // TODO REPLACE THIS SHIT OMG -- replacing FileStream << "# This is the BeamMP Server Configuration File v0.60\n" - "Debug = false # true or false to enable debug console output\n" - "Private = true # Private?\n" - "Port = 30814 # Port to run the server on UDP and TCP\n" - "Cars = 1 # Max cars for every player\n" - "MaxPlayers = 10 # Maximum Amount of Clients\n" - "Map = \"/levels/gridmap/info.json\" # Default Map\n" - "Name = \"BeamMP New Server\" # Server Name\n" - "Desc = \"BeamMP Default Description\" # Server Description\n" - "use = \"Resources\" # Resource file name\n" - "AuthKey = \"\" # Auth Key"; + "Debug = false # true or false to enable debug console output\n" + "Private = true # Private?\n" + "Port = 30814 # Port to run the server on UDP and TCP\n" + "Cars = 1 # Max cars for every player\n" + "MaxPlayers = 10 # Maximum Amount of Clients\n" + "Map = \"/levels/gridmap/info.json\" # Default Map\n" + "Name = \"BeamMP New Server\" # Server Name\n" + "Desc = \"BeamMP Default Description\" # Server Description\n" + "use = \"Resources\" # Resource file name\n" + "AuthKey = \"\" # Auth Key"; FileStream.close(); error("You are required to input the AuthKey"); std::this_thread::sleep_for(std::chrono::seconds(3)); _Exit(0); } - ManageJson(); - PrintDebug(); + */ } +void TConfig::CreateConfigFile(std::string_view name) { + toml::table tbl { { + + { "General", + toml::table { { + + { StrDebug, Application::Settings.DebugModeEnabled }, + { StrPrivate, Application::Settings.Private }, + { StrPort, Application::Settings.Port }, + { StrMaxCars, Application::Settings.MaxCars }, + { StrMaxPlayers, Application::Settings.MaxPlayers }, + { StrMap, Application::Settings.MapName }, + { StrName, Application::Settings.ServerName }, + { StrDescription, Application::Settings.ServerDesc }, + { StrResourceFolder, Application::Settings.Resource }, + { StrAuthKey, Application::Settings.Key }, + + } } }, + + } }; + std::ofstream ofs { std::string(name) }; + if (ofs.good()) { + ofs << "# This is the BeamMP-Server config file.\n" + "# Help & Documentation: `https://wiki.beammp.com/en/home/server-maintenance`\n" + "# IMPORTANT: Fill in the AuthKey with the key you got from `https://beammp.com/k/dashboard` on the left under \"Keys\"" + << '\n'; + ofs << tbl << '\n'; + error("There was no \"" + std::string(ConfigFileName) + "\" file (this is normal for the first time running the server), so one was generated for you. Please open it and fill in your AuthKey and other settings, then restart the server. The old Server.cfg file will no longer be used and cause a warning if it exists from now on."); + mFailed = true; + } else { + error("Couldn't create " + std::string(name) + ". Check permissions, try again, and contact support if it continues not to work."); + mFailed = true; + } +} + +void TConfig::ParseFromFile(std::string_view name) { + try { + toml::table FullTable = toml::parse_file(name); + toml::table GeneralTable = *FullTable["General"].as_table(); + if (auto val = GeneralTable[StrDebug].value(); val.has_value()) { + Application::Settings.DebugModeEnabled = val.value(); + } else { + throw std::runtime_error(std::string(StrDebug)); + } + if (auto val = GeneralTable[StrPrivate].value(); val.has_value()) { + Application::Settings.Private = val.value(); + } else { + throw std::runtime_error(std::string(StrPrivate)); + } + if (auto val = GeneralTable[StrPort].value(); val.has_value()) { + Application::Settings.Port = val.value(); + } else { + throw std::runtime_error(std::string(StrPort)); + } + if (auto val = GeneralTable[StrMaxCars].value(); val.has_value()) { + Application::Settings.MaxCars = val.value(); + } else { + throw std::runtime_error(std::string(StrMaxCars)); + } + if (auto val = GeneralTable[StrMaxPlayers].value(); val.has_value()) { + Application::Settings.MaxPlayers = val.value(); + } else { + throw std::runtime_error(std::string(StrMaxPlayers)); + } + if (auto val = GeneralTable[StrMap].value(); val.has_value()) { + Application::Settings.MapName = val.value(); + } else { + throw std::runtime_error(std::string(StrMap)); + } + if (auto val = GeneralTable[StrName].value(); val.has_value()) { + Application::Settings.ServerName = val.value(); + } else { + throw std::runtime_error(std::string(StrName)); + } + if (auto val = GeneralTable[StrDescription].value(); val.has_value()) { + Application::Settings.ServerDesc = val.value(); + } else { + throw std::runtime_error(std::string(StrDescription)); + } + if (auto val = GeneralTable[StrResourceFolder].value(); val.has_value()) { + Application::Settings.Resource = val.value(); + } else { + throw std::runtime_error(std::string(StrResourceFolder)); + } + if (auto val = GeneralTable[StrAuthKey].value(); val.has_value()) { + Application::Settings.Key = val.value(); + } else { + throw std::runtime_error(std::string(StrAuthKey)); + } + } catch (const std::exception& err) { + error("Error parsing config file value: " + std::string(err.what())); + mFailed = true; + return; + } + PrintDebug(); + // all good so far, let's check if there's a key + if (Application::Settings.Key.empty()) { + error("No AuthKey specified in the \"" + std::string(ConfigFileName) + "\" file. Please get an AuthKey, enter it into the config file, and restart this server."); + mFailed = true; + } +} + +/* + void TConfig::ReadJson() { auto Size = fs::file_size("Config.json"); - if(Size < 3)return; + if (Size < 3) + return; std::ifstream ifs("Config.json"); - if(ifs.is_open()) { + if (ifs.is_open()) { std::string cfg(Size, 0); ifs.read(&cfg[0], long(Size)); ifs.close(); - if(auto pos = cfg.find('{'); pos != std::string::npos) { - if(cfg.at(0) != '{') { + if (auto pos = cfg.find('{'); pos != std::string::npos) { + if (cfg.at(0) != '{') { cfg = cfg.substr(pos); } - }else{ + } else { error("Config file is not valid JSON!"); std::this_thread::sleep_for(std::chrono::seconds(3)); Application::GracefullyShutdown(); @@ -90,97 +212,97 @@ void TConfig::ReadJson() { rapidjson::Document d; d.Parse(cfg.c_str()); - if(!d.HasParseError()){ + if (!d.HasParseError()) { auto& Val = d["Debug"]; - if(!Val.IsNull() && Val.IsBool()) { + if (!Val.IsNull() && Val.IsBool()) { Application::Settings.DebugModeEnabled = Val.GetBool(); - }else{ + } else { info("'Debug' Missing in config! Setting to 'false' by default"); } Val = d["Private"]; - if(!Val.IsNull() && Val.IsBool()) { + if (!Val.IsNull() && Val.IsBool()) { Application::Settings.Private = Val.GetBool(); - }else{ + } else { info("'Private' Missing in config! Setting to 'true' by default"); } Val = d["Port"]; - if(!Val.IsNull() && Val.IsNumber()) { + if (!Val.IsNull() && Val.IsNumber()) { Application::Settings.Port = Val.GetInt(); - }else{ + } else { info("'Port' Missing in config! Setting to '30814' by default"); } Val = d["MaxCars"]; - if(!Val.IsNull() && Val.IsNumber()) { + if (!Val.IsNull() && Val.IsNumber()) { Application::Settings.MaxCars = Val.GetInt(); - }else{ + } else { info("'MaxCars' Missing in config! Setting to '1' by default"); } Val = d["MaxPlayers"]; - if(!Val.IsNull() && Val.IsNumber()) { + if (!Val.IsNull() && Val.IsNumber()) { Application::Settings.MaxPlayers = Val.GetInt(); - }else{ + } else { info("'MaxPlayers' Missing in config! Setting to '10' by default"); } Val = d["Map"]; - if(!Val.IsNull() && Val.IsString()) { + if (!Val.IsNull() && Val.IsString()) { Application::Settings.MapName = Val.GetString(); - }else{ + } else { info("'Map' Missing in config! Setting to '/levels/gridmap/info.json' by default"); } Val = d["Name"]; - if(!Val.IsNull() && Val.IsString()) { + if (!Val.IsNull() && Val.IsString()) { Application::Settings.ServerName = Val.GetString(); - }else{ + } else { info("'Name' Missing in config! Setting to 'BeamMP Server' by default"); } Val = d["Desc"]; - if(!Val.IsNull() && Val.IsString()) { + if (!Val.IsNull() && Val.IsString()) { Application::Settings.ServerDesc = Val.GetString(); - }else{ + } else { info("'Desc' Missing in config! Setting to 'BeamMP Default Description' by default"); } Val = d["Resource"]; - if(!Val.IsNull() && Val.IsString()) { + if (!Val.IsNull() && Val.IsString()) { Application::Settings.Resource = Val.GetString(); - }else{ + } else { info("'Resource' Missing in config! Setting to 'Resources' by default"); } Val = d["AuthKey"]; - if(!Val.IsNull() && Val.IsString()) { + if (!Val.IsNull() && Val.IsString()) { Application::Settings.Key = Val.GetString(); - if(Application::Settings.Key.empty()) { + if (Application::Settings.Key.empty()) { error("AuthKey cannot be empty check Config.json!"); std::this_thread::sleep_for(std::chrono::seconds(3)); Application::GracefullyShutdown(); } - }else{ + } else { error("'AuthKey' Missing in config!"); std::this_thread::sleep_for(std::chrono::seconds(3)); Application::GracefullyShutdown(); } - }else{ + } else { error("Failed to parse JSON config! code " + std::to_string(d.GetParseError())); } - }else{ + } else { error("Failed to read Config.json"); std::this_thread::sleep_for(std::chrono::seconds(3)); Application::GracefullyShutdown(); @@ -191,16 +313,16 @@ void TConfig::ReadJson() { void TConfig::ManageJson() { rapidjson::Document d; d.Parse("{}"); - d.AddMember("Debug",Application::Settings.DebugModeEnabled,d.GetAllocator()); - d.AddMember("Private",Application::Settings.Private,d.GetAllocator()); - d.AddMember("Port",Application::Settings.Port,d.GetAllocator()); - d.AddMember("MaxCars",Application::Settings.MaxCars,d.GetAllocator()); - d.AddMember("MaxPlayers",Application::Settings.MaxPlayers,d.GetAllocator()); - d.AddMember("Map", rapidjson::StringRef(Application::Settings.MapName.c_str()),d.GetAllocator()); - d.AddMember("Name", rapidjson::StringRef(Application::Settings.ServerName.c_str()),d.GetAllocator()); - d.AddMember("Desc", rapidjson::StringRef(Application::Settings.ServerDesc.c_str()),d.GetAllocator()); - d.AddMember("Resource", rapidjson::StringRef(Application::Settings.Resource.c_str()),d.GetAllocator()); - d.AddMember("AuthKey", rapidjson::StringRef(Application::Settings.Key.c_str()),d.GetAllocator()); + d.AddMember("Debug", Application::Settings.DebugModeEnabled, d.GetAllocator()); + d.AddMember("Private", Application::Settings.Private, d.GetAllocator()); + d.AddMember("Port", Application::Settings.Port, d.GetAllocator()); + d.AddMember("MaxCars", Application::Settings.MaxCars, d.GetAllocator()); + d.AddMember("MaxPlayers", Application::Settings.MaxPlayers, d.GetAllocator()); + d.AddMember("Map", rapidjson::StringRef(Application::Settings.MapName.c_str()), d.GetAllocator()); + d.AddMember("Name", rapidjson::StringRef(Application::Settings.ServerName.c_str()), d.GetAllocator()); + d.AddMember("Desc", rapidjson::StringRef(Application::Settings.ServerDesc.c_str()), d.GetAllocator()); + d.AddMember("Resource", rapidjson::StringRef(Application::Settings.Resource.c_str()), d.GetAllocator()); + d.AddMember("AuthKey", rapidjson::StringRef(Application::Settings.Key.c_str()), d.GetAllocator()); rapidjson::StringBuffer buffer; rapidjson::PrettyWriter writer(buffer); @@ -208,11 +330,11 @@ void TConfig::ManageJson() { std::ofstream cfg; cfg.open("Config.json"); - if(cfg.is_open()){ + if (cfg.is_open()) { cfg << "BeamMP Server Configuration File\n" << buffer.GetString(); cfg.close(); - }else{ + } else { error("Failed to create Config.json!"); } } @@ -283,17 +405,17 @@ void TConfig::SetValues(const std::string& Line, int Index) { break; } } - -void TConfig::PrintDebug(){ - debug("Debug : " + std::string(Application::Settings.DebugModeEnabled ? "true" : "false")); - debug("Private : " + std::string(Application::Settings.Private ? "true" : "false")); - debug("Port : " + std::to_string(Application::Settings.Port)); - debug("Max Cars : " + std::to_string(Application::Settings.MaxCars)); - debug("MaxPlayers : " + std::to_string(Application::Settings.MaxPlayers)); - debug("MapName : \"" + Application::Settings.MapName + "\""); - debug("ServerName : \"" + Application::Settings.ServerName + "\""); - debug("ServerDesc : \"" + Application::Settings.ServerDesc + "\""); - debug("File : \"" + Application::Settings.Resource + "\""); - debug("Key length : " + std::to_string(Application::Settings.Key.length()) + ""); +*/ +void TConfig::PrintDebug() { + debug(std::string(StrDebug) + ": " + std::string(Application::Settings.DebugModeEnabled ? "true" : "false")); + debug(std::string(StrPrivate) + ": " + std::string(Application::Settings.Private ? "true" : "false")); + debug(std::string(StrPort) + ": " + std::to_string(Application::Settings.Port)); + debug(std::string(StrMaxCars) + ": " + std::to_string(Application::Settings.MaxCars)); + debug(std::string(StrMaxPlayers) + ": " + std::to_string(Application::Settings.MaxPlayers)); + debug(std::string(StrMap) + ": \"" + Application::Settings.MapName + "\""); + debug(std::string(StrName) + ": \"" + Application::Settings.ServerName + "\""); + debug(std::string(StrDescription) + ": \"" + Application::Settings.ServerDesc + "\""); + debug(std::string(StrResourceFolder) + ": \"" + Application::Settings.Resource + "\""); + // special! + debug("Key Length: " + std::to_string(Application::Settings.Key.length()) + ""); } - diff --git a/src/main.cpp b/src/main.cpp index 699c3ff..5446c1b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -50,7 +50,14 @@ int main(int argc, char** argv) { Application::RegisterShutdownHandler([&Shutdown] { Shutdown = true; }); TServer Server(argc, argv); - [[maybe_unused]] TConfig Config("Server.cfg"); + [[maybe_unused]] TConfig Config; + + if (Config.Failed()) { + info("Closing in 10 seconds"); + std::this_thread::sleep_for(std::chrono::seconds(10)); + return 1; + } + RegisterThread("Main"); TResourceManager ResourceManager; TPPSMonitor PPSMonitor(Server);