Fully implement TOML config file, delete .idea folder

The new config is called "ServerConfig.toml" and uses the TOML library.
It's nice.
This commit is contained in:
Lion Kortlepel 2021-06-22 00:20:12 +02:00 committed by Lion
parent f626474b4f
commit 1abf6d5adf
8 changed files with 217 additions and 103 deletions

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
.idea/
*.toml
boost_* boost_*
Resources Resources
## Ignore Visual Studio temporary files, build results, and ## Ignore Visual Studio temporary files, build results, and

1
.idea/.name generated
View File

@ -1 +0,0 @@
Server

4
.idea/misc.xml generated
View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
</project>

16
.idea/vcs.xml generated
View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="$PROJECT_DIR$/asio" vcs="Git" />
<mapping directory="$PROJECT_DIR$/include/commandline" vcs="Git" />
<mapping directory="$PROJECT_DIR$/rapidjson" vcs="Git" />
<mapping directory="$PROJECT_DIR$/rapidjson/thirdparty/gtest" vcs="Git" />
<mapping directory="$PROJECT_DIR$/socket.io-client-cpp" vcs="Git" />
<mapping directory="$PROJECT_DIR$/socket.io-client-cpp/lib/asio" vcs="Git" />
<mapping directory="$PROJECT_DIR$/socket.io-client-cpp/lib/catch" vcs="Git" />
<mapping directory="$PROJECT_DIR$/socket.io-client-cpp/lib/rapidjson" vcs="Git" />
<mapping directory="$PROJECT_DIR$/socket.io-client-cpp/lib/rapidjson/thirdparty/gtest" vcs="Git" />
<mapping directory="$PROJECT_DIR$/socket.io-client-cpp/lib/websocketpp" vcs="Git" />
</component>
</project>

View File

@ -53,7 +53,7 @@ add_executable(BeamMP-Server
target_include_directories(BeamMP-Server PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/commandline") target_include_directories(BeamMP-Server PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/commandline")
find_package(Lua REQUIRED) 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) find_package(OpenSSL REQUIRED)

View File

@ -2,14 +2,18 @@
#include "Common.h" #include "Common.h"
#include <atomic>
class TConfig { class TConfig {
public: public:
explicit TConfig(const std::string& ConfigFile); explicit TConfig();
bool Failed() const { return mFailed; }
private: private:
static void ReadJson(); void CreateConfigFile(std::string_view name);
static void PrintDebug(); void ParseFromFile(std::string_view name);
static void ManageJson(); void PrintDebug();
static std::string RemoveComments(const std::string& Line);
static void SetValues(const std::string& Line, int Index); bool mFailed { false };
}; };

View File

