mirror of
https://github.com/BeamMP/BeamMP-Server.git
synced 2026-06-17 14:12:25 +00:00
Merge branch 'rc-v3.0.2' into rc-v3.1.0
This is a periodic merge to keep 3.1.0 up to date with 3.0.2
This commit is contained in:
@@ -6,10 +6,19 @@
|
|||||||
- ADDED FS.ListFiles and FS.ListDirectories
|
- ADDED FS.ListFiles and FS.ListDirectories
|
||||||
- FIXED issue with client->server events which contain ':'
|
- FIXED issue with client->server events which contain ':'
|
||||||
|
|
||||||
|
# v3.0.2
|
||||||
|
|
||||||
|
- ADDED Periodic update message if a new server is released
|
||||||
|
- CHANGED Default MaxPlayers to 8
|
||||||
|
- FIXED `MP.CreateEventTimer` filling up the queue (see https://wiki.beammp.com/en/Scripting/new-lua-scripting#mpcreateeventtimerevent_name-string-interval_ms-number-strategy-number-since-v302)
|
||||||
|
- FIXED `MP.TriggerClientEvent` not kicking the client if it failed
|
||||||
|
- FIXED Lua result queue handling not checking all results
|
||||||
|
|
||||||
# v3.0.1
|
# v3.0.1
|
||||||
|
|
||||||
- ADDED Backup URLs to UpdateCheck (will fail less often now)
|
- ADDED Backup URLs to UpdateCheck (will fail less often now)
|
||||||
- ADDED console cursor left and right movement (with arrow keys) and working HOME and END key (via github.com/lionkor/commandline)
|
- ADDED console cursor left and right movement (with arrow keys) and working HOME and END key (via github.com/lionkor/commandline)
|
||||||
|
- FIXED infinite snowmen / infinite unicycle spawning bug
|
||||||
- FIXED a bug where, when run with --working-directory, the Server.log would still be in the original directory
|
- FIXED a bug where, when run with --working-directory, the Server.log would still be in the original directory
|
||||||
- FIXED a bug which could cause the plugin reload thread to spin at 100% if the reloaded plugin's didn't terminate
|
- FIXED a bug which could cause the plugin reload thread to spin at 100% if the reloaded plugin's didn't terminate
|
||||||
- FIXED an issue which would cause servers to crash on mod download via SIGPIPE on POSIX
|
- FIXED an issue which would cause servers to crash on mod download via SIGPIPE on POSIX
|
||||||
|
|||||||
+3
-1
@@ -14,6 +14,7 @@ extern TSentry Sentry;
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
#include "Compat.h"
|
#include "Compat.h"
|
||||||
|
|
||||||
@@ -45,7 +46,7 @@ public:
|
|||||||
std::string SSLKeyPath { "./.ssl/HttpServer/key.pem" };
|
std::string SSLKeyPath { "./.ssl/HttpServer/key.pem" };
|
||||||
std::string SSLCertPath { "./.ssl/HttpServer/cert.pem" };
|
std::string SSLCertPath { "./.ssl/HttpServer/cert.pem" };
|
||||||
bool HTTPServerEnabled { false };
|
bool HTTPServerEnabled { false };
|
||||||
int MaxPlayers { 10 };
|
int MaxPlayers { 8 };
|
||||||
bool Private { true };
|
bool Private { true };
|
||||||
int MaxCars { 1 };
|
int MaxCars { 1 };
|
||||||
bool DebugModeEnabled { false };
|
bool DebugModeEnabled { false };
|
||||||
@@ -56,6 +57,7 @@ public:
|
|||||||
bool SendErrorsMessageEnabled { true };
|
bool SendErrorsMessageEnabled { true };
|
||||||
int HTTPServerPort { 8080 };
|
int HTTPServerPort { 8080 };
|
||||||
bool HTTPServerUseSSL { true };
|
bool HTTPServerUseSSL { true };
|
||||||
|
bool HideUpdateMessages { false };
|
||||||
[[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); }
|
[[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+20
-4
@@ -6,6 +6,7 @@
|
|||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
|
#include <list>
|
||||||
#include <lua.hpp>
|
#include <lua.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
@@ -73,6 +74,18 @@ private:
|
|||||||
|
|
||||||
class TLuaEngine : IThreaded {
|
class TLuaEngine : IThreaded {
|
||||||
public:
|
public:
|
||||||
|
enum CallStrategy : int {
|
||||||
|
BestEffort,
|
||||||
|
Precise,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct QueuedFunction {
|
||||||
|
std::string FunctionName;
|
||||||
|
std::shared_ptr<TLuaResult> Result;
|
||||||
|
std::vector<TLuaArgTypes> Args;
|
||||||
|
std::string EventName; // optional, may be empty
|
||||||
|
};
|
||||||
|
|
||||||
TLuaEngine();
|
TLuaEngine();
|
||||||
~TLuaEngine() noexcept {
|
~TLuaEngine() noexcept {
|
||||||
beammp_debug("Lua Engine terminated");
|
beammp_debug("Lua Engine terminated");
|
||||||
@@ -147,7 +160,7 @@ public:
|
|||||||
return Results; //
|
return Results; //
|
||||||
}
|
}
|
||||||
std::set<std::string> GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId);
|
std::set<std::string> GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId);
|
||||||
void CreateEventTimer(const std::string& EventName, TLuaStateId StateId, size_t IntervalMS);
|
void CreateEventTimer(const std::string& EventName, TLuaStateId StateId, size_t IntervalMS, CallStrategy Strategy);
|
||||||
void CancelEventTimers(const std::string& EventName, TLuaStateId StateId);
|
void CancelEventTimers(const std::string& EventName, TLuaStateId StateId);
|
||||||
sol::state_view GetStateForPlugin(const fs::path& PluginPath);
|
sol::state_view GetStateForPlugin(const fs::path& PluginPath);
|
||||||
TLuaStateId GetStateIDForPlugin(const fs::path& PluginPath);
|
TLuaStateId GetStateIDForPlugin(const fs::path& PluginPath);
|
||||||
@@ -176,6 +189,7 @@ private:
|
|||||||
~StateThreadData() noexcept { beammp_debug("\"" + mStateId + "\" destroyed"); }
|
~StateThreadData() noexcept { beammp_debug("\"" + mStateId + "\" destroyed"); }
|
||||||
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueScript(const TLuaChunk& Script);
|
[[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> 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);
|
||||||
void RegisterEvent(const std::string& EventName, const std::string& FunctionName);
|
void RegisterEvent(const std::string& EventName, const std::string& FunctionName);
|
||||||
void AddPath(const fs::path& Path); // to be added to path and cpath
|
void AddPath(const fs::path& Path); // to be added to path and cpath
|
||||||
void operator()() override;
|
void operator()() override;
|
||||||
@@ -207,7 +221,7 @@ private:
|
|||||||
std::thread mThread;
|
std::thread mThread;
|
||||||
std::queue<std::pair<TLuaChunk, std::shared_ptr<TLuaResult>>> mStateExecuteQueue;
|
std::queue<std::pair<TLuaChunk, std::shared_ptr<TLuaResult>>> mStateExecuteQueue;
|
||||||
std::recursive_mutex mStateExecuteQueueMutex;
|
std::recursive_mutex mStateExecuteQueueMutex;
|
||||||
std::queue<std::tuple<std::string, std::shared_ptr<TLuaResult>, std::vector<TLuaArgTypes>>> mStateFunctionQueue;
|
std::vector<QueuedFunction> mStateFunctionQueue;
|
||||||
std::mutex mStateFunctionQueueMutex;
|
std::mutex mStateFunctionQueueMutex;
|
||||||
std::condition_variable mStateFunctionQueueCond;
|
std::condition_variable mStateFunctionQueueCond;
|
||||||
TLuaEngine* mEngine;
|
TLuaEngine* mEngine;
|
||||||
@@ -223,6 +237,7 @@ private:
|
|||||||
std::chrono::high_resolution_clock::time_point LastCompletion {};
|
std::chrono::high_resolution_clock::time_point LastCompletion {};
|
||||||
std::string EventName;
|
std::string EventName;
|
||||||
TLuaStateId StateId;
|
TLuaStateId StateId;
|
||||||
|
CallStrategy Strategy;
|
||||||
bool Expired();
|
bool Expired();
|
||||||
void Reset();
|
void Reset();
|
||||||
};
|
};
|
||||||
@@ -239,8 +254,9 @@ private:
|
|||||||
std::recursive_mutex mLuaEventsMutex;
|
std::recursive_mutex mLuaEventsMutex;
|
||||||
std::vector<TimedEvent> mTimedEvents;
|
std::vector<TimedEvent> mTimedEvents;
|
||||||
std::recursive_mutex mTimedEventsMutex;
|
std::recursive_mutex mTimedEventsMutex;
|
||||||
std::queue<std::shared_ptr<TLuaResult>> mResultsToCheck;
|
std::list<std::shared_ptr<TLuaResult>> mResultsToCheck;
|
||||||
std::recursive_mutex mResultsToCheckMutex;
|
std::mutex mResultsToCheckMutex;
|
||||||
|
std::condition_variable mResultsToCheckCond;
|
||||||
};
|
};
|
||||||
|
|
||||||
// std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaPlugin* Caller, std::shared_ptr<TLuaArg> arg, bool Wait);
|
// std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaPlugin* Caller, std::shared_ptr<TLuaArg> arg, bool Wait);
|
||||||
|
|||||||
+1
-1
@@ -107,7 +107,7 @@ void Application::CheckForUpdates() {
|
|||||||
auto RemoteVersion = Version(VersionStrToInts(Response));
|
auto RemoteVersion = Version(VersionStrToInts(Response));
|
||||||
if (IsOutdated(MyVersion, RemoteVersion)) {
|
if (IsOutdated(MyVersion, RemoteVersion)) {
|
||||||
std::string RealVersionString = RemoteVersion.AsString();
|
std::string RealVersionString = RemoteVersion.AsString();
|
||||||
beammp_warn(std::string(ANSI_YELLOW_BOLD) + "NEW VERSION OUT! There's a new version (v" + RealVersionString + ") of the BeamMP-Server available! For more info visit https://wiki.beammp.com/en/home/server-maintenance#updating-the-server." + std::string(ANSI_RESET));
|
beammp_warn(std::string(ANSI_YELLOW_BOLD) + "NEW VERSION IS OUT! Please update to the new version (v" + RealVersionString + ") 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" + std::string(ANSI_RESET));
|
||||||
} else {
|
} else {
|
||||||
beammp_info("Server up-to-date!");
|
beammp_info("Server up-to-date!");
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -114,7 +114,8 @@ static inline bool InternalTriggerClientEvent(int PlayerID, const std::string& E
|
|||||||
}
|
}
|
||||||
auto c = MaybeClient.value().lock();
|
auto c = MaybeClient.value().lock();
|
||||||
if (!LuaAPI::MP::Engine->Network().Respond(*c, Packet, true)) {
|
if (!LuaAPI::MP::Engine->Network().Respond(*c, Packet, true)) {
|
||||||
beammp_lua_error("Respond failed");
|
beammp_lua_error("Respond failed, dropping client " + std::to_string(PlayerID));
|
||||||
|
Engine->Network().ClientKick(*c, "Disconnected after failing to receive packets");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-10
@@ -18,12 +18,15 @@ static constexpr std::string_view StrDescription = "Description";
|
|||||||
static constexpr std::string_view StrResourceFolder = "ResourceFolder";
|
static constexpr std::string_view StrResourceFolder = "ResourceFolder";
|
||||||
static constexpr std::string_view StrAuthKey = "AuthKey";
|
static constexpr std::string_view StrAuthKey = "AuthKey";
|
||||||
static constexpr std::string_view StrLogChat = "LogChat";
|
static constexpr std::string_view StrLogChat = "LogChat";
|
||||||
|
|
||||||
|
// Misc
|
||||||
static constexpr std::string_view StrSendErrors = "SendErrors";
|
static constexpr std::string_view StrSendErrors = "SendErrors";
|
||||||
static constexpr std::string_view StrSendErrorsMessageEnabled = "SendErrorsShowMessage";
|
static constexpr std::string_view StrSendErrorsMessageEnabled = "SendErrorsShowMessage";
|
||||||
static constexpr std::string_view StrHTTPServerEnabled = "HTTPServerEnabled";
|
static constexpr std::string_view StrHideUpdateMessages = "ImScaredOfUpdates";
|
||||||
static constexpr std::string_view StrHTTPServerUseSSL = "UseSSL";
|
|
||||||
|
|
||||||
// HTTP
|
// HTTP
|
||||||
|
static constexpr std::string_view StrHTTPServerEnabled = "HTTPServerEnabled";
|
||||||
|
static constexpr std::string_view StrHTTPServerUseSSL = "UseSSL";
|
||||||
static constexpr std::string_view StrSSLKeyPath = "SSLKeyPath";
|
static constexpr std::string_view StrSSLKeyPath = "SSLKeyPath";
|
||||||
static constexpr std::string_view StrSSLCertPath = "SSLCertPath";
|
static constexpr std::string_view StrSSLCertPath = "SSLCertPath";
|
||||||
static constexpr std::string_view StrHTTPServerPort = "HTTPServerPort";
|
static constexpr std::string_view StrHTTPServerPort = "HTTPServerPort";
|
||||||
@@ -60,7 +63,6 @@ void SetComment(CommentsT& Comments, const std::string& Comment) {
|
|||||||
*/
|
*/
|
||||||
void TConfig::FlushToFile() {
|
void TConfig::FlushToFile() {
|
||||||
auto data = toml::parse<toml::preserve_comments>(mConfigFileName);
|
auto data = toml::parse<toml::preserve_comments>(mConfigFileName);
|
||||||
data["General"] = toml::table();
|
|
||||||
data["General"][StrAuthKey.data()] = Application::Settings.Key;
|
data["General"][StrAuthKey.data()] = Application::Settings.Key;
|
||||||
SetComment(data["General"][StrAuthKey.data()].comments(), " AuthKey has to be filled out in order to run the server");
|
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.LogChat;
|
||||||
@@ -74,10 +76,14 @@ void TConfig::FlushToFile() {
|
|||||||
data["General"][StrMap.data()] = Application::Settings.MapName;
|
data["General"][StrMap.data()] = Application::Settings.MapName;
|
||||||
data["General"][StrDescription.data()] = Application::Settings.ServerDesc;
|
data["General"][StrDescription.data()] = Application::Settings.ServerDesc;
|
||||||
data["General"][StrResourceFolder.data()] = Application::Settings.Resource;
|
data["General"][StrResourceFolder.data()] = Application::Settings.Resource;
|
||||||
data["General"][StrSendErrors.data()] = Application::Settings.SendErrors;
|
// Misc
|
||||||
SetComment(data["General"][StrSendErrors.data()].comments(), " You can turn on/off the SendErrors message you get on startup here");
|
data["Misc"][StrHideUpdateMessages.data()] = Application::Settings.HideUpdateMessages;
|
||||||
data["General"][StrSendErrorsMessageEnabled.data()] = Application::Settings.SendErrorsMessageEnabled;
|
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.");
|
||||||
SetComment(data["General"][StrSendErrorsMessageEnabled.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"][StrSendErrors.data()] = Application::Settings.SendErrors;
|
||||||
|
SetComment(data["Misc"][StrSendErrors.data()].comments(), " You can turn on/off the SendErrors message you get on startup here");
|
||||||
|
data["Misc"][StrSendErrorsMessageEnabled.data()] = Application::Settings.SendErrorsMessageEnabled;
|
||||||
|
SetComment(data["Misc"][StrSendErrorsMessageEnabled.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`");
|
||||||
|
// HTTP
|
||||||
data["HTTP"][StrSSLKeyPath.data()] = Application::Settings.SSLKeyPath;
|
data["HTTP"][StrSSLKeyPath.data()] = Application::Settings.SSLKeyPath;
|
||||||
data["HTTP"][StrSSLCertPath.data()] = Application::Settings.SSLCertPath;
|
data["HTTP"][StrSSLCertPath.data()] = Application::Settings.SSLCertPath;
|
||||||
data["HTTP"][StrHTTPServerPort.data()] = Application::Settings.HTTPServerPort;
|
data["HTTP"][StrHTTPServerPort.data()] = Application::Settings.HTTPServerPort;
|
||||||
@@ -85,7 +91,7 @@ void TConfig::FlushToFile() {
|
|||||||
SetComment(data["HTTP"][StrHTTPServerUseSSL.data()].comments(), " Recommended to keep enabled. With SSL the server will serve https and requires valid key and cert files");
|
SetComment(data["HTTP"][StrHTTPServerUseSSL.data()].comments(), " Recommended to keep enabled. With SSL the server will serve https and requires valid key and cert files");
|
||||||
data["HTTP"][StrHTTPServerEnabled.data()] = Application::Settings.HTTPServerEnabled;
|
data["HTTP"][StrHTTPServerEnabled.data()] = Application::Settings.HTTPServerEnabled;
|
||||||
SetComment(data["HTTP"][StrHTTPServerEnabled.data()].comments(), " Enables the internal HTTP server");
|
SetComment(data["HTTP"][StrHTTPServerEnabled.data()].comments(), " Enables the internal HTTP server");
|
||||||
std::ofstream Stream(mConfigFileName);
|
std::ofstream Stream(mConfigFileName, std::ios::trunc | std::ios::out);
|
||||||
Stream << data << std::flush;
|
Stream << data << std::flush;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,8 +168,10 @@ void TConfig::ParseFromFile(std::string_view name) {
|
|||||||
TryReadValue(data, "General", StrResourceFolder, Application::Settings.Resource);
|
TryReadValue(data, "General", StrResourceFolder, Application::Settings.Resource);
|
||||||
TryReadValue(data, "General", StrAuthKey, Application::Settings.Key);
|
TryReadValue(data, "General", StrAuthKey, Application::Settings.Key);
|
||||||
TryReadValue(data, "General", StrLogChat, Application::Settings.LogChat);
|
TryReadValue(data, "General", StrLogChat, Application::Settings.LogChat);
|
||||||
TryReadValue(data, "General", StrSendErrors, Application::Settings.SendErrors);
|
// Misc
|
||||||
TryReadValue(data, "General", StrSendErrorsMessageEnabled, Application::Settings.SendErrorsMessageEnabled);
|
TryReadValue(data, "Misc", StrSendErrors, Application::Settings.SendErrors);
|
||||||
|
TryReadValue(data, "Misc", StrHideUpdateMessages, Application::Settings.HideUpdateMessages);
|
||||||
|
TryReadValue(data, "Misc", StrSendErrorsMessageEnabled, Application::Settings.SendErrorsMessageEnabled);
|
||||||
// HTTP
|
// HTTP
|
||||||
TryReadValue(data, "HTTP", StrSSLKeyPath, Application::Settings.SSLKeyPath);
|
TryReadValue(data, "HTTP", StrSSLKeyPath, Application::Settings.SSLKeyPath);
|
||||||
TryReadValue(data, "HTTP", StrSSLCertPath, Application::Settings.SSLCertPath);
|
TryReadValue(data, "HTTP", StrSSLCertPath, Application::Settings.SSLCertPath);
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ void THeartbeatThread::operator()() {
|
|||||||
|
|
||||||
static std::chrono::high_resolution_clock::time_point LastNormalUpdateTime = std::chrono::high_resolution_clock::now();
|
static std::chrono::high_resolution_clock::time_point LastNormalUpdateTime = std::chrono::high_resolution_clock::now();
|
||||||
bool isAuth = false;
|
bool isAuth = false;
|
||||||
|
size_t UpdateReminderCounter = 0;
|
||||||
while (!mShutdown) {
|
while (!mShutdown) {
|
||||||
|
++UpdateReminderCounter;
|
||||||
Body = GenerateCall();
|
Body = GenerateCall();
|
||||||
// a hot-change occurs when a setting has changed, to update the backend of that change.
|
// a hot-change occurs when a setting has changed, to update the backend of that change.
|
||||||
auto Now = std::chrono::high_resolution_clock::now();
|
auto Now = std::chrono::high_resolution_clock::now();
|
||||||
@@ -64,7 +66,7 @@ void THeartbeatThread::operator()() {
|
|||||||
if (Doc.HasParseError() || !Doc.IsObject()) {
|
if (Doc.HasParseError() || !Doc.IsObject()) {
|
||||||
if (!Application::Settings.Private) {
|
if (!Application::Settings.Private) {
|
||||||
beammp_error("Backend response failed to parse as valid json");
|
beammp_error("Backend response failed to parse as valid json");
|
||||||
beammp_debug("Response was: `" + T + "`");
|
beammp_trace("Response was: `" + T + "`");
|
||||||
}
|
}
|
||||||
Sentry.SetContext("JSON Response", { { "reponse", T } });
|
Sentry.SetContext("JSON Response", { { "reponse", T } });
|
||||||
SentryReportError(Url + Target, ResponseCode);
|
SentryReportError(Url + Target, ResponseCode);
|
||||||
@@ -107,6 +109,10 @@ void THeartbeatThread::operator()() {
|
|||||||
beammp_error("Missing/invalid json members in backend response");
|
beammp_error("Missing/invalid json members in backend response");
|
||||||
Sentry.LogError("Missing/invalid json members in backend response", __FILE__, std::to_string(__LINE__));
|
Sentry.LogError("Missing/invalid json members in backend response", __FILE__, std::to_string(__LINE__));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (!Application::Settings.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.Private) {
|
||||||
@@ -126,6 +132,9 @@ void THeartbeatThread::operator()() {
|
|||||||
if (isAuth || Application::Settings.Private) {
|
if (isAuth || Application::Settings.Private) {
|
||||||
Application::SetSubsystemStatus("Heartbeat", Application::Status::Good);
|
Application::SetSubsystemStatus("Heartbeat", Application::Status::Good);
|
||||||
}
|
}
|
||||||
|
if (!Application::Settings.HideUpdateMessages && UpdateReminderCounter % 5) {
|
||||||
|
Application::CheckForUpdates();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+89
-35
@@ -45,7 +45,7 @@ void TLuaEngine::operator()() {
|
|||||||
CollectAndInitPlugins();
|
CollectAndInitPlugins();
|
||||||
// now call all onInit's
|
// now call all onInit's
|
||||||
auto Futures = TriggerEvent("onInit", "");
|
auto Futures = TriggerEvent("onInit", "");
|
||||||
WaitForAll(Futures);
|
WaitForAll(Futures, std::chrono::seconds(5));
|
||||||
for (const auto& Future : Futures) {
|
for (const auto& Future : Futures) {
|
||||||
if (Future->Error && Future->ErrorMessage != BeamMPFnNotFoundError) {
|
if (Future->Error && Future->ErrorMessage != BeamMPFnNotFoundError) {
|
||||||
beammp_lua_error("Calling \"onInit\" on \"" + Future->StateId + "\" failed: " + Future->ErrorMessage);
|
beammp_lua_error("Calling \"onInit\" on \"" + Future->StateId + "\" failed: " + Future->ErrorMessage);
|
||||||
@@ -55,25 +55,21 @@ void TLuaEngine::operator()() {
|
|||||||
auto ResultCheckThread = std::thread([&] {
|
auto ResultCheckThread = std::thread([&] {
|
||||||
RegisterThread("ResultCheckThread");
|
RegisterThread("ResultCheckThread");
|
||||||
while (!mShutdown) {
|
while (!mShutdown) {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
||||||
std::unique_lock Lock(mResultsToCheckMutex);
|
std::unique_lock Lock(mResultsToCheckMutex);
|
||||||
|
mResultsToCheckCond.wait_for(Lock, std::chrono::milliseconds(20));
|
||||||
if (!mResultsToCheck.empty()) {
|
if (!mResultsToCheck.empty()) {
|
||||||
auto Res = mResultsToCheck.front();
|
mResultsToCheck.remove_if([](const std::shared_ptr<TLuaResult>& Ptr) -> bool {
|
||||||
mResultsToCheck.pop();
|
if (Ptr->Ready) {
|
||||||
Lock.unlock();
|
return true;
|
||||||
|
} else if (Ptr->Error) {
|
||||||
if (!Res->Ready) {
|
if (Ptr->ErrorMessage != BeamMPFnNotFoundError) {
|
||||||
Lock.lock();
|
beammp_lua_error(Ptr->Function + ": " + Ptr->ErrorMessage);
|
||||||
mResultsToCheck.push(Res);
|
}
|
||||||
Lock.unlock();
|
return true;
|
||||||
}
|
|
||||||
if (Res->Error) {
|
|
||||||
if (Res->ErrorMessage != BeamMPFnNotFoundError) {
|
|
||||||
beammp_lua_error(Res->Function + ": " + Res->ErrorMessage);
|
|
||||||
}
|
}
|
||||||
}
|
return false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
std::this_thread::yield();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// event loop
|
// event loop
|
||||||
@@ -86,21 +82,31 @@ void TLuaEngine::operator()() {
|
|||||||
std::unique_lock Lock(mTimedEventsMutex);
|
std::unique_lock Lock(mTimedEventsMutex);
|
||||||
for (auto& Timer : mTimedEvents) {
|
for (auto& Timer : mTimedEvents) {
|
||||||
if (Timer.Expired()) {
|
if (Timer.Expired()) {
|
||||||
|
auto LastCompletionBeforeReset = Timer.LastCompletion;
|
||||||
Timer.Reset();
|
Timer.Reset();
|
||||||
auto Handlers = GetEventHandlersForState(Timer.EventName, Timer.StateId);
|
auto Handlers = GetEventHandlersForState(Timer.EventName, Timer.StateId);
|
||||||
std::unique_lock StateLock(mLuaStatesMutex);
|
std::unique_lock StateLock(mLuaStatesMutex);
|
||||||
std::unique_lock Lock2(mResultsToCheckMutex);
|
std::unique_lock Lock2(mResultsToCheckMutex);
|
||||||
for (auto& Handler : Handlers) {
|
for (auto& Handler : Handlers) {
|
||||||
auto Res = mLuaStates[Timer.StateId]->EnqueueFunctionCall(Handler, {});
|
auto Res = mLuaStates[Timer.StateId]->EnqueueFunctionCallFromCustomEvent(Handler, {}, Timer.EventName, Timer.Strategy);
|
||||||
mResultsToCheck.push(Res);
|
if (Res) {
|
||||||
|
mResultsToCheck.push_back(Res);
|
||||||
|
mResultsToCheckCond.notify_one();
|
||||||
|
} else {
|
||||||
|
// "revert" reset
|
||||||
|
Timer.LastCompletion = LastCompletionBeforeReset;
|
||||||
|
// beammp_trace("Reverted reset of \"" + Timer.EventName + "\" timer");
|
||||||
|
// no need to try to enqueue more handlers for this event (they will all fail)
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::chrono::high_resolution_clock::duration Diff;
|
const auto Expected = std::chrono::milliseconds(10);
|
||||||
if ((Diff = std::chrono::high_resolution_clock::now() - Before)
|
if (auto Diff = std::chrono::high_resolution_clock::now() - Before;
|
||||||
< std::chrono::milliseconds(10)) {
|
Diff < Expected) {
|
||||||
std::this_thread::sleep_for(Diff);
|
std::this_thread::sleep_for(Expected - Diff);
|
||||||
} else {
|
} else {
|
||||||
beammp_trace("Event loop cannot keep up! Running " + std::to_string(Diff.count()) + "s behind");
|
beammp_trace("Event loop cannot keep up! Running " + std::to_string(Diff.count()) + "s behind");
|
||||||
}
|
}
|
||||||
@@ -145,7 +151,8 @@ TLuaStateId TLuaEngine::GetStateIDForPlugin(const fs::path& PluginPath) {
|
|||||||
|
|
||||||
void TLuaEngine::AddResultToCheck(const std::shared_ptr<TLuaResult>& Result) {
|
void TLuaEngine::AddResultToCheck(const std::shared_ptr<TLuaResult>& Result) {
|
||||||
std::unique_lock Lock(mResultsToCheckMutex);
|
std::unique_lock Lock(mResultsToCheckMutex);
|
||||||
mResultsToCheck.push(Result);
|
mResultsToCheck.push_back(Result);
|
||||||
|
mResultsToCheckCond.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unordered_map<std::string /*event name */, std::vector<std::string> /* handlers */> TLuaEngine::Debug_GetEventsForState(TLuaStateId StateId) {
|
std::unordered_map<std::string /*event name */, std::vector<std::string> /* handlers */> TLuaEngine::Debug_GetEventsForState(TLuaStateId StateId) {
|
||||||
@@ -215,8 +222,10 @@ void TLuaEngine::WaitForAll(std::vector<std::shared_ptr<TLuaResult>>& Results, c
|
|||||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
ms += 10;
|
ms += 10;
|
||||||
if (Max.has_value() && std::chrono::milliseconds(ms) > Max.value()) {
|
if (Max.has_value() && std::chrono::milliseconds(ms) > Max.value()) {
|
||||||
beammp_trace("'" + Result->Function + "' in '" + Result->StateId + "' did not finish executing in time (took: " + std::to_string(ms) + "ms)");
|
beammp_trace("'" + Result->Function + "' in '" + Result->StateId + "' did not finish executing in time (took: " + std::to_string(ms) + "ms).");
|
||||||
Cancelled = true;
|
Cancelled = true;
|
||||||
|
} else if (ms > 1000 * 60) {
|
||||||
|
beammp_lua_warn("'" + Result->Function + "' in '" + Result->StateId + "' is taking very long. The event it's handling is too important to discard the result of this handler, but may block this event and possibly the whole lua state.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Cancelled) {
|
if (Cancelled) {
|
||||||
@@ -234,7 +243,8 @@ void TLuaEngine::WaitForAll(std::vector<std::shared_ptr<TLuaResult>>& Results, c
|
|||||||
void TLuaEngine::ReportErrors(const std::vector<std::shared_ptr<TLuaResult>>& Results) {
|
void TLuaEngine::ReportErrors(const std::vector<std::shared_ptr<TLuaResult>>& Results) {
|
||||||
std::unique_lock Lock2(mResultsToCheckMutex);
|
std::unique_lock Lock2(mResultsToCheckMutex);
|
||||||
for (const auto& Result : Results) {
|
for (const auto& Result : Results) {
|
||||||
mResultsToCheck.push(Result);
|
mResultsToCheck.push_back(Result);
|
||||||
|
mResultsToCheckCond.notify_one();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -665,11 +675,27 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomi
|
|||||||
return Lua_GetPlayerIdentifiers(ID);
|
return Lua_GetPlayerIdentifiers(ID);
|
||||||
});
|
});
|
||||||
MPTable.set_function("Sleep", &LuaAPI::MP::Sleep);
|
MPTable.set_function("Sleep", &LuaAPI::MP::Sleep);
|
||||||
MPTable.set_function("CreateEventTimer", [&](const std::string& EventName, size_t IntervalMS) {
|
// const std::string& EventName, size_t IntervalMS, int strategy
|
||||||
|
MPTable.set_function("CreateEventTimer", [&](sol::variadic_args Args) {
|
||||||
|
if (Args.size() < 2 || Args.size() > 3) {
|
||||||
|
beammp_lua_error("CreateEventTimer expects 2 or 3 arguments.");
|
||||||
|
}
|
||||||
|
if (Args.get_type(0) != sol::type::string) {
|
||||||
|
beammp_lua_error("CreateEventTimer expects 1st argument to be a string");
|
||||||
|
}
|
||||||
|
if (Args.get_type(1) != sol::type::number) {
|
||||||
|
beammp_lua_error("CreateEventTimer expects 2nd argument to be a number");
|
||||||
|
}
|
||||||
|
if (Args.size() == 3 && Args.get_type(2) != sol::type::number) {
|
||||||
|
beammp_lua_error("CreateEventTimer expects 3rd argument to be a number (MP.CallStrategy)");
|
||||||
|
}
|
||||||
|
auto EventName = Args.get<std::string>(0);
|
||||||
|
auto IntervalMS = Args.get<size_t>(1);
|
||||||
|
CallStrategy Strategy = Args.size() > 2 ? Args.get<CallStrategy>(2) : CallStrategy::BestEffort;
|
||||||
if (IntervalMS < 25) {
|
if (IntervalMS < 25) {
|
||||||
beammp_warn("Timer for \"" + EventName + "\" on \"" + mStateId + "\" is set to trigger at <25ms, which is likely too fast and won't cancel properly.");
|
beammp_warn("Timer for \"" + EventName + "\" on \"" + mStateId + "\" is set to trigger at <25ms, which is likely too fast and won't cancel properly.");
|
||||||
}
|
}
|
||||||
mEngine->CreateEventTimer(EventName, mStateId, IntervalMS);
|
mEngine->CreateEventTimer(EventName, mStateId, IntervalMS, Strategy);
|
||||||
});
|
});
|
||||||
MPTable.set_function("CancelEventTimer", [&](const std::string& EventName) {
|
MPTable.set_function("CancelEventTimer", [&](const std::string& EventName) {
|
||||||
mEngine->CancelEventTimers(EventName, mStateId);
|
mEngine->CancelEventTimers(EventName, mStateId);
|
||||||
@@ -710,6 +736,10 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomi
|
|||||||
"Name", 5,
|
"Name", 5,
|
||||||
"Description", 6);
|
"Description", 6);
|
||||||
|
|
||||||
|
MPTable.create_named("CallStrategy",
|
||||||
|
"BestEffort", CallStrategy::BestEffort,
|
||||||
|
"Precise", CallStrategy::Precise);
|
||||||
|
|
||||||
auto FSTable = StateView.create_named_table("FS");
|
auto FSTable = StateView.create_named_table("FS");
|
||||||
FSTable.set_function("CreateDirectory", &LuaAPI::FS::CreateDirectory);
|
FSTable.set_function("CreateDirectory", &LuaAPI::FS::CreateDirectory);
|
||||||
FSTable.set_function("Exists", &LuaAPI::FS::Exists);
|
FSTable.set_function("Exists", &LuaAPI::FS::Exists);
|
||||||
@@ -738,12 +768,34 @@ std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueScript(const TLu
|
|||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args, const std::string& EventName, CallStrategy Strategy) {
|
||||||
|
// TODO: Document all this
|
||||||
|
decltype(mStateFunctionQueue)::iterator Iter = mStateFunctionQueue.end();
|
||||||
|
if (Strategy == CallStrategy::BestEffort) {
|
||||||
|
Iter = std::find_if(mStateFunctionQueue.begin(), mStateFunctionQueue.end(),
|
||||||
|
[&EventName](const QueuedFunction& Element) {
|
||||||
|
return Element.EventName == EventName;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (Iter == mStateFunctionQueue.end()) {
|
||||||
|
auto Result = std::make_shared<TLuaResult>();
|
||||||
|
Result->StateId = mStateId;
|
||||||
|
Result->Function = FunctionName;
|
||||||
|
std::unique_lock Lock(mStateFunctionQueueMutex);
|
||||||
|
mStateFunctionQueue.push_back({ FunctionName, Result, Args, EventName });
|
||||||
|
mStateFunctionQueueCond.notify_all();
|
||||||
|
return Result;
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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<TLuaArgTypes>& Args) {
|
||||||
auto Result = std::make_shared<TLuaResult>();
|
auto Result = std::make_shared<TLuaResult>();
|
||||||
Result->StateId = mStateId;
|
Result->StateId = mStateId;
|
||||||
Result->Function = FunctionName;
|
Result->Function = FunctionName;
|
||||||
std::unique_lock Lock(mStateFunctionQueueMutex);
|
std::unique_lock Lock(mStateFunctionQueueMutex);
|
||||||
mStateFunctionQueue.push({ FunctionName, Result, Args });
|
mStateFunctionQueue.push_back({ FunctionName, Result, Args, "" });
|
||||||
mStateFunctionQueueCond.notify_all();
|
mStateFunctionQueueCond.notify_all();
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
@@ -806,12 +858,13 @@ void TLuaEngine::StateThreadData::operator()() {
|
|||||||
std::chrono::milliseconds(500),
|
std::chrono::milliseconds(500),
|
||||||
[&]() -> bool { return !mStateFunctionQueue.empty(); });
|
[&]() -> bool { return !mStateFunctionQueue.empty(); });
|
||||||
if (NotExpired) {
|
if (NotExpired) {
|
||||||
auto FnNameResultPair = std::move(mStateFunctionQueue.front());
|
auto TheQueuedFunction = std::move(mStateFunctionQueue.front());
|
||||||
mStateFunctionQueue.pop();
|
mStateFunctionQueue.erase(mStateFunctionQueue.begin());
|
||||||
Lock.unlock();
|
Lock.unlock();
|
||||||
auto& FnName = std::get<0>(FnNameResultPair);
|
auto& FnName = TheQueuedFunction.FunctionName;
|
||||||
auto& Result = std::get<1>(FnNameResultPair);
|
auto& Result = TheQueuedFunction.Result;
|
||||||
auto Args = std::get<2>(FnNameResultPair);
|
auto Args = TheQueuedFunction.Args;
|
||||||
|
// TODO: Use TheQueuedFunction.EventName for errors, warnings, etc
|
||||||
Result->StateId = mStateId;
|
Result->StateId = mStateId;
|
||||||
sol::state_view StateView(mState);
|
sol::state_view StateView(mState);
|
||||||
auto Fn = StateView[FnName];
|
auto Fn = StateView[FnName];
|
||||||
@@ -869,13 +922,14 @@ std::queue<std::tuple<std::string, std::shared_ptr<TLuaResult>, std::vector<TLua
|
|||||||
return mStateFunctionQueue;
|
return mStateFunctionQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TLuaEngine::CreateEventTimer(const std::string& EventName, TLuaStateId StateId, size_t IntervalMS) {
|
void TLuaEngine::CreateEventTimer(const std::string& EventName, TLuaStateId StateId, size_t IntervalMS, CallStrategy Strategy) {
|
||||||
std::unique_lock Lock(mTimedEventsMutex);
|
std::unique_lock Lock(mTimedEventsMutex);
|
||||||
TimedEvent Event {
|
TimedEvent Event {
|
||||||
std::chrono::high_resolution_clock::duration { std::chrono::milliseconds(IntervalMS) },
|
std::chrono::high_resolution_clock::duration { std::chrono::milliseconds(IntervalMS) },
|
||||||
std::chrono::high_resolution_clock::now(),
|
std::chrono::high_resolution_clock::now(),
|
||||||
EventName,
|
EventName,
|
||||||
StateId
|
StateId,
|
||||||
|
Strategy
|
||||||
};
|
};
|
||||||
mTimedEvents.push_back(std::move(Event));
|
mTimedEvents.push_back(std::move(Event));
|
||||||
beammp_trace("created event timer for \"" + EventName + "\" on \"" + StateId + "\" with " + std::to_string(IntervalMS) + "ms interval");
|
beammp_trace("created event timer for \"" + EventName + "\" on \"" + StateId + "\" with " + std::to_string(IntervalMS) + "ms interval");
|
||||||
|
|||||||
@@ -522,6 +522,7 @@ void TNetwork::Looper(const std::weak_ptr<TClient>& c) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TNetwork::TCPClient(const std::weak_ptr<TClient>& c) {
|
void TNetwork::TCPClient(const std::weak_ptr<TClient>& c) {
|
||||||
// TODO: the c.expired() might cause issues here, remove if you end up here with your debugger
|
// TODO: the c.expired() might cause issues here, remove if you end up here with your debugger
|
||||||
if (c.expired() || c.lock()->GetTCPSock() == -1) {
|
if (c.expired() || c.lock()->GetTCPSock() == -1) {
|
||||||
|
|||||||
+1
-1
@@ -133,7 +133,7 @@ int BeamMPServerMain(MainArguments Arguments) {
|
|||||||
});
|
});
|
||||||
Application::RegisterShutdownHandler([] {
|
Application::RegisterShutdownHandler([] {
|
||||||
auto Futures = LuaAPI::MP::Engine->TriggerEvent("onShutdown", "");
|
auto Futures = LuaAPI::MP::Engine->TriggerEvent("onShutdown", "");
|
||||||
TLuaEngine::WaitForAll(Futures);
|
TLuaEngine::WaitForAll(Futures, std::chrono::seconds(5));
|
||||||
});
|
});
|
||||||
|
|
||||||
TServer Server(Arguments.List);
|
TServer Server(Arguments.List);
|
||||||
|
|||||||
Reference in New Issue
Block a user