From fd12ee672d76d690e96dabb3b7e97612f557b966 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 26 Jan 2022 20:33:12 +0100 Subject: [PATCH 01/51] Add various debug functions --- deps/cpp-httplib | 2 +- include/TConsole.h | 1 + include/TLuaEngine.h | 15 +++++++++-- src/TConsole.cpp | 64 +++++++++++++++++++++++++++++++++++++++----- src/TLuaEngine.cpp | 54 +++++++++++++++++++++++++++++++++++++ 5 files changed, 127 insertions(+), 9 deletions(-) diff --git a/deps/cpp-httplib b/deps/cpp-httplib index 301faa0..b324921 160000 --- a/deps/cpp-httplib +++ b/deps/cpp-httplib @@ -1 +1 @@ -Subproject commit 301faa074c4a0fa1dbe470dfb4f77912caa1c57f +Subproject commit b324921c1aeff2976544128e4bb2a0979a4aa595 diff --git a/include/TConsole.h b/include/TConsole.h index c393842..6edcbe2 100644 --- a/include/TConsole.h +++ b/include/TConsole.h @@ -21,6 +21,7 @@ private: void RunAsCommand(const std::string& cmd, bool IgnoreNotACommand = false); void ChangeToLuaConsole(const std::string& LuaStateId); void ChangeToRegularConsole(); + void HandleLuaInternalCommand(const std::string& cmd); void Command_Lua(const std::string& cmd); void Command_Help(const std::string& cmd); diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index 1673c61..897a653 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -32,8 +32,8 @@ static constexpr size_t TLuaArgTypes_Bool = 3; class TLuaPlugin; struct TLuaResult { - std::atomic_bool Ready; - std::atomic_bool Error; + bool Ready; + bool Error; std::string ErrorMessage; sol::object Result { sol::lua_nil }; TLuaStateId StateId; @@ -89,6 +89,7 @@ public: std::unique_lock Lock(mResultsToCheckMutex); return mResultsToCheck.size(); } + size_t GetLuaStateCount() { std::unique_lock Lock(mLuaStatesMutex); return mLuaStates.size(); @@ -152,6 +153,12 @@ public: static constexpr const char* BeamMPFnNotFoundError = "BEAMMP_FN_NOT_FOUND"; + // Debugging functions (slow) + std::unordered_map /* handlers */> Debug_GetEventsForState(TLuaStateId StateId); + std::queue>> Debug_GetStateExecuteQueueForState(TLuaStateId StateId); + std::queue, std::vector>> Debug_GetStateFunctionQueueForState(TLuaStateId StateId); + std::vector Debug_GetResultsToCheckForState(TLuaStateId StateId); + private: void CollectAndInitPlugins(); void InitializePlugin(const fs::path& Folder, const TLuaPluginConfig& Config); @@ -170,6 +177,10 @@ private: void operator()() override; sol::state_view State() { return sol::state_view(mState); } + // Debug functions, slow + std::queue>> Debug_GetStateExecuteQueue(); + std::queue, std::vector>> Debug_GetStateFunctionQueue(); + private: sol::table Lua_TriggerGlobalEvent(const std::string& EventName, sol::variadic_args EventArgs); sol::table Lua_TriggerLocalEvent(const std::string& EventName, sol::variadic_args EventArgs); diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 79df8d2..83d73cf 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -107,10 +107,10 @@ void TConsole::ChangeToLuaConsole(const std::string& LuaStateId) { mStateId = LuaStateId; mIsLuaConsole = true; if (mStateId != mDefaultStateId) { - Application::Console().WriteRaw("Entered Lua console for state '" + mStateId + "'. To exit, type `exit()`"); + Application::Console().WriteRaw("Attached to Lua state '" + mStateId + "'. For help, type `:help`. To detach, type `:detach`"); mCommandline.set_prompt("lua @" + LuaStateId + "> "); } else { - Application::Console().WriteRaw("Entered Lua console. To exit, type `exit()`"); + Application::Console().WriteRaw("Attached to Lua. For help, type `:help`. To detach, type `:detach`"); mCommandline.set_prompt("lua> "); } mCachedRegularHistory = mCommandline.history(); @@ -122,9 +122,9 @@ void TConsole::ChangeToRegularConsole() { if (mIsLuaConsole) { mIsLuaConsole = false; if (mStateId != mDefaultStateId) { - Application::Console().WriteRaw("Left Lua console for state '" + mStateId + "'."); + Application::Console().WriteRaw("Detached from Lua state '" + mStateId + "'."); } else { - Application::Console().WriteRaw("Left Lua console."); + Application::Console().WriteRaw("Detached from Lua."); } mCachedLuaHistory = mCommandline.history(); mCommandline.set_history(mCachedRegularHistory); @@ -364,6 +364,58 @@ void TConsole::RunAsCommand(const std::string& cmd, bool IgnoreNotACommand) { } } +void TConsole::HandleLuaInternalCommand(const std::string& cmd) { + if (cmd == "detach") { + ChangeToRegularConsole(); + } else if (cmd == "queued") { + auto QueuedFunctions = LuaAPI::MP::Engine->Debug_GetStateFunctionQueueForState(mStateId); + Application::Console().WriteRaw("Pending functions in State '" + mStateId + "'"); + std::unordered_map FunctionsCount; + std::vector FunctionsInOrder; + while (!QueuedFunctions.empty()) { + auto Tuple = QueuedFunctions.front(); + QueuedFunctions.pop(); + FunctionsInOrder.push_back(std::get<0>(Tuple)); + FunctionsCount[std::get<0>(Tuple)] += 1; + } + std::set Uniques; + for (const auto& Function : FunctionsInOrder) { + if (Uniques.count(Function) == 0) { + Uniques.insert(Function); + if (FunctionsCount.at(Function) > 1) { + Application::Console().WriteRaw(" " + Function + " (" + std::to_string(FunctionsCount.at(Function)) + "x)"); + } else { + Application::Console().WriteRaw(" " + Function); + } + } + } + Application::Console().WriteRaw("Executed functions waiting to be checked in State '" + mStateId + "'"); + for (const auto& Function : LuaAPI::MP::Engine->Debug_GetResultsToCheckForState(mStateId)) { + Application::Console().WriteRaw(" '" + Function.Function + "' (Ready? " + (Function.Ready ? "Yes" : "No") + ", Error? " + (Function.Error ? "Yes: '" + Function.ErrorMessage + "'" : "No") + ")"); + } + } else if (cmd == "events") { + auto Events = LuaAPI::MP::Engine->Debug_GetEventsForState(mStateId); + Application::Console().WriteRaw("Registered Events + Handlers for State '" + mStateId + "'"); + for (const auto& EventHandlerPair : Events) { + Application::Console().WriteRaw(" Event '" + EventHandlerPair.first + "'"); + for (const auto& Handler : EventHandlerPair.second) { + Application::Console().WriteRaw(" " + Handler); + } + } + } else if (cmd == "help") { + Application::Console().WriteRaw(R"(BeamMP Lua Debugger + All commands must be prefixed with a `:`. Non-prefixed commands are interpreted as Lua. + +Commands + :detach detaches (exits) from this Lua console + :help displays this help + :events shows a list of currently registered events + :queued shows a list of all pending and queued functions)"); + } else { + beammp_error("internal command '" + cmd + "' is not known"); + } +} + TConsole::TConsole() { mCommandline.enable_history(); mCommandline.set_history_limit(20); @@ -381,8 +433,8 @@ TConsole::TConsole() { if (mIsLuaConsole) { if (!mLuaEngine) { beammp_info("Lua not started yet, please try again in a second"); - } else if (cmd == "exit()") { - ChangeToRegularConsole(); + } else if (!cmd.empty() && cmd.at(0) == ':') { + HandleLuaInternalCommand(cmd.substr(1)); } else { auto Future = mLuaEngine->EnqueueScript(mStateId, { std::make_shared(cmd), "", "" }); while (!Future->Ready) { diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 89742e2..a81a1b0 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -149,6 +149,50 @@ TLuaStateId TLuaEngine::GetStateIDForPlugin(const fs::path& PluginPath) { return ""; } +std::unordered_map /* handlers */> TLuaEngine::Debug_GetEventsForState(TLuaStateId StateId) { + std::unordered_map> Result; + std::unique_lock Lock(mLuaEventsMutex); + for (const auto& EventNameToEventMap : mLuaEvents) { + for (const auto& IdSetOfHandlersPair : EventNameToEventMap.second) { + if (IdSetOfHandlersPair.first == StateId) { + for (const auto& Handler : IdSetOfHandlersPair.second) { + Result[EventNameToEventMap.first].push_back(Handler); + } + } + } + } + return Result; +} + +std::queue>> TLuaEngine::Debug_GetStateExecuteQueueForState(TLuaStateId StateId) { + std::queue>> Result; + std::unique_lock Lock(mLuaStatesMutex); + Result = mLuaStates.at(StateId)->Debug_GetStateExecuteQueue(); + return Result; +} + +std::queue, std::vector>> TLuaEngine::Debug_GetStateFunctionQueueForState(TLuaStateId StateId) { + std::queue, std::vector>> Result; + std::unique_lock Lock(mLuaStatesMutex); + Result = mLuaStates.at(StateId)->Debug_GetStateFunctionQueue(); + return Result; +} + +std::vector TLuaEngine::Debug_GetResultsToCheckForState(TLuaStateId StateId) { + std::unique_lock Lock(mResultsToCheckMutex); + auto ResultsToCheckCopy = mResultsToCheck; + Lock.unlock(); + std::vector Result; + while (!ResultsToCheckCopy.empty()) { + auto ResultToCheck = std::move(ResultsToCheckCopy.front()); + ResultsToCheckCopy.pop(); + if (ResultToCheck->StateId == StateId) { + Result.push_back(*ResultToCheck); + } + } + return Result; +} + void TLuaEngine::WaitForAll(std::vector>& Results, const std::optional& Max) { for (const auto& Result : Results) { bool Cancelled = false; @@ -671,6 +715,16 @@ void TLuaEngine::StateThreadData::operator()() { } } +std::queue>> TLuaEngine::StateThreadData::Debug_GetStateExecuteQueue() { + std::unique_lock Lock(mStateExecuteQueueMutex); + return mStateExecuteQueue; +} + +std::queue, std::vector>> TLuaEngine::StateThreadData::Debug_GetStateFunctionQueue() { + std::unique_lock Lock(mStateFunctionQueueMutex); + return mStateFunctionQueue; +} + void TLuaEngine::CreateEventTimer(const std::string& EventName, TLuaStateId StateId, size_t IntervalMS) { std::unique_lock Lock(mTimedEventsMutex); TimedEvent Event { From 9f892af9976c3324996c90ab1e5e422b9947482c Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 20 Jan 2022 15:46:13 +0100 Subject: [PATCH 02/51] start fixing backend heartbeat --- CMakeLists.txt | 2 ++ include/Common.h | 11 ++++++++--- include/Http.h | 2 +- src/Common.cpp | 2 +- src/Http.cpp | 5 ++++- src/THeartbeatThread.cpp | 34 +++++++++++++--------------------- 6 files changed, 29 insertions(+), 27 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a8f37ca..4f818d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,8 @@ project(BeamMP-Server HOMEPAGE_URL https://beammp.com LANGUAGES CXX C) +set(HTTPLIB_REQUIRE_OPENSSL ON) + include_directories("${PROJECT_SOURCE_DIR}/deps/asio/asio/include") include_directories("${PROJECT_SOURCE_DIR}/deps/rapidjson/include") include_directories("${PROJECT_SOURCE_DIR}/deps/websocketpp") diff --git a/include/Common.h b/include/Common.h index c61f98b..4b8a1e0 100644 --- a/include/Common.h +++ b/include/Common.h @@ -74,10 +74,15 @@ public: static TSettings Settings; + static std::vector GetBackendUrlsInOrder() { + return { + "backend.beammp.com", + "backup1.beammp.com", + "backup2.beammp.com" + }; + } + static std::string GetBackendUrlForAuth() { return "auth.beammp.com"; } - static std::string GetBackendHostname() { return "backend.beammp.com"; } - static std::string GetBackup1Hostname() { return "backup1.beammp.com"; } - static std::string GetBackup2Hostname() { return "backup2.beammp.com"; } static std::string GetBackendUrlForSocketIO() { return "https://backend.beammp.com"; } static void CheckForUpdates(); static std::array VersionStrToInts(const std::string& str); diff --git a/include/Http.h b/include/Http.h index 62ded67..3ff2cd5 100644 --- a/include/Http.h +++ b/include/Http.h @@ -25,7 +25,7 @@ constexpr size_t RSA_DEFAULT_KEYLENGTH { 2048 }; namespace Http { std::string GET(const std::string& host, int port, const std::string& target, unsigned int* status = nullptr); -std::string POST(const std::string& host, int port, const std::string& target, const std::string& body, const std::string& ContentType, unsigned int* status = nullptr); +std::string POST(const std::string& host, int port, const std::string& target, const std::string& body, const std::string& ContentType, unsigned int* status = nullptr, const httplib::Headers& headers = {}); namespace Status { std::string ToString(int code); } diff --git a/src/Common.cpp b/src/Common.cpp index 4ed5814..a6ef230 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -99,7 +99,7 @@ void Application::CheckForUpdates() { Application::SetSubsystemStatus("UpdateCheck", Application::Status::Starting); // checks current version against latest version std::regex VersionRegex { R"(\d+\.\d+\.\d+\n*)" }; - auto Response = Http::GET(GetBackendHostname(), 443, "/v/s"); + auto Response = Http::GET(GetBackendUrlsInOrder().at(0), 443, "/v/s"); bool Matches = std::regex_match(Response, VersionRegex); if (Matches) { auto MyVersion = ServerVersion(); diff --git a/src/Http.cpp b/src/Http.cpp index 11bea18..cc91e04 100644 --- a/src/Http.cpp +++ b/src/Http.cpp @@ -4,6 +4,7 @@ #include "Common.h" #include "CustomAssert.h" #include "LuaAPI.h" +#include "httplib.h" #include #include @@ -33,8 +34,9 @@ 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) { +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) { httplib::SSLClient client(host, port); + beammp_assert(client.is_valid()); client.enable_server_certificate_verification(false); client.set_address_family(AF_INET); auto res = client.Post(target.c_str(), body.c_str(), body.size(), ContentType.c_str()); @@ -44,6 +46,7 @@ std::string Http::POST(const std::string& host, int port, const std::string& tar } return res->body; } else { + beammp_debug("POST failed: " + httplib::to_string(res.error())); return Http::ErrorString; } } diff --git a/src/THeartbeatThread.cpp b/src/THeartbeatThread.cpp index 27ef253..4311cf5 100644 --- a/src/THeartbeatThread.cpp +++ b/src/THeartbeatThread.cpp @@ -54,34 +54,26 @@ void THeartbeatThread::operator()() { auto Target = "/heartbeat"; unsigned int ResponseCode = 0; - T = Http::POST(Application::GetBackendHostname(), 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode); - if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) { - beammp_trace("got " + T + " from backend"); - Application::SetSubsystemStatus("Heartbeat", Application::Status::Bad); - SentryReportError(Application::GetBackendHostname() + Target, ResponseCode); - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - T = Http::POST(Application::GetBackup1Hostname(), 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode); + bool Ok = true; + json::Document Doc; + for (const auto& Hostname : Application::GetBackendUrlsInOrder()) { + T = Http::POST(Hostname, 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode, { { "api-v", "2" } }); if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) { - SentryReportError(Application::GetBackup1Hostname() + Target, ResponseCode); + beammp_trace("heartbeat to " + Hostname + " returned: " + T); Application::SetSubsystemStatus("Heartbeat", Application::Status::Bad); - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - T = Http::POST(Application::GetBackup2Hostname(), 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode); - if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) { - beammp_warn("Backend system refused server! Server will not show in the public server list."); - Application::SetSubsystemStatus("Heartbeat", Application::Status::Bad); - isAuth = false; - SentryReportError(Application::GetBackup2Hostname() + Target, ResponseCode); - } else { - Application::SetSubsystemStatus("Heartbeat", Application::Status::Good); - } + isAuth = false; + SentryReportError(Hostname + Target, ResponseCode); + Ok = false; } else { Application::SetSubsystemStatus("Heartbeat", Application::Status::Good); + Ok = true; + break; } - } else { - Application::SetSubsystemStatus("Heartbeat", Application::Status::Good); } - + if (!Ok) { + beammp_warn("Backend system refused server! Server will not show in the public server list."); + } if (!isAuth) { if (T == "2000") { beammp_info(("Authenticated!")); From 7c1fb126254a4634a36b1e0d4bae03fc6f5c0fbc Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 20 Jan 2022 16:09:08 +0100 Subject: [PATCH 03/51] add api-v header to heartbeat post --- src/Http.cpp | 3 +- src/THeartbeatThread.cpp | 68 +++++++++++++++++++++++++++++++--------- 2 files changed, 55 insertions(+), 16 deletions(-) diff --git a/src/Http.cpp b/src/Http.cpp index cc91e04..467fd3c 100644 --- a/src/Http.cpp +++ b/src/Http.cpp @@ -36,10 +36,11 @@ 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) { httplib::SSLClient client(host, port); + client.set_read_timeout(std::chrono::seconds(10)); beammp_assert(client.is_valid()); client.enable_server_certificate_verification(false); client.set_address_family(AF_INET); - auto res = client.Post(target.c_str(), body.c_str(), body.size(), ContentType.c_str()); + auto res = client.Post(target.c_str(), headers, body.c_str(), body.size(), ContentType.c_str()); if (res) { if (status) { *status = res->status; diff --git a/src/THeartbeatThread.cpp b/src/THeartbeatThread.cpp index 4311cf5..f5be47b 100644 --- a/src/THeartbeatThread.cpp +++ b/src/THeartbeatThread.cpp @@ -55,32 +55,70 @@ void THeartbeatThread::operator()() { auto Target = "/heartbeat"; unsigned int ResponseCode = 0; - bool Ok = true; json::Document Doc; - for (const auto& Hostname : Application::GetBackendUrlsInOrder()) { - T = Http::POST(Hostname, 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode, { { "api-v", "2" } }); - if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) { - beammp_trace("heartbeat to " + Hostname + " returned: " + T); - Application::SetSubsystemStatus("Heartbeat", Application::Status::Bad); - isAuth = false; - SentryReportError(Hostname + Target, ResponseCode); - Ok = false; + bool Ok = false; + for (const auto& Url : Application::GetBackendUrlsInOrder()) { + T = Http::POST(Url, 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode, { { "api-v", "2" } }); + beammp_trace(T); + Doc.Parse(T.data(), T.size()); + if (Doc.HasParseError() || !Doc.IsObject()) { + beammp_error("Backend response failed to parse as valid json"); + beammp_debug("Response was: `" + T + "`"); + Sentry.SetContext("JSON Response", { { "reponse", T } }); + SentryReportError(Url + Target, ResponseCode); + } else if (ResponseCode != 200) { + SentryReportError(Url + Target, ResponseCode); } else { - Application::SetSubsystemStatus("Heartbeat", Application::Status::Good); + // all ok Ok = true; break; } + std::this_thread::sleep_for(std::chrono::milliseconds(500)); } - if (!Ok) { - beammp_warn("Backend system refused server! Server will not show in the public server list."); + std::string Status {}; + std::string Code {}; + std::string Message {}; + const auto StatusKey = "status"; + const auto CodeKey = "code"; + const auto MessageKey = "msg"; + + if (Ok) { + if (Doc.HasMember(StatusKey) && Doc[StatusKey].IsString()) { + Status = Doc[StatusKey].GetString(); + } else { + Sentry.SetContext("JSON Response", { { StatusKey, "invalid string / missing" } }); + Ok = false; + } + if (Doc.HasMember(CodeKey) && Doc[CodeKey].IsString()) { + Code = Doc[CodeKey].GetString(); + } else { + Sentry.SetContext("JSON Response", { { CodeKey, "invalid string / missing" } }); + Ok = false; + } + if (Doc.HasMember(MessageKey) && Doc[MessageKey].IsString()) { + Message = Doc[MessageKey].GetString(); + } else { + Sentry.SetContext("JSON Response", { { MessageKey, "invalid string / missing" } }); + Ok = false; + } + if (!Ok) { + beammp_error("Missing/invalid json members in backend response"); + Sentry.LogError("Missing/invalid json members in backend response", __FILE__, std::to_string(__LINE__)); + } } - if (!isAuth) { - if (T == "2000") { + + if (Ok && !isAuth) { + if (Status == "2000") { beammp_info(("Authenticated!")); isAuth = true; - } else if (T == "200") { + } else if (Status == "200") { beammp_info(("Resumed authenticated session!")); isAuth = true; + } else { + if (Message.empty()) { + Message = "Backend didn't provide a reason"; + } + beammp_error("Backend REFUSED the auth key. " + Message); } } } From 81780294f874f4b3a1f9d48459036cde9142a8c2 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 20 Jan 2022 21:31:00 +0100 Subject: [PATCH 04/51] advance to 3.0.1 --- include/Common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/Common.h b/include/Common.h index 4b8a1e0..2cf15b9 100644 --- a/include/Common.h +++ b/include/Common.h @@ -119,7 +119,7 @@ private: static inline std::mutex mShutdownHandlersMutex {}; static inline std::deque mShutdownHandlers {}; - static inline Version mVersion { 3, 0, 0 }; + static inline Version mVersion { 3, 0, 1 }; }; std::string ThreadName(bool DebugModeOverride = false); From 86b6aed35019654db3bd816c2756ac54121a48a4 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Fri, 21 Jan 2022 14:26:42 +0100 Subject: [PATCH 05/51] UpdateCheck: Try all URLs --- src/Common.cpp | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/Common.cpp b/src/Common.cpp index a6ef230..a113f74 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -99,25 +99,31 @@ void Application::CheckForUpdates() { Application::SetSubsystemStatus("UpdateCheck", Application::Status::Starting); // checks current version against latest version std::regex VersionRegex { R"(\d+\.\d+\.\d+\n*)" }; - auto Response = Http::GET(GetBackendUrlsInOrder().at(0), 443, "/v/s"); - bool Matches = std::regex_match(Response, VersionRegex); - if (Matches) { - auto MyVersion = ServerVersion(); - auto RemoteVersion = Version(VersionStrToInts(Response)); - if (IsOutdated(MyVersion, RemoteVersion)) { - 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)); + for (const auto& url : GetBackendUrlsInOrder()) { + auto Response = Http::GET(GetBackendUrlsInOrder().at(0), 443, "/v/s"); + bool Matches = std::regex_match(Response, VersionRegex); + if (Matches) { + auto MyVersion = ServerVersion(); + auto RemoteVersion = Version(VersionStrToInts(Response)); + if (IsOutdated(MyVersion, RemoteVersion)) { + 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)); + } else { + beammp_info("Server up-to-date!"); + } + Application::SetSubsystemStatus("UpdateCheck", Application::Status::Good); + break; } else { - beammp_info("Server up-to-date!"); + beammp_debug("Failed to fetch version from: " + url); + beammp_trace("got " + Response); + auto Lock = Sentry.CreateExclusiveContext(); + Sentry.SetContext("get-response", { { "response", Response } }); + Sentry.LogError("failed to get server version", _file_basename, _line); + Application::SetSubsystemStatus("UpdateCheck", Application::Status::Bad); } - Application::SetSubsystemStatus("UpdateCheck", Application::Status::Good); - } else { - beammp_warn("Unable to fetch version from backend."); - beammp_trace("got " + Response); - auto Lock = Sentry.CreateExclusiveContext(); - Sentry.SetContext("get-response", { { "response", Response } }); - Sentry.LogError("failed to get server version", _file_basename, _line); - Application::SetSubsystemStatus("UpdateCheck", Application::Status::Bad); + } + if (Application::GetSubsystemStatuses().at("UpdateCheck") == Application::Status::Bad) { + beammp_warn("Unable to fetch version info from backend."); } } From 0f5c476d09c663fa0c58fb4257f06ea9449608bf Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 3 Feb 2022 18:30:00 +0100 Subject: [PATCH 06/51] update libraries --- deps/commandline | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/commandline b/deps/commandline index 3d11606..0d3e107 160000 --- a/deps/commandline +++ b/deps/commandline @@ -1 +1 @@ -Subproject commit 3d11606d02b449b8afd40a7132160d2392043eb3 +Subproject commit 0d3e1073c1005270dfad851c1f8c59f4ce29d8c1 From 69656f95db9f2791d15c883d143881d84dd0a1e6 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 3 Feb 2022 18:30:42 +0100 Subject: [PATCH 07/51] Move commandline initialization after cwd setting This fixes an issue where the log file is written to the original directory, even if --working-directory=path was used. This can obviously be pretty bad. --- src/main.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 59764f2..a1c9354 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -77,11 +77,6 @@ int main(int argc, char** argv) { int BeamMPServerMain(MainArguments Arguments) { setlocale(LC_ALL, "C"); - Application::InitializeConsole(); - Application::SetSubsystemStatus("Main", Application::Status::Starting); - - SetupSignalHandlers(); - ArgsParser Parser; Parser.RegisterArgument({ "help" }, ArgsParser::NONE); Parser.RegisterArgument({ "version" }, ArgsParser::NONE); @@ -121,6 +116,11 @@ int BeamMPServerMain(MainArguments Arguments) { } } } + + Application::InitializeConsole(); + Application::SetSubsystemStatus("Main", Application::Status::Starting); + + SetupSignalHandlers(); bool Shutdown = false; Application::RegisterShutdownHandler([&Shutdown] { From 7b458e3e275b73eb0c9c0598ab990bd6094c8980 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 3 Feb 2022 18:57:52 +0100 Subject: [PATCH 08/51] Use yield() where possible Replaced calls of this_thread::sleep_* with this_thread::yield(), which yields the thread to the OS' scheduler. --- include/TLuaEngine.h | 1 + src/TConsole.cpp | 2 +- src/TLuaEngine.cpp | 29 +++++++++-------------------- src/TPPSMonitor.cpp | 4 ++-- 4 files changed, 13 insertions(+), 23 deletions(-) diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index 897a653..720d8ea 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -150,6 +150,7 @@ public: void CancelEventTimers(const std::string& EventName, TLuaStateId StateId); sol::state_view GetStateForPlugin(const fs::path& PluginPath); TLuaStateId GetStateIDForPlugin(const fs::path& PluginPath); + void AddResultToCheck(const std::shared_ptr& Result); static constexpr const char* BeamMPFnNotFoundError = "BEAMMP_FN_NOT_FOUND"; diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 83d73cf..3e41345 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -438,7 +438,7 @@ TConsole::TConsole() { } else { auto Future = mLuaEngine->EnqueueScript(mStateId, { std::make_shared(cmd), "", "" }); while (!Future->Ready) { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); // TODO: Add a timeout + std::this_thread::yield(); // TODO: Add a timeout } if (Future->Error) { beammp_lua_error(Future->ErrorMessage); diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index a81a1b0..a0536be 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -54,25 +54,17 @@ void TLuaEngine::operator()() { auto ResultCheckThread = std::thread([&] { RegisterThread("ResultCheckThread"); while (!mShutdown) { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::unique_lock Lock(mResultsToCheckMutex); if (!mResultsToCheck.empty()) { auto Res = mResultsToCheck.front(); mResultsToCheck.pop(); Lock.unlock(); - size_t Waited = 0; - while (!Res->Ready) { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - Waited++; - if (Waited > 250) { - // FIXME: This should *eventually* timeout. - // beammp_lua_error(Res->Function + " in " + Res->StateId + " took >1s to respond, not printing possible errors"); - Lock.lock(); - mResultsToCheck.push(Res); - Lock.unlock(); - break; - } + if (!Res->Ready) { + Lock.lock(); + mResultsToCheck.push(Res); + Lock.unlock(); } if (Res->Error) { if (Res->ErrorMessage != BeamMPFnNotFoundError) { @@ -80,13 +72,14 @@ void TLuaEngine::operator()() { } } } + std::this_thread::yield(); } }); // event loop auto Before = std::chrono::high_resolution_clock::now(); while (!mShutdown) { if (mLuaStates.size() == 0) { - std::this_thread::sleep_for(std::chrono::seconds(500)); + std::this_thread::sleep_for(std::chrono::seconds(100)); } { // Timed Events Scope std::unique_lock Lock(mTimedEventsMutex); @@ -759,6 +752,7 @@ void TLuaEngine::StateThreadData::AddPath(const fs::path& Path) { void TLuaResult::WaitUntilReady() { while (!Ready) { + std::this_thread::yield(); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } } @@ -816,12 +810,7 @@ void TPluginMonitor::operator()() { auto StateID = mEngine.GetStateIDForPlugin(fs::path(Pair.first).parent_path()); auto Res = mEngine.EnqueueScript(StateID, Chunk); // TODO: call onInit - while (!Res->Ready) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - if (Res->Error) { - beammp_lua_error(Res->ErrorMessage); - } + mEngine.AddResultToCheck(Res); } else { // TODO: trigger onFileChanged event beammp_trace("Change detected in file \"" + Pair.first + "\", event trigger not implemented yet"); diff --git a/src/TPPSMonitor.cpp b/src/TPPSMonitor.cpp index 8d58d84..b5b4951 100644 --- a/src/TPPSMonitor.cpp +++ b/src/TPPSMonitor.cpp @@ -21,8 +21,8 @@ TPPSMonitor::TPPSMonitor(TServer& Server) void TPPSMonitor::operator()() { RegisterThread("PPSMonitor"); while (!mNetwork) { - // hard spi - std::this_thread::sleep_for(std::chrono::milliseconds(1)); + // hard(-ish) spin + std::this_thread::yield(); } beammp_debug("PPSMonitor starting"); Application::SetSubsystemStatus("PPSMonitor", Application::Status::Good); From 40b23cbbe65abbdaef822efa1d0e6eca8b9fc5bd Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 3 Feb 2022 19:03:21 +0100 Subject: [PATCH 09/51] update changelog --- Changelog.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Changelog.md b/Changelog.md index 6774147..b633919 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,9 @@ +# v3.0.1 + +- ADDED Backup URLs to UpdateCheck (will fail less often now) +- 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 onInit didn't terminate + # v3.0.0 - CHANGED entire plugin Lua implementation (rewrite) From 144ccf14ec4079b30c17a7ba19953f71bdda5a9c Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 3 Feb 2022 19:45:51 +0100 Subject: [PATCH 10/51] Add AddResultToCheck --- src/TLuaEngine.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index a0536be..db25919 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -142,6 +142,11 @@ TLuaStateId TLuaEngine::GetStateIDForPlugin(const fs::path& PluginPath) { return ""; } +void TLuaEngine::AddResultToCheck(const std::shared_ptr& Result) { + std::unique_lock Lock(mResultsToCheckMutex); + mResultsToCheck.push(Result); +} + std::unordered_map /* handlers */> TLuaEngine::Debug_GetEventsForState(TLuaStateId StateId) { std::unordered_map> Result; std::unique_lock Lock(mLuaEventsMutex); From fdb7c9ce716723f5d4d12bcc12f7d7c7374794ed Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 3 Feb 2022 19:57:25 +0100 Subject: [PATCH 11/51] update version to 3.1.0 --- include/Common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/Common.h b/include/Common.h index 2cf15b9..9e0ee3e 100644 --- a/include/Common.h +++ b/include/Common.h @@ -119,7 +119,7 @@ private: static inline std::mutex mShutdownHandlersMutex {}; static inline std::deque mShutdownHandlers {}; - static inline Version mVersion { 3, 0, 1 }; + static inline Version mVersion { 3, 1, 0 }; }; std::string ThreadName(bool DebugModeOverride = false); From 17c571811ac5012cf69306b4cf82c0a73adea491 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Sun, 6 Feb 2022 21:37:51 +0100 Subject: [PATCH 12/51] Add config option to turn off chat logging When LogChat is disabled, using the `say` command in the console will trigger a "chat message sent!" reply, as UX feedback. --- include/Common.h | 1 + src/Common.cpp | 20 +++++++++++--------- src/TConfig.cpp | 5 +++++ src/TConsole.cpp | 3 +++ 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/include/Common.h b/include/Common.h index 9e0ee3e..930d329 100644 --- a/include/Common.h +++ b/include/Common.h @@ -49,6 +49,7 @@ public: bool DebugModeEnabled { false }; int Port { 30814 }; std::string CustomIP {}; + bool LogChat { true }; bool SendErrors { true }; bool SendErrorsMessageEnabled { true }; int HTTPServerPort { 8080 }; diff --git a/src/Common.cpp b/src/Common.cpp index a113f74..ac6319c 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -177,16 +177,18 @@ std::string Version::AsString() { } void LogChatMessage(const std::string& name, int id, const std::string& msg) { - std::stringstream ss; - ss << ThreadName(); - ss << "[CHAT] "; - if (id != -1) { - ss << "(" << id << ") <" << name << "> "; - } else { - ss << name << ""; + if (Application::Settings.LogChat) { + std::stringstream ss; + ss << ThreadName(); + ss << "[CHAT] "; + if (id != -1) { + ss << "(" << id << ") <" << name << "> "; + } else { + ss << name << ""; + } + ss << msg; + Application::Console().Write(ss.str()); } - ss << msg; - Application::Console().Write(ss.str()); } std::string GetPlatformAgnosticErrorString() { diff --git a/src/TConfig.cpp b/src/TConfig.cpp index f1c3126..54669eb 100644 --- a/src/TConfig.cpp +++ b/src/TConfig.cpp @@ -17,6 +17,7 @@ static constexpr std::string_view StrName = "Name"; static constexpr std::string_view StrDescription = "Description"; static constexpr std::string_view StrResourceFolder = "ResourceFolder"; static constexpr std::string_view StrAuthKey = "AuthKey"; +static constexpr std::string_view StrLogChat = "LogChat"; static constexpr std::string_view StrSendErrors = "SendErrors"; static constexpr std::string_view StrSendErrorsMessageEnabled = "SendErrorsShowMessage"; static constexpr std::string_view StrHTTPServerEnabled = "HTTPServerEnabled"; @@ -62,6 +63,8 @@ void TConfig::FlushToFile() { data["General"] = toml::table(); 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"); + data["General"][StrLogChat.data()] = Application::Settings.LogChat; + SetComment(data["General"][StrLogChat.data()].comments(), " Whether to log chat messages in the console / log"); data["General"][StrDebug.data()] = Application::Settings.DebugModeEnabled; data["General"][StrPrivate.data()] = Application::Settings.Private; data["General"][StrPort.data()] = Application::Settings.Port; @@ -158,6 +161,7 @@ void TConfig::ParseFromFile(std::string_view name) { TryReadValue(data, "General", StrDescription, Application::Settings.ServerDesc); TryReadValue(data, "General", StrResourceFolder, Application::Settings.Resource); TryReadValue(data, "General", StrAuthKey, Application::Settings.Key); + TryReadValue(data, "General", StrLogChat, Application::Settings.LogChat); TryReadValue(data, "General", StrSendErrors, Application::Settings.SendErrors); TryReadValue(data, "General", StrSendErrorsMessageEnabled, Application::Settings.SendErrorsMessageEnabled); // HTTP @@ -198,6 +202,7 @@ void TConfig::PrintDebug() { 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(StrLogChat) + ": \"" + (Application::Settings.LogChat ? "true" : "false") + "\""); beammp_debug(std::string(StrResourceFolder) + ": \"" + Application::Settings.Resource + "\""); beammp_debug(std::string(StrSSLKeyPath) + ": \"" + Application::Settings.SSLKeyPath + "\""); beammp_debug(std::string(StrSSLCertPath) + ": \"" + Application::Settings.SSLCertPath + "\""); diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 3e41345..cf74c60 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -199,6 +199,9 @@ void TConsole::Command_Say(const std::string& cmd) { if (cmd.size() > 3) { auto Message = cmd.substr(4); LuaAPI::MP::SendChatMessage(-1, Message); + if (!Application::Settings.LogChat) { + Application::Console().WriteRaw("Chat message sent!"); + } } } From 944b68c6d5ea6630c94e9e9a73b2b3a3cceb2ca8 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Tue, 15 Feb 2022 15:19:41 +0100 Subject: [PATCH 13/51] format backend refused message nicer --- src/THeartbeatThread.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/THeartbeatThread.cpp b/src/THeartbeatThread.cpp index 6efdd74..df95aab 100644 --- a/src/THeartbeatThread.cpp +++ b/src/THeartbeatThread.cpp @@ -116,9 +116,9 @@ void THeartbeatThread::operator()() { isAuth = true; } else { if (Message.empty()) { - Message = "Backend didn't provide a reason"; + Message = "Backend didn't provide a reason."; } - beammp_error("Backend REFUSED the auth key. " + Message); + beammp_error("Backend REFUSED the auth key. Reason: " + Message); } } if (isAuth) { From a44684f6e7ab2ecaedc1971632728e753c1b1af2 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Tue, 15 Feb 2022 15:20:25 +0100 Subject: [PATCH 14/51] Add backend provided message to all auth loggings --- src/THeartbeatThread.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/THeartbeatThread.cpp b/src/THeartbeatThread.cpp index df95aab..b611664 100644 --- a/src/THeartbeatThread.cpp +++ b/src/THeartbeatThread.cpp @@ -109,10 +109,10 @@ void THeartbeatThread::operator()() { if (Ok && !isAuth) { if (Status == "2000") { - beammp_info(("Authenticated!")); + beammp_info(("Authenticated! " + Message)); isAuth = true; } else if (Status == "200") { - beammp_info(("Resumed authenticated session!")); + beammp_info(("Resumed authenticated session! " + Message)); isAuth = true; } else { if (Message.empty()) { From 8ff94a57d77e3071a96223541298f79a6c7adcc3 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Tue, 15 Feb 2022 15:34:33 +0100 Subject: [PATCH 15/51] Add ParseCommand implementation --- include/TConsole.h | 6 ++++++ src/TConsole.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/include/TConsole.h b/include/TConsole.h index 6edcbe2..a46c093 100644 --- a/include/TConsole.h +++ b/include/TConsole.h @@ -4,6 +4,9 @@ #include "commandline.h" #include #include +#include +#include +#include class TLuaEngine; @@ -29,6 +32,9 @@ private: void Command_Say(const std::string& cmd); void Command_List(const std::string& cmd); void Command_Status(const std::string& cmd); + void Command_Settings(const std::string& cmd); + + static std::tuple> ParseCommand(const std::string& cmd); Commandline mCommandline; std::vector mCachedLuaHistory; diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 9bd88f5..b9e1897 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -156,6 +156,7 @@ void TConsole::Command_Help(const std::string&) { list lists all players and info about them say sends the message to all players in chat 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)"; Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString)); } @@ -195,6 +196,59 @@ void TConsole::Command_Kick(const std::string& cmd) { } } +std::tuple> TConsole::ParseCommand(const std::string& CommandWithArgs) { + // Algorithm designed and implemented by Lion Kortlepel (c) 2022 + // It correctly splits arguments, including respecting single and double quotes, as well as backticks + auto End_i = CommandWithArgs.find_first_of(' '); + std::string Command = CommandWithArgs.substr(0, End_i); + std::string ArgsStr = CommandWithArgs.substr(End_i); + std::vector Args; + char* PrevPtr = ArgsStr.data(); + char* Ptr = ArgsStr.data(); + const char* End = ArgsStr.data() + ArgsStr.size(); + while (Ptr != End) { + std::string Arg = ""; + // advance while space + while (Ptr != End && std::isspace(*Ptr)) + ++Ptr; + PrevPtr = Ptr; + // advance while NOT space, also handle quotes + while (Ptr != End && !std::isspace(*Ptr)) { + // TODO: backslash escaping quotes + for (char Quote : { '"', '\'', '`' }) { + if (*Ptr == Quote) { + // seek if there's a closing quote + // if there is, go there and continue, otherwise ignore + char* Seeker = Ptr + 1; + while (Seeker != End && *Seeker != Quote) + ++Seeker; + if (Seeker != End) { + // found closing quote + Ptr = Seeker; + } + break; // exit for loop + } + } + ++Ptr; + } + Arg = std::string(PrevPtr, Ptr - PrevPtr); + // remove quotes if enclosed in quotes + for (char Quote : { '"', '\'', '`' }) { + if (!Arg.empty() && Arg.at(0) == Quote && Arg.at(Arg.size() - 1) == Quote) { + Arg = Arg.substr(1, Arg.size() - 2); + break; + } + } + if (!Arg.empty()) { + Args.push_back(Arg); + } + } + return { Command, Args }; +} + +void TConsole::Command_Settings(const std::string& cmd) { +} + void TConsole::Command_Say(const std::string& cmd) { if (cmd.size() > 3) { auto Message = cmd.substr(4); From 548b2512cc475b22cbf5199c0b42a746590bd642 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Tue, 15 Feb 2022 16:06:59 +0100 Subject: [PATCH 16/51] proper command parsing --- include/TConsole.h | 17 ++++---- src/TConsole.cpp | 98 ++++++++++++++++++++++++++++++---------------- 2 files changed, 74 insertions(+), 41 deletions(-) diff --git a/include/TConsole.h b/include/TConsole.h index a46c093..c6d0b42 100644 --- a/include/TConsole.h +++ b/include/TConsole.h @@ -26,13 +26,16 @@ private: void ChangeToRegularConsole(); void HandleLuaInternalCommand(const std::string& cmd); - void Command_Lua(const std::string& cmd); - void Command_Help(const std::string& cmd); - void Command_Kick(const std::string& cmd); - void Command_Say(const std::string& cmd); - void Command_List(const std::string& cmd); - void Command_Status(const std::string& cmd); - void Command_Settings(const std::string& cmd); + void Command_Lua(const std::string& cmd, const std::vector& args); + void Command_Help(const std::string& cmd, const std::vector& args); + void Command_Kick(const std::string& cmd, const std::vector& args); + void Command_List(const std::string& cmd, const std::vector& args); + void Command_Status(const std::string& cmd, const std::vector& args); + void Command_Settings(const std::string& cmd, const std::vector& args); + + void Command_Say(const std::string& FullCommand); + bool ExpectArgsCount(const std::vector& args, size_t n); + bool ExpectArgsCount(const std::vector& args, size_t min, size_t max); static std::tuple> ParseCommand(const std::string& cmd); diff --git a/src/TConsole.cpp b/src/TConsole.cpp index b9e1897..2588c3a 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -133,21 +133,47 @@ void TConsole::ChangeToRegularConsole() { } } -void TConsole::Command_Lua(const std::string& cmd) { - if (cmd.size() > 3) { - auto NewStateId = cmd.substr(4); +bool TConsole::ExpectArgsCount(const std::vector& args, size_t n) { + if (args.size() != n) { + Application::Console().WriteRaw("Expected " + std::to_string(n) + " argument(s), instead got " + std::to_string(args.size())); + return false; + } else { + return true; + } +} + +bool TConsole::ExpectArgsCount(const std::vector& args, size_t min, size_t max) { + if (min == max) { + return ExpectArgsCount(args, min); + } else { + if (args.size() > max) { + Application::Console().WriteRaw("Too many arguments. At most " + std::to_string(max) + " arguments expected, got " + std::to_string(args.size()) + " instead."); + return false; + } else if (args.size() < min) { + Application::Console().WriteRaw("Too few arguments. At least " + std::to_string(max) + " arguments expected, got " + std::to_string(args.size()) + " instead."); + return false; + } + } + return true; +} + +void TConsole::Command_Lua(const std::string& cmd, const std::vector& args) { + if (!ExpectArgsCount(args, 0, 1)) { + } + if (args.size() == 1) { + auto NewStateId = args.at(0); beammp_assert(!NewStateId.empty()); if (mLuaEngine->HasState(NewStateId)) { ChangeToLuaConsole(NewStateId); } else { Application::Console().WriteRaw("Lua state '" + NewStateId + "' is not a known state. Didn't switch to Lua."); } - } else { + } else if (args.size() == 0) { ChangeToLuaConsole(mDefaultStateId); } } -void TConsole::Command_Help(const std::string&) { +void TConsole::Command_Help(const std::string&, const std::vector& args) { static constexpr const char* sHelpString = R"( Commands: help displays this help @@ -161,7 +187,7 @@ void TConsole::Command_Help(const std::string&) { Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString)); } -void TConsole::Command_Kick(const std::string& cmd) { +void TConsole::Command_Kick(const std::string& cmd, const std::vector& args) { if (cmd.size() > 4) { auto Name = cmd.substr(5); std::string Reason = "Kicked by server console"; @@ -201,7 +227,10 @@ std::tuple> TConsole::ParseCommand(const s // It correctly splits arguments, including respecting single and double quotes, as well as backticks auto End_i = CommandWithArgs.find_first_of(' '); std::string Command = CommandWithArgs.substr(0, End_i); - std::string ArgsStr = CommandWithArgs.substr(End_i); + std::string ArgsStr {}; + if (End_i != std::string::npos) { + ArgsStr = CommandWithArgs.substr(End_i); + } std::vector Args; char* PrevPtr = ArgsStr.data(); char* Ptr = ArgsStr.data(); @@ -246,12 +275,12 @@ std::tuple> TConsole::ParseCommand(const s return { Command, Args }; } -void TConsole::Command_Settings(const std::string& cmd) { +void TConsole::Command_Settings(const std::string& cmd, const std::vector& args) { } -void TConsole::Command_Say(const std::string& cmd) { - if (cmd.size() > 3) { - auto Message = cmd.substr(4); +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) { Application::Console().WriteRaw("Chat message sent!"); @@ -259,7 +288,7 @@ void TConsole::Command_Say(const std::string& cmd) { } } -void TConsole::Command_List(const std::string&) { +void TConsole::Command_List(const std::string&, const std::vector& args) { if (mLuaEngine->Server().ClientCount() == 0) { Application::Console().WriteRaw("No players online."); } else { @@ -279,7 +308,7 @@ void TConsole::Command_List(const std::string&) { } } -void TConsole::Command_Status(const std::string&) { +void TConsole::Command_Status(const std::string&, const std::vector& args) { std::stringstream Status; size_t CarCount = 0; @@ -480,9 +509,10 @@ TConsole::TConsole() { BackupOldLog(); mCommandline.on_command = [this](Commandline& c) { try { - auto cmd = c.get_command(); - cmd = TrimString(cmd); - mCommandline.write(mCommandline.prompt() + cmd); + auto TrimmedCmd = c.get_command(); + TrimmedCmd = TrimString(TrimmedCmd); + auto [cmd, args] = ParseCommand(TrimmedCmd); + mCommandline.write(mCommandline.prompt() + TrimmedCmd); if (mIsLuaConsole) { if (!mLuaEngine) { beammp_info("Lua not started yet, please try again in a second"); @@ -503,25 +533,25 @@ TConsole::TConsole() { } else if (cmd == "exit") { beammp_info("gracefully shutting down"); Application::GracefullyShutdown(); - } else if (StringStartsWith(cmd, "lua")) { - Command_Lua(cmd); - } else if (StringStartsWith(cmd, "help")) { - RunAsCommand(cmd, true); - Command_Help(cmd); - } else if (StringStartsWith(cmd, "kick")) { - RunAsCommand(cmd, true); - Command_Kick(cmd); - } else if (StringStartsWith(cmd, "say")) { - RunAsCommand(cmd, true); - Command_Say(cmd); - } else if (StringStartsWith(cmd, "list")) { - RunAsCommand(cmd, true); - Command_List(cmd); - } else if (StringStartsWith(cmd, "status")) { - RunAsCommand(cmd, true); - Command_Status(cmd); + } else if (cmd == "lua") { + Command_Lua(cmd, args); + } else if (cmd == "help") { + RunAsCommand(TrimmedCmd, true); + Command_Help(cmd, args); + } else if (cmd == "kick") { + RunAsCommand(TrimmedCmd, true); + Command_Kick(cmd, args); + } else if (cmd == "say") { + RunAsCommand(TrimmedCmd, true); + Command_Say(TrimmedCmd); + } else if (cmd == "list") { + RunAsCommand(TrimmedCmd, true); + Command_List(cmd, args); + } else if (cmd == "status") { + RunAsCommand(TrimmedCmd, true); + Command_Status(cmd, args); } else if (!cmd.empty()) { - RunAsCommand(cmd); + RunAsCommand(TrimmedCmd); } } } catch (const std::exception& e) { From 588c68ebe14d9739b8a27fbf907168656ecfb912 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 17 Feb 2022 11:08:48 +0100 Subject: [PATCH 17/51] Use proper argument parser --- include/TConsole.h | 16 ++++++- src/TConsole.cpp | 115 +++++++++++++++++++++++++-------------------- 2 files changed, 79 insertions(+), 52 deletions(-) diff --git a/include/TConsole.h b/include/TConsole.h index c6d0b42..8a9f383 100644 --- a/include/TConsole.h +++ b/include/TConsole.h @@ -4,8 +4,10 @@ #include "commandline.h" #include #include +#include #include #include +#include #include class TLuaEngine; @@ -34,10 +36,20 @@ private: void Command_Settings(const std::string& cmd, const std::vector& args); void Command_Say(const std::string& FullCommand); - bool ExpectArgsCount(const std::vector& args, size_t n); - bool ExpectArgsCount(const std::vector& args, size_t min, size_t max); + bool EnsureArgsCount(const std::vector& args, size_t n); + bool EnsureArgsCount(const std::vector& args, size_t min, size_t max); static std::tuple> ParseCommand(const std::string& cmd); + static std::string ConcatArgs(const std::vector& args, char space = ' '); + + std::unordered_map&)>> mCommandMap = { + { "lua", [this](const auto& a, const auto& b) { Command_Lua(a, b); } }, + { "help", [this](const auto& a, const auto& b) { Command_Help(a, b); } }, + { "kick", [this](const auto& a, const auto& b) { Command_Kick(a, b); } }, + { "list", [this](const auto& a, const auto& b) { Command_List(a, b); } }, + { "status", [this](const auto& a, const auto& b) { Command_Status(a, b); } }, + { "settings", [this](const auto& a, const auto& b) { Command_Settings(a, b); } }, + }; Commandline mCommandline; std::vector mCachedLuaHistory; diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 2588c3a..6d20a7e 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -133,8 +133,11 @@ void TConsole::ChangeToRegularConsole() { } } -bool TConsole::ExpectArgsCount(const std::vector& args, size_t n) { - if (args.size() != n) { +bool TConsole::EnsureArgsCount(const std::vector& args, size_t n) { + if (n == 0 && args.size() != 0) { + Application::Console().WriteRaw("This command expects no arguments."); + return false; + } else if (args.size() != n) { Application::Console().WriteRaw("Expected " + std::to_string(n) + " argument(s), instead got " + std::to_string(args.size())); return false; } else { @@ -142,9 +145,9 @@ bool TConsole::ExpectArgsCount(const std::vector& args, size_t n) { } } -bool TConsole::ExpectArgsCount(const std::vector& args, size_t min, size_t max) { +bool TConsole::EnsureArgsCount(const std::vector& args, size_t min, size_t max) { if (min == max) { - return ExpectArgsCount(args, min); + return EnsureArgsCount(args, min); } else { if (args.size() > max) { Application::Console().WriteRaw("Too many arguments. At most " + std::to_string(max) + " arguments expected, got " + std::to_string(args.size()) + " instead."); @@ -158,7 +161,8 @@ bool TConsole::ExpectArgsCount(const std::vector& args, size_t min, } void TConsole::Command_Lua(const std::string& cmd, const std::vector& args) { - if (!ExpectArgsCount(args, 0, 1)) { + if (!EnsureArgsCount(args, 0, 1)) { + return; } if (args.size() == 1) { auto NewStateId = args.at(0); @@ -174,6 +178,9 @@ void TConsole::Command_Lua(const std::string& cmd, const std::vector& args) { + if (!EnsureArgsCount(args, 0)) { + return; + } static constexpr const char* sHelpString = R"( Commands: help displays this help @@ -187,38 +194,46 @@ void TConsole::Command_Help(const std::string&, const std::vector& Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString)); } +std::string TConsole::ConcatArgs(const std::vector& args, char space) { + std::string Result; + for (const auto& arg : args) { + Result += arg + space; + } + Result = Result.substr(0, Result.size() - 1); // strip trailing space + return Result; +} + void TConsole::Command_Kick(const std::string& cmd, const std::vector& args) { - if (cmd.size() > 4) { - auto Name = cmd.substr(5); - std::string Reason = "Kicked by server console"; - auto SpacePos = Name.find(' '); - if (SpacePos != Name.npos) { - Reason = Name.substr(SpacePos + 1); - Name = cmd.substr(5, cmd.size() - Reason.size() - 5 - 1); - } - beammp_trace("attempt to kick '" + Name + "' for '" + Reason + "'"); - bool Kicked = false; - auto NameCompare = [](std::string Name1, std::string Name2) -> bool { - std::for_each(Name1.begin(), Name1.end(), [](char& c) { c = tolower(c); }); - std::for_each(Name2.begin(), Name2.end(), [](char& c) { c = tolower(c); }); - return StringStartsWith(Name1, Name2) || StringStartsWith(Name2, Name1); - }; - mLuaEngine->Server().ForEachClient([&](std::weak_ptr Client) -> bool { - if (!Client.expired()) { - auto locked = Client.lock(); - if (NameCompare(locked->GetName(), Name)) { - mLuaEngine->Network().ClientKick(*locked, Reason); - Kicked = true; - return false; - } + if (!EnsureArgsCount(args, 1, size_t(-1))) { + return; + } + auto Name = args.at(0); + std::string Reason = "Kicked by server console"; + if (args.size() > 1) { + Reason = ConcatArgs({ args.begin() + 1, args.end() }); + } + beammp_trace("attempt to kick '" + Name + "' for '" + Reason + "'"); + bool Kicked = false; + auto NameCompare = [](std::string Name1, std::string Name2) -> bool { + std::for_each(Name1.begin(), Name1.end(), [](char& c) { c = tolower(c); }); + std::for_each(Name2.begin(), Name2.end(), [](char& c) { c = tolower(c); }); + return StringStartsWith(Name1, Name2) || StringStartsWith(Name2, Name1); + }; + mLuaEngine->Server().ForEachClient([&](std::weak_ptr Client) -> bool { + if (!Client.expired()) { + auto locked = Client.lock(); + if (NameCompare(locked->GetName(), Name)) { + mLuaEngine->Network().ClientKick(*locked, Reason); + Kicked = true; + return false; } - return true; - }); - if (!Kicked) { - Application::Console().WriteRaw("Error: No player with name matching '" + Name + "' was found."); - } else { - Application::Console().WriteRaw("Kicked player '" + Name + "' for reason: '" + Reason + "'."); } + return true; + }); + if (!Kicked) { + Application::Console().WriteRaw("Error: No player with name matching '" + Name + "' was found."); + } else { + Application::Console().WriteRaw("Kicked player '" + Name + "' for reason: '" + Reason + "'."); } } @@ -276,6 +291,9 @@ std::tuple> TConsole::ParseCommand(const s } void TConsole::Command_Settings(const std::string& cmd, const std::vector& args) { + if (!EnsureArgsCount(args, 0)) { + return; + } } void TConsole::Command_Say(const std::string& FullCmd) { @@ -289,6 +307,9 @@ void TConsole::Command_Say(const std::string& FullCmd) { } void TConsole::Command_List(const std::string&, const std::vector& args) { + if (!EnsureArgsCount(args, 0)) { + return; + } if (mLuaEngine->Server().ClientCount() == 0) { Application::Console().WriteRaw("No players online."); } else { @@ -309,6 +330,9 @@ void TConsole::Command_List(const std::string&, const std::vector& } void TConsole::Command_Status(const std::string&, const std::vector& args) { + if (!EnsureArgsCount(args, 0)) { + return; + } std::stringstream Status; size_t CarCount = 0; @@ -533,25 +557,16 @@ TConsole::TConsole() { } else if (cmd == "exit") { beammp_info("gracefully shutting down"); Application::GracefullyShutdown(); - } else if (cmd == "lua") { - Command_Lua(cmd, args); - } else if (cmd == "help") { - RunAsCommand(TrimmedCmd, true); - Command_Help(cmd, args); - } else if (cmd == "kick") { - RunAsCommand(TrimmedCmd, true); - Command_Kick(cmd, args); } else if (cmd == "say") { RunAsCommand(TrimmedCmd, true); Command_Say(TrimmedCmd); - } else if (cmd == "list") { - RunAsCommand(TrimmedCmd, true); - Command_List(cmd, args); - } else if (cmd == "status") { - RunAsCommand(TrimmedCmd, true); - Command_Status(cmd, args); - } else if (!cmd.empty()) { - RunAsCommand(TrimmedCmd); + } else { + if (mCommandMap.find(cmd) != mCommandMap.end()) { + mCommandMap.at(cmd)(cmd, args); + RunAsCommand(TrimmedCmd, true); + } else { + RunAsCommand(TrimmedCmd); + } } } } catch (const std::exception& e) { From 95b417bb36bdaa3af393a501f21492b39177d9a6 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 2 Mar 2022 11:43:48 +0100 Subject: [PATCH 18/51] Add MP.JsonSerialize --- include/LuaAPI.h | 1 + src/LuaAPI.cpp | 76 ++++++++++++++++++++++++++++++++++++++++++++++ src/TLuaEngine.cpp | 3 ++ 3 files changed, 80 insertions(+) diff --git a/include/LuaAPI.h b/include/LuaAPI.h index 0cd0e1f..f7530d0 100644 --- a/include/LuaAPI.h +++ b/include/LuaAPI.h @@ -22,6 +22,7 @@ namespace MP { bool IsPlayerConnected(int ID); void Sleep(size_t Ms); void PrintRaw(sol::variadic_args); + std::string JsonSerialize(sol::table object); } namespace FS { std::pair CreateDirectory(const std::string& Path); diff --git a/src/LuaAPI.cpp b/src/LuaAPI.cpp index 14cd8f6..5e60a16 100644 --- a/src/LuaAPI.cpp +++ b/src/LuaAPI.cpp @@ -3,6 +3,8 @@ #include "Common.h" #include "TLuaEngine.h" +#include + #define SOL_ALL_SAFETIES_ON 1 #include @@ -351,3 +353,77 @@ std::string LuaAPI::FS::ConcatPaths(sol::variadic_args Args) { auto Result = Path.lexically_normal().string(); return Result; } + +static void JsonSerializeRecursive(nlohmann::json& json, sol::object left, sol::object right) { + std::string key; + bool is_array = false; + switch (left.get_type()) { + case sol::type::lua_nil: + case sol::type::none: + case sol::type::poly: + case sol::type::boolean: + case sol::type::lightuserdata: + case sol::type::userdata: + case sol::type::thread: + case sol::type::function: + case sol::type::table: + beammp_lua_error("JsonSerialize: left side of table field is unexpected type"); + return; + case sol::type::string: + key = left.as(); + break; + case sol::type::number: + is_array = true; + // TODO: deal with this + break; + } + nlohmann::json value; + switch (right.get_type()) { + case sol::type::lua_nil: + case sol::type::none: + return; + case sol::type::poly: + beammp_lua_warn("unsure what to do with poly type in JsonSerialize, ignoring"); + break; + case sol::type::boolean: + value = right.as(); + break; + case sol::type::lightuserdata: + beammp_lua_warn("unsure what to do with lightuserdata in JsonSerialize, ignoring"); + break; + case sol::type::userdata: + beammp_lua_warn("unsure what to do with userdata in JsonSerialize, ignoring"); + break; + case sol::type::thread: + beammp_lua_warn("unsure what to do with thread in JsonSerialize, ignoring"); + break; + case sol::type::string: + value = right.as(); + break; + case sol::type::number: + value = right.as(); + break; + case sol::type::function: + beammp_lua_warn("unsure what to do with function in JsonSerialize, ignoring"); + break; + case sol::type::table: + for (const auto& pair : right.as()) { + JsonSerializeRecursive(value, pair.first, pair.second); + } + break; + } + if (is_array) { + json.push_back(value); + } else { + json[key] = value; + } +} + +std::string LuaAPI::MP::JsonSerialize(sol::table object) { + nlohmann::json json; + // table + for (const auto& entry : object) { + JsonSerializeRecursive(json, entry.first, entry.second); + } + return json.dump(); +} diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index db25919..3f1ec58 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -556,10 +556,13 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomi mEngine->CancelEventTimers(EventName, mStateId); }); MPTable.set_function("Set", &LuaAPI::MP::Set); + MPTable.set_function("JsonSerialize", &LuaAPI::MP::JsonSerialize); + auto HttpTable = StateView.create_named_table("Http"); HttpTable.set_function("CreateConnection", [this](const std::string& host, uint16_t port) { return Lua_HttpCreateConnection(host, port); }); + MPTable.create_named("Settings", "Debug", 0, From b52677e5851397a3a3adcde74d61ea4d067563f5 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 2 Mar 2022 13:22:26 +0100 Subject: [PATCH 19/51] Add JsonDeserialize --- include/LuaAPI.h | 2 +- include/TLuaEngine.h | 1 + src/LuaAPI.cpp | 22 +++++++----- src/TLuaEngine.cpp | 81 ++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 94 insertions(+), 12 deletions(-) diff --git a/include/LuaAPI.h b/include/LuaAPI.h index f7530d0..b25c740 100644 --- a/include/LuaAPI.h +++ b/include/LuaAPI.h @@ -22,7 +22,7 @@ namespace MP { bool IsPlayerConnected(int ID); void Sleep(size_t Ms); void PrintRaw(sol::variadic_args); - std::string JsonSerialize(sol::table object); + std::string JsonSerialize(const sol::table& object); } namespace FS { std::pair CreateDirectory(const std::string& Path); diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index 720d8ea..fb1353e 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -190,6 +190,7 @@ private: std::string Lua_GetPlayerName(int ID); sol::table Lua_GetPlayerVehicles(int ID); sol::table Lua_HttpCreateConnection(const std::string& host, uint16_t port); + sol::table Lua_JsonDeserialize(const std::string& str); int Lua_GetPlayerIDByName(const std::string& Name); std::string mName; diff --git a/src/LuaAPI.cpp b/src/LuaAPI.cpp index 5e60a16..53f28ad 100644 --- a/src/LuaAPI.cpp +++ b/src/LuaAPI.cpp @@ -354,7 +354,11 @@ std::string LuaAPI::FS::ConcatPaths(sol::variadic_args Args) { return Result; } -static void JsonSerializeRecursive(nlohmann::json& json, sol::object left, sol::object right) { +static void JsonSerializeRecursive(nlohmann::json& json, const sol::object& left, const sol::object& right, size_t depth = 0) { + if (depth > 100) { + beammp_lua_error("json serialize will not go deeper than 100 nested tables, internal references assumed, aborted this path"); + return; + } std::string key; bool is_array = false; switch (left.get_type()) { @@ -384,31 +388,31 @@ static void JsonSerializeRecursive(nlohmann::json& json, sol::object left, sol:: return; case sol::type::poly: beammp_lua_warn("unsure what to do with poly type in JsonSerialize, ignoring"); - break; + return; case sol::type::boolean: value = right.as(); break; case sol::type::lightuserdata: beammp_lua_warn("unsure what to do with lightuserdata in JsonSerialize, ignoring"); - break; + return; case sol::type::userdata: beammp_lua_warn("unsure what to do with userdata in JsonSerialize, ignoring"); - break; + return; case sol::type::thread: beammp_lua_warn("unsure what to do with thread in JsonSerialize, ignoring"); - break; + return; case sol::type::string: value = right.as(); break; case sol::type::number: - value = right.as(); + value = right.as(); break; case sol::type::function: beammp_lua_warn("unsure what to do with function in JsonSerialize, ignoring"); - break; + return; case sol::type::table: for (const auto& pair : right.as()) { - JsonSerializeRecursive(value, pair.first, pair.second); + JsonSerializeRecursive(value, pair.first, pair.second, depth + 1); } break; } @@ -419,7 +423,7 @@ static void JsonSerializeRecursive(nlohmann::json& json, sol::object left, sol:: } } -std::string LuaAPI::MP::JsonSerialize(sol::table object) { +std::string LuaAPI::MP::JsonSerialize(const sol::table& object) { nlohmann::json json; // table for (const auto& entry : object) { diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 3f1ec58..5212e0e 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -473,6 +474,80 @@ sol::table TLuaEngine::StateThreadData::Lua_HttpCreateConnection(const std::stri return table; } +template +static void AddToTable(sol::table& table, const std::string& left, const T& value) { + if (left.empty()) { + table[table.size() + 1] = value; + } else { + table[left] = value; + } +} + +static void JsonDeserializeRecursive(sol::state_view& StateView, sol::table& table, const std::string& left, const nlohmann::json& right) { + switch (right.type()) { + case nlohmann::detail::value_t::null: + return; + case nlohmann::detail::value_t::object: { + auto value = table.create(); + value.clear(); + for (const auto& entry : right.items()) { + JsonDeserializeRecursive(StateView, value, entry.key(), entry.value()); + } + AddToTable(table, left, value); + break; + } + case nlohmann::detail::value_t::array: { + auto value = table.create(); + value.clear(); + for (const auto& entry : right.items()) { + JsonDeserializeRecursive(StateView, value, "", entry.value()); + } + AddToTable(table, left, value); + break; + } + case nlohmann::detail::value_t::string: + AddToTable(table, left, right.get()); + break; + case nlohmann::detail::value_t::boolean: + AddToTable(table, left, right.get()); + break; + case nlohmann::detail::value_t::number_integer: + AddToTable(table, left, right.get()); + break; + case nlohmann::detail::value_t::number_unsigned: + AddToTable(table, left, right.get()); + break; + case nlohmann::detail::value_t::number_float: + AddToTable(table, left, right.get()); + break; + case nlohmann::detail::value_t::binary: + beammp_lua_error("JsonDeserialize can't handle binary blob in json, ignoring"); + return; + case nlohmann::detail::value_t::discarded: + return; + } +} + +sol::table TLuaEngine::StateThreadData::Lua_JsonDeserialize(const std::string& str) { + sol::state_view StateView(mState); + auto table = StateView.create_table(); + // TODO: try / catch + nlohmann::json json = nlohmann::json::parse(str); + if (json.is_object()) { + for (const auto& entry : json.items()) { + JsonDeserializeRecursive(StateView, table, entry.key(), entry.value()); + } + } else if (json.is_array()) { + for (const auto& entry : json) { + JsonDeserializeRecursive(StateView, table, "", entry); + } + } else { + beammp_lua_error("JsonDeserialize expected array or object json, instead got " + std::string(json.type_name())); + return sol::nil; + } + return table; +} + TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomic_bool& Shutdown, TLuaStateId StateId, TLuaEngine& Engine) : mName(Name) , mShutdown(Shutdown) @@ -557,12 +632,14 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomi }); MPTable.set_function("Set", &LuaAPI::MP::Set); MPTable.set_function("JsonSerialize", &LuaAPI::MP::JsonSerialize); - + MPTable.set_function("JsonDeserialize", [this](const std::string& str) { + return Lua_JsonDeserialize(str); + }); + auto HttpTable = StateView.create_named_table("Http"); HttpTable.set_function("CreateConnection", [this](const std::string& host, uint16_t port) { return Lua_HttpCreateConnection(host, port); }); - MPTable.create_named("Settings", "Debug", 0, From 96a0e4b6fbef79c9fc94ec31d91e307a77b17e9d Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 2 Mar 2022 13:23:08 +0100 Subject: [PATCH 20/51] update changelog --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 28fb6cf..4ce45d7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ # v3.0.1 - ADDED Backup URLs to UpdateCheck (will fail less often now) +- ADDED MP.JsonSerialize() and MP.JsonDeserialize(), which turn lua tables into json and vice-versa - 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 an issue which would cause servers to crash on mod download via SIGPIPE on POSIX From 965935a0e60c1b46371f2220583f93a7b02f0fda Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 2 Mar 2022 13:25:29 +0100 Subject: [PATCH 21/51] catch invalid json to JsonDeserialize, use lua_nil instead of nil --- src/TLuaEngine.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 5212e0e..69b3c11 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -531,7 +531,10 @@ static void JsonDeserializeRecursive(sol::state_view& StateView, sol::table& tab sol::table TLuaEngine::StateThreadData::Lua_JsonDeserialize(const std::string& str) { sol::state_view StateView(mState); auto table = StateView.create_table(); - // TODO: try / catch + if (!nlohmann::json::accept(str)) { + beammp_lua_error("string given to JsonDeserialize is not valid json."); + return sol::lua_nil; + } nlohmann::json json = nlohmann::json::parse(str); if (json.is_object()) { for (const auto& entry : json.items()) { @@ -543,7 +546,7 @@ sol::table TLuaEngine::StateThreadData::Lua_JsonDeserialize(const std::string& s } } else { beammp_lua_error("JsonDeserialize expected array or object json, instead got " + std::string(json.type_name())); - return sol::nil; + return sol::lua_nil; } return table; } From 9a0cdc6517940f3950862c5d26e6ddba4afecb97 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 2 Mar 2022 13:26:19 +0100 Subject: [PATCH 22/51] rename JsonSerialize to JsonEncode, JsonDeserialize to JsonDecode --- Changelog.md | 2 +- include/LuaAPI.h | 2 +- include/TLuaEngine.h | 2 +- src/LuaAPI.cpp | 20 ++++++++++---------- src/TLuaEngine.cpp | 24 ++++++++++++------------ 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Changelog.md b/Changelog.md index 4ce45d7..00f0b48 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,7 +1,7 @@ # v3.0.1 - ADDED Backup URLs to UpdateCheck (will fail less often now) -- ADDED MP.JsonSerialize() and MP.JsonDeserialize(), which turn lua tables into json and vice-versa +- ADDED MP.JsonEncode() and MP.JsonDecode(), which turn lua tables into json and vice-versa - 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 an issue which would cause servers to crash on mod download via SIGPIPE on POSIX diff --git a/include/LuaAPI.h b/include/LuaAPI.h index b25c740..e317a23 100644 --- a/include/LuaAPI.h +++ b/include/LuaAPI.h @@ -22,7 +22,7 @@ namespace MP { bool IsPlayerConnected(int ID); void Sleep(size_t Ms); void PrintRaw(sol::variadic_args); - std::string JsonSerialize(const sol::table& object); + std::string JsonEncode(const sol::table& object); } namespace FS { std::pair CreateDirectory(const std::string& Path); diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index fb1353e..973841d 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -190,7 +190,7 @@ private: std::string Lua_GetPlayerName(int ID); sol::table Lua_GetPlayerVehicles(int ID); sol::table Lua_HttpCreateConnection(const std::string& host, uint16_t port); - sol::table Lua_JsonDeserialize(const std::string& str); + sol::table Lua_JsonDecode(const std::string& str); int Lua_GetPlayerIDByName(const std::string& Name); std::string mName; diff --git a/src/LuaAPI.cpp b/src/LuaAPI.cpp index 53f28ad..1baa467 100644 --- a/src/LuaAPI.cpp +++ b/src/LuaAPI.cpp @@ -354,7 +354,7 @@ std::string LuaAPI::FS::ConcatPaths(sol::variadic_args Args) { return Result; } -static void JsonSerializeRecursive(nlohmann::json& json, const sol::object& left, const sol::object& right, size_t depth = 0) { +static void JsonEncodeRecursive(nlohmann::json& json, const sol::object& left, const sol::object& right, size_t depth = 0) { if (depth > 100) { beammp_lua_error("json serialize will not go deeper than 100 nested tables, internal references assumed, aborted this path"); return; @@ -371,7 +371,7 @@ static void JsonSerializeRecursive(nlohmann::json& json, const sol::object& left case sol::type::thread: case sol::type::function: case sol::type::table: - beammp_lua_error("JsonSerialize: left side of table field is unexpected type"); + beammp_lua_error("JsonEncode: left side of table field is unexpected type"); return; case sol::type::string: key = left.as(); @@ -387,19 +387,19 @@ static void JsonSerializeRecursive(nlohmann::json& json, const sol::object& left case sol::type::none: return; case sol::type::poly: - beammp_lua_warn("unsure what to do with poly type in JsonSerialize, ignoring"); + beammp_lua_warn("unsure what to do with poly type in JsonEncode, ignoring"); return; case sol::type::boolean: value = right.as(); break; case sol::type::lightuserdata: - beammp_lua_warn("unsure what to do with lightuserdata in JsonSerialize, ignoring"); + beammp_lua_warn("unsure what to do with lightuserdata in JsonEncode, ignoring"); return; case sol::type::userdata: - beammp_lua_warn("unsure what to do with userdata in JsonSerialize, ignoring"); + beammp_lua_warn("unsure what to do with userdata in JsonEncode, ignoring"); return; case sol::type::thread: - beammp_lua_warn("unsure what to do with thread in JsonSerialize, ignoring"); + beammp_lua_warn("unsure what to do with thread in JsonEncode, ignoring"); return; case sol::type::string: value = right.as(); @@ -408,11 +408,11 @@ static void JsonSerializeRecursive(nlohmann::json& json, const sol::object& left value = right.as(); break; case sol::type::function: - beammp_lua_warn("unsure what to do with function in JsonSerialize, ignoring"); + beammp_lua_warn("unsure what to do with function in JsonEncode, ignoring"); return; case sol::type::table: for (const auto& pair : right.as()) { - JsonSerializeRecursive(value, pair.first, pair.second, depth + 1); + JsonEncodeRecursive(value, pair.first, pair.second, depth + 1); } break; } @@ -423,11 +423,11 @@ static void JsonSerializeRecursive(nlohmann::json& json, const sol::object& left } } -std::string LuaAPI::MP::JsonSerialize(const sol::table& object) { +std::string LuaAPI::MP::JsonEncode(const sol::table& object) { nlohmann::json json; // table for (const auto& entry : object) { - JsonSerializeRecursive(json, entry.first, entry.second); + JsonEncodeRecursive(json, entry.first, entry.second); } return json.dump(); } diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 69b3c11..21adcc2 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -483,7 +483,7 @@ static void AddToTable(sol::table& table, const std::string& left, const T& valu } } -static void JsonDeserializeRecursive(sol::state_view& StateView, sol::table& table, const std::string& left, const nlohmann::json& right) { +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: return; @@ -491,7 +491,7 @@ static void JsonDeserializeRecursive(sol::state_view& StateView, sol::table& tab auto value = table.create(); value.clear(); for (const auto& entry : right.items()) { - JsonDeserializeRecursive(StateView, value, entry.key(), entry.value()); + JsonDecodeRecursive(StateView, value, entry.key(), entry.value()); } AddToTable(table, left, value); break; @@ -500,7 +500,7 @@ static void JsonDeserializeRecursive(sol::state_view& StateView, sol::table& tab auto value = table.create(); value.clear(); for (const auto& entry : right.items()) { - JsonDeserializeRecursive(StateView, value, "", entry.value()); + JsonDecodeRecursive(StateView, value, "", entry.value()); } AddToTable(table, left, value); break; @@ -521,31 +521,31 @@ static void JsonDeserializeRecursive(sol::state_view& StateView, sol::table& tab AddToTable(table, left, right.get()); break; case nlohmann::detail::value_t::binary: - beammp_lua_error("JsonDeserialize can't handle binary blob in json, ignoring"); + beammp_lua_error("JsonDecode can't handle binary blob in json, ignoring"); return; case nlohmann::detail::value_t::discarded: return; } } -sol::table TLuaEngine::StateThreadData::Lua_JsonDeserialize(const std::string& str) { +sol::table TLuaEngine::StateThreadData::Lua_JsonDecode(const std::string& str) { sol::state_view StateView(mState); auto table = StateView.create_table(); if (!nlohmann::json::accept(str)) { - beammp_lua_error("string given to JsonDeserialize is not valid json."); + beammp_lua_error("string given to JsonDecode is not valid json."); return sol::lua_nil; } nlohmann::json json = nlohmann::json::parse(str); if (json.is_object()) { for (const auto& entry : json.items()) { - JsonDeserializeRecursive(StateView, table, entry.key(), entry.value()); + JsonDecodeRecursive(StateView, table, entry.key(), entry.value()); } } else if (json.is_array()) { for (const auto& entry : json) { - JsonDeserializeRecursive(StateView, table, "", entry); + JsonDecodeRecursive(StateView, table, "", entry); } } else { - beammp_lua_error("JsonDeserialize expected array or object json, instead got " + std::string(json.type_name())); + beammp_lua_error("JsonDecode expected array or object json, instead got " + std::string(json.type_name())); return sol::lua_nil; } return table; @@ -634,9 +634,9 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomi mEngine->CancelEventTimers(EventName, mStateId); }); MPTable.set_function("Set", &LuaAPI::MP::Set); - MPTable.set_function("JsonSerialize", &LuaAPI::MP::JsonSerialize); - MPTable.set_function("JsonDeserialize", [this](const std::string& str) { - return Lua_JsonDeserialize(str); + MPTable.set_function("JsonEncode", &LuaAPI::MP::JsonEncode); + MPTable.set_function("JsonDecode", [this](const std::string& str) { + return Lua_JsonDecode(str); }); auto HttpTable = StateView.create_named_table("Http"); From dd5cf1a4af8b84f9760b2bab8b6541781dfb861a Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 2 Mar 2022 13:30:56 +0100 Subject: [PATCH 23/51] move some things in changelog from 3.0.1 to 3.1.0 --- Changelog.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 00f0b48..31d7627 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,7 +1,11 @@ +# v3.1.0 + +- ADDED lua debug facilities (type `:help` when attached to lua via `lua`) +- ADDED MP.JsonEncode() and MP.JsonDecode(), which turn lua tables into json and vice-versa + # v3.0.1 - ADDED Backup URLs to UpdateCheck (will fail less often now) -- ADDED MP.JsonEncode() and MP.JsonDecode(), which turn lua tables into json and vice-versa - 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 an issue which would cause servers to crash on mod download via SIGPIPE on POSIX From 7d97c3b560b3db66c04b99c67342d3fddd51d222 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 2 Mar 2022 23:40:48 +0100 Subject: [PATCH 24/51] add FS.ListFiles, FS.ListDirectories --- Changelog.md | 1 + include/LuaAPI.h | 2 ++ include/TLuaEngine.h | 2 ++ src/LuaAPI.cpp | 14 ++++++++++++++ src/TLuaEngine.cpp | 35 ++++++++++++++++++++++++++++++++++- 5 files changed, 53 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 31d7627..f798e41 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ - ADDED lua debug facilities (type `:help` when attached to lua via `lua`) - ADDED MP.JsonEncode() and MP.JsonDecode(), which turn lua tables into json and vice-versa +- ADDED FS.ListFiles and FS.ListDirectories # v3.0.1 diff --git a/include/LuaAPI.h b/include/LuaAPI.h index e317a23..23d40c0 100644 --- a/include/LuaAPI.h +++ b/include/LuaAPI.h @@ -23,7 +23,9 @@ namespace MP { void Sleep(size_t Ms); void PrintRaw(sol::variadic_args); std::string JsonEncode(const sol::table& object); + std::string JsonDiff(const std::string& a, const std::string& b); } + namespace FS { std::pair CreateDirectory(const std::string& Path); std::pair Remove(const std::string& Path); diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index 973841d..64d1a9c 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -192,6 +192,8 @@ private: sol::table Lua_HttpCreateConnection(const std::string& host, uint16_t port); sol::table Lua_JsonDecode(const std::string& str); int Lua_GetPlayerIDByName(const std::string& Name); + sol::table Lua_FS_ListFiles(const std::string& Path); + sol::table Lua_FS_ListDirectories(const std::string& Path); std::string mName; std::atomic_bool& mShutdown; diff --git a/src/LuaAPI.cpp b/src/LuaAPI.cpp index 1baa467..a7ced6f 100644 --- a/src/LuaAPI.cpp +++ b/src/LuaAPI.cpp @@ -431,3 +431,17 @@ std::string LuaAPI::MP::JsonEncode(const sol::table& object) { } return json.dump(); } + +std::string LuaAPI::MP::JsonDiff(const std::string& a, const std::string& b) { + if (!nlohmann::json::accept(a)) { + beammp_lua_error("JsonDiff first argument is not valid json: `" + a + "`"); + return ""; + } + if (!nlohmann::json::accept(b)) { + beammp_lua_error("JsonDiff second argument is not valid json: `" + b + "`"); + return ""; + } + auto a_json = nlohmann::json::parse(a); + auto b_json = nlohmann::json::parse(b); + return nlohmann::json::diff(a_json, b_json).dump(); +} diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 21adcc2..5ae4b0d 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -421,6 +421,32 @@ int TLuaEngine::StateThreadData::Lua_GetPlayerIDByName(const std::string& Name) return Id; } +sol::table TLuaEngine::StateThreadData::Lua_FS_ListFiles(const std::string& Path) { + if (!std::filesystem::exists(Path)) { + return sol::lua_nil; + } + auto table = mStateView.create_table(); + for (const auto& entry : std::filesystem::directory_iterator(Path)) { + if (entry.is_regular_file() || entry.is_symlink()) { + table[table.size() + 1] = entry.path().lexically_relative(Path).string(); + } + } + return table; +} + +sol::table TLuaEngine::StateThreadData::Lua_FS_ListDirectories(const std::string& Path) { + if (!std::filesystem::exists(Path)) { + return sol::lua_nil; + } + auto table = mStateView.create_table(); + for (const auto& entry : std::filesystem::directory_iterator(Path)) { + if (entry.is_directory()) { + table[table.size() + 1] = entry.path().lexically_relative(Path).string(); + } + } + return table; +} + std::string TLuaEngine::StateThreadData::Lua_GetPlayerName(int ID) { auto MaybeClient = GetClient(mEngine->Server(), ID); if (MaybeClient && !MaybeClient.value().expired()) { @@ -532,7 +558,7 @@ sol::table TLuaEngine::StateThreadData::Lua_JsonDecode(const std::string& str) { sol::state_view StateView(mState); auto table = StateView.create_table(); if (!nlohmann::json::accept(str)) { - beammp_lua_error("string given to JsonDecode is not valid json."); + beammp_lua_error("string given to JsonDecode is not valid json: `" + str + "`"); return sol::lua_nil; } nlohmann::json json = nlohmann::json::parse(str); @@ -638,6 +664,7 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomi MPTable.set_function("JsonDecode", [this](const std::string& str) { return Lua_JsonDecode(str); }); + MPTable.set_function("JsonDiff", &LuaAPI::MP::JsonDiff); auto HttpTable = StateView.create_named_table("Http"); HttpTable.set_function("CreateConnection", [this](const std::string& host, uint16_t port) { @@ -665,6 +692,12 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomi FSTable.set_function("IsDirectory", &LuaAPI::FS::IsDirectory); FSTable.set_function("IsFile", &LuaAPI::FS::IsFile); FSTable.set_function("ConcatPaths", &LuaAPI::FS::ConcatPaths); + FSTable.set_function("ListFiles", [this](const std::string& Path) { + return Lua_FS_ListFiles(Path); + }); + FSTable.set_function("ListDirectories", [this](const std::string& Path) { + return Lua_FS_ListDirectories(Path); + }); Start(); } From ca3314b416e72d4431e3ae14cad19480ac512ac5 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 3 Mar 2022 12:25:06 +0100 Subject: [PATCH 25/51] add JsonDiffApply, JsonMinify, JsonPrettify, JsonFlatten, JsonUnflatten --- include/LuaAPI.h | 5 +++++ src/LuaAPI.cpp | 47 ++++++++++++++++++++++++++++++++++++++++++++++ src/TLuaEngine.cpp | 4 ++++ 3 files changed, 56 insertions(+) diff --git a/include/LuaAPI.h b/include/LuaAPI.h index 23d40c0..74286fd 100644 --- a/include/LuaAPI.h +++ b/include/LuaAPI.h @@ -24,6 +24,11 @@ namespace MP { void PrintRaw(sol::variadic_args); std::string JsonEncode(const sol::table& object); std::string JsonDiff(const std::string& a, const std::string& b); + std::string JsonDiffApply(const std::string& data, const std::string& patch); + std::string JsonPrettify(const std::string& json); + std::string JsonMinify(const std::string& json); + std::string JsonFlatten(const std::string& json); + std::string JsonUnflatten(const std::string& json); } namespace FS { diff --git a/src/LuaAPI.cpp b/src/LuaAPI.cpp index a7ced6f..faa0739 100644 --- a/src/LuaAPI.cpp +++ b/src/LuaAPI.cpp @@ -445,3 +445,50 @@ std::string LuaAPI::MP::JsonDiff(const std::string& a, const std::string& b) { auto b_json = nlohmann::json::parse(b); return nlohmann::json::diff(a_json, b_json).dump(); } + +std::string LuaAPI::MP::JsonDiffApply(const std::string& data, const std::string& patch) { + if (!nlohmann::json::accept(data)) { + beammp_lua_error("JsonDiffApply first argument is not valid json: `" + data + "`"); + return ""; + } + if (!nlohmann::json::accept(patch)) { + beammp_lua_error("JsonDiffApply second argument is not valid json: `" + patch + "`"); + return ""; + } + auto a_json = nlohmann::json::parse(data); + auto b_json = nlohmann::json::parse(patch); + a_json.patch(b_json); + return a_json.dump(); +} + +std::string LuaAPI::MP::JsonPrettify(const std::string& json) { + if (!nlohmann::json::accept(json)) { + beammp_lua_error("JsonPrettify argument is not valid json: `" + json + "`"); + return ""; + } + return nlohmann::json::parse(json).dump(4); +} + +std::string LuaAPI::MP::JsonMinify(const std::string& json) { + if (!nlohmann::json::accept(json)) { + beammp_lua_error("JsonMinify argument is not valid json: `" + json + "`"); + return ""; + } + return nlohmann::json::parse(json).dump(-1); +} + +std::string LuaAPI::MP::JsonFlatten(const std::string& json) { + if (!nlohmann::json::accept(json)) { + beammp_lua_error("JsonFlatten argument is not valid json: `" + json + "`"); + return ""; + } + return nlohmann::json::parse(json).flatten().dump(-1); +} + +std::string LuaAPI::MP::JsonUnflatten(const std::string& json) { + if (!nlohmann::json::accept(json)) { + beammp_lua_error("JsonUnflatten argument is not valid json: `" + json + "`"); + return ""; + } + return nlohmann::json::parse(json).unflatten().dump(-1); +} diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 5ae4b0d..c71a9c5 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -665,6 +665,10 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomi return Lua_JsonDecode(str); }); MPTable.set_function("JsonDiff", &LuaAPI::MP::JsonDiff); + MPTable.set_function("JsonFlatten", &LuaAPI::MP::JsonFlatten); + MPTable.set_function("JsonUnflatten", &LuaAPI::MP::JsonUnflatten); + MPTable.set_function("JsonPrettify", &LuaAPI::MP::JsonPrettify); + MPTable.set_function("JsonMinify", &LuaAPI::MP::JsonMinify); auto HttpTable = StateView.create_named_table("Http"); HttpTable.set_function("CreateConnection", [this](const std::string& host, uint16_t port) { From ace7aaada744d71bc00f9493f05efdbedf7ae668 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 3 Mar 2022 12:38:23 +0100 Subject: [PATCH 26/51] Fix issue which caused assignments and similar lua code to not work in the console --- src/TConsole.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 6d20a7e..18af61d 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -543,7 +543,7 @@ TConsole::TConsole() { } else if (!cmd.empty() && cmd.at(0) == ':') { HandleLuaInternalCommand(cmd.substr(1)); } else { - auto Future = mLuaEngine->EnqueueScript(mStateId, { std::make_shared(cmd), "", "" }); + auto Future = mLuaEngine->EnqueueScript(mStateId, { std::make_shared(TrimmedCmd), "", "" }); while (!Future->Ready) { std::this_thread::yield(); // TODO: Add a timeout } From 09c9b24cbf48483e255ab93dbd45edf6d51e23c0 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 10 Mar 2022 01:27:16 +0100 Subject: [PATCH 27/51] Fix Unicycle "infinite spawning" bug --- src/TServer.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/TServer.cpp b/src/TServer.cpp index a4328e2..cee40b3 100644 --- a/src/TServer.cpp +++ b/src/TServer.cpp @@ -174,7 +174,7 @@ bool TServer::IsUnicycle(TClient& c, const std::string& CarJson) { try { auto Car = nlohmann::json::parse(CarJson); const std::string jbm = "jbm"; - if (Car.contains(jbm) && Car["jbm"].is_string() && Car["jbm"] == "unicycle") { + if (Car.contains(jbm) && Car[jbm].is_string() && Car[jbm] == "unicycle") { return true; } } catch (const std::exception& e) { @@ -184,17 +184,12 @@ bool TServer::IsUnicycle(TClient& c, const std::string& CarJson) { } bool TServer::ShouldSpawn(TClient& c, const std::string& CarJson, int ID) { - - if (c.GetUnicycleID() > -1 && (c.GetCarCount() - 1) < Application::Settings.MaxCars) { - return true; - } - - if (IsUnicycle(c, CarJson)) { + if (IsUnicycle(c, CarJson) && c.GetUnicycleID() < 0) { c.SetUnicycleID(ID); return true; + } else { + return c.GetCarCount() < Application::Settings.MaxCars; } - - return Application::Settings.MaxCars > c.GetCarCount(); } void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Network) { From e3d9d11bbd5dea512a8322850029c2de73026365 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 10 Mar 2022 01:40:47 +0100 Subject: [PATCH 28/51] only use sentry if URL is specified, possibly fix stupid microsoft compiler error hey @microsoft, maybe don't have a limit on the size of obj files. --- CMakeLists.txt | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6cbe134..6a36c83 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,11 +41,23 @@ set(SENTRY_BUILD_SHARED_LIBS OFF) if (MSVC) set(SENTRY_BUILD_RUNTIMESTATIC ON) endif() -set(SENTRY_BACKEND breakpad) +message(STATUS "Checking for Sentry URL") +# this is set by the build system. +# IMPORTANT: if you're building from source, just leave this empty +if (NOT DEFINED BEAMMP_SECRET_SENTRY_URL) + message(WARNING "No sentry URL configured. Sentry logging is disabled for this build. \ + This is not an error, and if you're building the BeamMP-Server yourself, this is expected and can be ignored.") + set(BEAMMP_SECRET_SENTRY_URL "") + set(SENTRY_BACKEND none) +else() + string(LENGTH ${BEAMMP_SECRET_SENTRY_URL} URL_LEN) + message(STATUS "Sentry URL is length ${URL_LEN}") + set(SENTRY_BACKEND breakpad) +endif() add_subdirectory("deps/sentry-native") if (MSVC) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /bigobj") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj") endif () message(STATUS "Setting compiler flags") @@ -65,17 +77,6 @@ elseif (UNIX) endif (SANITIZE) endif () -message(STATUS "Checking for Sentry URL") -# this is set by the build system. -# IMPORTANT: if you're building from source, just leave this empty -if (NOT DEFINED BEAMMP_SECRET_SENTRY_URL) - message(WARNING "No sentry URL configured. Sentry logging is disabled for this build. \ - This is not an error, and if you're building the BeamMP-Server yourself, this is expected and can be ignored.") - set(BEAMMP_SECRET_SENTRY_URL "") -else() - string(LENGTH ${BEAMMP_SECRET_SENTRY_URL} URL_LEN) - message(STATUS "Sentry URL is length ${URL_LEN}") -endif() message(STATUS "Adding local source dependencies") # this has to happen before -DDEBUG since it wont compile properly with -DDEBUG From 2caa74d913f22b71e50c819a57d88a1fa225accf Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 10 Mar 2022 12:17:46 +0100 Subject: [PATCH 29/51] update lionkor/commandline to 1.0.0 (adds cursor left- and right movement) --- deps/commandline | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/commandline b/deps/commandline index 0d3e107..71240f6 160000 --- a/deps/commandline +++ b/deps/commandline @@ -1 +1 @@ -Subproject commit 0d3e1073c1005270dfad851c1f8c59f4ce29d8c1 +Subproject commit 71240f634b211d830679e7d2841b897c7c30dad9 From 3c08e544715543c78411155b06a6fe4d00db76b6 Mon Sep 17 00:00:00 2001 From: 20dka Date: Tue, 15 Mar 2022 01:58:26 +0100 Subject: [PATCH 30/51] Add basic autocomplete (fix #95) --- deps/commandline | 2 +- include/Common.h | 1 + include/TConfig.h | 1 + include/TConsole.h | 1 + include/TLuaEngine.h | 4 ++++ src/TConsole.cpp | 45 ++++++++++++++++++++++++++++++++++++++++++-- src/TLuaEngine.cpp | 15 +++++++++++++++ 7 files changed, 66 insertions(+), 3 deletions(-) diff --git a/deps/commandline b/deps/commandline index 71240f6..e9218e5 160000 --- a/deps/commandline +++ b/deps/commandline @@ -1 +1 @@ -Subproject commit 71240f634b211d830679e7d2841b897c7c30dad9 +Subproject commit e9218e554bb93c383873344f4703c103834c6005 diff --git a/include/Common.h b/include/Common.h index 930d329..76640aa 100644 --- a/include/Common.h +++ b/include/Common.h @@ -12,6 +12,7 @@ extern TSentry Sentry; #include #include #include +#include #include "Compat.h" diff --git a/include/TConfig.h b/include/TConfig.h index 3fcc597..fe43d5c 100644 --- a/include/TConfig.h +++ b/include/TConfig.h @@ -3,6 +3,7 @@ #include "Common.h" #include +#include #define TOML11_PRESERVE_COMMENTS_BY_DEFAULT #include // header-only version of TOML++ diff --git a/include/TConsole.h b/include/TConsole.h index 8a9f383..c181c5c 100644 --- a/include/TConsole.h +++ b/include/TConsole.h @@ -49,6 +49,7 @@ private: { "list", [this](const auto& a, const auto& b) { Command_List(a, b); } }, { "status", [this](const auto& a, const auto& b) { Command_Status(a, b); } }, { "settings", [this](const auto& a, const auto& b) { Command_Settings(a, b); } }, + { "say", [this](const auto& a, const auto& b) { Command_Say(""); } }, // shouldn't actually be called }; Commandline mCommandline; diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index 64d1a9c..9017400 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -154,6 +154,8 @@ public: static constexpr const char* BeamMPFnNotFoundError = "BEAMMP_FN_NOT_FOUND"; + std::vector GetStateGlobalKeysForState(TLuaStateId StateId); + // Debugging functions (slow) std::unordered_map /* handlers */> Debug_GetEventsForState(TLuaStateId StateId); std::queue>> Debug_GetStateExecuteQueueForState(TLuaStateId StateId); @@ -178,6 +180,8 @@ private: void operator()() override; sol::state_view State() { return sol::state_view(mState); } + std::vector GetStateGlobalKeys(); + // Debug functions, slow std::queue>> Debug_GetStateExecuteQueue(); std::queue, std::vector>> Debug_GetStateFunctionQueue(); diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 18af61d..1682a3a 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -417,12 +417,12 @@ void TConsole::Command_Status(const std::string&, const std::vector << "\t\tEvent handlers: " << mLuaEngine->GetRegisteredEventHandlerCount() << "\n" << "\tSubsystems:\n" << "\t\tGood/Starting/Bad: " << SystemsGood << "/" << SystemsStarting << "/" << SystemsBad << "\n" - << "\t\tShutting down/Shutdown: " << SystemsShuttingDown << "/" << SystemsShutdown << "\n" + << "\t\tShutting down/Shut down: " << SystemsShuttingDown << "/" << SystemsShutdown << "\n" << "\t\tGood: [ " << SystemsGoodList << " ]\n" << "\t\tStarting: [ " << SystemsStartingList << " ]\n" << "\t\tBad: [ " << SystemsBadList << " ]\n" << "\t\tShutting down: [ " << SystemsShuttingDownList << " ]\n" - << "\t\tShutdown: [ " << SystemsShutdownList << " ]\n" + << "\t\tShut down: [ " << SystemsShutdownList << " ]\n" << ""; Application::Console().WriteRaw(Status.str()); @@ -573,6 +573,47 @@ TConsole::TConsole() { beammp_error("Console died with: " + std::string(e.what()) + ". This could be a fatal error and could cause the server to terminate."); } }; + mCommandline.on_autocomplete = [this](Commandline& c, std::string stub) { + std::vector suggestions; + try { + auto cmd = TrimString(stub); + //beammp_error("yes 1"); + //beammp_error(stub); + if (mIsLuaConsole) { // if lua + if (!mLuaEngine) { + beammp_info("Lua not started yet, please try again in a second"); + } else { + std::string prefix {}; + for (size_t i = stub.length(); i > 0; i--) { + if (!std::isalnum(stub[i - 1]) && stub[i - 1] != '_') { + prefix = stub.substr(0, i); + stub = stub.substr(i); + break; + } + } + auto keys = mLuaEngine->GetStateGlobalKeysForState(mStateId); + for (const auto& key : keys) { + std::string::size_type n = key.find(stub); + if (n == 0) { + suggestions.push_back(prefix + key); + //beammp_warn(cmd_name); + } + } + } + } else { // if not lua + for (const auto& [cmd_name, cmd_fn] : mCommandMap) { + std::string::size_type n = cmd_name.find(stub); + if (n == 0) { + suggestions.push_back(cmd_name); + //beammp_warn(cmd_name); + } + } + } + } catch (const std::exception& e) { + beammp_error("Console died with: " + std::string(e.what()) + ". This could be a fatal error and could cause the server to terminate."); + } + return suggestions; + }; } void TConsole::Write(const std::string& str) { diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index c71a9c5..f17a34f 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -192,6 +192,21 @@ std::vector TLuaEngine::Debug_GetResultsToCheckForState(TLuaStateId return Result; } +std::vector TLuaEngine::GetStateGlobalKeysForState(TLuaStateId StateId) { + std::unique_lock Lock(mLuaStatesMutex); + auto Result = mLuaStates.at(StateId)->GetStateGlobalKeys(); + return Result; +} + +std::vector TLuaEngine::StateThreadData::GetStateGlobalKeys() { + auto globals = mStateView.globals(); + std::vector Result; + for (const auto& [key, value] : globals) { + Result.push_back(key.as()); + } + return Result; +} + void TLuaEngine::WaitForAll(std::vector>& Results, const std::optional& Max) { for (const auto& Result : Results) { bool Cancelled = false; From fe06726d7552ef1189ad6eafd8cfa524c5a8f646 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 16 Mar 2022 16:03:23 +0100 Subject: [PATCH 31/51] update commandline --- deps/commandline | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/commandline b/deps/commandline index e9218e5..d0bad60 160000 --- a/deps/commandline +++ b/deps/commandline @@ -1 +1 @@ -Subproject commit e9218e554bb93c383873344f4703c103834c6005 +Subproject commit d0bad60eff2858bd95c2f33af5d8bd0c4ef7dedd From 6a43694c0ff813f12d4373404cf22418b5217d99 Mon Sep 17 00:00:00 2001 From: 20dka Date: Thu, 17 Mar 2022 01:30:24 +0100 Subject: [PATCH 32/51] Ignore backend response if server is Private --- src/THeartbeatThread.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/THeartbeatThread.cpp b/src/THeartbeatThread.cpp index b611664..5707dd4 100644 --- a/src/THeartbeatThread.cpp +++ b/src/THeartbeatThread.cpp @@ -62,8 +62,10 @@ void THeartbeatThread::operator()() { beammp_trace(T); Doc.Parse(T.data(), T.size()); if (Doc.HasParseError() || !Doc.IsObject()) { - beammp_error("Backend response failed to parse as valid json"); - beammp_debug("Response was: `" + T + "`"); + if (!Application::Settings.Private) { + beammp_error("Backend response failed to parse as valid json"); + beammp_debug("Response was: `" + T + "`"); + } Sentry.SetContext("JSON Response", { { "reponse", T } }); SentryReportError(Url + Target, ResponseCode); } else if (ResponseCode != 200) { @@ -107,7 +109,7 @@ void THeartbeatThread::operator()() { } } - if (Ok && !isAuth) { + if (Ok && !isAuth && !Application::Settings.Private) { if (Status == "2000") { beammp_info(("Authenticated! " + Message)); isAuth = true; @@ -121,7 +123,7 @@ void THeartbeatThread::operator()() { beammp_error("Backend REFUSED the auth key. Reason: " + Message); } } - if (isAuth) { + if (isAuth || Application::Settings.Private) { Application::SetSubsystemStatus("Heartbeat", Application::Status::Good); } } From be498be661454f11bc861d3f69a586e197c34c20 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 17 Mar 2022 18:48:50 +0100 Subject: [PATCH 33/51] change TriggerClientEvent to take object, not string, and add TriggerClientEventJson --- include/LuaAPI.h | 3 ++- src/LuaAPI.cpp | 17 +++++++++++++---- src/TConsole.cpp | 2 +- src/TLuaEngine.cpp | 3 ++- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/include/LuaAPI.h b/include/LuaAPI.h index 74286fd..4871a0b 100644 --- a/include/LuaAPI.h +++ b/include/LuaAPI.h @@ -12,7 +12,8 @@ namespace MP { std::string GetOSName(); std::tuple GetServerVersion(); - bool TriggerClientEvent(int PlayerID, const std::string& EventName, const std::string& Data); + bool TriggerClientEvent(int PlayerID, const std::string& EventName, const sol::object& Data); + bool TriggerClientEventJson(int PlayerID, const std::string& EventName, const sol::table& Data); inline size_t GetPlayerCount() { return Engine->Server().ClientCount(); } void DropPlayer(int ID, std::optional MaybeReason); void SendChatMessage(int ID, const std::string& Message); diff --git a/src/LuaAPI.cpp b/src/LuaAPI.cpp index faa0739..33488aa 100644 --- a/src/LuaAPI.cpp +++ b/src/LuaAPI.cpp @@ -102,18 +102,18 @@ void LuaAPI::Print(sol::variadic_args Args) { luaprint(ToPrint); } -bool LuaAPI::MP::TriggerClientEvent(int PlayerID, const std::string& EventName, const std::string& Data) { +static inline bool InternalTriggerClientEvent(int PlayerID, const std::string& EventName, const std::string& Data) { std::string Packet = "E:" + EventName + ":" + Data; if (PlayerID == -1) - Engine->Network().SendToAll(nullptr, Packet, true, true); + LuaAPI::MP::Engine->Network().SendToAll(nullptr, Packet, true, true); else { - auto MaybeClient = GetClient(Engine->Server(), PlayerID); + auto MaybeClient = GetClient(LuaAPI::MP::Engine->Server(), PlayerID); if (!MaybeClient || MaybeClient.value().expired()) { beammp_lua_error("TriggerClientEvent invalid Player ID"); return false; } auto c = MaybeClient.value().lock(); - if (!Engine->Network().Respond(*c, Packet, true)) { + if (!LuaAPI::MP::Engine->Network().Respond(*c, Packet, true)) { beammp_lua_error("Respond failed"); return false; } @@ -121,6 +121,11 @@ bool LuaAPI::MP::TriggerClientEvent(int PlayerID, const std::string& EventName, return true; } +bool LuaAPI::MP::TriggerClientEvent(int PlayerID, const std::string& EventName, const sol::object& DataObj) { + std::string Data = DataObj.as(); + return InternalTriggerClientEvent(PlayerID, EventName, Data); +} + void LuaAPI::MP::DropPlayer(int ID, std::optional MaybeReason) { auto MaybeClient = GetClient(Engine->Server(), ID); if (!MaybeClient || MaybeClient.value().expired()) { @@ -492,3 +497,7 @@ std::string LuaAPI::MP::JsonUnflatten(const std::string& json) { } return nlohmann::json::parse(json).unflatten().dump(-1); } + +bool LuaAPI::MP::TriggerClientEventJson(int PlayerID, const std::string& EventName, const sol::table& Data) { + return InternalTriggerClientEvent(PlayerID, EventName, JsonEncode(Data)); +} diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 1682a3a..42e9bc4 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -548,7 +548,7 @@ TConsole::TConsole() { std::this_thread::yield(); // TODO: Add a timeout } if (Future->Error) { - beammp_lua_error(Future->ErrorMessage); + beammp_lua_error("error in " + mStateId + ": " + Future->ErrorMessage); } } } else { diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index f17a34f..274a032 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -385,7 +385,7 @@ sol::table TLuaEngine::StateThreadData::Lua_TriggerLocalEvent(const std::string& Result.add(FnRet); } else { sol::error Err = FnRet; - beammp_lua_error(Err.what()); + beammp_lua_error(std::string("TriggerLocalEvent: ") + Err.what()); } } } @@ -637,6 +637,7 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomi return Lua_TriggerLocalEvent(EventName, EventArgs); }); MPTable.set_function("TriggerClientEvent", &LuaAPI::MP::TriggerClientEvent); + MPTable.set_function("TriggerClientEventJson", &LuaAPI::MP::TriggerClientEventJson); MPTable.set_function("GetPlayerCount", &LuaAPI::MP::GetPlayerCount); MPTable.set_function("IsPlayerConnected", &LuaAPI::MP::IsPlayerConnected); MPTable.set_function("GetPlayerIDByName", [&](const std::string& Name) -> int { From 39db1a5e42a813906df27a912532539dbd2caa09 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 17 Mar 2022 19:27:17 +0100 Subject: [PATCH 34/51] Fix JsonEncode with mixed key/value index/value tables --- src/LuaAPI.cpp | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/LuaAPI.cpp b/src/LuaAPI.cpp index 33488aa..6e5b81d 100644 --- a/src/LuaAPI.cpp +++ b/src/LuaAPI.cpp @@ -359,13 +359,12 @@ std::string LuaAPI::FS::ConcatPaths(sol::variadic_args Args) { return Result; } -static void JsonEncodeRecursive(nlohmann::json& json, const sol::object& left, const sol::object& right, size_t depth = 0) { +static void JsonEncodeRecursive(nlohmann::json& json, const sol::object& left, const sol::object& right, bool is_array, size_t depth = 0) { if (depth > 100) { beammp_lua_error("json serialize will not go deeper than 100 nested tables, internal references assumed, aborted this path"); return; } std::string key; - bool is_array = false; switch (left.get_type()) { case sol::type::lua_nil: case sol::type::none: @@ -382,8 +381,7 @@ static void JsonEncodeRecursive(nlohmann::json& json, const sol::object& left, c key = left.as(); break; case sol::type::number: - is_array = true; - // TODO: deal with this + key = std::to_string(left.as()); break; } nlohmann::json value; @@ -415,12 +413,19 @@ static void JsonEncodeRecursive(nlohmann::json& json, const sol::object& left, c case sol::type::function: beammp_lua_warn("unsure what to do with function in JsonEncode, ignoring"); return; - case sol::type::table: + case sol::type::table: { + bool local_is_array = true; for (const auto& pair : right.as()) { - JsonEncodeRecursive(value, pair.first, pair.second, depth + 1); + if (pair.first.get_type() != sol::type::number) { + local_is_array = false; + } + } + for (const auto& pair : right.as()) { + JsonEncodeRecursive(value, pair.first, pair.second, local_is_array, depth + 1); } break; } + } if (is_array) { json.push_back(value); } else { @@ -431,8 +436,14 @@ static void JsonEncodeRecursive(nlohmann::json& json, const sol::object& left, c std::string LuaAPI::MP::JsonEncode(const sol::table& object) { nlohmann::json json; // table + bool is_array = true; + for (const auto& pair : object.as()) { + if (pair.first.get_type() != sol::type::number) { + is_array = false; + } + } for (const auto& entry : object) { - JsonEncodeRecursive(json, entry.first, entry.second); + JsonEncodeRecursive(json, entry.first, entry.second, is_array); } return json.dump(); } From c1e216957bf8b526ce714a36d6ce674e02b7fa6e Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Fri, 18 Mar 2022 01:52:31 +0100 Subject: [PATCH 35/51] move Json* to Util, add Random, RandomRange, RandomIntRange, catch errors in TPluginMonitor --- include/TLuaEngine.h | 5 ++- src/TLuaEngine.cpp | 92 +++++++++++++++++++++++++++----------------- 2 files changed, 60 insertions(+), 37 deletions(-) diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index 9017400..d8d39a4 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -89,7 +90,7 @@ public: std::unique_lock Lock(mResultsToCheckMutex); return mResultsToCheck.size(); } - + size_t GetLuaStateCount() { std::unique_lock Lock(mLuaStatesMutex); return mLuaStates.size(); @@ -213,6 +214,8 @@ private: sol::state_view mStateView { mState }; std::queue mPaths; std::recursive_mutex mPathsMutex; + std::mt19937 mMersenneTwister; + std::uniform_real_distribution mUniformRealDistribution01; }; struct TimedEvent { diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 274a032..392807b 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -676,15 +676,26 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomi mEngine->CancelEventTimers(EventName, mStateId); }); MPTable.set_function("Set", &LuaAPI::MP::Set); - MPTable.set_function("JsonEncode", &LuaAPI::MP::JsonEncode); - MPTable.set_function("JsonDecode", [this](const std::string& str) { + + auto UtilTable = StateView.create_named_table("Util"); + UtilTable.set_function("JsonEncode", &LuaAPI::MP::JsonEncode); + UtilTable.set_function("JsonDecode", [this](const std::string& str) { return Lua_JsonDecode(str); }); - MPTable.set_function("JsonDiff", &LuaAPI::MP::JsonDiff); - MPTable.set_function("JsonFlatten", &LuaAPI::MP::JsonFlatten); - MPTable.set_function("JsonUnflatten", &LuaAPI::MP::JsonUnflatten); - MPTable.set_function("JsonPrettify", &LuaAPI::MP::JsonPrettify); - MPTable.set_function("JsonMinify", &LuaAPI::MP::JsonMinify); + UtilTable.set_function("JsonDiff", &LuaAPI::MP::JsonDiff); + UtilTable.set_function("JsonFlatten", &LuaAPI::MP::JsonFlatten); + UtilTable.set_function("JsonUnflatten", &LuaAPI::MP::JsonUnflatten); + UtilTable.set_function("JsonPrettify", &LuaAPI::MP::JsonPrettify); + UtilTable.set_function("JsonMinify", &LuaAPI::MP::JsonMinify); + UtilTable.set_function("Random", [this] { + return mUniformRealDistribution01(mMersenneTwister); + }); + UtilTable.set_function("RandomRange", [this](double min, double max) -> double { + return std::uniform_real_distribution(min, max)(mMersenneTwister); + }); + UtilTable.set_function("RandomIntRange", [this](int64_t min, int64_t max) -> int64_t { + return std::uniform_int_distribution(min, max)(mMersenneTwister); + }); auto HttpTable = StateView.create_named_table("Http"); HttpTable.set_function("CreateConnection", [this](const std::string& host, uint16_t port) { @@ -934,38 +945,47 @@ void TPluginMonitor::operator()() { beammp_info("PluginMonitor started"); while (!mShutdown) { std::this_thread::sleep_for(std::chrono::seconds(3)); + std::vector ToRemove; for (const auto& Pair : mFileTimes) { - auto CurrentTime = fs::last_write_time(Pair.first); - if (CurrentTime != Pair.second) { - mFileTimes[Pair.first] = CurrentTime; - // grandparent of the path should be Resources/Server - if (fs::equivalent(fs::path(Pair.first).parent_path().parent_path(), mPath)) { - beammp_info("File \"" + Pair.first + "\" changed, reloading"); - // is in root folder, so reload - std::ifstream FileStream(Pair.first, std::ios::in | std::ios::binary); - auto Size = std::filesystem::file_size(Pair.first); - auto Contents = std::make_shared(); - Contents->resize(Size); - FileStream.read(Contents->data(), Contents->size()); - TLuaChunk Chunk(Contents, Pair.first, fs::path(Pair.first).parent_path().string()); - auto StateID = mEngine.GetStateIDForPlugin(fs::path(Pair.first).parent_path()); - auto Res = mEngine.EnqueueScript(StateID, Chunk); - // TODO: call onInit - mEngine.AddResultToCheck(Res); - } else { - // TODO: trigger onFileChanged event - beammp_trace("Change detected in file \"" + Pair.first + "\", event trigger not implemented yet"); - /* - // is in subfolder, dont reload, just trigger an event - auto Results = mEngine.TriggerEvent("onFileChanged", "", Pair.first); - mEngine.WaitForAll(Results); - for (const auto& Result : Results) { - if (Result->Error) { - beammp_lua_error(Result->ErrorMessage); - } - }*/ + try { + auto CurrentTime = fs::last_write_time(Pair.first); + if (CurrentTime != Pair.second) { + mFileTimes[Pair.first] = CurrentTime; + // grandparent of the path should be Resources/Server + if (fs::equivalent(fs::path(Pair.first).parent_path().parent_path(), mPath)) { + beammp_info("File \"" + Pair.first + "\" changed, reloading"); + // is in root folder, so reload + std::ifstream FileStream(Pair.first, std::ios::in | std::ios::binary); + auto Size = std::filesystem::file_size(Pair.first); + auto Contents = std::make_shared(); + Contents->resize(Size); + FileStream.read(Contents->data(), Contents->size()); + TLuaChunk Chunk(Contents, Pair.first, fs::path(Pair.first).parent_path().string()); + auto StateID = mEngine.GetStateIDForPlugin(fs::path(Pair.first).parent_path()); + auto Res = mEngine.EnqueueScript(StateID, Chunk); + // TODO: call onInit + mEngine.AddResultToCheck(Res); + } else { + // TODO: trigger onFileChanged event + beammp_trace("Change detected in file \"" + Pair.first + "\", event trigger not implemented yet"); + /* + // is in subfolder, dont reload, just trigger an event + auto Results = mEngine.TriggerEvent("onFileChanged", "", Pair.first); + mEngine.WaitForAll(Results); + for (const auto& Result : Results) { + if (Result->Error) { + beammp_lua_error(Result->ErrorMessage); + } + }*/ + } } + } catch (const std::exception& e) { + ToRemove.push_back(Pair.first); } } + for (const auto& File : ToRemove) { + mFileTimes.erase(File); + beammp_warn("file '" + File + "' couldn't be accessed, so it was removed from plugin hot reload monitor (probably got deleted)"); + } } } From ef902a03f3ae8c2a0b0df66fb892e78be596e634 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 23 Mar 2022 12:05:22 +0100 Subject: [PATCH 36/51] update lionkor/commandline to v2.0.0 --- deps/commandline | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/commandline b/deps/commandline index d0bad60..01434c1 160000 --- a/deps/commandline +++ b/deps/commandline @@ -1 +1 @@ -Subproject commit d0bad60eff2858bd95c2f33af5d8bd0c4ef7dedd +Subproject commit 01434c11aaf82d37a126dc70f5aa02cc523dbbb4 From 4cb299061e713124e22d120cb51d186999ea73d7 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 23 Mar 2022 16:10:51 +0100 Subject: [PATCH 37/51] add pos argument to on_autocomplete --- Changelog.md | 1 + src/Common.cpp | 2 +- src/TConsole.cpp | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index d985b28..18580b3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,6 @@ # v3.1.0 +- ADDED Tab autocomplete in console, smart tab autocomplete (understands lua tables and types) in the lua console - ADDED lua debug facilities (type `:help` when attached to lua via `lua`) - ADDED MP.JsonEncode() and MP.JsonDecode(), which turn lua tables into json and vice-versa - ADDED FS.ListFiles and FS.ListDirectories diff --git a/src/Common.cpp b/src/Common.cpp index ac6319c..43c1253 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -105,7 +105,7 @@ void Application::CheckForUpdates() { if (Matches) { auto MyVersion = ServerVersion(); auto RemoteVersion = Version(VersionStrToInts(Response)); - if (IsOutdated(MyVersion, RemoteVersion)) { + if (!IsOutdated(MyVersion, RemoteVersion)) { 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)); } else { diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 42e9bc4..d8f11d0 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -573,7 +573,7 @@ TConsole::TConsole() { beammp_error("Console died with: " + std::string(e.what()) + ". This could be a fatal error and could cause the server to terminate."); } }; - mCommandline.on_autocomplete = [this](Commandline& c, std::string stub) { + mCommandline.on_autocomplete = [this](Commandline& c, std::string stub, int) { std::vector suggestions; try { auto cmd = TrimString(stub); From dbfe4a4d119fa0ff2f4f6f3a0269a9087c1b3207 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 24 Mar 2022 14:05:40 +0100 Subject: [PATCH 38/51] Fix inconsistencies with handling errors in early network startup In most cases, when socket creation, bind, listen, or similar fails, it's best to gracefully shutdown. We do that now. --- include/Compat.h | 11 +++++++--- src/Common.cpp | 3 ++- src/TNetwork.cpp | 52 ++++++++++++++++++++++++++++-------------------- 3 files changed, 40 insertions(+), 26 deletions(-) diff --git a/include/Compat.h b/include/Compat.h index e1e906d..1a3cc63 100644 --- a/include/Compat.h +++ b/include/Compat.h @@ -6,10 +6,10 @@ #ifdef BEAMMP_LINUX #include +#include #include #include #include -#include using SOCKET = int; using DWORD = unsigned long; using PDWORD = unsigned long*; @@ -25,10 +25,10 @@ inline void CloseSocketProper(int TheSocket) { #ifdef BEAMMP_APPLE #include +#include #include #include #include -#include using SOCKET = int; using DWORD = unsigned long; using PDWORD = unsigned long*; @@ -48,6 +48,11 @@ inline void CloseSocketProper(int TheSocket) { inline void CloseSocketProper(SOCKET TheSocket) { shutdown(TheSocket, 2); // 2 == SD_BOTH closesocket(TheSocket); - } #endif // WIN32 + +#ifdef INVALID_SOCKET +static inline constexpr int BEAMMP_INVALID_SOCKET = INVALID_SOCKET; +#else +static inline constexpr int BEAMMP_INVALID_SOCKET = -1; +#endif diff --git a/src/Common.cpp b/src/Common.cpp index 43c1253..efdf240 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -43,6 +43,7 @@ void Application::GracefullyShutdown() { beammp_info("Subsystem " + std::to_string(i + 1) + "/" + std::to_string(mShutdownHandlers.size()) + " shutting down"); mShutdownHandlers[i](); } + // std::exit(-1); } std::string Application::ServerVersionString() { @@ -105,7 +106,7 @@ void Application::CheckForUpdates() { if (Matches) { auto MyVersion = ServerVersion(); auto RemoteVersion = Version(VersionStrToInts(Response)); - if (!IsOutdated(MyVersion, RemoteVersion)) { + if (IsOutdated(MyVersion, RemoteVersion)) { 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)); } else { diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index 1e0e5be..6a86788 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -108,40 +108,48 @@ void TNetwork::TCPServerMain() { #if defined(BEAMMP_WINDOWS) WSADATA wsaData; if (WSAStartup(514, &wsaData)) { - beammp_error("Can't start Winsock!"); - return; + beammp_error("Can't start Winsock! Shutting down"); + Application::GracefullyShutdown(); } #endif // WINDOWS TConnection client {}; SOCKET Listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - int optval = 1; + if (Listener == BEAMMP_INVALID_SOCKET) { + beammp_error("Failed to create socket: " + GetPlatformAgnosticErrorString() + + ". This is a fatal error, as a socket is needed for the server to operate. Shutting down."); + Application::GracefullyShutdown(); + } #if defined(BEAMMP_WINDOWS) - const char* optval_ptr = reinterpret_cast(&optval); + const char optval = 0; + int ret = ::setsockopt(Listener, SOL_SOCKET, SO_DONTLINGER, &optval, sizeof(optval)); #elif defined(BEAMMP_LINUX) || defined(BEAMMP_APPLE) - void* optval_ptr = reinterpret_cast(&optval); + int optval = true; + int ret = ::setsockopt(Listener, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&optval), sizeof(optval)); #endif - setsockopt(Listener, SOL_SOCKET, SO_REUSEADDR, optval_ptr, sizeof(optval)); - // TODO: check optval or return value idk + // not a fatal error + if (ret < 0) { + beammp_error("Failed to set up listening socket to not linger / reuse address. " + "This may cause the socket to refuse to bind(). Error: " + + GetPlatformAgnosticErrorString()); + } sockaddr_in addr {}; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_family = AF_INET; addr.sin_port = htons(uint16_t(Application::Settings.Port)); - if (bind(Listener, (sockaddr*)&addr, sizeof(addr)) != 0) { - beammp_error("bind() failed: " + GetPlatformAgnosticErrorString()); - std::this_thread::sleep_for(std::chrono::seconds(5)); - exit(-1); // TODO: Wtf. + if (bind(Listener, (sockaddr*)&addr, sizeof(addr)) < 0) { + beammp_error("bind() failed, the server cannot operate and will shut down now. " + "Error: " + + GetPlatformAgnosticErrorString()); + Application::GracefullyShutdown(); } - if (Listener == -1) { - beammp_error("Invalid listening socket"); - return; - } - if (listen(Listener, SOMAXCONN)) { - beammp_error("listen() failed: " + GetPlatformAgnosticErrorString()); - // FIXME leak Listener - return; + if (listen(Listener, SOMAXCONN) < 0) { + beammp_error("listen() failed, which is needed for the server to operate. " + "Shutting down. Error: " + + GetPlatformAgnosticErrorString()); + Application::GracefullyShutdown(); } Application::SetSubsystemStatus("TCPNetwork", Application::Status::Good); - beammp_info(("Vehicle event network online")); + beammp_info("Vehicle event network online"); do { try { if (mShutdown) { @@ -157,9 +165,9 @@ void TNetwork::TCPServerMain() { std::thread ID(&TNetwork::Identify, this, client); ID.detach(); // TODO: Add to a queue and attempt to join periodically } catch (const std::exception& e) { - beammp_error(("fatal: ") + std::string(e.what())); + beammp_error("fatal: " + std::string(e.what())); } - } while (client.Socket); + } while (client.Socket != BEAMMP_INVALID_SOCKET); beammp_debug("all ok, arrived at " + std::string(__func__) + ":" + std::to_string(__LINE__)); From 5180c96e2b00b851065948d77a0da5b5fc117537 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 24 Mar 2022 14:06:50 +0100 Subject: [PATCH 39/51] TServer::HandleEvent: Fix mistreatment of ':' in event data --- src/TServer.cpp | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/src/TServer.cpp b/src/TServer.cpp index cee40b3..85e5566 100644 --- a/src/TServer.cpp +++ b/src/TServer.cpp @@ -150,26 +150,18 @@ void TServer::GlobalParser(const std::weak_ptr& Client, std::string Pac } } -void TServer::HandleEvent(TClient& c, const std::string& Data) { - std::stringstream ss(Data); - std::string t, Name; - int a = 0; - while (std::getline(ss, t, ':')) { - switch (a) { - case 1: - Name = t; - break; - case 2: - LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent(Name, "", c.GetID(), t)); - break; - default: - break; - } - if (a == 2) - break; - a++; +void TServer::HandleEvent(TClient& c, const std::string& RawData) { + // E:Name:Data + // Data is allowed to have ':' + auto NameDataSep = RawData.find(':', 2); + if (NameDataSep == std::string::npos) { + beammp_warn("received event in invalid format (missing ':'), got: '" + RawData + "'"); } + std::string Name = RawData.substr(2, NameDataSep - 2); + std::string Data = RawData.substr(NameDataSep + 1); + LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent(Name, "", c.GetID(), Data)); } + bool TServer::IsUnicycle(TClient& c, const std::string& CarJson) { try { auto Car = nlohmann::json::parse(CarJson); From d0bb32ec63df9171648d61e02d960eb55655e8be Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 24 Mar 2022 14:26:02 +0100 Subject: [PATCH 40/51] cleanup fixme's, todo's --- src/Client.cpp | 2 -- src/Common.cpp | 1 - src/TLuaEngine.cpp | 1 - src/TNetwork.cpp | 2 +- src/TServer.cpp | 2 +- 5 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Client.cpp b/src/Client.cpp index d5736fe..fe90463 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -5,8 +5,6 @@ #include #include -// FIXME: add debug prints - void TClient::DeleteCar(int Ident) { std::unique_lock lock(mVehicleDataMutex); auto iter = std::find_if(mVehicleData.begin(), mVehicleData.end(), [&](auto& elem) { diff --git a/src/Common.cpp b/src/Common.cpp index efdf240..5de708d 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -61,7 +61,6 @@ std::array Application::VersionStrToInts(const std::string& str) { return Version; } -// FIXME: This should be used by operator< on Version bool Application::IsOutdated(const Version& Current, const Version& Newest) { if (Newest.major > Current.major) { return true; diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 392807b..db5e0b3 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -327,7 +327,6 @@ std::set TLuaEngine::GetEventHandlersForState(const std::string& Ev sol::table TLuaEngine::StateThreadData::Lua_TriggerGlobalEvent(const std::string& EventName, sol::variadic_args EventArgs) { auto Return = mEngine->TriggerEvent(EventName, mStateId, EventArgs); - // TODO Synchronous call to the event handlers auto MyHandlers = mEngine->GetEventHandlersForState(EventName, mStateId); for (const auto& Handler : MyHandlers) { auto Fn = mStateView[Handler]; diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index 6a86788..e4b05df 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -472,7 +472,7 @@ std::string TNetwork::TCPRcv(TClient& c) { void TNetwork::ClientKick(TClient& c, const std::string& R) { beammp_info("Client kicked: " + R); if (!TCPSend(c, "K" + R)) { - // TODO handle + beammp_warn("tried to kick player '" + c.GetName() + "' (id " + std::to_string(c.GetID()) + "), but was already disconnected"); } c.SetStatus(-2); diff --git a/src/TServer.cpp b/src/TServer.cpp index 85e5566..48b2bbb 100644 --- a/src/TServer.cpp +++ b/src/TServer.cpp @@ -125,7 +125,7 @@ void TServer::GlobalParser(const std::weak_ptr& Client, std::string Pac break; auto Futures = LuaAPI::MP::Engine->TriggerEvent("onChatMessage", "", LockedClient->GetID(), LockedClient->GetName(), Packet.substr(Packet.find(':', 3) + 2)); TLuaEngine::WaitForAll(Futures); - LogChatMessage(LockedClient->GetName(), LockedClient->GetID(), Packet.substr(Packet.find(':', 3) + 1)); // FIXME: this needs to be adjusted once lua is merged + LogChatMessage(LockedClient->GetName(), LockedClient->GetID(), Packet.substr(Packet.find(':', 3) + 1)); if (std::any_of(Futures.begin(), Futures.end(), [](const std::shared_ptr& Elem) { return !Elem->Error From 9e0d02c6db7f2165a3a083001eddd23dab7e6055 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 24 Mar 2022 14:36:39 +0100 Subject: [PATCH 41/51] add fmt library, add beammp_*f --- .gitmodules | 3 +++ deps/CMakeLists.txt | 1 + deps/fmt | 1 + include/Common.h | 8 +++++++- 4 files changed, 12 insertions(+), 1 deletion(-) create mode 160000 deps/fmt diff --git a/.gitmodules b/.gitmodules index b54a97b..6293066 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,3 +25,6 @@ [submodule "deps/json"] path = deps/json url = https://github.com/nlohmann/json +[submodule "deps/fmt"] + path = deps/fmt + url = https://github.com/fmtlib/fmt diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 4e556b0..6167b39 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -7,3 +7,4 @@ include_directories("${PROJECT_SOURCE_DIR}/deps") add_subdirectory("${PROJECT_SOURCE_DIR}/deps/commandline") add_subdirectory("${PROJECT_SOURCE_DIR}/deps/sol2") +add_subdirectory("${PROJECT_SOURCE_DIR}/deps/fmt") diff --git a/deps/fmt b/deps/fmt new file mode 160000 index 0000000..17dda58 --- /dev/null +++ b/deps/fmt @@ -0,0 +1 @@ +Subproject commit 17dda58391fba627a56482f5d2652a396619bc26 diff --git a/include/Common.h b/include/Common.h index 76640aa..289b81e 100644 --- a/include/Common.h +++ b/include/Common.h @@ -7,12 +7,13 @@ extern TSentry Sentry; #include #include #include +#include +#include #include #include #include #include #include -#include #include "Compat.h" @@ -208,6 +209,11 @@ void RegisterThread(const std::string& str); #define beammp_trace(x) #endif // defined(DEBUG) +#define beammp_errorf(_format, _args) beammp_error(fmt::format(_format, _args)) +#define beammp_infof(_format, _args) beammp_info(fmt::format(_format, _args)) +#define beammp_warnf(_format, _args) beammp_warn(fmt::format(_format, _args)) +#define beammp_tracef(_format, _args) beammp_trace(fmt::format(_format, _args)) + void LogChatMessage(const std::string& name, int id, const std::string& msg); #define Biggest 30000 From 7a814ed35e9c5e41d5ba3a4aa77c0ebe8c57797a Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 24 Mar 2022 14:45:53 +0100 Subject: [PATCH 42/51] use fmt properly in beammp_*f logging functions --- CMakeLists.txt | 2 +- include/Common.h | 10 ++++++---- include/TConsole.h | 2 +- src/ArgsParser.cpp | 4 ++-- src/TConsole.cpp | 16 ++++++++-------- src/TNetwork.cpp | 2 +- src/main.cpp | 2 +- 7 files changed, 20 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a36c83..7722c97 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -138,7 +138,7 @@ else() find_package(OpenSSL REQUIRED) endif() -target_link_libraries(BeamMP-Server sol2::sol2 ${LUA_LIBRARIES}) +target_link_libraries(BeamMP-Server sol2::sol2 ${LUA_LIBRARIES} fmt::fmt) message(STATUS "CURL IS ${CURL_LIBRARIES}") if (UNIX) diff --git a/include/Common.h b/include/Common.h index 289b81e..4872408 100644 --- a/include/Common.h +++ b/include/Common.h @@ -209,10 +209,12 @@ void RegisterThread(const std::string& str); #define beammp_trace(x) #endif // defined(DEBUG) -#define beammp_errorf(_format, _args) beammp_error(fmt::format(_format, _args)) -#define beammp_infof(_format, _args) beammp_info(fmt::format(_format, _args)) -#define beammp_warnf(_format, _args) beammp_warn(fmt::format(_format, _args)) -#define beammp_tracef(_format, _args) beammp_trace(fmt::format(_format, _args)) +#define beammp_errorf(...) beammp_error(fmt::format(__VA_ARGS__)) +#define beammp_infof(...) beammp_info(fmt::format(__VA_ARGS__)) +#define beammp_warnf(...) beammp_warn(fmt::format(__VA_ARGS__)) +#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__)) void LogChatMessage(const std::string& name, int id, const std::string& msg); diff --git a/include/TConsole.h b/include/TConsole.h index c181c5c..aa2fcbd 100644 --- a/include/TConsole.h +++ b/include/TConsole.h @@ -49,7 +49,7 @@ private: { "list", [this](const auto& a, const auto& b) { Command_List(a, b); } }, { "status", [this](const auto& a, const auto& b) { Command_Status(a, b); } }, { "settings", [this](const auto& a, const auto& b) { Command_Settings(a, b); } }, - { "say", [this](const auto& a, const auto& b) { Command_Say(""); } }, // shouldn't actually be called + { "say", [this](const auto&, const auto&) { Command_Say(""); } }, // shouldn't actually be called }; Commandline mCommandline; diff --git a/src/ArgsParser.cpp b/src/ArgsParser.cpp index 8440661..f00548f 100644 --- a/src/ArgsParser.cpp +++ b/src/ArgsParser.cpp @@ -12,7 +12,7 @@ void ArgsParser::Parse(const std::vector& ArgList) { ConsumeLongFlag(std::string(Arg)); } } else { - beammp_error("Error parsing commandline arguments: Supplied argument '" + std::string(Arg) + "' is not a valid argument and was ignored."); + beammp_errorf("Error parsing commandline arguments: Supplied argument '{}' is not a valid argument and was ignored.", Arg); } } } @@ -21,7 +21,7 @@ bool ArgsParser::Verify() { bool Ok = true; for (const auto& RegisteredArg : mRegisteredArguments) { if (RegisteredArg.Flags & Flags::REQUIRED && !FoundArgument(RegisteredArg.Names)) { - beammp_error("Error in commandline arguments: Argument '" + std::string(RegisteredArg.Names.at(0)) + "' is required but wasn't found."); + beammp_errorf("Error in commandline arguments: Argument '{}' is required but wasn't found.", RegisteredArg.Names.at(0)); Ok = false; continue; } else if (FoundArgument(RegisteredArg.Names)) { diff --git a/src/TConsole.cpp b/src/TConsole.cpp index d8f11d0..480981a 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -160,7 +160,7 @@ bool TConsole::EnsureArgsCount(const std::vector& args, size_t min, return true; } -void TConsole::Command_Lua(const std::string& cmd, const std::vector& args) { +void TConsole::Command_Lua(const std::string&, const std::vector& args) { if (!EnsureArgsCount(args, 0, 1)) { return; } @@ -203,7 +203,7 @@ std::string TConsole::ConcatArgs(const std::vector& args, char spac return Result; } -void TConsole::Command_Kick(const std::string& cmd, const std::vector& args) { +void TConsole::Command_Kick(const std::string&, const std::vector& args) { if (!EnsureArgsCount(args, 1, size_t(-1))) { return; } @@ -290,7 +290,7 @@ std::tuple> TConsole::ParseCommand(const s return { Command, Args }; } -void TConsole::Command_Settings(const std::string& cmd, const std::vector& args) { +void TConsole::Command_Settings(const std::string&, const std::vector& args) { if (!EnsureArgsCount(args, 0)) { return; } @@ -573,12 +573,12 @@ TConsole::TConsole() { beammp_error("Console died with: " + std::string(e.what()) + ". This could be a fatal error and could cause the server to terminate."); } }; - mCommandline.on_autocomplete = [this](Commandline& c, std::string stub, int) { + mCommandline.on_autocomplete = [this](Commandline&, std::string stub, int) { std::vector suggestions; try { auto cmd = TrimString(stub); - //beammp_error("yes 1"); - //beammp_error(stub); + // beammp_error("yes 1"); + // beammp_error(stub); if (mIsLuaConsole) { // if lua if (!mLuaEngine) { beammp_info("Lua not started yet, please try again in a second"); @@ -596,7 +596,7 @@ TConsole::TConsole() { std::string::size_type n = key.find(stub); if (n == 0) { suggestions.push_back(prefix + key); - //beammp_warn(cmd_name); + // beammp_warn(cmd_name); } } } @@ -605,7 +605,7 @@ TConsole::TConsole() { std::string::size_type n = cmd_name.find(stub); if (n == 0) { suggestions.push_back(cmd_name); - //beammp_warn(cmd_name); + // beammp_warn(cmd_name); } } } diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index e4b05df..72f489c 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -116,7 +116,7 @@ void TNetwork::TCPServerMain() { SOCKET Listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (Listener == BEAMMP_INVALID_SOCKET) { beammp_error("Failed to create socket: " + GetPlatformAgnosticErrorString() - + ". This is a fatal error, as a socket is needed for the server to operate. Shutting down."); + + ". This is a fatal error, as a socket is needed for the server to operate. Shutting down."); Application::GracefullyShutdown(); } #if defined(BEAMMP_WINDOWS) diff --git a/src/main.cpp b/src/main.cpp index 8b2f3c5..a9cc089 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -113,7 +113,7 @@ int BeamMPServerMain(MainArguments Arguments) { try { fs::current_path(fs::path(MaybeWorkingDirectory.value())); } catch (const std::exception& e) { - beammp_error("Could not set working directory to '" + MaybeWorkingDirectory.value() + "': " + e.what()); + beammp_errorf("Could not set working directory to '{}': {}", MaybeWorkingDirectory.value(), e.what()); } } } From cd4332b79003506296cece3b11f99f27f4fcebff Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 24 Mar 2022 14:47:15 +0100 Subject: [PATCH 43/51] prepend . to Threads.log to make it invisible on *nix --- src/Common.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Common.cpp b/src/Common.cpp index 5de708d..916873a 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -154,7 +154,7 @@ void RegisterThread(const std::string& str) { ThreadId = std::to_string(gettid()); #endif if (Application::Settings.DebugModeEnabled) { - std::ofstream ThreadFile("Threads.log", std::ios::app); + std::ofstream ThreadFile(".Threads.log", std::ios::app); ThreadFile << ("Thread \"" + str + "\" is TID " + ThreadId) << std::endl; } auto Lock = std::unique_lock(ThreadNameMapMutex); From b780a08f73cdef9ecfff67e0d9f81c73ec65b02c Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 24 Mar 2022 15:16:24 +0100 Subject: [PATCH 44/51] use MB constant --- src/Common.cpp | 4 +--- src/TNetwork.cpp | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Common.cpp b/src/Common.cpp index 916873a..79b5939 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -171,9 +171,7 @@ Version::Version(const std::array& v) } std::string Version::AsString() { - std::stringstream ss {}; - ss << int(major) << "." << int(minor) << "." << int(patch); - return ss.str(); + return fmt::format("{:d}.{:d}.{:d}", major, minor, patch); } void LogChatMessage(const std::string& name, int id, const std::string& msg) { diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index 72f489c..e229f69 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -749,7 +749,7 @@ void TNetwork::SendFile(TClient& c, const std::string& UnsafeName) { void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std::string& Name) { std::ifstream f(Name.c_str(), std::ios::binary); - uint32_t Split = 0x7735940; // 125MB + uint32_t Split = 125 * MB; char* Data; if (Size > Split) Data = new char[Split]; From b2f27c21be79fea736c46b698fabaf68a3adeae2 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 24 Mar 2022 16:53:39 +0100 Subject: [PATCH 45/51] update changelog --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 18580b3..3d10f2e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ - ADDED lua debug facilities (type `:help` when attached to lua via `lua`) - ADDED MP.JsonEncode() and MP.JsonDecode(), which turn lua tables into json and vice-versa - ADDED FS.ListFiles and FS.ListDirectories +- FIXED issue with client->server events which contain ':' # v3.0.1 From d86efabb1aadb6125d63ec23d1105550378ef803 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 30 Mar 2022 12:14:13 +0200 Subject: [PATCH 46/51] Modernize CMakeLists, automatically update submodules CMake will now find packages in a modern way (include(Find*)), and will also ensure that submodules are updated, unless told otherwise. Also removed some apple-specific workarounds, we will need to look at that again. --- CMakeLists.txt | 136 ++++++++++++++++++++--------------------------- include/Common.h | 4 ++ 2 files changed, 62 insertions(+), 78 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7722c97..3fb1abf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,5 @@ -cmake_minimum_required(VERSION 3.0) +# 3.4 is required for imported targets. +cmake_minimum_required(VERSION 3.4 FATAL_ERROR) message(STATUS "You can find build instructions and a list of dependencies in the README at \ https://github.com/BeamMP/BeamMP-Server") @@ -8,7 +9,22 @@ project(BeamMP-Server HOMEPAGE_URL https://beammp.com LANGUAGES CXX C) +find_package(Git REQUIRED) +# Update submodules as needed +option(GIT_SUBMODULE "Check submodules during build" ON) +if(GIT_SUBMODULE) + message(STATUS "Submodule update") + execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + RESULT_VARIABLE GIT_SUBMOD_RESULT) + if(NOT GIT_SUBMOD_RESULT EQUAL "0") + message(FATAL_ERROR "git submodule update --init --recursive failed with ${GIT_SUBMOD_RESULT}, please checkout submodules") + endif() +endif() + + set(HTTPLIB_REQUIRE_OPENSSL ON) +set(SENTRY_BUILD_SHARED_LIBS OFF) include_directories("${PROJECT_SOURCE_DIR}/deps/asio/asio/include") include_directories("${PROJECT_SOURCE_DIR}/deps/rapidjson/include") @@ -21,26 +37,40 @@ include_directories("${PROJECT_SOURCE_DIR}/deps") add_compile_definitions(CPPHTTPLIB_OPENSSL_SUPPORT) +# ------------------------ APPLE --------------------------------- if(APPLE) set(LUA_INCLUDE_DIR /usr/local/Cellar/lua@5.3/5.3.6/include/lua5.3) set(LUA_LIBRARIES lua) include_directories(/usr/local/opt/openssl@1.1/include) link_directories(/usr/local/Cellar/lua@5.3/5.3.6/lib) link_directories(/usr/local/opt/openssl@1.1/lib) -endif() - -if (WIN32) +# ------------------------ WINDOWS --------------------------------- +elseif (WIN32) # this has to happen before sentry, so that crashpad on windows links with these settings. message(STATUS "MSVC -> forcing use of statically-linked runtime.") STRING(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE}) STRING(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) -endif() + if (MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj") + set(SENTRY_BUILD_RUNTIMESTATIC ON) + endif () + set(VcpkgRoot ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}) + include_directories(${VcpkgRoot}/include) + link_directories(${VcpkgRoot}/lib) +# ------------------------ LINUX --------------------------------- +elseif (UNIX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -static-libstdc++ -static-libgcc") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -fno-builtin") + if (SANITIZE) + message(STATUS "sanitize is ON") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined,thread") + endif (SANITIZE) +endif () include_directories("include/sentry-native/include") -set(SENTRY_BUILD_SHARED_LIBS OFF) -if (MSVC) - set(SENTRY_BUILD_RUNTIMESTATIC ON) -endif() + +# ------------------------ SENTRY --------------------------------- message(STATUS "Checking for Sentry URL") # this is set by the build system. # IMPORTANT: if you're building from source, just leave this empty @@ -50,41 +80,15 @@ if (NOT DEFINED BEAMMP_SECRET_SENTRY_URL) set(BEAMMP_SECRET_SENTRY_URL "") set(SENTRY_BACKEND none) else() - string(LENGTH ${BEAMMP_SECRET_SENTRY_URL} URL_LEN) - message(STATUS "Sentry URL is length ${URL_LEN}") set(SENTRY_BACKEND breakpad) endif() add_subdirectory("deps/sentry-native") -if (MSVC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj") -endif () - -message(STATUS "Setting compiler flags") -if (WIN32) - - #-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static - set(VcpkgRoot ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}) - include_directories(${VcpkgRoot}/include) - link_directories(${VcpkgRoot}/lib) -elseif (UNIX) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -static-libstdc++") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -fno-builtin") - if (SANITIZE) - message(STATUS "sanitize is ON") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined,thread") - endif (SANITIZE) -endif () - - -message(STATUS "Adding local source dependencies") -# this has to happen before -DDEBUG since it wont compile properly with -DDEBUG -add_subdirectory(deps) - +# ------------------------ C++ SETUP --------------------------------- set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") +# ------------------------ DEPENDENCIES ------------------------------ +add_subdirectory(deps) add_executable(BeamMP-Server src/main.cpp @@ -116,12 +120,10 @@ target_include_directories(BeamMP-Server PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/commandline") -if (APPLE) - message(STATUS "NOT looking for Lua on APPLE") -else() - message(STATUS "Looking for Lua") - find_package(Lua REQUIRED VERSION 5.3) -endif() +include(FindLua) +include(FindOpenSSL) +include(FindThreads) +include(FindZLIB) target_include_directories(BeamMP-Server PUBLIC ${LUA_INCLUDE_DIR} @@ -130,39 +132,17 @@ target_include_directories(BeamMP-Server PUBLIC "include/sentry-native/include" "include/curl/include") -message(STATUS "Looking for SSL") +target_link_libraries(BeamMP-Server + OpenSSL::SSL + OpenSSL::Crypto + sol2::sol2 + fmt::fmt + Threads::Threads + ZLIB::ZLIB + ${LUA_LIBRARIES} + commandline + sentry) -if (APPLE) - set(OPENSSL_LIBRARIES ssl crypto) -else() - find_package(OpenSSL REQUIRED) -endif() - -target_link_libraries(BeamMP-Server sol2::sol2 ${LUA_LIBRARIES} fmt::fmt) -message(STATUS "CURL IS ${CURL_LIBRARIES}") - -if (UNIX) - target_link_libraries(BeamMP-Server - z - pthread - ${LUA_LIBRARIES} - crypto - ${OPENSSL_LIBRARIES} - commandline - sentry - ssl) -elseif (WIN32) - include(FindLua) - message(STATUS "Looking for libz") - find_package(ZLIB REQUIRED) - message(STATUS "Looking for RapidJSON") - find_package(RapidJSON CONFIG REQUIRED) - target_include_directories(BeamMP-Server PRIVATE ${RAPIDJSON_INCLUDE_DIRS}) - target_link_libraries(BeamMP-Server - ws2_32 - ZLIB::ZLIB - ${LUA_LIBRARIES} - ${OPENSSL_LIBRARIES} - commandline - sentry) +if (WIN32) + target_link_libraries(BeamMP-Server wsock32 ws2_32) endif () diff --git a/include/Common.h b/include/Common.h index 4872408..b46cc7c 100644 --- a/include/Common.h +++ b/include/Common.h @@ -148,6 +148,10 @@ void RegisterThread(const std::string& str); #define _function_name std::string(__func__) #endif +#ifndef NDEBUG +#define DEBUG +#endif + #if defined(DEBUG) // if this is defined, we will show the full function signature infront of From 450f0a6875c7e9c84b8069007ae1b7ec788e5dbe Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 31 Mar 2022 22:17:10 +0200 Subject: [PATCH 47/51] Fixup merge --- include/TLuaEngine.h | 4 ++-- src/LuaAPI.cpp | 2 +- src/TConsole.cpp | 6 +++--- src/TLuaEngine.cpp | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index 234cafb..a49477b 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -173,7 +173,7 @@ public: // Debugging functions (slow) std::unordered_map /* handlers */> Debug_GetEventsForState(TLuaStateId StateId); std::queue>> Debug_GetStateExecuteQueueForState(TLuaStateId StateId); - std::queue, std::vector>> Debug_GetStateFunctionQueueForState(TLuaStateId StateId); + std::vector Debug_GetStateFunctionQueueForState(TLuaStateId StateId); std::vector Debug_GetResultsToCheckForState(TLuaStateId StateId); private: @@ -199,7 +199,7 @@ private: // Debug functions, slow std::queue>> Debug_GetStateExecuteQueue(); - std::queue, std::vector>> Debug_GetStateFunctionQueue(); + std::vector Debug_GetStateFunctionQueue(); private: sol::table Lua_TriggerGlobalEvent(const std::string& EventName, sol::variadic_args EventArgs); diff --git a/src/LuaAPI.cpp b/src/LuaAPI.cpp index 12c1a6b..4a5df29 100644 --- a/src/LuaAPI.cpp +++ b/src/LuaAPI.cpp @@ -115,7 +115,7 @@ static inline bool InternalTriggerClientEvent(int PlayerID, const std::string& E auto c = MaybeClient.value().lock(); if (!LuaAPI::MP::Engine->Network().Respond(*c, Packet, true)) { beammp_lua_error("Respond failed, dropping client " + std::to_string(PlayerID)); - Engine->Network().ClientKick(*c, "Disconnected after failing to receive packets"); + LuaAPI::MP::Engine->Network().ClientKick(*c, "Disconnected after failing to receive packets"); return false; } } diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 480981a..f347a27 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -484,9 +484,9 @@ void TConsole::HandleLuaInternalCommand(const std::string& cmd) { std::vector FunctionsInOrder; while (!QueuedFunctions.empty()) { auto Tuple = QueuedFunctions.front(); - QueuedFunctions.pop(); - FunctionsInOrder.push_back(std::get<0>(Tuple)); - FunctionsCount[std::get<0>(Tuple)] += 1; + QueuedFunctions.erase(QueuedFunctions.begin()); + FunctionsInOrder.push_back(Tuple.FunctionName); + FunctionsCount[Tuple.FunctionName] += 1; } std::set Uniques; for (const auto& Function : FunctionsInOrder) { diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index e85a83c..1f2011a 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -177,8 +177,8 @@ std::queue>> TLuaEngine::Debug_ return Result; } -std::queue, std::vector>> TLuaEngine::Debug_GetStateFunctionQueueForState(TLuaStateId StateId) { - std::queue, std::vector>> Result; +std::vector TLuaEngine::Debug_GetStateFunctionQueueForState(TLuaStateId StateId) { + std::vector Result; std::unique_lock Lock(mLuaStatesMutex); Result = mLuaStates.at(StateId)->Debug_GetStateFunctionQueue(); return Result; @@ -191,7 +191,7 @@ std::vector TLuaEngine::Debug_GetResultsToCheckForState(TLuaStateId std::vector Result; while (!ResultsToCheckCopy.empty()) { auto ResultToCheck = std::move(ResultsToCheckCopy.front()); - ResultsToCheckCopy.pop(); + ResultsToCheckCopy.pop_front(); if (ResultToCheck->StateId == StateId) { Result.push_back(*ResultToCheck); } @@ -917,7 +917,7 @@ std::queue>> TLuaEngine::StateT return mStateExecuteQueue; } -std::queue, std::vector>> TLuaEngine::StateThreadData::Debug_GetStateFunctionQueue() { +std::vector TLuaEngine::StateThreadData::Debug_GetStateFunctionQueue() { std::unique_lock Lock(mStateFunctionQueueMutex); return mStateFunctionQueue; } From 5755ead9be9b510c57013b976d3f682bcf929735 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 31 Mar 2022 22:18:55 +0200 Subject: [PATCH 48/51] Change :detach to :exit @20dka --- src/TConsole.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TConsole.cpp b/src/TConsole.cpp index f347a27..0688fe3 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -475,7 +475,7 @@ void TConsole::RunAsCommand(const std::string& cmd, bool IgnoreNotACommand) { } void TConsole::HandleLuaInternalCommand(const std::string& cmd) { - if (cmd == "detach") { + if (cmd == "exit") { ChangeToRegularConsole(); } else if (cmd == "queued") { auto QueuedFunctions = LuaAPI::MP::Engine->Debug_GetStateFunctionQueueForState(mStateId); @@ -517,7 +517,7 @@ void TConsole::HandleLuaInternalCommand(const std::string& cmd) { All commands must be prefixed with a `:`. Non-prefixed commands are interpreted as Lua. Commands - :detach detaches (exits) from this Lua console + :exit detaches (exits) from this Lua console :help displays this help :events shows a list of currently registered events :queued shows a list of all pending and queued functions)"); From 23af76dba1d27e5b9643557ceb6a8e5a18889f8a Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 31 Mar 2022 23:12:50 +0200 Subject: [PATCH 49/51] Only warn once about event handlers taking >60s --- src/TLuaEngine.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 1f2011a..e5885ec 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -218,6 +218,7 @@ void TLuaEngine::WaitForAll(std::vector>& Results, c for (const auto& Result : Results) { bool Cancelled = false; size_t ms = 0; + std::set WarnedResults; while (!Result->Ready && !Cancelled) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); ms += 10; @@ -225,7 +226,11 @@ void TLuaEngine::WaitForAll(std::vector>& Results, c beammp_trace("'" + Result->Function + "' in '" + Result->StateId + "' did not finish executing in time (took: " + std::to_string(ms) + "ms)."); 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."); + auto ResultId = Result->StateId + "_" + Result->Function; + if (WarnedResults.count(ResultId) == 0) { + WarnedResults.insert(ResultId); + 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) { From 952631bb807010265162c498a39281bcbd4b8632 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 31 Mar 2022 23:48:07 +0200 Subject: [PATCH 50/51] add send timeout to client tcp socket --- src/TNetwork.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index 3490d5e..356d07c 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -162,6 +162,19 @@ void TNetwork::TCPServerMain() { beammp_warn(("Got an invalid client socket on connect! Skipping...")); continue; } + // set timeout + size_t SendTimeoutMS = 30 * 1000; +#if defined(BEAMMP_WINDOWS) + ret = ::setsockopt(client.Socket, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast(&ms), sizeof(ms)); +#else // POSIX + struct timeval optval; + optval.tv_sec = (int)(SendTimeoutMS / 1000); + optval.tv_usec = (SendTimeoutMS % 1000) * 1000; + ret = ::setsockopt(client.Socket, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast(&optval), sizeof(optval)); +#endif + if (ret < 0) { + throw std::runtime_error("setsockopt recv timeout: " + GetPlatformAgnosticErrorString()); + } std::thread ID(&TNetwork::Identify, this, client); ID.detach(); // TODO: Add to a queue and attempt to join periodically } catch (const std::exception& e) { From ed03096cf59301501d4269d0265742fe000c4c4c Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 28 Apr 2022 14:58:07 +0200 Subject: [PATCH 51/51] Windows moment Windows deprecated when --- src/TConsole.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/TConsole.cpp b/src/TConsole.cpp index eefedac..8bc98c6 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -97,14 +97,6 @@ void TConsole::BackupOldLog() { } } -enum EscState { - None, - Escape, - FeSeqStart, - FeSeqMid, - SeqEnd -}; - void TConsole::StartLoggingToFile() { mLogFileStream.open("Server.log"); Application::Console().Internal().on_write = [this](const std::string& ToWrite) {