@ -1,16 +1,34 @@
#include <toml.hpp> // header-only version of TOML++
#include "TConfig.h" #include "TConfig.h"
#include <fstream> #include <fstream>
#include "Json.h"
#include <iostream> #include <iostream>
TConfig::TConfig(const std::string& ConfigFile) {
if(fs::exists("Config.json")){ static const char* ConfigFileName = static_cast<const char*>("ServerConfig.toml");
info("New Config found updating values");
ReadJson(); static constexpr std::string_view StrDebug = "Debug";
return; 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 (!mFailed) {
if(!fs::exists("Server.cfg")){ 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"); info("Config not found generating default");
ManageJson(); ManageJson();
error("AuthKey cannot be empty check Config.json!"); error("AuthKey cannot be empty check Config.json!");
@ -48,41 +66,145 @@ TConfig::TConfig(const std::string& ConfigFile) {
FileStream.open(ConfigFile); FileStream.open(ConfigFile);
// TODO REPLACE THIS SHIT OMG -- replacing // TODO REPLACE THIS SHIT OMG -- replacing
FileStream << "# This is the BeamMP Server Configuration File v0.60\n" FileStream << "# This is the BeamMP Server Configuration File v0.60\n"
"Debug = false # true or false to enable debug console output\n" "Debug = false # true or false to enable debug console output\n"
"Private = true # Private?\n" "Private = true # Private?\n"
"Port = 30814 # Port to run the server on UDP and TCP\n" "Port = 30814 # Port to run the server on UDP and TCP\n"
"Cars = 1 # Max cars for every player\n" "Cars = 1 # Max cars for every player\n"
"MaxPlayers = 10 # Maximum Amount of Clients\n" "MaxPlayers = 10 # Maximum Amount of Clients\n"
"Map = \"/levels/gridmap/info.json\" # Default Map\n" "Map = \"/levels/gridmap/info.json\" # Default Map\n"
"Name = \"BeamMP New Server\" # Server Name\n" "Name = \"BeamMP New Server\" # Server Name\n"
"Desc = \"BeamMP Default Description\" # Server Description\n" "Desc = \"BeamMP Default Description\" # Server Description\n"
"use = \"Resources\" # Resource file name\n" "use = \"Resources\" # Resource file name\n"
"AuthKey = \"\" # Auth Key"; "AuthKey = \"\" # Auth Key";
FileStream.close(); FileStream.close();
error("You are required to input the AuthKey"); error("You are required to input the AuthKey");
std::this_thread::sleep_for(std::chrono::seconds(3)); std::this_thread::sleep_for(std::chrono::seconds(3));
_Exit(0); _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<bool>(); val.has_value()) {
Application::Settings.DebugModeEnabled = val.value();
} else {
throw std::runtime_error(std::string(StrDebug));
}
if (auto val = GeneralTable[StrPrivate].value<bool>(); val.has_value()) {
Application::Settings.Private = val.value();
} else {
throw std::runtime_error(std::string(StrPrivate));
}
if (auto val = GeneralTable[StrPort].value<int>(); val.has_value()) {
Application::Settings.Port = val.value();
} else {
throw std::runtime_error(std::string(StrPort));
}
if (auto val = GeneralTable[StrMaxCars].value<int>(); val.has_value()) {
Application::Settings.MaxCars = val.value();
} else {
throw std::runtime_error(std::string(StrMaxCars));
}
if (auto val = GeneralTable[StrMaxPlayers].value<int>(); val.has_value()) {
Application::Settings.MaxPlayers = val.value();
} else {
throw std::runtime_error(std::string(StrMaxPlayers));
}
if (auto val = GeneralTable[StrMap].value<std::string>(); val.has_value()) {
Application::Settings.MapName = val.value();
} else {
throw std::runtime_error(std::string(StrMap));
}
if (auto val = GeneralTable[StrName].value<std::string>(); val.has_value()) {
Application::Settings.ServerName = val.value();
} else {
throw std::runtime_error(std::string(StrName));
}
if (auto val = GeneralTable[StrDescription].value<std::string>(); val.has_value()) {
Application::Settings.ServerDesc = val.value();
} else {
throw std::runtime_error(std::string(StrDescription));
}
if (auto val = GeneralTable[StrResourceFolder].value<std::string>(); val.has_value()) {
Application::Settings.Resource = val.value();
} else {
throw std::runtime_error(std::string(StrResourceFolder));
}
if (auto val = GeneralTable[StrAuthKey].value<std::string>(); 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() { void TConfig::ReadJson() {
auto Size = fs::file_size("Config.json"); auto Size = fs::file_size("Config.json");
if(Size < 3)return; if (Size < 3)
return;
std::ifstream ifs("Config.json"); std::ifstream ifs("Config.json");
if(ifs.is_open()) { if (ifs.is_open()) {
std::string cfg(Size, 0); std::string cfg(Size, 0);
ifs.read(&cfg[0], long(Size)); ifs.read(&cfg[0], long(Size));
ifs.close(); ifs.close();
if(auto pos = cfg.find('{'); pos != std::string::npos) { if (auto pos = cfg.find('{'); pos != std::string::npos) {
if(cfg.at(0) != '{') { if (cfg.at(0) != '{') {
cfg = cfg.substr(pos); cfg = cfg.substr(pos);
} }
}else{ } else {
error("Config file is not valid JSON!"); error("Config file is not valid JSON!");
std::this_thread::sleep_for(std::chrono::seconds(3)); std::this_thread::sleep_for(std::chrono::seconds(3));
Application::GracefullyShutdown(); Application::GracefullyShutdown();
@ -90,97 +212,97 @@ void TConfig::ReadJson() {
rapidjson::Document d; rapidjson::Document d;
d.Parse(cfg.c_str()); d.Parse(cfg.c_str());
if(!d.HasParseError()){ if (!d.HasParseError()) {
auto& Val = d["Debug"]; auto& Val = d["Debug"];
if(!Val.IsNull() && Val.IsBool()) { if (!Val.IsNull() && Val.IsBool()) {
Application::Settings.DebugModeEnabled = Val.GetBool(); Application::Settings.DebugModeEnabled = Val.GetBool();
}else{ } else {
info("'Debug' Missing in config! Setting to 'false' by default"); info("'Debug' Missing in config! Setting to 'false' by default");
} }
Val = d["Private"]; Val = d["Private"];
if(!Val.IsNull() && Val.IsBool()) { if (!Val.IsNull() && Val.IsBool()) {
Application::Settings.Private = Val.GetBool(); Application::Settings.Private = Val.GetBool();
}else{ } else {
info("'Private' Missing in config! Setting to 'true' by default"); info("'Private' Missing in config! Setting to 'true' by default");
} }
Val = d["Port"]; Val = d["Port"];
if(!Val.IsNull() && Val.IsNumber()) { if (!Val.IsNull() && Val.IsNumber()) {
Application::Settings.Port = Val.GetInt(); Application::Settings.Port = Val.GetInt();
}else{ } else {
info("'Port' Missing in config! Setting to '30814' by default"); info("'Port' Missing in config! Setting to '30814' by default");
} }
Val = d["MaxCars"]; Val = d["MaxCars"];
if(!Val.IsNull() && Val.IsNumber()) { if (!Val.IsNull() && Val.IsNumber()) {
Application::Settings.MaxCars = Val.GetInt(); Application::Settings.MaxCars = Val.GetInt();
}else{ } else {
info("'MaxCars' Missing in config! Setting to '1' by default"); info("'MaxCars' Missing in config! Setting to '1' by default");
} }
Val = d["MaxPlayers"]; Val = d["MaxPlayers"];
if(!Val.IsNull() && Val.IsNumber()) { if (!Val.IsNull() && Val.IsNumber()) {
Application::Settings.MaxPlayers = Val.GetInt(); Application::Settings.MaxPlayers = Val.GetInt();
}else{ } else {
info("'MaxPlayers' Missing in config! Setting to '10' by default"); info("'MaxPlayers' Missing in config! Setting to '10' by default");
} }
Val = d["Map"]; Val = d["Map"];
if(!Val.IsNull() && Val.IsString()) { if (!Val.IsNull() && Val.IsString()) {
Application::Settings.MapName = Val.GetString(); Application::Settings.MapName = Val.GetString();
}else{ } else {
info("'Map' Missing in config! Setting to '/levels/gridmap/info.json' by default"); info("'Map' Missing in config! Setting to '/levels/gridmap/info.json' by default");
} }
Val = d["Name"]; Val = d["Name"];
if(!Val.IsNull() && Val.IsString()) { if (!Val.IsNull() && Val.IsString()) {
Application::Settings.ServerName = Val.GetString(); Application::Settings.ServerName = Val.GetString();
}else{ } else {
info("'Name' Missing in config! Setting to 'BeamMP Server' by default"); info("'Name' Missing in config! Setting to 'BeamMP Server' by default");
} }
Val = d["Desc"]; Val = d["Desc"];
if(!Val.IsNull() && Val.IsString()) { if (!Val.IsNull() && Val.IsString()) {
Application::Settings.ServerDesc = Val.GetString(); Application::Settings.ServerDesc = Val.GetString();
}else{ } else {
info("'Desc' Missing in config! Setting to 'BeamMP Default Description' by default"); info("'Desc' Missing in config! Setting to 'BeamMP Default Description' by default");
} }
Val = d["Resource"]; Val = d["Resource"];
if(!Val.IsNull() && Val.IsString()) { if (!Val.IsNull() && Val.IsString()) {
Application::Settings.Resource = Val.GetString(); Application::Settings.Resource = Val.GetString();
}else{ } else {
info("'Resource' Missing in config! Setting to 'Resources' by default"); info("'Resource' Missing in config! Setting to 'Resources' by default");
} }
Val = d["AuthKey"]; Val = d["AuthKey"];
if(!Val.IsNull() && Val.IsString()) { if (!Val.IsNull() && Val.IsString()) {
Application::Settings.Key = Val.GetString(); Application::Settings.Key = Val.GetString();
if(Application::Settings.Key.empty()) { if (Application::Settings.Key.empty()) {
error("AuthKey cannot be empty check Config.json!"); error("AuthKey cannot be empty check Config.json!");
std::this_thread::sleep_for(std::chrono::seconds(3)); std::this_thread::sleep_for(std::chrono::seconds(3));
Application::GracefullyShutdown(); Application::GracefullyShutdown();
} }
}else{ } else {
error("'AuthKey' Missing in config!"); error("'AuthKey' Missing in config!");
std::this_thread::sleep_for(std::chrono::seconds(3)); std::this_thread::sleep_for(std::chrono::seconds(3));
Application::GracefullyShutdown(); Application::GracefullyShutdown();
} }
}else{ } else {
error("Failed to parse JSON config! code " + std::to_string(d.GetParseError())); error("Failed to parse JSON config! code " + std::to_string(d.GetParseError()));
} }
}else{ } else {
error("Failed to read Config.json"); error("Failed to read Config.json");
std::this_thread::sleep_for(std::chrono::seconds(3)); std::this_thread::sleep_for(std::chrono::seconds(3));
Application::GracefullyShutdown(); Application::GracefullyShutdown();
@ -191,16 +313,16 @@ void TConfig::ReadJson() {
void TConfig::ManageJson() { void TConfig::ManageJson() {
rapidjson::Document d; rapidjson::Document d;
d.Parse("{}"); d.Parse("{}");
d.AddMember("Debug",Application::Settings.DebugModeEnabled,d.GetAllocator()); d.AddMember("Debug", Application::Settings.DebugModeEnabled, d.GetAllocator());
d.AddMember("Private",Application::Settings.Private,d.GetAllocator()); d.AddMember("Private", Application::Settings.Private, d.GetAllocator());
d.AddMember("Port",Application::Settings.Port,d.GetAllocator()); d.AddMember("Port", Application::Settings.Port, d.GetAllocator());
d.AddMember("MaxCars",Application::Settings.MaxCars,d.GetAllocator()); d.AddMember("MaxCars", Application::Settings.MaxCars, d.GetAllocator());
d.AddMember("MaxPlayers",Application::Settings.MaxPlayers,d.GetAllocator()); d.AddMember("MaxPlayers", Application::Settings.MaxPlayers, d.GetAllocator());
d.AddMember("Map", rapidjson::StringRef(Application::Settings.MapName.c_str()),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("Name", rapidjson::StringRef(Application::Settings.ServerName.c_str()), d.GetAllocator());
d.AddMember("Desc", rapidjson::StringRef(Application::Settings.ServerDesc.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("Resource", rapidjson::StringRef(Application::Settings.Resource.c_str()), d.GetAllocator());
d.AddMember("AuthKey", rapidjson::StringRef(Application::Settings.Key.c_str()),d.GetAllocator()); d.AddMember("AuthKey", rapidjson::StringRef(Application::Settings.Key.c_str()), d.GetAllocator());
rapidjson::StringBuffer buffer; rapidjson::StringBuffer buffer;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer); rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
@ -208,11 +330,11 @@ void TConfig::ManageJson() {
std::ofstream cfg; std::ofstream cfg;
cfg.open("Config.json"); cfg.open("Config.json");
if(cfg.is_open()){ if (cfg.is_open()) {
cfg << "BeamMP Server Configuration File\n" cfg << "BeamMP Server Configuration File\n"
<< buffer.GetString(); << buffer.GetString();
cfg.close(); cfg.close();
}else{ } else {
error("Failed to create Config.json!"); error("Failed to create Config.json!");
} }
} }
@ -283,17 +405,17 @@ void TConfig::SetValues(const std::string& Line, int Index) {
break; break;
} }
} }
*/
void TConfig::PrintDebug(){ void TConfig::PrintDebug() {
debug("Debug : " + std::string(Application::Settings.DebugModeEnabled ? "true" : "false")); debug(std::string(StrDebug) + ": " + std::string(Application::Settings.DebugModeEnabled ? "true" : "false"));
debug("Private : " + std::string(Application::Settings.Private ? "true" : "false")); debug(std::string(StrPrivate) + ": " + std::string(Application::Settings.Private ? "true" : "false"));
debug("Port : " + std::to_string(Application::Settings.Port)); debug(std::string(StrPort) + ": " + std::to_string(Application::Settings.Port));
debug("Max Cars : " + std::to_string(Application::Settings.MaxCars)); debug(std::string(StrMaxCars) + ": " + std::to_string(Application::Settings.MaxCars));
debug("MaxPlayers : " + std::to_string(Application::Settings.MaxPlayers)); debug(std::string(StrMaxPlayers) + ": " + std::to_string(Application::Settings.MaxPlayers));
debug("MapName : \"" + Application::Settings.MapName + "\""); debug(std::string(StrMap) + ": \"" + Application::Settings.MapName + "\"");
debug("ServerName : \"" + Application::Settings.ServerName + "\""); debug(std::string(StrName) + ": \"" + Application::Settings.ServerName + "\"");
debug("ServerDesc : \"" + Application::Settings.ServerDesc + "\""); debug(std::string(StrDescription) + ": \"" + Application::Settings.ServerDesc + "\"");
debug("File : \"" + Application::Settings.Resource + "\""); debug(std::string(StrResourceFolder) + ": \"" + Application::Settings.Resource + "\"");
debug("Key length : " + std::to_string(Application::Settings.Key.length()) + ""); // special!
debug("Key Length: " + std::to_string(Application::Settings.Key.length()) + "");
} }

View File

@ -50,7 +50,14 @@ int main(int argc, char** argv) {
Application::RegisterShutdownHandler([&Shutdown] { Shutdown = true; }); Application::RegisterShutdownHandler([&Shutdown] { Shutdown = true; });
TServer Server(argc, argv); 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"); RegisterThread("Main");
TResourceManager ResourceManager; TResourceManager ResourceManager;
TPPSMonitor PPSMonitor(Server); TPPSMonitor PPSMonitor(Server);