mirror of
https://github.com/BeamMP/BeamMP-Server.git
synced 2025-07-01 15:26:59 +00:00
commit
72022e3349
4
.github/workflows/linux.yml
vendored
4
.github/workflows/linux.yml
vendored
@ -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:
|
||||
@ -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:
|
||||
|
@ -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
|
||||
)
|
||||
@ -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
|
||||
)
|
||||
|
@ -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,6 +37,7 @@
|
||||
#include <filesystem>
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
#include "Settings.h"
|
||||
#include "TConsole.h"
|
||||
|
||||
struct Version {
|
||||
@ -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()>;
|
||||
|
||||
@ -104,7 +77,7 @@ public:
|
||||
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 {
|
||||
@ -226,13 +199,13 @@ 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)
|
||||
@ -240,7 +213,7 @@ void RegisterThread(const std::string& str);
|
||||
#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)
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include <thread>
|
||||
|
||||
#include "Common.h"
|
||||
#include "Compat.h"
|
||||
|
||||
static const char* const ANSI_RESET = "\u001b[0m";
|
||||
|
||||
@ -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) {
|
||||
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
144
include/Settings.h
Normal file
144
include/Settings.h
Normal file
@ -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
include/Sync.h
Normal file
9
include/Sync.h
Normal file
@ -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>;
|
@ -19,6 +19,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "Settings.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <filesystem>
|
||||
@ -40,9 +41,7 @@ public:
|
||||
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;
|
||||
|
@ -27,7 +27,7 @@ class TNetwork;
|
||||
class TPPSMonitor : public IThreaded {
|
||||
public:
|
||||
explicit TPPSMonitor(TServer& Server);
|
||||
virtual ~TPPSMonitor() {}
|
||||
virtual ~TPPSMonitor() { }
|
||||
|
||||
void operator()() override;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -22,20 +22,18 @@
|
||||
#include "TConsole.h"
|
||||
#include <array>
|
||||
#include <charconv>
|
||||
#include <chrono>
|
||||
#include <fmt/core.h>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "Compat.h"
|
||||
#include "CustomAssert.h"
|
||||
#include "Http.h"
|
||||
|
||||
Application::TSettings Application::Settings = {};
|
||||
|
||||
void Application::RegisterShutdownHandler(const TShutdownHandler& Handler) {
|
||||
std::unique_lock Lock(mShutdownHandlersMutex);
|
||||
if (Handler) {
|
||||
@ -257,7 +255,7 @@ static std::mutex ThreadNameMapMutex {};
|
||||
|
||||
std::string ThreadName(bool DebugModeOverride) {
|
||||
auto Lock = std::unique_lock(ThreadNameMapMutex);
|
||||
if (DebugModeOverride || Application::Settings.DebugModeEnabled) {
|
||||
if (DebugModeOverride || Application::Settings.getAsBool(Settings::Key::General_Debug)) {
|
||||
auto id = std::this_thread::get_id();
|
||||
if (threadNameMap.find(id) != threadNameMap.end()) {
|
||||
// found
|
||||
@ -269,21 +267,21 @@ std::string ThreadName(bool DebugModeOverride) {
|
||||
|
||||
TEST_CASE("ThreadName") {
|
||||
RegisterThread("MyThread");
|
||||
auto OrigDebug = Application::Settings.DebugModeEnabled;
|
||||
auto OrigDebug = Application::Settings.getAsBool(Settings::Key::General_Debug);
|
||||
|
||||
// ThreadName adds a space at the end, legacy but we need it still
|
||||
SUBCASE("Debug mode enabled") {
|
||||
Application::Settings.DebugModeEnabled = true;
|
||||
Application::Settings.set(Settings::Key::General_Debug, true);
|
||||
CHECK(ThreadName(true) == "MyThread ");
|
||||
CHECK(ThreadName(false) == "MyThread ");
|
||||
}
|
||||
SUBCASE("Debug mode disabled") {
|
||||
Application::Settings.DebugModeEnabled = false;
|
||||
Application::Settings.set(Settings::Key::General_Debug, false);
|
||||
CHECK(ThreadName(true) == "MyThread ");
|
||||
CHECK(ThreadName(false) == "");
|
||||
}
|
||||
// cleanup
|
||||
Application::Settings.DebugModeEnabled = OrigDebug;
|
||||
Application::Settings.set(Settings::Key::General_Debug, OrigDebug);
|
||||
}
|
||||
|
||||
void RegisterThread(const std::string& str) {
|
||||
@ -297,7 +295,7 @@ void RegisterThread(const std::string& str) {
|
||||
#elif defined(BEAMMP_FREEBSD)
|
||||
ThreadId = std::to_string(getpid());
|
||||
#endif
|
||||
if (Application::Settings.DebugModeEnabled) {
|
||||
if (Application::Settings.getAsBool(Settings::Key::General_Debug)) {
|
||||
std::ofstream ThreadFile(".Threads.log", std::ios::app);
|
||||
ThreadFile << ("Thread \"" + str + "\" is TID " + ThreadId) << std::endl;
|
||||
}
|
||||
@ -330,7 +328,7 @@ TEST_CASE("Version::AsString") {
|
||||
}
|
||||
|
||||
void LogChatMessage(const std::string& name, int id, const std::string& msg) {
|
||||
if (Application::Settings.LogChat) {
|
||||
if (Application::Settings.getAsBool(Settings::Key::General_LogChat)) {
|
||||
std::stringstream ss;
|
||||
ss << ThreadName();
|
||||
ss << "[CHAT] ";
|
||||
@ -385,4 +383,3 @@ void SplitString(const std::string& str, const char delim, std::vector<std::stri
|
||||
out.push_back(str.substr(start, end - start));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,7 @@ TEST_CASE("init and reset termios") {
|
||||
CHECK_EQ(current.c_lflag, original.c_lflag);
|
||||
#ifndef BEAMMP_FREEBSD // The 'c_line' attribute seems to only exist on Linux, so we need to omit it on other platforms
|
||||
CHECK_EQ(current.c_line, original.c_line);
|
||||
#endif
|
||||
#endif
|
||||
CHECK_EQ(current.c_oflag, original.c_oflag);
|
||||
CHECK_EQ(current.c_ospeed, original.c_ospeed);
|
||||
}
|
||||
|
15
src/Http.cpp
15
src/Http.cpp
@ -30,8 +30,8 @@
|
||||
|
||||
using json = nlohmann::json;
|
||||
struct Connection {
|
||||
std::string host{};
|
||||
int port{};
|
||||
std::string host {};
|
||||
int port {};
|
||||
Connection() = default;
|
||||
Connection(std::string host, int port)
|
||||
: host(host)
|
||||
@ -54,13 +54,13 @@ static thread_local std::array<std::shared_ptr<httplib::SSLClient>, CONNECTION_A
|
||||
write_index++;
|
||||
write_index %= CONNECTION_AMOUNT;
|
||||
clients[i] = std::make_shared<httplib::SSLClient>(connectionInfo.host, connectionInfo.port);
|
||||
connections[i] = {connectionInfo.host, connectionInfo.port};
|
||||
connections[i] = { connectionInfo.host, connectionInfo.port };
|
||||
beammp_tracef("New client connected, with ip {} and port {}", connectionInfo.host, connectionInfo.port);
|
||||
return clients[i];
|
||||
}
|
||||
|
||||
std::string Http::GET(const std::string& host, int port, const std::string& target, unsigned int* status) {
|
||||
std::shared_ptr<httplib::SSLClient> client = getClient({host, port});
|
||||
std::shared_ptr<httplib::SSLClient> client = getClient({ host, port });
|
||||
client->enable_server_certificate_verification(false);
|
||||
client->set_address_family(AF_INET);
|
||||
auto res = client->Get(target.c_str());
|
||||
@ -75,7 +75,7 @@ std::string Http::GET(const std::string& host, int port, const std::string& targ
|
||||
}
|
||||
|
||||
std::string Http::POST(const std::string& host, int port, const std::string& target, const std::string& body, const std::string& ContentType, unsigned int* status, const httplib::Headers& headers) {
|
||||
std::shared_ptr<httplib::SSLClient> client = getClient({host, port});
|
||||
std::shared_ptr<httplib::SSLClient> client = getClient({ host, port });
|
||||
client->set_read_timeout(std::chrono::seconds(10));
|
||||
beammp_assert(client->is_valid());
|
||||
client->enable_server_certificate_verification(false);
|
||||
@ -199,7 +199,6 @@ Http::Server::THttpServerInstance::THttpServerInstance() {
|
||||
}
|
||||
|
||||
void Http::Server::THttpServerInstance::operator()() try {
|
||||
beammp_info("HTTP(S) Server started on port " + std::to_string(Application::Settings.HTTPServerPort));
|
||||
std::unique_ptr<httplib::Server> HttpLibServerInstance;
|
||||
HttpLibServerInstance = std::make_unique<httplib::Server>();
|
||||
// todo: make this IP agnostic so people can set their own IP
|
||||
@ -241,10 +240,6 @@ void Http::Server::THttpServerInstance::operator()() try {
|
||||
beammp_debug("Http Server: " + Req.method + " " + Req.target + " -> " + std::to_string(Res.status));
|
||||
});
|
||||
Application::SetSubsystemStatus("HTTPServer", Application::Status::Good);
|
||||
auto ret = HttpLibServerInstance->listen(Application::Settings.HTTPServerIP.c_str(), Application::Settings.HTTPServerPort);
|
||||
if (!ret) {
|
||||
beammp_error("Failed to start http server (failed to listen). Please ensure the http server is configured properly in the ServerConfig.toml, or turn it off if you don't need it.");
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
beammp_error("Failed to start http server. Please ensure the http server is configured properly in the ServerConfig.toml, or turn it off if you don't need it. Error: " + std::string(e.what()));
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "Client.h"
|
||||
#include "Common.h"
|
||||
#include "CustomAssert.h"
|
||||
#include "Settings.h"
|
||||
#include "TLuaEngine.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
@ -230,56 +231,56 @@ void LuaAPI::MP::Set(int ConfigID, sol::object NewValue) {
|
||||
switch (ConfigID) {
|
||||
case 0: // debug
|
||||
if (NewValue.is<bool>()) {
|
||||
Application::Settings.DebugModeEnabled = NewValue.as<bool>();
|
||||
beammp_info(std::string("Set `Debug` to ") + (Application::Settings.DebugModeEnabled ? "true" : "false"));
|
||||
Application::Settings.set(Settings::Key::General_Debug, NewValue.as<bool>());
|
||||
beammp_info(std::string("Set `Debug` to ") + (Application::Settings.getAsBool(Settings::Key::General_Debug) ? "true" : "false"));
|
||||
} else {
|
||||
beammp_lua_error("set invalid argument [2] expected boolean");
|
||||
}
|
||||
break;
|
||||
case 1: // private
|
||||
if (NewValue.is<bool>()) {
|
||||
Application::Settings.Private = NewValue.as<bool>();
|
||||
beammp_info(std::string("Set `Private` to ") + (Application::Settings.Private ? "true" : "false"));
|
||||
Application::Settings.set(Settings::Key::General_Private, NewValue.as<bool>());
|
||||
beammp_info(std::string("Set `Private` to ") + (Application::Settings.getAsBool(Settings::Key::General_Private) ? "true" : "false"));
|
||||
} else {
|
||||
beammp_lua_error("set invalid argument [2] expected boolean");
|
||||
}
|
||||
break;
|
||||
case 2: // max cars
|
||||
if (NewValue.is<int>()) {
|
||||
Application::Settings.MaxCars = NewValue.as<int>();
|
||||
beammp_info(std::string("Set `MaxCars` to ") + std::to_string(Application::Settings.MaxCars));
|
||||
Application::Settings.set(Settings::Key::General_MaxCars, NewValue.as<int>());
|
||||
beammp_info(std::string("Set `MaxCars` to ") + std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxCars)));
|
||||
} else {
|
||||
beammp_lua_error("set invalid argument [2] expected integer");
|
||||
}
|
||||
break;
|
||||
case 3: // max players
|
||||
if (NewValue.is<int>()) {
|
||||
Application::Settings.MaxPlayers = NewValue.as<int>();
|
||||
beammp_info(std::string("Set `MaxPlayers` to ") + std::to_string(Application::Settings.MaxPlayers));
|
||||
Application::Settings.set(Settings::Key::General_MaxPlayers, NewValue.as<int>());
|
||||
beammp_info(std::string("Set `MaxPlayers` to ") + std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxPlayers)));
|
||||
} else {
|
||||
beammp_lua_error("set invalid argument [2] expected integer");
|
||||
}
|
||||
break;
|
||||
case 4: // Map
|
||||
if (NewValue.is<std::string>()) {
|
||||
Application::Settings.MapName = NewValue.as<std::string>();
|
||||
beammp_info(std::string("Set `Map` to ") + Application::Settings.MapName);
|
||||
Application::Settings.set(Settings::Key::General_Map, NewValue.as<std::string>());
|
||||
beammp_info(std::string("Set `Map` to ") + Application::Settings.getAsString(Settings::Key::General_Map));
|
||||
} else {
|
||||
beammp_lua_error("set invalid argument [2] expected string");
|
||||
}
|
||||
break;
|
||||
case 5: // Name
|
||||
if (NewValue.is<std::string>()) {
|
||||
Application::Settings.ServerName = NewValue.as<std::string>();
|
||||
beammp_info(std::string("Set `Name` to ") + Application::Settings.ServerName);
|
||||
Application::Settings.set(Settings::Key::General_Name, NewValue.as<std::string>());
|
||||
beammp_info(std::string("Set `Name` to ") + Application::Settings.getAsString(Settings::Key::General_Name));
|
||||
} else {
|
||||
beammp_lua_error("set invalid argument [2] expected string");
|
||||
}
|
||||
break;
|
||||
case 6: // Desc
|
||||
if (NewValue.is<std::string>()) {
|
||||
Application::Settings.ServerDesc = NewValue.as<std::string>();
|
||||
beammp_info(std::string("Set `Description` to ") + Application::Settings.ServerDesc);
|
||||
Application::Settings.set(Settings::Key::General_Description, NewValue.as<std::string>());
|
||||
beammp_info(std::string("Set `Description` to ") + Application::Settings.getAsString(Settings::Key::General_Description));
|
||||
} else {
|
||||
beammp_lua_error("set invalid argument [2] expected string");
|
||||
}
|
||||
|
@ -57,4 +57,3 @@ size_t prof::UnitExecutionTime::measurement_count() const {
|
||||
std::unique_lock lock(m_mtx);
|
||||
return m_total_calls;
|
||||
}
|
||||
|
||||
|
191
src/Settings.cpp
Normal file
191
src/Settings.cpp
Normal file
@ -0,0 +1,191 @@
|
||||
// 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/>.
|
||||
|
||||
#include "Settings.h"
|
||||
|
||||
Settings::Settings() {
|
||||
SettingsMap = std::unordered_map<Key, SettingsTypeVariant> {
|
||||
// All entries which contain std::strings must be explicitly constructed, otherwise they become 'bool'
|
||||
{ General_Description, std::string("BeamMP Default Description") },
|
||||
{ General_Tags, std::string("Freeroam") },
|
||||
{ General_MaxPlayers, 8 },
|
||||
{ General_Name, std::string("BeamMP Server") },
|
||||
{ General_Map, std::string("/levels/gridmap_v2/info.json") },
|
||||
{ General_AuthKey, std::string("") },
|
||||
{ General_Private, true },
|
||||
{ General_Port, 30814 },
|
||||
{ General_MaxCars, 1 },
|
||||
{ General_LogChat, true },
|
||||
{ General_ResourceFolder, std::string("Resources") },
|
||||
{ General_Debug, false },
|
||||
{ General_AllowGuests, true },
|
||||
{ Misc_SendErrorsShowMessage, true },
|
||||
{ Misc_SendErrors, true },
|
||||
{ Misc_ImScaredOfUpdates, true },
|
||||
{ Misc_UpdateReminderTime, "30s" }
|
||||
};
|
||||
|
||||
InputAccessMapping = std::unordered_map<ComposedKey, SettingsAccessControl> {
|
||||
{ { "General", "Description" }, { General_Description, READ_WRITE } },
|
||||
{ { "General", "Tags" }, { General_Tags, READ_WRITE } },
|
||||
{ { "General", "MaxPlayers" }, { General_MaxPlayers, READ_WRITE } },
|
||||
{ { "General", "Name" }, { General_Name, READ_WRITE } },
|
||||
{ { "General", "Map" }, { General_Map, READ_WRITE } },
|
||||
{ { "General", "AuthKey" }, { General_AuthKey, NO_ACCESS } },
|
||||
{ { "General", "Private" }, { General_Private, READ_ONLY } },
|
||||
{ { "General", "Port" }, { General_Port, READ_ONLY } },
|
||||
{ { "General", "MaxCars" }, { General_MaxCars, READ_WRITE } },
|
||||
{ { "General", "LogChat" }, { General_LogChat, READ_ONLY } },
|
||||
{ { "General", "ResourceFolder" }, { General_ResourceFolder, READ_ONLY } },
|
||||
{ { "General", "Debug" }, { General_Debug, READ_WRITE } },
|
||||
{ { "General", "AllowGuests" }, { General_AllowGuests, READ_WRITE } },
|
||||
{ { "Misc", "SendErrorsShowMessage" }, { Misc_SendErrorsShowMessage, READ_WRITE } },
|
||||
{ { "Misc", "SendErrors" }, { Misc_SendErrors, READ_WRITE } },
|
||||
{ { "Misc", "ImScaredOfUpdates" }, { Misc_ImScaredOfUpdates, READ_WRITE } },
|
||||
{ { "Misc", "UpdateReminderTime" }, { Misc_UpdateReminderTime, READ_WRITE } }
|
||||
};
|
||||
}
|
||||
|
||||
std::string Settings::getAsString(Key key) {
|
||||
auto map = SettingsMap.synchronize();
|
||||
if (!map->contains(key)) {
|
||||
throw std::logic_error { "Undefined key accessed in Settings::getAsString" };
|
||||
}
|
||||
return std::get<std::string>(map->at(key));
|
||||
}
|
||||
int Settings::getAsInt(Key key) {
|
||||
auto map = SettingsMap.synchronize();
|
||||
if (!map->contains(key)) {
|
||||
throw std::logic_error { "Undefined key accessed in Settings::getAsInt" };
|
||||
}
|
||||
return std::get<int>(map->at(key));
|
||||
}
|
||||
|
||||
bool Settings::getAsBool(Key key) {
|
||||
auto map = SettingsMap.synchronize();
|
||||
if (!map->contains(key)) {
|
||||
throw std::logic_error { "Undefined key accessed in Settings::getAsBool" };
|
||||
}
|
||||
return std::get<bool>(map->at(key));
|
||||
}
|
||||
|
||||
Settings::SettingsTypeVariant Settings::get(Key key) {
|
||||
auto map = SettingsMap.synchronize();
|
||||
if (!map->contains(key)) {
|
||||
throw std::logic_error { "Undefined setting key accessed in Settings::get" };
|
||||
}
|
||||
return map->at(key);
|
||||
}
|
||||
|
||||
void Settings::set(Key key, const std::string& value) {
|
||||
auto map = SettingsMap.synchronize();
|
||||
if (!map->contains(key)) {
|
||||
throw std::logic_error { "Undefined setting key accessed in Settings::set(std::string)" };
|
||||
}
|
||||
if (!std::holds_alternative<std::string>(map->at(key))) {
|
||||
throw std::logic_error { fmt::format("Wrong value type in Settings::set(std::string): index {}", map->at(key).index()) };
|
||||
}
|
||||
map->at(key) = value;
|
||||
}
|
||||
|
||||
const std::unordered_map<ComposedKey, Settings::SettingsAccessControl> Settings::getAccessControlMap() const {
|
||||
return *InputAccessMapping;
|
||||
}
|
||||
|
||||
Settings::SettingsAccessControl Settings::getConsoleInputAccessMapping(const ComposedKey& keyName) {
|
||||
auto acl_map = InputAccessMapping.synchronize();
|
||||
if (!acl_map->contains(keyName)) {
|
||||
throw std::logic_error { "Unknown key name accessed in Settings::getConsoleInputAccessMapping" };
|
||||
} else if (acl_map->at(keyName).second == SettingsAccessMask::NO_ACCESS) {
|
||||
throw std::logic_error { "Setting '" + keyName.Category + "::" + keyName.Key + "' is not accessible from within the runtime!" };
|
||||
}
|
||||
return acl_map->at(keyName);
|
||||
}
|
||||
|
||||
void Settings::setConsoleInputAccessMapping(const ComposedKey& keyName, const std::string& value) {
|
||||
auto [map, acl_map] = boost::synchronize(SettingsMap, InputAccessMapping);
|
||||
if (!acl_map->contains(keyName)) {
|
||||
throw std::logic_error { "Unknown key name accessed in Settings::setConsoleInputAccessMapping" };
|
||||
} else if (acl_map->at(keyName).second == SettingsAccessMask::NO_ACCESS) {
|
||||
throw std::logic_error { "Setting '" + keyName.Category + "::" + keyName.Key + "' is not accessible from within the runtime!" };
|
||||
} else if (acl_map->at(keyName).second == SettingsAccessMask::READ_ONLY) {
|
||||
throw std::logic_error { "Setting '" + keyName.Category + "::" + keyName.Key + "' is not writeable from within the runtime!" };
|
||||
}
|
||||
|
||||
Key key = acl_map->at(keyName).first;
|
||||
|
||||
if (!std::holds_alternative<std::string>(map->at(key))) {
|
||||
throw std::logic_error { "Wrong value type in Settings::setConsoleInputAccessMapping: expected std::string" };
|
||||
}
|
||||
|
||||
map->at(key) = value;
|
||||
}
|
||||
|
||||
void Settings::setConsoleInputAccessMapping(const ComposedKey& keyName, int value) {
|
||||
auto [map, acl_map] = boost::synchronize(SettingsMap, InputAccessMapping);
|
||||
if (!acl_map->contains(keyName)) {
|
||||
throw std::logic_error { "Unknown key name accessed in Settings::setConsoleInputAccessMapping" };
|
||||
} else if (acl_map->at(keyName).second == SettingsAccessMask::NO_ACCESS) {
|
||||
throw std::logic_error { "Key '" + keyName.Category + "::" + keyName.Key + "' is not accessible from within the runtime!" };
|
||||
} else if (acl_map->at(keyName).second == SettingsAccessMask::READ_ONLY) {
|
||||
throw std::logic_error { "Key '" + keyName.Category + "::" + keyName.Key + "' is not writeable from within the runtime!" };
|
||||
}
|
||||
|
||||
Key key = acl_map->at(keyName).first;
|
||||
|
||||
if (!std::holds_alternative<int>(map->at(key))) {
|
||||
throw std::logic_error { "Wrong value type in Settings::setConsoleInputAccessMapping: expected int" };
|
||||
}
|
||||
|
||||
map->at(key) = value;
|
||||
}
|
||||
|
||||
void Settings::setConsoleInputAccessMapping(const ComposedKey& keyName, bool value) {
|
||||
auto [map, acl_map] = boost::synchronize(SettingsMap, InputAccessMapping);
|
||||
if (!acl_map->contains(keyName)) {
|
||||
throw std::logic_error { "Unknown key name accessed in Settings::setConsoleInputAccessMapping" };
|
||||
} else if (acl_map->at(keyName).second == SettingsAccessMask::NO_ACCESS) {
|
||||
throw std::logic_error { "Key '" + keyName.Category + "::" + keyName.Key + "' is not accessible from within the runtime!" };
|
||||
} else if (acl_map->at(keyName).second == SettingsAccessMask::READ_ONLY) {
|
||||
throw std::logic_error { "Key '" + keyName.Category + "::" + keyName.Key + "' is not writeable from within the runtime!" };
|
||||
}
|
||||
|
||||
Key key = acl_map->at(keyName).first;
|
||||
|
||||
if (!std::holds_alternative<bool>(map->at(key))) {
|
||||
throw std::logic_error { "Wrong value type in Settings::setConsoleInputAccessMapping: expected bool" };
|
||||
}
|
||||
|
||||
map->at(key) = value;
|
||||
}
|
||||
|
||||
TEST_CASE("settings get/set") {
|
||||
Settings settings;
|
||||
settings.set(Settings::General_Name, "hello, world");
|
||||
CHECK_EQ(settings.getAsString(Settings::General_Name), "hello, world");
|
||||
settings.set(Settings::General_Name, std::string("hello, world"));
|
||||
CHECK_EQ(settings.getAsString(Settings::General_Name), "hello, world");
|
||||
settings.set(Settings::General_MaxPlayers, 12);
|
||||
CHECK_EQ(settings.getAsInt(Settings::General_MaxPlayers), 12);
|
||||
}
|
||||
|
||||
TEST_CASE("settings check for exception on wrong input type") {
|
||||
Settings settings;
|
||||
CHECK_THROWS(settings.set(Settings::General_Debug, "hello, world"));
|
||||
CHECK_NOTHROW(settings.set(Settings::General_Debug, false));
|
||||
}
|
258
src/TConfig.cpp
258
src/TConfig.cpp
@ -19,12 +19,15 @@
|
||||
#include "Common.h"
|
||||
|
||||
#include "Env.h"
|
||||
#include "Settings.h"
|
||||
#include "TConfig.h"
|
||||
#include <cstdlib>
|
||||
#include <exception>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <istream>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
|
||||
// General
|
||||
static constexpr std::string_view StrDebug = "Debug";
|
||||
@ -123,33 +126,33 @@ void SetComment(CommentsT& Comments, const std::string& Comment) {
|
||||
void TConfig::FlushToFile() {
|
||||
// auto data = toml::parse<toml::preserve_comments>(mConfigFileName);
|
||||
auto data = toml::value {};
|
||||
data["General"][StrAuthKey.data()] = Application::Settings.Key;
|
||||
data["General"][StrAuthKey.data()] = Application::Settings.getAsString(Settings::Key::General_AuthKey);
|
||||
SetComment(data["General"][StrAuthKey.data()].comments(), " AuthKey has to be filled out in order to run the server");
|
||||
data["General"][StrLogChat.data()] = Application::Settings.LogChat;
|
||||
data["General"][StrLogChat.data()] = Application::Settings.getAsBool(Settings::Key::General_LogChat);
|
||||
SetComment(data["General"][StrLogChat.data()].comments(), " Whether to log chat messages in the console / log");
|
||||
data["General"][StrAllowGuests.data()] = Application::Settings.AllowGuests;
|
||||
data["General"][StrDebug.data()] = Application::Settings.getAsBool(Settings::Key::General_Debug);
|
||||
data["General"][StrPrivate.data()] = Application::Settings.getAsBool(Settings::Key::General_Private);
|
||||
data["General"][StrAllowGuests.data()] = Application::Settings.getAsBool(Settings::Key::General_AllowGuests);
|
||||
SetComment(data["General"][StrAllowGuests.data()].comments(), " Whether to allow guests");
|
||||
data["General"][StrDebug.data()] = Application::Settings.DebugModeEnabled;
|
||||
data["General"][StrPrivate.data()] = Application::Settings.Private;
|
||||
data["General"][StrPort.data()] = Application::Settings.Port;
|
||||
data["General"][StrName.data()] = Application::Settings.ServerName;
|
||||
data["General"][StrPort.data()] = Application::Settings.getAsInt(Settings::Key::General_Port);
|
||||
data["General"][StrName.data()] = Application::Settings.getAsString(Settings::Key::General_Name);
|
||||
SetComment(data["General"][StrTags.data()].comments(), " Add custom identifying tags to your server to make it easier to find. Format should be TagA,TagB,TagC. Note the comma seperation.");
|
||||
data["General"][StrTags.data()] = Application::Settings.ServerTags;
|
||||
data["General"][StrMaxCars.data()] = Application::Settings.MaxCars;
|
||||
data["General"][StrMaxPlayers.data()] = Application::Settings.MaxPlayers;
|
||||
data["General"][StrMap.data()] = Application::Settings.MapName;
|
||||
data["General"][StrDescription.data()] = Application::Settings.ServerDesc;
|
||||
data["General"][StrResourceFolder.data()] = Application::Settings.Resource;
|
||||
data["General"][StrTags.data()] = Application::Settings.getAsString(Settings::Key::General_Tags);
|
||||
data["General"][StrMaxCars.data()] = Application::Settings.getAsInt(Settings::Key::General_MaxCars);
|
||||
data["General"][StrMaxPlayers.data()] = Application::Settings.getAsInt(Settings::Key::General_MaxCars);
|
||||
data["General"][StrMap.data()] = Application::Settings.getAsString(Settings::Key::General_Map);
|
||||
data["General"][StrDescription.data()] = Application::Settings.getAsString(Settings::Key::General_Description);
|
||||
data["General"][StrResourceFolder.data()] = Application::Settings.getAsString(Settings::Key::General_ResourceFolder);
|
||||
// data["General"][StrPassword.data()] = Application::Settings.Password;
|
||||
// SetComment(data["General"][StrPassword.data()].comments(), " Sets a password on this server, which restricts people from joining. To join, a player must enter this exact password. Leave empty ("") to disable the password.");
|
||||
// Misc
|
||||
data["Misc"][StrHideUpdateMessages.data()] = Application::Settings.HideUpdateMessages;
|
||||
data["Misc"][StrHideUpdateMessages.data()] = Application::Settings.getAsBool(Settings::Key::Misc_ImScaredOfUpdates);
|
||||
SetComment(data["Misc"][StrHideUpdateMessages.data()].comments(), " Hides the periodic update message which notifies you of a new server version. You should really keep this on and always update as soon as possible. For more information visit https://wiki.beammp.com/en/home/server-maintenance#updating-the-server. An update message will always appear at startup regardless.");
|
||||
data["Misc"][StrUpdateReminderTime.data()] = Application::Settings.UpdateReminderTime;
|
||||
data["Misc"][StrSendErrors.data()] = Application::Settings.getAsBool(Settings::Key::Misc_SendErrors);
|
||||
data["Misc"][StrUpdateReminderTime.data()] = Application::Settings.getAsString(Settings::Key::Misc_UpdateReminderTime);
|
||||
SetComment(data["Misc"][StrUpdateReminderTime.data()].comments(), " Specifies the time between update reminders. You can use any of \"s, min, h, d\" at the end to specify the units seconds, minutes, hours or days. So 30d or 0.5min will print the update message every 30 days or half a minute.");
|
||||
data["Misc"][StrSendErrors.data()] = Application::Settings.SendErrors;
|
||||
SetComment(data["Misc"][StrSendErrors.data()].comments(), " If SendErrors is `true`, the server will send helpful info about crashes and other issues back to the BeamMP developers. This info may include your config, who is on your server at the time of the error, and similar general information. This kind of data is vital in helping us diagnose and fix issues faster. This has no impact on server performance. You can opt-out of this system by setting this to `false`");
|
||||
data["Misc"][StrSendErrorsMessageEnabled.data()] = Application::Settings.SendErrorsMessageEnabled;
|
||||
data["Misc"][StrSendErrorsMessageEnabled.data()] = Application::Settings.getAsBool(Settings::Key::Misc_SendErrorsShowMessage);
|
||||
SetComment(data["Misc"][StrSendErrorsMessageEnabled.data()].comments(), " You can turn on/off the SendErrors message you get on startup here");
|
||||
std::stringstream Ss;
|
||||
Ss << "# This is the BeamMP-Server config file.\n"
|
||||
@ -174,62 +177,63 @@ void TConfig::CreateConfigFile() {
|
||||
if (mDisableConfig) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (fs::exists("Server.cfg")) {
|
||||
// parse it (this is weird and bad and should be removed in some future version)
|
||||
ParseOldFormat();
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
beammp_error("an error occurred and was ignored during config transfer: " + std::string(e.what()));
|
||||
}
|
||||
|
||||
FlushToFile();
|
||||
}
|
||||
|
||||
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 (const char* envp = std::getenv(Env.data()); envp != nullptr && std::strcmp(envp, "") != 0) {
|
||||
OutValue = std::string(envp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (mDisableConfig) {
|
||||
return;
|
||||
}
|
||||
if (Table[Category.c_str()][Key.data()].is_string()) {
|
||||
OutValue = Table[Category.c_str()][Key.data()].as_string();
|
||||
}
|
||||
}
|
||||
// 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, bool& OutValue) {
|
||||
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) {
|
||||
auto Str = std::string(envp);
|
||||
OutValue = Str == "1" || Str == "true";
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (mDisableConfig) {
|
||||
return;
|
||||
}
|
||||
if (Table[Category.c_str()][Key.data()].is_boolean()) {
|
||||
OutValue = Table[Category.c_str()][Key.data()].as_boolean();
|
||||
}
|
||||
}
|
||||
if (const char* envp = std::getenv(Env.data());
|
||||
envp != nullptr && std::strcmp(envp, "") != 0) {
|
||||
|
||||
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 (const char* envp = std::getenv(Env.data()); envp != nullptr && std::strcmp(envp, "") != 0) {
|
||||
OutValue = int(std::strtol(envp, nullptr, 10));
|
||||
std::visit(
|
||||
overloaded {
|
||||
[&envp, &key](std::string) {
|
||||
Application::Settings.set(key, std::string(envp));
|
||||
},
|
||||
[&envp, &key](int) {
|
||||
Application::Settings.set(key, int(std::strtol(envp, nullptr, 10)));
|
||||
},
|
||||
[&envp, &key](bool) {
|
||||
auto Str = std::string(envp);
|
||||
Application::Settings.set(key, bool(Str == "1" || Str == "true"));
|
||||
} },
|
||||
|
||||
Application::Settings.get(key));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (mDisableConfig) {
|
||||
return;
|
||||
}
|
||||
if (Table[Category.c_str()][Key.data()].is_integer()) {
|
||||
OutValue = int(Table[Category.c_str()][Key.data()].as_integer());
|
||||
}
|
||||
|
||||
std::visit([&Table, &Category, &Key, &key](auto&& arg) {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr (std::is_same_v<T, std::string>) {
|
||||
if (Table[Category.c_str()][Key.data()].is_string())
|
||||
Application::Settings.set(key, Table[Category.c_str()][Key.data()].as_string());
|
||||
else
|
||||
beammp_warnf("Value '{}.{}' has unexpected type, expected type 'string'", Category, Key);
|
||||
} else if constexpr (std::is_same_v<T, int>) {
|
||||
if (Table[Category.c_str()][Key.data()].is_integer())
|
||||
Application::Settings.set(key, int(Table[Category.c_str()][Key.data()].as_integer()));
|
||||
else
|
||||
beammp_warnf("Value '{}.{}' has unexpected type, expected type 'integer'", Category, Key);
|
||||
} else if constexpr (std::is_same_v<T, bool>) {
|
||||
if (Table[Category.c_str()][Key.data()].is_boolean())
|
||||
Application::Settings.set(key, Table[Category.c_str()][Key.data()].as_boolean());
|
||||
else
|
||||
beammp_warnf("Value '{}.{}' has unexpected type, expected type 'boolean'", Category, Key);
|
||||
} else {
|
||||
throw std::logic_error { "Invalid type for config value during read attempt" };
|
||||
}
|
||||
},
|
||||
Application::Settings.get(key));
|
||||
}
|
||||
|
||||
void TConfig::ParseFromFile(std::string_view name) {
|
||||
@ -238,30 +242,29 @@ void TConfig::ParseFromFile(std::string_view name) {
|
||||
if (!mDisableConfig) {
|
||||
data = toml::parse<toml::preserve_comments>(name.data());
|
||||
}
|
||||
|
||||
// GENERAL
|
||||
TryReadValue(data, "General", StrDebug, EnvStrDebug, Application::Settings.DebugModeEnabled);
|
||||
TryReadValue(data, "General", StrPrivate, EnvStrPrivate, Application::Settings.Private);
|
||||
if (Env::Get(Env::Key::PROVIDER_PORT_ENV).has_value()) {
|
||||
TryReadValue(data, "General", StrPort, Env::Get(Env::Key::PROVIDER_PORT_ENV).value(), Application::Settings.Port);
|
||||
} else {
|
||||
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", StrAllowGuests, EnvStrAllowGuests, Application::Settings.AllowGuests);
|
||||
TryReadValue(data, "General", StrPassword, "", Application::Settings.Password);
|
||||
|
||||
// Read into new Settings Singleton
|
||||
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);
|
||||
TryReadValue(data, "General", StrAllowGuests, EnvStrAllowGuests, Settings::Key::General_AllowGuests);
|
||||
// Misc
|
||||
TryReadValue(data, "Misc", StrSendErrors, "", Application::Settings.SendErrors);
|
||||
TryReadValue(data, "Misc", StrHideUpdateMessages, "", Application::Settings.HideUpdateMessages);
|
||||
TryReadValue(data, "Misc", StrUpdateReminderTime, "", Application::Settings.UpdateReminderTime);
|
||||
TryReadValue(data, "Misc", StrSendErrorsMessageEnabled, "", Application::Settings.SendErrorsMessageEnabled);
|
||||
TryReadValue(data, "Misc", StrSendErrors, "", Settings::Key::Misc_SendErrors);
|
||||
TryReadValue(data, "Misc", StrHideUpdateMessages, "", Settings::Key::Misc_ImScaredOfUpdates);
|
||||
TryReadValue(data, "Misc", StrSendErrorsMessageEnabled, "", Settings::Key::Misc_SendErrorsShowMessage);
|
||||
TryReadValue(data, "Misc", StrUpdateReminderTime, "", Settings::Key::Misc_UpdateReminderTime);
|
||||
|
||||
} catch (const std::exception& err) {
|
||||
beammp_error("Error parsing config file value: " + std::string(err.what()));
|
||||
mFailed = true;
|
||||
@ -274,7 +277,7 @@ void TConfig::ParseFromFile(std::string_view name) {
|
||||
FlushToFile();
|
||||
}
|
||||
// all good so far, let's check if there's a key
|
||||
if (Application::Settings.Key.empty()) {
|
||||
if (Application::Settings.getAsString(Settings::Key::General_AuthKey).empty()) {
|
||||
if (mDisableConfig) {
|
||||
beammp_error("No AuthKey specified in the environment.");
|
||||
} else {
|
||||
@ -285,7 +288,7 @@ void TConfig::ParseFromFile(std::string_view name) {
|
||||
return;
|
||||
}
|
||||
Application::SetSubsystemStatus("Config", Application::Status::Good);
|
||||
if (Application::Settings.Key.size() != 36) {
|
||||
if (Application::Settings.getAsString(Settings::Key::General_AuthKey).size() != 36) {
|
||||
beammp_warn("AuthKey specified is the wrong length and likely isn't valid.");
|
||||
}
|
||||
}
|
||||
@ -294,78 +297,25 @@ void TConfig::PrintDebug() {
|
||||
if (mDisableConfig) {
|
||||
beammp_debug("Provider turned off the generation and parsing of the ServerConfig.toml");
|
||||
}
|
||||
beammp_debug(std::string(StrDebug) + ": " + std::string(Application::Settings.DebugModeEnabled ? "true" : "false"));
|
||||
beammp_debug(std::string(StrPrivate) + ": " + std::string(Application::Settings.Private ? "true" : "false"));
|
||||
beammp_debug(std::string(StrPort) + ": " + std::to_string(Application::Settings.Port));
|
||||
beammp_debug(std::string(StrMaxCars) + ": " + std::to_string(Application::Settings.MaxCars));
|
||||
beammp_debug(std::string(StrMaxPlayers) + ": " + std::to_string(Application::Settings.MaxPlayers));
|
||||
beammp_debug(std::string(StrMap) + ": \"" + Application::Settings.MapName + "\"");
|
||||
beammp_debug(std::string(StrName) + ": \"" + Application::Settings.ServerName + "\"");
|
||||
beammp_debug(std::string(StrDescription) + ": \"" + Application::Settings.ServerDesc + "\"");
|
||||
beammp_debug(std::string(StrDebug) + ": " + std::string(Application::Settings.getAsBool(Settings::Key::General_Debug) ? "true" : "false"));
|
||||
beammp_debug(std::string(StrPrivate) + ": " + std::string(Application::Settings.getAsBool(Settings::Key::General_Private) ? "true" : "false"));
|
||||
beammp_debug(std::string(StrPort) + ": " + std::to_string(Application::Settings.getAsInt(Settings::Key::General_Port)));
|
||||
beammp_debug(std::string(StrMaxCars) + ": " + std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxCars)));
|
||||
beammp_debug(std::string(StrMaxPlayers) + ": " + std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxPlayers)));
|
||||
beammp_debug(std::string(StrMap) + ": \"" + Application::Settings.getAsString(Settings::Key::General_Map) + "\"");
|
||||
beammp_debug(std::string(StrName) + ": \"" + Application::Settings.getAsString(Settings::Key::General_Name) + "\"");
|
||||
beammp_debug(std::string(StrDescription) + ": \"" + Application::Settings.getAsString(Settings::Key::General_Description) + "\"");
|
||||
beammp_debug(std::string(StrTags) + ": " + TagsAsPrettyArray());
|
||||
beammp_debug(std::string(StrLogChat) + ": \"" + (Application::Settings.LogChat ? "true" : "false") + "\"");
|
||||
beammp_debug(std::string(StrAllowGuests) + ": \"" + (Application::Settings.AllowGuests ? "true" : "false") + "\"");
|
||||
beammp_debug(std::string(StrResourceFolder) + ": \"" + Application::Settings.Resource + "\"");
|
||||
beammp_debug(std::string(StrLogChat) + ": \"" + (Application::Settings.getAsBool(Settings::Key::General_LogChat) ? "true" : "false") + "\"");
|
||||
beammp_debug(std::string(StrResourceFolder) + ": \"" + Application::Settings.getAsString(Settings::Key::General_ResourceFolder) + "\"");
|
||||
beammp_debug(std::string(StrAllowGuests) + ": \"" + (Application::Settings.getAsBool(Settings::Key::General_AllowGuests) ? "true" : "false") + "\"");
|
||||
// special!
|
||||
beammp_debug("Key Length: " + std::to_string(Application::Settings.Key.length()) + "");
|
||||
beammp_debug("Password Protected: " + std::string(Application::Settings.Password.empty() ? "false" : "true"));
|
||||
beammp_debug("Key Length: " + std::to_string(Application::Settings.getAsString(Settings::Key::General_AuthKey).length()) + "");
|
||||
}
|
||||
|
||||
void TConfig::ParseOldFormat() {
|
||||
std::ifstream File("Server.cfg");
|
||||
// read all, strip comments
|
||||
std::string Content;
|
||||
for (;;) {
|
||||
std::string Line;
|
||||
std::getline(File, Line);
|
||||
if (!Line.empty() && Line.at(0) != '#') {
|
||||
Line = Line.substr(0, Line.find_first_of('#'));
|
||||
Content += Line + "\n";
|
||||
}
|
||||
if (!File.good()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::stringstream Str(Content);
|
||||
std::string Key, Ignore, Value;
|
||||
for (;;) {
|
||||
Str >> Key >> std::ws >> Ignore >> std::ws;
|
||||
std::getline(Str, Value);
|
||||
if (Str.eof()) {
|
||||
break;
|
||||
}
|
||||
std::stringstream ValueStream(Value);
|
||||
ValueStream >> std::ws; // strip leading whitespace if any
|
||||
Value = ValueStream.str();
|
||||
if (Key == "Debug") {
|
||||
Application::Settings.DebugModeEnabled = Value.find("true") != std::string::npos;
|
||||
} else if (Key == "Private") {
|
||||
Application::Settings.Private = Value.find("true") != std::string::npos;
|
||||
} else if (Key == "Port") {
|
||||
ValueStream >> Application::Settings.Port;
|
||||
} else if (Key == "Cars") {
|
||||
ValueStream >> Application::Settings.MaxCars;
|
||||
} else if (Key == "MaxPlayers") {
|
||||
ValueStream >> Application::Settings.MaxPlayers;
|
||||
} else if (Key == "Map") {
|
||||
Application::Settings.MapName = Value.substr(1, Value.size() - 3);
|
||||
} else if (Key == "Name") {
|
||||
Application::Settings.ServerName = Value.substr(1, Value.size() - 3);
|
||||
} else if (Key == "Desc") {
|
||||
Application::Settings.ServerDesc = Value.substr(1, Value.size() - 3);
|
||||
} else if (Key == "use") {
|
||||
Application::Settings.Resource = Value.substr(1, Value.size() - 3);
|
||||
} else if (Key == "AuthKey") {
|
||||
Application::Settings.Key = Value.substr(1, Value.size() - 3);
|
||||
} else {
|
||||
beammp_warn("unknown key in old auth file (ignored): " + Key);
|
||||
}
|
||||
Str >> std::ws;
|
||||
}
|
||||
}
|
||||
std::string TConfig::TagsAsPrettyArray() const {
|
||||
std::vector<std::string> TagsArray = {};
|
||||
SplitString(Application::Settings.ServerTags, ',', TagsArray);
|
||||
SplitString(Application::Settings.getAsString(Settings::General_Tags), ',', TagsArray);
|
||||
std::string Pretty = {};
|
||||
for (size_t i = 0; i < TagsArray.size() - 1; ++i) {
|
||||
Pretty += '\"' + TagsArray[i] + "\", ";
|
||||
|
177
src/TConsole.cpp
177
src/TConsole.cpp
@ -30,6 +30,8 @@
|
||||
#include <mutex>
|
||||
#include <openssl/opensslv.h>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
|
||||
static inline bool StringStartsWith(const std::string& What, const std::string& StartsWith) {
|
||||
return What.size() >= StartsWith.size() && What.substr(0, StartsWith.size()) == StartsWith;
|
||||
@ -78,7 +80,7 @@ static std::string GetDate() {
|
||||
auto local_tm = std::localtime(&tt);
|
||||
char buf[30];
|
||||
std::string date;
|
||||
if (Application::Settings.DebugModeEnabled) {
|
||||
if (Application::Settings.getAsBool(Settings::Key::General_Debug)) {
|
||||
std::strftime(buf, sizeof(buf), "[%d/%m/%y %T.", local_tm);
|
||||
date += buf;
|
||||
auto seconds = std::chrono::time_point_cast<std::chrono::seconds>(now);
|
||||
@ -106,41 +108,6 @@ void TConsole::BackupOldLog() {
|
||||
} catch (const std::exception& e) {
|
||||
beammp_warn(e.what());
|
||||
}
|
||||
/*
|
||||
int err = 0;
|
||||
zip* z = zip_open("ServerLogs.zip", ZIP_CREATE, &err);
|
||||
if (!z) {
|
||||
std::cerr << GetPlatformAgnosticErrorString() << std::endl;
|
||||
return;
|
||||
}
|
||||
FILE* File = std::fopen(Path.string().c_str(), "r");
|
||||
if (!File) {
|
||||
std::cerr << GetPlatformAgnosticErrorString() << std::endl;
|
||||
return;
|
||||
}
|
||||
std::vector<uint8_t> Buffer;
|
||||
Buffer.resize(fs::file_size(Path));
|
||||
std::fread(Buffer.data(), 1, Buffer.size(), File);
|
||||
std::fclose(File);
|
||||
|
||||
auto s = zip_source_buffer(z, Buffer.data(), Buffer.size(), 0);
|
||||
|
||||
auto TimePoint = fs::last_write_time(Path);
|
||||
auto Secs = TimePoint.time_since_epoch().count();
|
||||
auto MyTimeT = std::time(&Secs);
|
||||
|
||||
std::string NewName = Path.stem().string();
|
||||
NewName += "_";
|
||||
std::string Time;
|
||||
Time.resize(32);
|
||||
size_t n = strftime(Time.data(), Time.size(), "%F_%H.%M.%S", localtime(&MyTimeT));
|
||||
Time.resize(n);
|
||||
NewName += Time;
|
||||
NewName += ".log";
|
||||
|
||||
zip_file_add(z, NewName.c_str(), s, 0);
|
||||
zip_close(z);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@ -386,8 +353,142 @@ std::tuple<std::string, std::vector<std::string>> TConsole::ParseCommand(const s
|
||||
return { Command, Args };
|
||||
}
|
||||
|
||||
template <class... Ts>
|
||||
struct overloaded : Ts... {
|
||||
using Ts::operator()...;
|
||||
};
|
||||
template <class... Ts>
|
||||
overloaded(Ts...) -> overloaded<Ts...>;
|
||||
|
||||
void TConsole::Command_Settings(const std::string&, const std::vector<std::string>& args) {
|
||||
if (!EnsureArgsCount(args, 1, 2)) {
|
||||
|
||||
static constexpr const char* sHelpString = R"(
|
||||
Settings:
|
||||
settings help displays this help
|
||||
settings list lists all settings
|
||||
settings get <category> <setting> prints current value of specified setting
|
||||
settings set <category> <setting> <value> sets specified setting to value
|
||||
)";
|
||||
|
||||
if (args.size() == 0) {
|
||||
beammp_errorf("No arguments specified for command 'settings'!");
|
||||
Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString));
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.front() == "help") {
|
||||
|
||||
Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString));
|
||||
return;
|
||||
} else if (args.front() == "get") {
|
||||
if (args.size() < 3) {
|
||||
beammp_errorf("'settings get' needs at least two arguments!");
|
||||
|
||||
Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Settings::SettingsAccessControl acl = Application::Settings.getConsoleInputAccessMapping(ComposedKey { args.at(1), args.at(2) });
|
||||
Settings::SettingsTypeVariant keyType = Application::Settings.get(acl.first);
|
||||
|
||||
std::visit(
|
||||
overloaded {
|
||||
[&args](std::string keyValue) {
|
||||
Application::Console().WriteRaw(fmt::format("'{}::{}' = {}", args.at(1), args.at(2), keyValue));
|
||||
},
|
||||
[&args](int keyValue) {
|
||||
Application::Console().WriteRaw(fmt::format("'{}::{}' = {}", args.at(1), args.at(2), keyValue));
|
||||
},
|
||||
[&args](bool keyValue) {
|
||||
Application::Console().WriteRaw(fmt::format("'{}::{}' = {}", args.at(1), args.at(2), keyValue));
|
||||
}
|
||||
|
||||
},
|
||||
keyType);
|
||||
|
||||
} catch (std::logic_error& e) {
|
||||
beammp_errorf("Error when getting key: {}", e.what());
|
||||
return;
|
||||
}
|
||||
} else if (args.front() == "set") {
|
||||
if (args.size() <= 3) {
|
||||
beammp_errorf("'settings set' needs at least three arguments!");
|
||||
|
||||
Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
Settings::SettingsAccessControl acl = Application::Settings.getConsoleInputAccessMapping(ComposedKey { args.at(1), args.at(2) });
|
||||
Settings::SettingsTypeVariant keyType = Application::Settings.get(acl.first);
|
||||
|
||||
std::visit(
|
||||
overloaded {
|
||||
[&args](std::string keyValue) {
|
||||
Application::Settings.setConsoleInputAccessMapping(ComposedKey { args.at(1), args.at(2) }, std::string(args.at(3)));
|
||||
Application::Console().WriteRaw(fmt::format("{}::{} := {}", args.at(1), args.at(2), std::string(args.at(3))));
|
||||
},
|
||||
[&args](int keyValue) {
|
||||
Application::Settings.setConsoleInputAccessMapping(ComposedKey { args.at(1), args.at(2) }, std::stoi(args.at(3)));
|
||||
Application::Console().WriteRaw(fmt::format("{}::{} := {}", args.at(1), args.at(2), std::stoi(args.at(3))));
|
||||
},
|
||||
[&args](bool keyValue) {
|
||||
if (args.at(3) == "true") {
|
||||
Application::Settings.setConsoleInputAccessMapping(ComposedKey { args.at(1), args.at(2) }, true);
|
||||
Application::Console().WriteRaw(fmt::format("{}::{} := {}", args.at(1), args.at(2), "true"));
|
||||
} else if (args.at(3) == "false") {
|
||||
Application::Settings.setConsoleInputAccessMapping(ComposedKey { args.at(1), args.at(2) }, false);
|
||||
Application::Console().WriteRaw(fmt::format("{}::{} := {}", args.at(1), args.at(2), "false"));
|
||||
} else {
|
||||
beammp_errorf("Error when setting key: {}::{} : Unknown literal, use either 'true', or 'false' to set boolean values.", args.at(1), args.at(2));
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
keyType);
|
||||
|
||||
} catch (std::logic_error& e) {
|
||||
beammp_errorf("Exception when setting settings key via console: {}", e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
} else if (args.front() == "list") {
|
||||
for (const auto& [composedKey, keyACL] : Application::Settings.getAccessControlMap()) {
|
||||
// even though we have the value, we want to ignore it in order to make use of access
|
||||
// control checks
|
||||
|
||||
if (keyACL.second != Settings::SettingsAccessMask::NO_ACCESS) {
|
||||
|
||||
try {
|
||||
|
||||
Settings::SettingsAccessControl acl = Application::Settings.getConsoleInputAccessMapping(composedKey);
|
||||
Settings::SettingsTypeVariant keyType = Application::Settings.get(acl.first);
|
||||
|
||||
std::visit(
|
||||
overloaded {
|
||||
[&composedKey](std::string keyValue) {
|
||||
Application::Console().WriteRaw(fmt::format("{} = {}", composedKey, keyValue));
|
||||
},
|
||||
[&composedKey](int keyValue) {
|
||||
Application::Console().WriteRaw(fmt::format("{} = {}", composedKey, keyValue));
|
||||
},
|
||||
[&composedKey](bool keyValue) {
|
||||
Application::Console().WriteRaw(fmt::format("{} = {}", composedKey, keyValue));
|
||||
}
|
||||
|
||||
},
|
||||
keyType);
|
||||
} catch (std::logic_error& e) {
|
||||
beammp_errorf("Error when getting key: {}", e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
beammp_errorf("Unknown argument for command 'settings': {}", args.front());
|
||||
|
||||
Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -396,7 +497,7 @@ void TConsole::Command_Say(const std::string& FullCmd) {
|
||||
if (FullCmd.size() > 3) {
|
||||
auto Message = FullCmd.substr(4);
|
||||
LuaAPI::MP::SendChatMessage(-1, Message);
|
||||
if (!Application::Settings.LogChat) {
|
||||
if (!Application::Settings.getAsBool(Settings::Key::General_LogChat)) {
|
||||
Application::Console().WriteRaw("Chat message sent!");
|
||||
}
|
||||
}
|
||||
|
@ -18,10 +18,10 @@
|
||||
|
||||
#include "THeartbeatThread.h"
|
||||
|
||||
#include "ChronoWrapper.h"
|
||||
#include "Client.h"
|
||||
#include "Http.h"
|
||||
#include "ChronoWrapper.h"
|
||||
//#include "SocketIO.h"
|
||||
// #include "SocketIO.h"
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/rapidjson.h>
|
||||
#include <sstream>
|
||||
@ -40,8 +40,8 @@ void THeartbeatThread::operator()() {
|
||||
static std::chrono::high_resolution_clock::time_point LastUpdateReminderTime = std::chrono::high_resolution_clock::now();
|
||||
bool isAuth = false;
|
||||
std::chrono::high_resolution_clock::duration UpdateReminderTimePassed;
|
||||
auto UpdateReminderTimeout = ChronoWrapper::TimeFromStringWithLiteral(Application::Settings.UpdateReminderTime);
|
||||
while (!Application::IsShuttingDown()) {
|
||||
auto UpdateReminderTimeout = ChronoWrapper::TimeFromStringWithLiteral(Application::Settings.getAsString(Settings::Key::Misc_UpdateReminderTime));
|
||||
Body = GenerateCall();
|
||||
// a hot-change occurs when a setting has changed, to update the backend of that change.
|
||||
auto Now = std::chrono::high_resolution_clock::now();
|
||||
@ -57,9 +57,6 @@ void THeartbeatThread::operator()() {
|
||||
|
||||
Last = Body;
|
||||
LastNormalUpdateTime = Now;
|
||||
if (!Application::Settings.CustomIP.empty()) {
|
||||
Body += "&ip=" + Application::Settings.CustomIP;
|
||||
}
|
||||
|
||||
auto Target = "/heartbeat";
|
||||
unsigned int ResponseCode = 0;
|
||||
@ -70,7 +67,7 @@ void THeartbeatThread::operator()() {
|
||||
T = Http::POST(Url, 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode, { { "api-v", "2" } });
|
||||
Doc.Parse(T.data(), T.size());
|
||||
if (Doc.HasParseError() || !Doc.IsObject()) {
|
||||
if (!Application::Settings.Private) {
|
||||
if (!Application::Settings.getAsBool(Settings::Key::General_Private)) {
|
||||
beammp_trace("Backend response failed to parse as valid json");
|
||||
beammp_trace("Response was: `" + T + "`");
|
||||
}
|
||||
@ -110,12 +107,12 @@ void THeartbeatThread::operator()() {
|
||||
beammp_error("Missing/invalid json members in backend response");
|
||||
}
|
||||
} else {
|
||||
if (!Application::Settings.Private) {
|
||||
if (!Application::Settings.getAsBool(Settings::Key::General_Private)) {
|
||||
beammp_warn("Backend failed to respond to a heartbeat. Your server may temporarily disappear from the server list. This is not an error, and will likely resolve itself soon. Direct connect will still work.");
|
||||
}
|
||||
}
|
||||
|
||||
if (Ok && !isAuth && !Application::Settings.Private) {
|
||||
if (Ok && !isAuth && !Application::Settings.getAsBool(Settings::Key::General_Private)) {
|
||||
if (Status == "2000") {
|
||||
beammp_info(("Authenticated! " + Message));
|
||||
isAuth = true;
|
||||
@ -129,11 +126,10 @@ void THeartbeatThread::operator()() {
|
||||
beammp_error("Backend REFUSED the auth key. Reason: " + Message);
|
||||
}
|
||||
}
|
||||
if (isAuth || Application::Settings.Private) {
|
||||
if (isAuth || Application::Settings.getAsBool(Settings::Key::General_Private)) {
|
||||
Application::SetSubsystemStatus("Heartbeat", Application::Status::Good);
|
||||
}
|
||||
// beammp_debugf("Update reminder time passed: {}, Update reminder time: {}", UpdateReminderTimePassed.count(), UpdateReminderTimeout.count());
|
||||
if (!Application::Settings.HideUpdateMessages && UpdateReminderTimePassed.count() > UpdateReminderTimeout.count()) {
|
||||
if (!Application::Settings.getAsBool(Settings::Key::Misc_ImScaredOfUpdates) && UpdateReminderTimePassed.count() > UpdateReminderTimeout.count()) {
|
||||
LastUpdateReminderTime = std::chrono::high_resolution_clock::now();
|
||||
Application::CheckForUpdates();
|
||||
}
|
||||
@ -143,23 +139,22 @@ void THeartbeatThread::operator()() {
|
||||
std::string THeartbeatThread::GenerateCall() {
|
||||
std::stringstream Ret;
|
||||
|
||||
Ret << "uuid=" << Application::Settings.Key
|
||||
Ret << "uuid=" << Application::Settings.getAsString(Settings::Key::General_AuthKey)
|
||||
<< "&players=" << mServer.ClientCount()
|
||||
<< "&maxplayers=" << Application::Settings.MaxPlayers
|
||||
<< "&port=" << Application::Settings.Port
|
||||
<< "&map=" << Application::Settings.MapName
|
||||
<< "&private=" << (Application::Settings.Private ? "true" : "false")
|
||||
<< "&maxplayers=" << Application::Settings.getAsInt(Settings::Key::General_MaxPlayers)
|
||||
<< "&port=" << Application::Settings.getAsInt(Settings::Key::General_Port)
|
||||
<< "&map=" << Application::Settings.getAsString(Settings::Key::General_Map)
|
||||
<< "&private=" << (Application::Settings.getAsBool(Settings::Key::General_Private) ? "true" : "false")
|
||||
<< "&version=" << Application::ServerVersionString()
|
||||
<< "&clientversion=" << std::to_string(Application::ClientMajorVersion()) + ".0" // FIXME: Wtf.
|
||||
<< "&name=" << Application::Settings.ServerName
|
||||
<< "&tags=" << Application::Settings.ServerTags
|
||||
<< "&guests=" << (Application::Settings.AllowGuests ? "true" : "false")
|
||||
<< "&name=" << Application::Settings.getAsString(Settings::Key::General_Name)
|
||||
<< "&tags=" << Application::Settings.getAsString(Settings::Key::General_Tags)
|
||||
<< "&guests=" << (Application::Settings.getAsBool(Settings::Key::General_AllowGuests) ? "true" : "false")
|
||||
<< "&modlist=" << mResourceManager.TrimmedList()
|
||||
<< "&modstotalsize=" << mResourceManager.MaxModSize()
|
||||
<< "&modstotal=" << mResourceManager.ModsLoaded()
|
||||
<< "&playerslist=" << GetPlayers()
|
||||
<< "&desc=" << Application::Settings.ServerDesc
|
||||
<< "&pass=" << (Application::Settings.Password.empty() ? "false" : "true");
|
||||
<< "&desc=" << Application::Settings.getAsString(Settings::Key::General_Description);
|
||||
return Ret.str();
|
||||
}
|
||||
THeartbeatThread::THeartbeatThread(TResourceManager& ResourceManager, TServer& Server)
|
||||
|
@ -37,11 +37,11 @@
|
||||
TLuaEngine* LuaAPI::MP::Engine;
|
||||
|
||||
TLuaEngine::TLuaEngine()
|
||||
: mResourceServerPath(fs::path(Application::Settings.Resource) / "Server") {
|
||||
: mResourceServerPath(fs::path(Application::Settings.getAsString(Settings::Key::General_ResourceFolder)) / "Server") {
|
||||
Application::SetSubsystemStatus("LuaEngine", Application::Status::Starting);
|
||||
LuaAPI::MP::Engine = this;
|
||||
if (!fs::exists(Application::Settings.Resource)) {
|
||||
fs::create_directory(Application::Settings.Resource);
|
||||
if (!fs::exists(Application::Settings.getAsString(Settings::Key::General_ResourceFolder))) {
|
||||
fs::create_directory(Application::Settings.getAsString(Settings::Key::General_ResourceFolder));
|
||||
}
|
||||
if (!fs::exists(mResourceServerPath)) {
|
||||
fs::create_directory(mResourceServerPath);
|
||||
@ -57,7 +57,7 @@ TLuaEngine::TLuaEngine()
|
||||
}
|
||||
|
||||
TEST_CASE("TLuaEngine ctor & dtor") {
|
||||
Application::Settings.Resource = "beammp_server_test_resources";
|
||||
Application::Settings.set(Settings::Key::General_ResourceFolder, "beammp_server_test_resources");
|
||||
TLuaEngine engine;
|
||||
Application::GracefullyShutdown();
|
||||
}
|
||||
@ -896,7 +896,7 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, TLuaStateI
|
||||
ToPrint += LuaAPI::LuaToString(static_cast<const sol::object>(arg));
|
||||
ToPrint += "\t";
|
||||
}
|
||||
if (Application::Settings.DebugModeEnabled) {
|
||||
if (Application::Settings.getAsBool(Settings::Key::General_Debug)) {
|
||||
beammp_lua_log("DEBUG", mStateId, ToPrint);
|
||||
}
|
||||
});
|
||||
@ -1135,7 +1135,7 @@ void TLuaEngine::StateThreadData::operator()() {
|
||||
case TLuaType::Bool:
|
||||
LuaArgs.push_back(sol::make_object(StateView, std::get<bool>(Arg)));
|
||||
break;
|
||||
case TLuaType::StringStringMap: {
|
||||
case TLuaType::StringStringMap: {
|
||||
auto Map = std::get<std::unordered_map<std::string, std::string>>(Arg);
|
||||
auto Table = StateView.create_table();
|
||||
for (const auto& [k, v] : Map) {
|
||||
|
@ -80,7 +80,7 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R
|
||||
|
||||
void TNetwork::UDPServerMain() {
|
||||
RegisterThread("UDPServer");
|
||||
ip::udp::endpoint UdpListenEndpoint(ip::address::from_string("0.0.0.0"), Application::Settings.Port);
|
||||
ip::udp::endpoint UdpListenEndpoint(ip::address::from_string("0.0.0.0"), Application::Settings.getAsInt(Settings::Key::General_Port));
|
||||
boost::system::error_code ec;
|
||||
mUDPSock.open(UdpListenEndpoint.protocol(), ec);
|
||||
if (ec) {
|
||||
@ -95,8 +95,8 @@ void TNetwork::UDPServerMain() {
|
||||
Application::GracefullyShutdown();
|
||||
}
|
||||
Application::SetSubsystemStatus("UDPNetwork", Application::Status::Good);
|
||||
beammp_info(("Vehicle data network online on port ") + std::to_string(Application::Settings.Port) + (" with a Max of ")
|
||||
+ std::to_string(Application::Settings.MaxPlayers) + (" Clients"));
|
||||
beammp_info(("Vehicle data network online on port ") + std::to_string(Application::Settings.getAsInt(Settings::Key::General_Port)) + (" with a Max of ")
|
||||
+ std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxPlayers)) + (" Clients"));
|
||||
while (!Application::IsShuttingDown()) {
|
||||
try {
|
||||
ip::udp::endpoint client {};
|
||||
@ -133,7 +133,7 @@ void TNetwork::UDPServerMain() {
|
||||
void TNetwork::TCPServerMain() {
|
||||
RegisterThread("TCPServer");
|
||||
|
||||
ip::tcp::endpoint ListenEp(ip::address::from_string("0.0.0.0"), Application::Settings.Port);
|
||||
ip::tcp::endpoint ListenEp(ip::address::from_string("0.0.0.0"), Application::Settings.getAsInt(Settings::Key::General_Port));
|
||||
ip::tcp::socket Listener(mServer.IoCtx());
|
||||
boost::system::error_code ec;
|
||||
Listener.open(ListenEp.protocol(), ec);
|
||||
@ -290,9 +290,9 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
|
||||
}
|
||||
|
||||
std::string Key(reinterpret_cast<const char*>(Data.data()), Data.size());
|
||||
std::string AuthKey = Application::Settings.Key;
|
||||
std::string AuthKey = Application::Settings.getAsString(Settings::Key::General_AuthKey);
|
||||
std::string ClientIp = Client->GetIdentifiers().at("ip");
|
||||
|
||||
|
||||
nlohmann::json AuthReq {};
|
||||
std::string AuthResStr {};
|
||||
try {
|
||||
@ -339,22 +339,6 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!Application::Settings.Password.empty()) { // ask password
|
||||
if (!TCPSend(*Client, StringToVector("S"))) {
|
||||
// TODO: handle
|
||||
}
|
||||
beammp_info("Waiting for password");
|
||||
Data = TCPRcv(*Client);
|
||||
std::string Pass = std::string(reinterpret_cast<const char*>(Data.data()), Data.size());
|
||||
if (Pass != HashPassword(Application::Settings.Password)) {
|
||||
beammp_debug(Client->GetName() + " attempted to connect with a wrong password");
|
||||
ClientKick(*Client, "Wrong password!");
|
||||
return {};
|
||||
} else {
|
||||
beammp_debug(Client->GetName() + " used the correct password");
|
||||
}
|
||||
}
|
||||
|
||||
beammp_debug("Name -> " + Client->GetName() + ", Guest -> " + std::to_string(Client->IsGuest()) + ", Roles -> " + Client->GetRoles());
|
||||
mServer.ForEachClient([&](const std::weak_ptr<TClient>& ClientPtr) -> bool {
|
||||
std::shared_ptr<TClient> Cl;
|
||||
@ -389,7 +373,7 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!NotAllowedWithReason && !Application::Settings.AllowGuests && Client->IsGuest()) { //!NotAllowedWithReason because this message has the lowest priority
|
||||
if (!NotAllowedWithReason && !Application::Settings.getAsBool(Settings::Key::General_AllowGuests) && Client->IsGuest()) { //! NotAllowedWithReason because this message has the lowest priority
|
||||
NotAllowedWithReason = true;
|
||||
Reason = "No guests are allowed on this server! To join, sign up at: forum.beammp.com.";
|
||||
}
|
||||
@ -402,7 +386,7 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (mServer.ClientCount() < size_t(Application::Settings.MaxPlayers)) {
|
||||
if (mServer.ClientCount() < size_t(Application::Settings.getAsInt(Settings::Key::General_MaxPlayers))) {
|
||||
beammp_info("Identification success");
|
||||
mServer.InsertClient(Client);
|
||||
TCPClient(Client);
|
||||
@ -595,7 +579,7 @@ void TNetwork::TCPClient(const std::weak_ptr<TClient>& c) {
|
||||
}
|
||||
|
||||
void TNetwork::UpdatePlayer(TClient& Client) {
|
||||
std::string Packet = ("Ss") + std::to_string(mServer.ClientCount()) + "/" + std::to_string(Application::Settings.MaxPlayers) + ":";
|
||||
std::string Packet = ("Ss") + std::to_string(mServer.ClientCount()) + "/" + std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxPlayers)) + ":";
|
||||
mServer.ForEachClient([&](const std::weak_ptr<TClient>& ClientPtr) -> bool {
|
||||
ReadLock Lock(mServer.GetClientMutex());
|
||||
if (!ClientPtr.expired()) {
|
||||
@ -670,7 +654,7 @@ void TNetwork::OnConnect(const std::weak_ptr<TClient>& c) {
|
||||
SyncResources(*LockedClient);
|
||||
if (LockedClient->IsDisconnected())
|
||||
return;
|
||||
(void)Respond(*LockedClient, StringToVector("M" + Application::Settings.MapName), true); // Send the Map on connect
|
||||
(void)Respond(*LockedClient, StringToVector("M" + Application::Settings.getAsString(Settings::Key::General_Map)), true); // Send the Map on connect
|
||||
beammp_info(LockedClient->GetName() + " : Connected");
|
||||
LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onPlayerJoining", "", LockedClient->GetID()));
|
||||
}
|
||||
@ -729,7 +713,7 @@ void TNetwork::SendFile(TClient& c, const std::string& UnsafeName) {
|
||||
return;
|
||||
}
|
||||
auto FileName = fs::path(UnsafeName).filename().string();
|
||||
FileName = Application::Settings.Resource + "/Client/" + FileName;
|
||||
FileName = Application::Settings.getAsString(Settings::Key::General_ResourceFolder) + "/Client/" + FileName;
|
||||
|
||||
if (!std::filesystem::exists(FileName)) {
|
||||
if (!TCPSend(c, StringToVector("CO"))) {
|
||||
|
@ -65,13 +65,13 @@ void TPPSMonitor::operator()() {
|
||||
V += c->GetCarCount();
|
||||
}
|
||||
// kick on "no ping"
|
||||
if (c->SecondsSinceLastPing() > (20 * 60) ){
|
||||
if (c->SecondsSinceLastPing() > (20 * 60)) {
|
||||
beammp_debugf("client {} ({}) timing out: {}", c->GetID(), c->GetName(), c->SecondsSinceLastPing());
|
||||
TimedOutClients.push_back(c);
|
||||
} else if (c->IsSynced() && c->SecondsSinceLastPing() > (1 * 60)) {
|
||||
beammp_debugf("client {} ({}) timing out: {}", c->GetName(), c->GetID(), c->SecondsSinceLastPing());
|
||||
TimedOutClients.push_back(c);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
@ -25,7 +25,7 @@ namespace fs = std::filesystem;
|
||||
|
||||
TResourceManager::TResourceManager() {
|
||||
Application::SetSubsystemStatus("ResourceManager", Application::Status::Starting);
|
||||
std::string Path = Application::Settings.Resource + "/Client";
|
||||
std::string Path = Application::Settings.getAsString(Settings::Key::General_ResourceFolder) + "/Client";
|
||||
if (!fs::exists(Path))
|
||||
fs::create_directories(Path);
|
||||
for (const auto& entry : fs::directory_iterator(Path)) {
|
||||
|
@ -123,17 +123,6 @@ TEST_CASE("GetPidVid") {
|
||||
TServer::TServer(const std::vector<std::string_view>& Arguments) {
|
||||
beammp_info("BeamMP Server v" + Application::ServerVersionString());
|
||||
Application::SetSubsystemStatus("Server", Application::Status::Starting);
|
||||
if (Arguments.size() > 1) {
|
||||
Application::Settings.CustomIP = Arguments[0];
|
||||
size_t n = std::count(Application::Settings.CustomIP.begin(), Application::Settings.CustomIP.end(), '.');
|
||||
auto p = Application::Settings.CustomIP.find_first_not_of(".0123456789");
|
||||
if (p != std::string::npos || n != 3 || Application::Settings.CustomIP.substr(0, 3) == "127") {
|
||||
Application::Settings.CustomIP.clear();
|
||||
beammp_warn("IP Specified is invalid! Ignoring");
|
||||
} else {
|
||||
beammp_info("server started with custom IP");
|
||||
}
|
||||
}
|
||||
Application::SetSubsystemStatus("Server", Application::Status::Good);
|
||||
}
|
||||
|
||||
@ -297,7 +286,7 @@ bool TServer::ShouldSpawn(TClient& c, const std::string& CarJson, int ID) {
|
||||
c.SetUnicycleID(ID);
|
||||
return true;
|
||||
} else {
|
||||
return c.GetCarCount() < Application::Settings.MaxCars;
|
||||
return c.GetCarCount() < Application::Settings.getAsInt(Settings::Key::General_MaxCars);
|
||||
}
|
||||
}
|
||||
|
||||
|
12
src/main.cpp
12
src/main.cpp
@ -20,6 +20,7 @@
|
||||
#include "Common.h"
|
||||
#include "Http.h"
|
||||
#include "LuaAPI.h"
|
||||
#include "Settings.h"
|
||||
#include "SignalHandling.h"
|
||||
#include "TConfig.h"
|
||||
#include "THeartbeatThread.h"
|
||||
@ -150,7 +151,7 @@ int BeamMPServerMain(MainArguments Arguments) {
|
||||
beammp_errorf("Custom port requested via --port is invalid: '{}'", Port.value());
|
||||
return 1;
|
||||
} else {
|
||||
Application::Settings.Port = P;
|
||||
Application::Settings.set(Settings::Key::General_Port, P);
|
||||
beammp_info("Custom port requested via commandline arguments: " + Port.value());
|
||||
}
|
||||
}
|
||||
@ -165,6 +166,9 @@ int BeamMPServerMain(MainArguments Arguments) {
|
||||
|
||||
SetupSignalHandlers();
|
||||
|
||||
Settings settings {};
|
||||
beammp_infof("Server name set in new impl: {}", settings.getAsString(Settings::Key::General_Name));
|
||||
|
||||
bool Shutdown = false;
|
||||
Application::RegisterShutdownHandler([&Shutdown] {
|
||||
beammp_info("If this takes too long, you can press Ctrl+C repeatedly to force a shutdown.");
|
||||
@ -193,11 +197,7 @@ int BeamMPServerMain(MainArguments Arguments) {
|
||||
PPSMonitor.SetNetwork(Network);
|
||||
Application::CheckForUpdates();
|
||||
|
||||
TPluginMonitor PluginMonitor(fs::path(Application::Settings.Resource) / "Server", LuaEngine);
|
||||
|
||||
if (Application::Settings.HTTPServerEnabled) {
|
||||
Http::Server::THttpServerInstance HttpServerInstance {};
|
||||
}
|
||||
TPluginMonitor PluginMonitor(fs::path(Application::Settings.getAsString(Settings::Key::General_ResourceFolder)) / "Server", LuaEngine);
|
||||
|
||||
Application::SetSubsystemStatus("Main", Application::Status::Good);
|
||||
RegisterThread("Main(Waiting)");
|
||||
|
Loading…
x
Reference in New Issue
Block a user