Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor config, add settings command #295

Merged
merged 44 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
55f5437
Add new type `Settings` & refactor TSettings behavior
jimkoen Feb 18, 2024
89034a6
Add logic for new Settings type
jimkoen Feb 22, 2024
86f0052
Add rudimentary access control to type `Settings`
jimkoen Feb 26, 2024
a357ff8
Add missing options to defaults
jimkoen Feb 26, 2024
3989961
Add `get` and `set` to console command `settings`
jimkoen Feb 26, 2024
8693b8a
Add `list` argument to command `settings`
jimkoen Feb 26, 2024
f5e2f74
Fix `ComposedKey` metadata not printing to console
jimkoen May 7, 2024
d6b78b9
Fix argument parsing at wrong index
jimkoen May 7, 2024
37109ae
Make 'General::Debug' setting r/w from runtime
jimkoen May 7, 2024
c6dac55
Wrap Settings into synchronization primitives
jimkoen May 7, 2024
158875a
Remove debug code for new Settings implementation
jimkoen May 7, 2024
639c46a
Remove additional debug calls
jimkoen May 7, 2024
ff81242
Fix `settings set` not accepting boolean literals
jimkoen May 7, 2024
13e641b
Remove interfering legacy code (http,password,etc)
jimkoen May 15, 2024
8c15b87
Refactor all references to settings to use new `Settings` type
jimkoen May 15, 2024
bcd4b5a
Fix Debug asserts on FreeBSD
jimkoen May 15, 2024
a5e3fc8
Add Sync.h
jimkoen May 15, 2024
3609fd7
Potential fix for compiler issues on Ubuntu
jimkoen May 15, 2024
f567645
Add issue with implicit argument type conversions for `Settings.set()`
jimkoen May 15, 2024
3a8f4de
fix errors in Settings::set
lionkor May 17, 2024
31ce0cc
add unit test + concept constraints for set(string)
lionkor May 17, 2024
4c9fbc2
Change concepts in Settings.set + add test for remaining set overloads
jimkoen May 17, 2024
67db935
Fix concepts related error (for compat with gcc9)
jimkoen May 21, 2024
84f5f95
Refactor: feedback from code review
jimkoen May 21, 2024
d30dccc
Separate settings tests
jimkoen May 21, 2024
04e8d00
fix invalid default initialization in SettingsMap
lionkor May 24, 2024
73ecef1
Move map declarations in Settings.h into .cpp
jimkoen Jun 26, 2024
509225f
Move tests from .h to .cpp
jimkoen Jun 26, 2024
0d3256c
Remove todo in accordance with review
jimkoen Jun 26, 2024
6c0a8d1
remove superflous code
jimkoen Jun 26, 2024
814927d
change log output for consistency
jimkoen Jun 26, 2024
25739cb
Merge branch 'minor' into 158-bug-running-settings-help-returns-nothing
jimkoen Jun 26, 2024
2451e08
update remaining sections of code after merge
jimkoen Jun 26, 2024
26ef398
fix AuthKey being writable from console
jimkoen Jun 26, 2024
7919f81
remove dead code for deprecated config format
jimkoen Jun 26, 2024
8c32d76
fix confusing error when setting wrong key
jimkoen Jun 26, 2024
0748267
remove superflous comments
jimkoen Jun 26, 2024
e7c7f45
fix chrono wrapper
jimkoen Jun 26, 2024
6731b3e
fix typo
jimkoen Jun 26, 2024
461fb5d
improve error messages
jimkoen Jun 26, 2024
5919fc6
improve acl error message consistency
jimkoen Jun 26, 2024
3c80bcb
remove line ChronoWrapper.cpp:13 as discussed in review
jimkoen Jun 26, 2024
29f4d0d
run clang-format
jimkoen Jun 26, 2024
08374b1
deprecate Ubuntu 20.04
jimkoen Jun 26, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ set(PRJ_HEADERS
include/TServer.h
include/VehicleData.h
include/Env.h
include/Settings.h
include/Profiling.h
include/ChronoWrapper.h
)
Expand All @@ -74,6 +75,7 @@ set(PRJ_SOURCES
src/TServer.cpp
src/VehicleData.cpp
src/Env.cpp
src/Settings.cpp
src/Profiling.cpp
src/ChronoWrapper.cpp
)
Expand Down
2 changes: 1 addition & 1 deletion include/ChronoWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
#include <string>

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);
}
37 changes: 5 additions & 32 deletions include/Common.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include <filesystem>
namespace fs = std::filesystem;

