mirror of
https://github.com/BeamMP/BeamMP-Server.git
synced 2026-07-13 18:24:16 +00:00
Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0c740ccedf | |||
| b0cf5c7838 | |||
| 586510041d | |||
| 1794c3fe45 | |||
| ee599e970d | |||
| 40158dc252 | |||
| 5ece60574a | |||
| c605498a2d | |||
| 4d7967d5d9 | |||
| 5dd4c97ed1 | |||
| 13390c5388 | |||
| 2f995a71ae | |||
| 3b5c6491a3 | |||
| db8719b5cd | |||
| a3c4b82a5d | |||
| 3b0e49fb06 | |||
| cf8f10b949 | |||
| 6f50cad76b | |||
| 03d91b1f4d | |||
| e407d246e2 | |||
| 234bdf5877 | |||
| c3b4528c89 | |||
| 5cf6d1d228 | |||
| 65017d0834 | |||
| 1237e7e533 | |||
| d81087b5af | |||
| 7e1f86478d | |||
| c85e026c2d | |||
| e4826e8bf1 | |||
| 590b159f14 | |||
| 40533c04bc | |||
| 946c1362e1 | |||
| 4347cb4af2 | |||
| 88721d4f7f | |||
| 7deea900fb | |||
| cbc1483537 | |||
| e2d7721438 | |||
| 0146a1cbe8 | |||
| 352a3aa536 | |||
| ac8c386c61 | |||
| d1fb15fc9a | |||
| f376d9f7be | |||
| 2f3dcfeb5a | |||
| 913ba1c793 | |||
| 9a0c9d3ce4 | |||
| a0d9be18b7 | |||
| 87d2e3355a | |||
| afb3763eab | |||
| 9de5a08b0c | |||
| bef623a281 | |||
| e852245bae | |||
| 5f5af9b0a7 | |||
| 64f2e08ed2 | |||
| 707682f4a8 | |||
| 913674740d | |||
| 1c575ff1bc | |||
| e72c217e63 | |||
| 75bae8ee5a | |||
| acc2dd29e9 | |||
| 39badf432d | |||
| 03307e71fb |
@@ -0,0 +1 @@
|
||||
patreon: BeamMP
|
||||
@@ -1,7 +1,11 @@
|
||||
name: Linux
|
||||
|
||||
on: [push]
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'develop'
|
||||
- 'minor'
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
name: Windows
|
||||
|
||||
on: [push]
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'develop'
|
||||
- 'minor'
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
VCPKG_DEFAULT_TRIPLET: x64-windows-static
|
||||
|
||||
@@ -49,6 +49,7 @@ set(PRJ_HEADERS
|
||||
include/TServer.h
|
||||
include/VehicleData.h
|
||||
include/Env.h
|
||||
include/Profiling.h
|
||||
)
|
||||
# add all source files (.cpp) to this, except the one with main()
|
||||
set(PRJ_SOURCES
|
||||
@@ -72,6 +73,7 @@ set(PRJ_SOURCES
|
||||
src/TServer.cpp
|
||||
src/VehicleData.cpp
|
||||
src/Env.cpp
|
||||
src/Profiling.cpp
|
||||
)
|
||||
|
||||
find_package(Lua REQUIRED)
|
||||
@@ -141,6 +143,7 @@ endif(UNIX)
|
||||
|
||||
if (WIN32)
|
||||
add_compile_options("-D_WIN32_WINNT=0x0601")
|
||||
add_compile_options("/bigobj")
|
||||
endif(WIN32)
|
||||
|
||||
|
||||
@@ -194,6 +197,7 @@ target_compile_definitions(${PROJECT_NAME} PRIVATE ${PRJ_DEFINITIONS} ${PRJ_WARN
|
||||
)
|
||||
|
||||
if(MSVC)
|
||||
target_compile_options(${PROJECT_NAME} PUBLIC "/bigobj")
|
||||
target_link_options(${PROJECT_NAME} PRIVATE "/SUBSYSTEM:CONSOLE")
|
||||
endif(MSVC)
|
||||
|
||||
|
||||
+20
-3
@@ -53,11 +53,28 @@ Do **NOT** pull with merge. This is the default git behavior for `git pull`, but
|
||||
|
||||
The only acceptable merge commits are those which actually merge functionally different branches into each other, for example for merging one feature branch into another.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Making an issue and fixing it
|
||||
|
||||
1. Create an issue detailing the feature or bug.
|
||||
2. Assign a milestone to the issue, or wait for a maintainer to add a milestone to your issue.
|
||||
3. Fork the repository and base your work on the branch mentioned in the milestone attached to your issue (e.g. `v3.0.0 (develop)` -> `develop`).
|
||||
4. Program your feature or bug fix.
|
||||
5. Open a PR that references the issue by number in the format: `#12345`.
|
||||
6. Someone will review your PR and merge it, or ask for changes.
|
||||
|
||||
### Fixing an existing issue
|
||||
|
||||
1. Fork the repository and base your work on the branch mentioned in the milestone attached to your issue (e.g. `v3.0.0 (develop)` -> `develop`).
|
||||
2. Program your feature or bug fix.
|
||||
3. Open a PR that references the issue by number in the format: `#12345`.
|
||||
4. Someone will review your PR and merge it, or ask for changes.
|
||||
|
||||
## Branches
|
||||
|
||||
### Which branch should I base my work on?
|
||||
|
||||
Each *feature* or *bug-fix* is implemented on a new Git branch, branched off of the branch it should be based on. The `master` branch is usually stable, so we don't do development on it. It is always a safe bet to branch off of `master`, but it may be more work to merge later. Branches to base your work on are usually branches like `rc-v3.3.0`, when the latest public version is `3.2.0`, for example. These can often be found in Pull-Requests on GitHub which are tagged `Release Candidate`.
|
||||
- `minor`: Minor releases, like `v1.2.3` -> `v1.3.0` or `v1.2.3` -> `v1.2.4`.
|
||||
- `develop`: Major releases, like `v1.2.3` -> `v2.0.0`, and larger feature/minor releases.
|
||||
|
||||
## Unit tests & CI/CD
|
||||
|
||||
|
||||
Vendored
+1
-1
Submodule deps/commandline updated: b2a29733f9...04952e4811
+6
-3
@@ -106,8 +106,6 @@ public:
|
||||
static std::vector<std::string> GetBackendUrlsInOrder() {
|
||||
return {
|
||||
"backend.beammp.com",
|
||||
"backup1.beammp.com",
|
||||
"backup2.beammp.com"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -152,7 +150,7 @@ private:
|
||||
static inline std::mutex mShutdownHandlersMutex {};
|
||||
static inline std::deque<TShutdownHandler> mShutdownHandlers {};
|
||||
|
||||
static inline Version mVersion { 3, 2, 2 };
|
||||
static inline Version mVersion { 3, 4, 0 };
|
||||
};
|
||||
|
||||
void SplitString(std::string const& str, const char delim, std::vector<std::string>& out);
|
||||
@@ -214,6 +212,10 @@ void RegisterThread(const std::string& str);
|
||||
do { \
|
||||
Application::Console().Write(_this_location + std::string("[LUA ERROR] ") + (x)); \
|
||||
} while (false)
|
||||
#define beammp_lua_log(level, plugin, x) \
|
||||
do { \
|
||||
Application::Console().Write(_this_location + fmt::format("[{}] [{}] ", plugin, level) + (x)); \
|
||||
} while (false)
|
||||
#define beammp_lua_warn(x) \
|
||||
do { \
|
||||
Application::Console().Write(_this_location + std::string("[LUA WARN] ") + (x)); \
|
||||
@@ -269,6 +271,7 @@ void RegisterThread(const std::string& str);
|
||||
#define beammp_tracef(...) beammp_trace(fmt::format(__VA_ARGS__))
|
||||
#define beammp_lua_errorf(...) beammp_lua_error(fmt::format(__VA_ARGS__))
|
||||
#define beammp_lua_warnf(...) beammp_lua_warn(fmt::format(__VA_ARGS__))
|
||||
#define beammp_lua_log(level, plugin, x) /* x */
|
||||
|
||||
#endif // DOCTEST_CONFIG_DISABLE
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ namespace Env {
|
||||
enum class Key {
|
||||
// provider settings
|
||||
PROVIDER_UPDATE_MESSAGE,
|
||||
PROVIDER_DISABLE_CONFIG,
|
||||
PROVIDER_PORT_ENV,
|
||||
};
|
||||
|
||||
std::optional<std::string> Get(Key key);
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/thread/synchronized_value.hpp>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace prof {
|
||||
|
||||
using Duration = std::chrono::duration<double, std::milli>;
|
||||
using TimePoint = std::chrono::high_resolution_clock::time_point;
|
||||
|
||||
/// Returns the current time.
|
||||
TimePoint now();
|
||||
|
||||
/// Returns a sub-millisecond resolution duration between start and end.
|
||||
Duration duration(const TimePoint& start, const TimePoint& end);
|
||||
|
||||
struct Stats {
|
||||
double mean;
|
||||
double stdev;
|
||||
double min;
|
||||
double max;
|
||||
size_t n;
|
||||
};
|
||||
|
||||
/// Calculates and stores the moving average over K samples of execution time data
|
||||
/// for some single unit of code. Threadsafe.
|
||||
struct UnitExecutionTime {
|
||||
UnitExecutionTime();
|
||||
|
||||
/// Adds a sample to the collection, overriding the oldest sample if needed.
|
||||
void add_sample(const Duration& dur);
|
||||
|
||||
/// Calculates the mean duration over the `measurement_count()` measurements,
|
||||
/// as well as the standard deviation.
|
||||
Stats stats() const;
|
||||
|
||||
/// Returns the number of elements the moving average is calculated over.
|
||||
size_t measurement_count() const;
|
||||
|
||||
private:
|
||||
mutable std::mutex m_mtx {};
|
||||
size_t m_total_calls {};
|
||||
double m_sum {};
|
||||
// sum of measurements squared (for running stdev)
|
||||
double m_measurement_sqr_sum {};
|
||||
double m_min { std::numeric_limits<double>::max() };
|
||||
double m_max { std::numeric_limits<double>::min() };
|
||||
};
|
||||
|
||||
/// Holds profiles for multiple units by name. Threadsafe.
|
||||
struct UnitProfileCollection {
|
||||
/// Adds a sample to the collection, overriding the oldest sample if needed.
|
||||
void add_sample(const std::string& unit, const Duration& duration);
|
||||
|
||||
/// Calculates the mean duration over the `measurement_count()` measurements,
|
||||
/// as well as the standard deviation.
|
||||
Stats stats(const std::string& unit);
|
||||
|
||||
/// Returns the number of elements the moving average is calculated over.
|
||||
size_t measurement_count(const std::string& unit);
|
||||
|
||||
/// Returns the stats for all stored units.
|
||||
std::unordered_map<std::string, Stats> all_stats();
|
||||
|
||||
private:
|
||||
boost::synchronized_value<std::unordered_map<std::string, UnitExecutionTime>> m_map;
|
||||
};
|
||||
|
||||
}
|
||||
+2
-1
@@ -35,11 +35,11 @@ public:
|
||||
[[nodiscard]] bool Failed() const { return mFailed; }
|
||||
|
||||
void FlushToFile();
|
||||
void PrintDebug();
|
||||
|
||||
private:
|
||||
void CreateConfigFile();
|
||||
void ParseFromFile(std::string_view name);
|
||||
void PrintDebug();
|
||||
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);
|
||||
@@ -48,5 +48,6 @@ private:
|
||||
std::string TagsAsPrettyArray() const;
|
||||
bool IsDefault();
|
||||
bool mFailed { false };
|
||||
bool mDisableConfig { false };
|
||||
std::string mConfigFileName;
|
||||
};
|
||||
|
||||
@@ -58,6 +58,7 @@ private:
|
||||
void Command_Status(const std::string& cmd, const std::vector<std::string>& args);
|
||||
void Command_Settings(const std::string& cmd, const std::vector<std::string>& args);
|
||||
void Command_Clear(const std::string&, const std::vector<std::string>& args);
|
||||
void Command_Version(const std::string& cmd, const std::vector<std::string>& args);
|
||||
|
||||
void Command_Say(const std::string& FullCommand);
|
||||
bool EnsureArgsCount(const std::vector<std::string>& args, size_t n);
|
||||
@@ -75,6 +76,7 @@ private:
|
||||
{ "settings", [this](const auto& a, const auto& b) { Command_Settings(a, b); } },
|
||||
{ "clear", [this](const auto& a, const auto& b) { Command_Clear(a, b); } },
|
||||
{ "say", [this](const auto&, const auto&) { Command_Say(""); } }, // shouldn't actually be called
|
||||
{ "version", [this](const auto& a, const auto& b) { Command_Version(a, b); } },
|
||||
};
|
||||
|
||||
std::unique_ptr<Commandline> mCommandline { nullptr };
|
||||
|
||||
+34
-12
@@ -18,9 +18,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Profiling.h"
|
||||
#include "TNetwork.h"
|
||||
#include "TServer.h"
|
||||
#include <any>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <filesystem>
|
||||
#include <initializer_list>
|
||||
@@ -28,6 +30,7 @@
|
||||
#include <lua.hpp>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <queue>
|
||||
#include <random>
|
||||
#include <set>
|
||||
@@ -36,19 +39,34 @@
|
||||
#include <vector>
|
||||
|
||||
#define SOL_ALL_SAFETIES_ON 1
|
||||
#define SOL_USER_C_ASSERT SOL_ON
|
||||
#define SOL_C_ASSERT(...) \
|
||||
beammp_lua_errorf("SOL2 assertion failure: Assertion `{}` failed in {}:{}. This *should* be a fatal error, but BeamMP Server overrides it to not be fatal. This may cause the Lua Engine to crash, or cause other issues.", #__VA_ARGS__, __FILE__, __LINE__)
|
||||
#include <sol/sol.hpp>
|
||||
|
||||
struct JsonString {
|
||||
std::string value;
|
||||
};
|
||||
|
||||
// value used to keep nils in a table or array, across serialization boundaries like
|
||||
// JsonEncode, so that the nil stays at the same index and isn't treated like a special
|
||||
// value (e.g. one that can be ignored or discarded).
|
||||
const inline std::string BEAMMP_INTERNAL_NIL = "BEAMMP_SERVER_INTERNAL_NIL_VALUE";
|
||||
|
||||
using TLuaStateId = std::string;
|
||||
namespace fs = std::filesystem;
|
||||
/**
|
||||
* std::variant means, that TLuaArgTypes may be one of the Types listed as template args
|
||||
*/
|
||||
using TLuaArgTypes = std::variant<std::string, int, sol::variadic_args, bool, std::unordered_map<std::string, std::string>>;
|
||||
static constexpr size_t TLuaArgTypes_String = 0;
|
||||
static constexpr size_t TLuaArgTypes_Int = 1;
|
||||
static constexpr size_t TLuaArgTypes_VariadicArgs = 2;
|
||||
static constexpr size_t TLuaArgTypes_Bool = 3;
|
||||
static constexpr size_t TLuaArgTypes_StringStringMap = 4;
|
||||
using TLuaValue = std::variant<std::string, int, JsonString, bool, std::unordered_map<std::string, std::string>, float>;
|
||||
enum TLuaType {
|
||||
String = 0,
|
||||
Int = 1,
|
||||
Json = 2,
|
||||
Bool = 3,
|
||||
StringStringMap = 4,
|
||||
Float = 5,
|
||||
};
|
||||
|
||||
class TLuaPlugin;
|
||||
|
||||
@@ -96,7 +114,7 @@ public:
|
||||
struct QueuedFunction {
|
||||
std::string FunctionName;
|
||||
std::shared_ptr<TLuaResult> Result;
|
||||
std::vector<TLuaArgTypes> Args;
|
||||
std::vector<TLuaValue> Args;
|
||||
std::string EventName; // optional, may be empty
|
||||
};
|
||||
|
||||
@@ -149,7 +167,7 @@ public:
|
||||
void ReportErrors(const std::vector<std::shared_ptr<TLuaResult>>& Results);
|
||||
bool HasState(TLuaStateId StateId);
|
||||
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueScript(TLuaStateId StateID, const TLuaChunk& Script);
|
||||
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCall(TLuaStateId StateID, const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args);
|
||||
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCall(TLuaStateId StateID, const std::string& FunctionName, const std::vector<TLuaValue>& Args);
|
||||
void EnsureStateExists(TLuaStateId StateId, const std::string& Name, bool DontCallOnInit = false);
|
||||
void RegisterEvent(const std::string& EventName, TLuaStateId StateId, const std::string& FunctionName);
|
||||
/**
|
||||
@@ -169,7 +187,7 @@ public:
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<TLuaResult>> Results;
|
||||
std::vector<TLuaArgTypes> Arguments { TLuaArgTypes { std::forward<ArgsT>(Args) }... };
|
||||
std::vector<TLuaValue> Arguments { TLuaValue { std::forward<ArgsT>(Args) }... };
|
||||
|
||||
for (const auto& Event : mLuaEvents.at(EventName)) {
|
||||
for (const auto& Function : Event.second) {
|
||||
@@ -188,7 +206,7 @@ public:
|
||||
return {};
|
||||
}
|
||||
std::vector<std::shared_ptr<TLuaResult>> Results;
|
||||
std::vector<TLuaArgTypes> Arguments { TLuaArgTypes { std::forward<ArgsT>(Args) }... };
|
||||
std::vector<TLuaValue> Arguments { TLuaValue { std::forward<ArgsT>(Args) }... };
|
||||
const auto Handlers = GetEventHandlersForState(EventName, StateId);
|
||||
for (const auto& Handler : Handlers) {
|
||||
Results.push_back(EnqueueFunctionCall(StateId, Handler, Arguments));
|
||||
@@ -225,8 +243,8 @@ private:
|
||||
StateThreadData(const StateThreadData&) = delete;
|
||||
virtual ~StateThreadData() noexcept { beammp_debug("\"" + mStateId + "\" destroyed"); }
|
||||
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueScript(const TLuaChunk& Script);
|
||||
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCall(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args);
|
||||
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args, const std::string& EventName, CallStrategy Strategy);
|
||||
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCall(const std::string& FunctionName, const std::vector<TLuaValue>& Args);
|
||||
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector<TLuaValue>& Args, const std::string& EventName, CallStrategy Strategy);
|
||||
void RegisterEvent(const std::string& EventName, const std::string& FunctionName);
|
||||
void AddPath(const fs::path& Path); // to be added to path and cpath
|
||||
void operator()() override;
|
||||
@@ -253,6 +271,9 @@ private:
|
||||
sol::table Lua_FS_ListFiles(const std::string& Path);
|
||||
sol::table Lua_FS_ListDirectories(const std::string& Path);
|
||||
|
||||
prof::UnitProfileCollection mProfile {};
|
||||
std::unordered_map<std::string, prof::TimePoint> mProfileStarts;
|
||||
|
||||
std::string mName;
|
||||
TLuaStateId mStateId;
|
||||
lua_State* mState;
|
||||
@@ -268,6 +289,7 @@ private:
|
||||
std::recursive_mutex mPathsMutex;
|
||||
std::mt19937 mMersenneTwister;
|
||||
std::uniform_real_distribution<double> mUniformRealDistribution01;
|
||||
std::vector<sol::object> JsonStringToArray(JsonString Str);
|
||||
};
|
||||
|
||||
struct TimedEvent {
|
||||
|
||||
@@ -55,6 +55,7 @@ private:
|
||||
TResourceManager& mResourceManager;
|
||||
std::thread mUDPThread;
|
||||
std::thread mTCPThread;
|
||||
std::mutex mOpenIDMutex;
|
||||
|
||||
std::vector<uint8_t> UDPRcvFromClient(ip::udp::endpoint& ClientEndpoint);
|
||||
void HandleDownload(TConnection&& TCPSock);
|
||||
|
||||
@@ -121,6 +121,14 @@ void TClient::SetCarData(int Ident, const std::string& Data) {
|
||||
}
|
||||
|
||||
int TClient::GetCarCount() const {
|
||||
// mVechileData holds both unicycle and cars which both count towards the maximum car count
|
||||
// spawning a unicycle meant reaching the max, hence being unable to spawn car. this dirty fixes the problem for now.
|
||||
std::unique_lock lock(mVehicleDataMutex);
|
||||
for (auto& v : mVehicleData) {
|
||||
if (v.ID() == mUnicycleID) {
|
||||
return int(mVehicleData.size() - 1);
|
||||
}
|
||||
}
|
||||
return int(mVehicleData.size());
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -222,7 +222,7 @@ void Application::CheckForUpdates() {
|
||||
auto RemoteVersion = Version(VersionStrToInts(Response));
|
||||
if (IsOutdated(MyVersion, RemoteVersion)) {
|
||||
std::string RealVersionString = std::string("v") + RemoteVersion.AsString();
|
||||
const std::string DefaultUpdateMsg = "NEW VERSION IS OUT! Please update to the new version ({}) of the BeamMP-Server! Download it here: https://beammp.com/! For a guide on how to update, visit: https://wiki.beammp.com/en/home/server-maintenance#updating-the-server";
|
||||
const std::string DefaultUpdateMsg = "NEW VERSION IS OUT! Please update to the new version ({}) of the BeamMP-Server! Download it here: https://beammp.com/! For a guide on how to update, visit: https://docs.beammp.com/server/server-maintenance/#updating-the-server";
|
||||
auto UpdateMsg = Env::Get(Env::Key::PROVIDER_UPDATE_MESSAGE).value_or(DefaultUpdateMsg);
|
||||
UpdateMsg = fmt::vformat(std::string_view(UpdateMsg), fmt::make_format_args(RealVersionString));
|
||||
beammp_warnf("{}{}{}", ANSI_YELLOW_BOLD, UpdateMsg, ANSI_RESET);
|
||||
|
||||
@@ -33,6 +33,12 @@ std::string_view Env::ToString(Env::Key key) {
|
||||
case Key::PROVIDER_UPDATE_MESSAGE:
|
||||
return "BEAMMP_PROVIDER_UPDATE_MESSAGE";
|
||||
break;
|
||||
case Key::PROVIDER_DISABLE_CONFIG:
|
||||
return "BEAMMP_PROVIDER_DISABLE_CONFIG";
|
||||
break;
|
||||
case Key::PROVIDER_PORT_ENV:
|
||||
return "BEAMMP_PROVIDER_PORT_ENV";
|
||||
break;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
#include "Profiling.h"
|
||||
#include <limits>
|
||||
|
||||
prof::Duration prof::duration(const TimePoint& start, const TimePoint& end) {
|
||||
return end - start;
|
||||
}
|
||||
prof::TimePoint prof::now() {
|
||||
return std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
prof::Stats prof::UnitProfileCollection::stats(const std::string& unit) {
|
||||
return m_map->operator[](unit).stats();
|
||||
}
|
||||
|
||||
size_t prof::UnitProfileCollection::measurement_count(const std::string& unit) {
|
||||
return m_map->operator[](unit).measurement_count();
|
||||
}
|
||||
|
||||
void prof::UnitProfileCollection::add_sample(const std::string& unit, const Duration& duration) {
|
||||
m_map->operator[](unit).add_sample(duration);
|
||||
}
|
||||
|
||||
prof::Stats prof::UnitExecutionTime::stats() const {
|
||||
std::unique_lock lock(m_mtx);
|
||||
Stats result {};
|
||||
// calculate sum
|
||||
result.n = m_total_calls;
|
||||
result.max = m_min;
|
||||
result.min = m_max;
|
||||
// calculate mean: mean = sum_x / n
|
||||
result.mean = m_sum / double(m_total_calls);
|
||||
// calculate stdev: stdev = sqrt((sum_x2 / n) - (mean * mean))
|
||||
result.stdev = std::sqrt((m_measurement_sqr_sum / double(result.n)) - (result.mean * result.mean));
|
||||
return result;
|
||||
}
|
||||
|
||||
void prof::UnitExecutionTime::add_sample(const Duration& dur) {
|
||||
std::unique_lock lock(m_mtx);
|
||||
m_sum += dur.count();
|
||||
m_measurement_sqr_sum += dur.count() * dur.count();
|
||||
m_min = std::min(dur.count(), m_min);
|
||||
m_max = std::max(dur.count(), m_max);
|
||||
++m_total_calls;
|
||||
}
|
||||
|
||||
prof::UnitExecutionTime::UnitExecutionTime() {
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, prof::Stats> prof::UnitProfileCollection::all_stats() {
|
||||
auto map = m_map.synchronize();
|
||||
std::unordered_map<std::string, Stats> result {};
|
||||
for (const auto& [name, time] : *map) {
|
||||
result[name] = time.stats();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
size_t prof::UnitExecutionTime::measurement_count() const {
|
||||
std::unique_lock lock(m_mtx);
|
||||
return m_total_calls;
|
||||
}
|
||||
|
||||
+37
-8
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
#include "Env.h"
|
||||
#include "TConfig.h"
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
@@ -87,7 +88,9 @@ TEST_CASE("TConfig::TConfig") {
|
||||
TConfig::TConfig(const std::string& ConfigFileName)
|
||||
: mConfigFileName(ConfigFileName) {
|
||||
Application::SetSubsystemStatus("Config", Application::Status::Starting);
|
||||
if (!fs::exists(mConfigFileName) || !fs::is_regular_file(mConfigFileName)) {
|
||||
auto DisableConfig = Env::Get(Env::Key::PROVIDER_DISABLE_CONFIG).value_or("false");
|
||||
mDisableConfig = DisableConfig == "true" || DisableConfig == "1";
|
||||
if (!mDisableConfig && (!fs::exists(mConfigFileName) || !fs::is_regular_file(mConfigFileName))) {
|
||||
beammp_info("No config file found! Generating one...");
|
||||
CreateConfigFile();
|
||||
}
|
||||
@@ -143,7 +146,7 @@ void TConfig::FlushToFile() {
|
||||
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"
|
||||
"# Help & Documentation: `https://wiki.beammp.com/en/home/server-maintenance`\n"
|
||||
"# Help & Documentation: `https://docs.beammp.com/server/server-maintenance/`\n"
|
||||
"# IMPORTANT: Fill in the AuthKey with the key you got from `https://keymaster.beammp.com/` on the left under \"Keys\"\n"
|
||||
<< data;
|
||||
auto File = std::fopen(mConfigFileName.c_str(), "w+");
|
||||
@@ -161,7 +164,9 @@ void TConfig::FlushToFile() {
|
||||
|
||||
void TConfig::CreateConfigFile() {
|
||||
// build from old config Server.cfg
|
||||
|
||||
if (mDisableConfig) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (fs::exists("Server.cfg")) {
|
||||
// parse it (this is weird and bad and should be removed in some future version)
|
||||
@@ -181,6 +186,9 @@ void TConfig::TryReadValue(toml::value& Table, const std::string& Category, cons
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (mDisableConfig) {
|
||||
return;
|
||||
}
|
||||
if (Table[Category.c_str()][Key.data()].is_string()) {
|
||||
OutValue = Table[Category.c_str()][Key.data()].as_string();
|
||||
}
|
||||
@@ -194,6 +202,9 @@ void TConfig::TryReadValue(toml::value& Table, const std::string& Category, cons
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (mDisableConfig) {
|
||||
return;
|
||||
}
|
||||
if (Table[Category.c_str()][Key.data()].is_boolean()) {
|
||||
OutValue = Table[Category.c_str()][Key.data()].as_boolean();
|
||||
}
|
||||
@@ -206,6 +217,9 @@ void TConfig::TryReadValue(toml::value& Table, const std::string& Category, cons
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (mDisableConfig) {
|
||||
return;
|
||||
}
|
||||
if (Table[Category.c_str()][Key.data()].is_integer()) {
|
||||
OutValue = int(Table[Category.c_str()][Key.data()].as_integer());
|
||||
}
|
||||
@@ -213,11 +227,18 @@ void TConfig::TryReadValue(toml::value& Table, const std::string& Category, cons
|
||||
|
||||
void TConfig::ParseFromFile(std::string_view name) {
|
||||
try {
|
||||
toml::value data = toml::parse<toml::preserve_comments>(name.data());
|
||||
toml::value data {};
|
||||
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);
|
||||
TryReadValue(data, "General", StrPort, EnvStrPort, Application::Settings.Port);
|
||||
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);
|
||||
@@ -238,13 +259,18 @@ void TConfig::ParseFromFile(std::string_view name) {
|
||||
Application::SetSubsystemStatus("Config", Application::Status::Bad);
|
||||
return;
|
||||
}
|
||||
PrintDebug();
|
||||
|
||||
// Update in any case
|
||||
FlushToFile();
|
||||
if (!mDisableConfig) {
|
||||
FlushToFile();
|
||||
}
|
||||
// all good so far, let's check if there's a key
|
||||
if (Application::Settings.Key.empty()) {
|
||||
beammp_error("No AuthKey specified in the \"" + std::string(mConfigFileName) + "\" file. Please get an AuthKey, enter it into the config file, and restart this server.");
|
||||
if (mDisableConfig) {
|
||||
beammp_error("No AuthKey specified in the environment.");
|
||||
} else {
|
||||
beammp_error("No AuthKey specified in the \"" + std::string(mConfigFileName) + "\" file. Please get an AuthKey, enter it into the config file, and restart this server.");
|
||||
}
|
||||
Application::SetSubsystemStatus("Config", Application::Status::Bad);
|
||||
mFailed = true;
|
||||
return;
|
||||
@@ -256,6 +282,9 @@ void TConfig::ParseFromFile(std::string_view name) {
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
+10
-1
@@ -247,7 +247,8 @@ void TConsole::Command_Help(const std::string&, const std::vector<std::string>&
|
||||
lua [state id] switches to lua, optionally into a specific state id's lua
|
||||
settings [command] sets or gets settings for the server, run `settings help` for more info
|
||||
status how the server is doing and what it's up to
|
||||
clear clears the console window)";
|
||||
clear clears the console window
|
||||
version displays the server version)";
|
||||
Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString));
|
||||
}
|
||||
|
||||
@@ -267,6 +268,14 @@ void TConsole::Command_Clear(const std::string&, const std::vector<std::string>&
|
||||
mCommandline->write("\x1b[;H\x1b[2J");
|
||||
}
|
||||
|
||||
void TConsole::Command_Version(const std::string& cmd, const std::vector<std::string>& args) {
|
||||
if (!EnsureArgsCount(args, 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Application::Console().WriteRaw("Current version: v" + Application::ServerVersionString());
|
||||
}
|
||||
|
||||
void TConsole::Command_Kick(const std::string&, const std::vector<std::string>& args) {
|
||||
if (!EnsureArgsCount(args, 1, size_t(-1))) {
|
||||
return;
|
||||
|
||||
+138
-20
@@ -18,14 +18,17 @@
|
||||
|
||||
#include "TLuaEngine.h"
|
||||
#include "Client.h"
|
||||
#include "Common.h"
|
||||
#include "CustomAssert.h"
|
||||
#include "Http.h"
|
||||
#include "LuaAPI.h"
|
||||
#include "Profiling.h"
|
||||
#include "TLuaPlugin.h"
|
||||
#include "sol/object.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <fmt/core.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <random>
|
||||
#include <thread>
|
||||
@@ -350,7 +353,7 @@ std::shared_ptr<TLuaResult> TLuaEngine::EnqueueScript(TLuaStateId StateID, const
|
||||
return mLuaStates.at(StateID)->EnqueueScript(Script);
|
||||
}
|
||||
|
||||
std::shared_ptr<TLuaResult> TLuaEngine::EnqueueFunctionCall(TLuaStateId StateID, const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args) {
|
||||
std::shared_ptr<TLuaResult> TLuaEngine::EnqueueFunctionCall(TLuaStateId StateID, const std::string& FunctionName, const std::vector<TLuaValue>& Args) {
|
||||
std::unique_lock Lock(mLuaStatesMutex);
|
||||
return mLuaStates.at(StateID)->EnqueueFunctionCall(FunctionName, Args);
|
||||
}
|
||||
@@ -359,17 +362,30 @@ void TLuaEngine::CollectAndInitPlugins() {
|
||||
if (!fs::exists(mResourceServerPath)) {
|
||||
fs::create_directories(mResourceServerPath);
|
||||
}
|
||||
for (const auto& Dir : fs::directory_iterator(mResourceServerPath)) {
|
||||
auto Path = Dir.path();
|
||||
Path = fs::relative(Path);
|
||||
if (!Dir.is_directory()) {
|
||||
beammp_error("\"" + Dir.path().string() + "\" is not a directory, skipping");
|
||||
|
||||
std::vector<fs::path> PluginsEntries;
|
||||
for (const auto& Entry : fs::directory_iterator(mResourceServerPath)) {
|
||||
if (Entry.is_directory()) {
|
||||
PluginsEntries.push_back(Entry);
|
||||
} else {
|
||||
TLuaPluginConfig Config { Path.stem().string() };
|
||||
FindAndParseConfig(Path, Config);
|
||||
InitializePlugin(Path, Config);
|
||||
beammp_error("\"" + Entry.path().string() + "\" is not a directory, skipping");
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(PluginsEntries.begin(), PluginsEntries.end(), [](const fs::path& first, const fs::path& second) {
|
||||
auto firstStr = first.string();
|
||||
auto secondStr = second.string();
|
||||
std::transform(firstStr.begin(), firstStr.end(), firstStr.begin(), ::tolower);
|
||||
std::transform(secondStr.begin(), secondStr.end(), secondStr.begin(), ::tolower);
|
||||
return firstStr < secondStr;
|
||||
});
|
||||
|
||||
for (const auto& Dir : PluginsEntries) {
|
||||
auto Path = fs::relative(Dir);
|
||||
TLuaPluginConfig Config { Path.stem().string() };
|
||||
FindAndParseConfig(Path, Config);
|
||||
InitializePlugin(Path, Config);
|
||||
}
|
||||
}
|
||||
|
||||
void TLuaEngine::InitializePlugin(const fs::path& Folder, const TLuaPluginConfig& Config) {
|
||||
@@ -430,13 +446,50 @@ std::set<std::string> TLuaEngine::GetEventHandlersForState(const std::string& Ev
|
||||
return mLuaEvents[EventName][StateId];
|
||||
}
|
||||
|
||||
std::vector<sol::object> TLuaEngine::StateThreadData::JsonStringToArray(JsonString Str) {
|
||||
auto LocalTable = Lua_JsonDecode(Str.value).as<std::vector<sol::object>>();
|
||||
for (auto& value : LocalTable) {
|
||||
if (value.is<std::string>() && value.as<std::string>() == BEAMMP_INTERNAL_NIL) {
|
||||
value = sol::object {};
|
||||
}
|
||||
}
|
||||
return LocalTable;
|
||||
}
|
||||
|
||||
sol::table TLuaEngine::StateThreadData::Lua_TriggerGlobalEvent(const std::string& EventName, sol::variadic_args EventArgs) {
|
||||
auto Return = mEngine->TriggerEvent(EventName, mStateId, EventArgs);
|
||||
auto Table = mStateView.create_table();
|
||||
for (const sol::stack_proxy& Arg : EventArgs) {
|
||||
switch (Arg.get_type()) {
|
||||
case sol::type::none:
|
||||
case sol::type::userdata:
|
||||
case sol::type::lightuserdata:
|
||||
case sol::type::thread:
|
||||
case sol::type::function:
|
||||
case sol::type::poly:
|
||||
Table.add(BEAMMP_INTERNAL_NIL);
|
||||
beammp_warnf("Passed a value of type '{}' to TriggerGlobalEvent(\"{}\", ...). This type can not be serialized, and cannot be passed between states. It will arrive as <nil> in handlers.", sol::type_name(EventArgs.lua_state(), Arg.get_type()), EventName);
|
||||
break;
|
||||
case sol::type::lua_nil:
|
||||
Table.add(BEAMMP_INTERNAL_NIL);
|
||||
break;
|
||||
case sol::type::string:
|
||||
case sol::type::number:
|
||||
case sol::type::boolean:
|
||||
case sol::type::table:
|
||||
Table.add(Arg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
JsonString Str { LuaAPI::MP::JsonEncode(Table) };
|
||||
beammp_debugf("json: {}", Str.value);
|
||||
auto Return = mEngine->TriggerEvent(EventName, mStateId, Str);
|
||||
auto MyHandlers = mEngine->GetEventHandlersForState(EventName, mStateId);
|
||||
|
||||
sol::variadic_results LocalArgs = JsonStringToArray(Str);
|
||||
for (const auto& Handler : MyHandlers) {
|
||||
auto Fn = mStateView[Handler];
|
||||
if (Fn.valid()) {
|
||||
auto LuaResult = Fn(EventArgs);
|
||||
auto LuaResult = Fn(LocalArgs);
|
||||
auto Result = std::make_shared<TLuaResult>();
|
||||
if (LuaResult.valid()) {
|
||||
Result->Error = false;
|
||||
@@ -658,6 +711,7 @@ static void AddToTable(sol::table& table, const std::string& left, const T& valu
|
||||
static void JsonDecodeRecursive(sol::state_view& StateView, sol::table& table, const std::string& left, const nlohmann::json& right) {
|
||||
switch (right.type()) {
|
||||
case nlohmann::detail::value_t::null:
|
||||
AddToTable(table, left, sol::lua_nil_t {});
|
||||
return;
|
||||
case nlohmann::detail::value_t::object: {
|
||||
auto value = table.create();
|
||||
@@ -829,6 +883,40 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, TLuaStateI
|
||||
MPTable.set_function("Set", &LuaAPI::MP::Set);
|
||||
|
||||
auto UtilTable = StateView.create_named_table("Util");
|
||||
UtilTable.set_function("LogDebug", [this](sol::variadic_args args) {
|
||||
std::string ToPrint = "";
|
||||
for (const auto& arg : args) {
|
||||
ToPrint += LuaAPI::LuaToString(static_cast<const sol::object>(arg));
|
||||
ToPrint += "\t";
|
||||
}
|
||||
if (Application::Settings.DebugModeEnabled) {
|
||||
beammp_lua_log("DEBUG", mStateId, ToPrint);
|
||||
}
|
||||
});
|
||||
UtilTable.set_function("LogInfo", [this](sol::variadic_args args) {
|
||||
std::string ToPrint = "";
|
||||
for (const auto& arg : args) {
|
||||
ToPrint += LuaAPI::LuaToString(static_cast<const sol::object>(arg));
|
||||
ToPrint += "\t";
|
||||
}
|
||||
beammp_lua_log("INFO", mStateId, ToPrint);
|
||||
});
|
||||
UtilTable.set_function("LogWarn", [this](sol::variadic_args args) {
|
||||
std::string ToPrint = "";
|
||||
for (const auto& arg : args) {
|
||||
ToPrint += LuaAPI::LuaToString(static_cast<const sol::object>(arg));
|
||||
ToPrint += "\t";
|
||||
}
|
||||
beammp_lua_log("WARN", mStateId, ToPrint);
|
||||
});
|
||||
UtilTable.set_function("LogError", [this](sol::variadic_args args) {
|
||||
std::string ToPrint = "";
|
||||
for (const auto& arg : args) {
|
||||
ToPrint += LuaAPI::LuaToString(static_cast<const sol::object>(arg));
|
||||
ToPrint += "\t";
|
||||
}
|
||||
beammp_lua_log("ERROR", mStateId, ToPrint);
|
||||
});
|
||||
UtilTable.set_function("JsonEncode", &LuaAPI::MP::JsonEncode);
|
||||
UtilTable.set_function("JsonDecode", [this](const std::string& str) {
|
||||
return Lua_JsonDecode(str);
|
||||
@@ -847,6 +935,30 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, TLuaStateI
|
||||
UtilTable.set_function("RandomIntRange", [this](int64_t min, int64_t max) -> int64_t {
|
||||
return std::uniform_int_distribution(min, max)(mMersenneTwister);
|
||||
});
|
||||
UtilTable.set_function("DebugExecutionTime", [this]() -> sol::table {
|
||||
sol::state_view StateView(mState);
|
||||
sol::table Result = StateView.create_table();
|
||||
auto stats = mProfile.all_stats();
|
||||
for (const auto& [name, stat] : stats) {
|
||||
Result[name] = StateView.create_table();
|
||||
Result[name]["mean"] = stat.mean;
|
||||
Result[name]["stdev"] = stat.stdev;
|
||||
Result[name]["min"] = stat.min;
|
||||
Result[name]["max"] = stat.max;
|
||||
Result[name]["n"] = stat.n;
|
||||
}
|
||||
return Result;
|
||||
});
|
||||
UtilTable.set_function("DebugStartProfile", [this](const std::string& name) {
|
||||
mProfileStarts[name] = prof::now();
|
||||
});
|
||||
UtilTable.set_function("DebugStopProfile", [this](const std::string& name) {
|
||||
if (!mProfileStarts.contains(name)) {
|
||||
beammp_lua_errorf("DebugStopProfile('{}') failed, because a profile for '{}' wasn't started", name, name);
|
||||
return;
|
||||
}
|
||||
mProfile.add_sample(name, prof::duration(mProfileStarts.at(name), prof::now()));
|
||||
});
|
||||
|
||||
auto HttpTable = StateView.create_named_table("Http");
|
||||
HttpTable.set_function("CreateConnection", [this](const std::string& host, uint16_t port) {
|
||||
@@ -894,7 +1006,7 @@ std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueScript(const TLu
|
||||
return Result;
|
||||
}
|
||||
|
||||
std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args, const std::string& EventName, CallStrategy Strategy) {
|
||||
std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector<TLuaValue>& Args, const std::string& EventName, CallStrategy Strategy) {
|
||||
// TODO: Document all this
|
||||
decltype(mStateFunctionQueue)::iterator Iter = mStateFunctionQueue.end();
|
||||
if (Strategy == CallStrategy::BestEffort) {
|
||||
@@ -916,7 +1028,7 @@ std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCallFrom
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCall(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args) {
|
||||
std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCall(const std::string& FunctionName, const std::vector<TLuaValue>& Args) {
|
||||
auto Result = std::make_shared<TLuaResult>();
|
||||
Result->StateId = mStateId;
|
||||
Result->Function = FunctionName;
|
||||
@@ -984,6 +1096,7 @@ void TLuaEngine::StateThreadData::operator()() {
|
||||
std::chrono::milliseconds(500),
|
||||
[&]() -> bool { return !mStateFunctionQueue.empty(); });
|
||||
if (NotExpired) {
|
||||
auto ProfStart = prof::now();
|
||||
auto TheQueuedFunction = std::move(mStateFunctionQueue.front());
|
||||
mStateFunctionQueue.erase(mStateFunctionQueue.begin());
|
||||
Lock.unlock();
|
||||
@@ -1001,19 +1114,21 @@ void TLuaEngine::StateThreadData::operator()() {
|
||||
continue;
|
||||
}
|
||||
switch (Arg.index()) {
|
||||
case TLuaArgTypes_String:
|
||||
case TLuaType::String:
|
||||
LuaArgs.push_back(sol::make_object(StateView, std::get<std::string>(Arg)));
|
||||
break;
|
||||
case TLuaArgTypes_Int:
|
||||
case TLuaType::Int:
|
||||
LuaArgs.push_back(sol::make_object(StateView, std::get<int>(Arg)));
|
||||
break;
|
||||
case TLuaArgTypes_VariadicArgs:
|
||||
LuaArgs.push_back(sol::make_object(StateView, std::get<sol::variadic_args>(Arg)));
|
||||
case TLuaType::Json: {
|
||||
auto LocalArgs = JsonStringToArray(std::get<JsonString>(Arg));
|
||||
LuaArgs.insert(LuaArgs.end(), LocalArgs.begin(), LocalArgs.end());
|
||||
break;
|
||||
case TLuaArgTypes_Bool:
|
||||
}
|
||||
case TLuaType::Bool:
|
||||
LuaArgs.push_back(sol::make_object(StateView, std::get<bool>(Arg)));
|
||||
break;
|
||||
case TLuaArgTypes_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) {
|
||||
@@ -1042,6 +1157,9 @@ void TLuaEngine::StateThreadData::operator()() {
|
||||
Result->ErrorMessage = BeamMPFnNotFoundError; // special error kind that we can ignore later
|
||||
Result->MarkAsReady();
|
||||
}
|
||||
auto ProfEnd = prof::now();
|
||||
auto ProfDuration = prof::duration(ProfStart, ProfEnd);
|
||||
mProfile.add_sample(FnName, ProfDuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1101,7 +1219,7 @@ void TLuaResult::MarkAsReady() {
|
||||
void TLuaResult::WaitUntilReady() {
|
||||
std::unique_lock readyLock(*this->ReadyMutex);
|
||||
// wait if not ready yet
|
||||
if(!this->Ready)
|
||||
if (!this->Ready)
|
||||
this->ReadyCondition->wait(readyLock);
|
||||
}
|
||||
|
||||
|
||||
@@ -630,6 +630,7 @@ void TNetwork::OnDisconnect(const std::weak_ptr<TClient>& ClientPtr) {
|
||||
}
|
||||
|
||||
int TNetwork::OpenID() {
|
||||
std::unique_lock OpenIDLock(mOpenIDMutex);
|
||||
int ID = 0;
|
||||
bool found;
|
||||
do {
|
||||
|
||||
+6
-3
@@ -65,10 +65,13 @@ void TPPSMonitor::operator()() {
|
||||
V += c->GetCarCount();
|
||||
}
|
||||
// kick on "no ping"
|
||||
if (c->SecondsSinceLastPing() > (20 * 60)) {
|
||||
beammp_debug("client " + std::string("(") + std::to_string(c->GetID()) + ")" + c->GetName() + " timing out: " + std::to_string(c->SecondsSinceLastPing()) + ", pps: " + Application::PPS());
|
||||
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;
|
||||
});
|
||||
|
||||
+24
-1
@@ -30,6 +30,7 @@
|
||||
#include "TResourceManager.h"
|
||||
#include "TServer.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
@@ -40,6 +41,9 @@ USAGE:
|
||||
ARGUMENTS:
|
||||
--help
|
||||
Displays this help and exits.
|
||||
--port=1234
|
||||
Sets the server's listening TCP and
|
||||
UDP port. Overrides ENV and ServerConfig.
|
||||
--config=/path/to/ServerConfig.toml
|
||||
Absolute or relative path to the
|
||||
Server Config file, including the
|
||||
@@ -91,6 +95,7 @@ int BeamMPServerMain(MainArguments Arguments) {
|
||||
Parser.RegisterArgument({ "help" }, ArgsParser::NONE);
|
||||
Parser.RegisterArgument({ "version" }, ArgsParser::NONE);
|
||||
Parser.RegisterArgument({ "config" }, ArgsParser::HAS_VALUE);
|
||||
Parser.RegisterArgument({ "port" }, ArgsParser::HAS_VALUE);
|
||||
Parser.RegisterArgument({ "working-directory" }, ArgsParser::HAS_VALUE);
|
||||
Parser.Parse(Arguments.List);
|
||||
if (!Parser.Verify()) {
|
||||
@@ -124,7 +129,7 @@ int BeamMPServerMain(MainArguments Arguments) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TConfig Config(ConfigPath);
|
||||
|
||||
if (Config.Failed()) {
|
||||
@@ -135,6 +140,24 @@ int BeamMPServerMain(MainArguments Arguments) {
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// override port if provided via arguments
|
||||
if (Parser.FoundArgument({ "port" })) {
|
||||
auto Port = Parser.GetValueOfArgument({ "port" });
|
||||
if (Port.has_value()) {
|
||||
auto P = int(std::strtoul(Port.value().c_str(), nullptr, 10));
|
||||
if (P == 0 || P < 0 || P > UINT16_MAX) {
|
||||
beammp_errorf("Custom port requested via --port is invalid: '{}'", Port.value());
|
||||
return 1;
|
||||
} else {
|
||||
Application::Settings.Port = P;
|
||||
beammp_info("Custom port requested via commandline arguments: " + Port.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Config.PrintDebug();
|
||||
|
||||
Application::InitializeConsole();
|
||||
Application::Console().StartLoggingToFile();
|
||||
|
||||
|
||||
+1
-1
Submodule vcpkg updated: 8397227251...326d8b43e3
Reference in New Issue
Block a user