#include "Settings.h"
#include "TConsole.h"

struct Version {
Expand All @@ -59,34 +60,6 @@ using SparseArray = std::unordered_map<size_t, T>;
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 AllowGuests { true };
bool SendErrors { true };
bool SendErrorsMessageEnabled { true };
int HTTPServerPort { 8080 };
std::string HTTPServerIP { "127.0.0.1" };
bool HTTPServerUseSSL { false };
bool HideUpdateMessages { false };
std::string UpdateReminderTime { "30s" };
[[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); }
};

using TShutdownHandler = std::function<void()>;

Expand All @@ -104,7 +77,7 @@ class Application final {
static std::string PPS() { return mPPS; }
static void SetPPS(const std::string& NewPPS) { mPPS = NewPPS; }

static TSettings Settings;
static inline struct Settings Settings { };

static std::vector<std::string> GetBackendUrlsInOrder() {
return {
Expand Down Expand Up @@ -226,21 +199,21 @@ 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)
// trace() is a debug-build debug()
#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)
Expand Down
7 changes: 4 additions & 3 deletions include/CustomAssert.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <thread>

#include "Common.h"
#include "Compat.h"

static const char* const ANSI_RESET = "\u001b[0m";

Expand All @@ -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 <iostream>
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) {
Expand All @@ -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
4 changes: 2 additions & 2 deletions include/Json.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.

#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"
2 changes: 1 addition & 1 deletion include/RWMutex.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
* and write locks and read locks are mutually exclusive.
*/

#include <shared_mutex>
#include <mutex>
#include <shared_mutex>

// Use ReadLock(m) and WriteLock(m) to lock it.
using RWMutex = std::shared_mutex;
Expand Down
144 changes: 144 additions & 0 deletions include/Settings.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// 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 <https://www.gnu.org/licenses/>.

#pragma once
#include "Sync.h"
#include <concepts>
#include <cstdint>
#include <doctest/doctest.h>
#include <fmt/core.h>
#include <fmt/format.h>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <variant>

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<ComposedKey> : formatter<std::string> {
auto format(ComposedKey key, format_context& ctx) const;
};

inline auto fmt::formatter<ComposedKey>::format(ComposedKey key, fmt::format_context& ctx) const {
std::string key_metadata = fmt::format("{}::{}", key.Category, key.Key);
return formatter<std::string>::format(key_metadata, ctx);
}

namespace std {
template <>
class hash<ComposedKey> {
public:
std::uint64_t operator()(const ComposedKey& key) const {
std::hash<std::string> hash_fn;
return hash_fn(key.Category + key.Key);
}
};
}

struct Settings {
using SettingsTypeVariant = std::variant<std::string, bool, int>;

Settings();

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,
Misc_UpdateReminderTime,

// [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,
General_AllowGuests
};

Sync<std::unordered_map<Key, SettingsTypeVariant>> SettingsMap;
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<std::unordered_map<ComposedKey, SettingsAccessControl>> InputAccessMapping;
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 <typename Integer, std::enable_if_t<std::is_same_v<Integer, int>, 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<int>(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 <typename Boolean, std::enable_if_t<std::is_same_v<bool, Boolean>, 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<bool>(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<ComposedKey, SettingsAccessControl> 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);
};
9 changes: 9 additions & 0 deletions include/Sync.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once

#include <boost/thread/synchronized_value.hpp>
#include <mutex>

/// This header provides convenience aliases for synchronization primitives.

template <typename T>
using Sync = boost::synchronized_value<T, std::recursive_mutex>;
5 changes: 2 additions & 3 deletions include/TConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#pragma once

#include "Common.h"
#include "Settings.h"

#include <atomic>
#include <filesystem>
Expand All @@ -40,9 +41,7 @@ class TConfig {
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();
std::string TagsAsPrettyArray() const;
Expand Down
2 changes: 1 addition & 1 deletion include/TPPSMonitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class TNetwork;
class TPPSMonitor : public IThreaded {
public:
explicit TPPSMonitor(TServer& Server);
virtual ~TPPSMonitor() {}
virtual ~TPPSMonitor() { }

void operator()() override;

Expand Down
File renamed without changes.
22 changes: 9 additions & 13 deletions src/ChronoWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,21 @@
#include "Common.h"
#include <regex>

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));
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
}
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);
Expand Down
Loading
Loading