From fd12ee672d76d690e96dabb3b7e97612f557b966 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 26 Jan 2022 20:33:12 +0100 Subject: [PATCH 001/184] 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 002/184] 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 003/184] 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 004/184] 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 005/184] 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 006/184] 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 007/184] 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 008/184] 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 009/184] 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 010/184] 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 011/184] 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 012/184] 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 013/184] 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 014/184] 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 015/184] 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 016/184] 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 017/184] 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 018/184] 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 019/184] 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 020/184] 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 021/184] 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 022/184] 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 023/184] 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 024/184] 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 025/184] 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 026/184] 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 027/184] 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 028/184] 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 029/184] 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 030/184] 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 031/184] 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 032/184] 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 033/184] 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 034/184] 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 035/184] 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 036/184] 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 037/184] 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 038/184] 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 039/184] 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 040/184] 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 041/184] 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 042/184] 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 043/184] 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 044/184] 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 045/184] 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 046/184] 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 047/184] 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 048/184] 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 049/184] 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 050/184] 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 051/184] 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) { From 2dd181d49210f80d6c20dcf53b3a0c9eb7dae6d4 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 11:18:39 +0200 Subject: [PATCH 052/184] gracefully shutdown on bind() failure --- include/TLuaEngine.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index a49477b..32e5bbf 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -48,6 +48,7 @@ struct TLuaPluginConfig { static inline const std::string FileName = "PluginConfig.toml"; TLuaStateId StateId; // TODO: Add execute list + // TODO: Build a better toml serializer, or some way to do this in an easier way }; struct TLuaChunk { From 019c5202ea9bf44eb7baa3ac69661818b9fe1350 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 11:43:02 +0200 Subject: [PATCH 053/184] add tests executable --- .gitmodules | 3 + CMakeLists.txt | 131 ++++++++++++++++++++++++++++++-------------- deps/CMakeLists.txt | 1 + deps/doctest | 1 + test/test_main.cpp | 2 + 5 files changed, 98 insertions(+), 40 deletions(-) create mode 160000 deps/doctest create mode 100644 test/test_main.cpp diff --git a/.gitmodules b/.gitmodules index 6293066..e513ccf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,3 +28,6 @@ [submodule "deps/fmt"] path = deps/fmt url = https://github.com/fmtlib/fmt +[submodule "deps/doctest"] + path = deps/doctest + url = https://github.com/doctest/doctest diff --git a/CMakeLists.txt b/CMakeLists.txt index cb1aeab..204b76d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,51 +92,53 @@ message(STATUS "Adding local source dependencies") # this has to happen before -DDEBUG since it wont compile properly with -DDEBUG add_subdirectory(deps) -# ------------------------ BEAMMP SERVER ----------------------------- - -add_executable(BeamMP-Server - src/main.cpp - include/TConsole.h src/TConsole.cpp - include/TServer.h src/TServer.cpp - include/Compat.h src/Compat.cpp - include/Common.h src/Common.cpp - include/Client.h src/Client.cpp - include/VehicleData.h src/VehicleData.cpp - include/TConfig.h src/TConfig.cpp - include/TLuaEngine.h src/TLuaEngine.cpp - include/TLuaPlugin.h src/TLuaPlugin.cpp - include/TResourceManager.h src/TResourceManager.cpp - include/THeartbeatThread.h src/THeartbeatThread.cpp - include/Http.h src/Http.cpp - include/TSentry.h src/TSentry.cpp - include/TPPSMonitor.h src/TPPSMonitor.cpp - include/TNetwork.h src/TNetwork.cpp - include/LuaAPI.h src/LuaAPI.cpp - include/TScopedTimer.h src/TScopedTimer.cpp - include/SignalHandling.h src/SignalHandling.cpp - include/ArgsParser.h src/ArgsParser.cpp - include/Environment.h) - -target_compile_definitions(BeamMP-Server PRIVATE SECRET_SENTRY_URL="${BEAMMP_SECRET_SENTRY_URL}") -include_directories(BeamMP-Server PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) - -target_include_directories(BeamMP-Server PUBLIC - "${CMAKE_CURRENT_SOURCE_DIR}/include" - "${CMAKE_CURRENT_SOURCE_DIR}/commandline") +# ------------------------ VARIABLES --------------------------------- include(FindLua) include(FindOpenSSL) include(FindThreads) include(FindZLIB) -target_include_directories(BeamMP-Server PUBLIC - ${LUA_INCLUDE_DIR} - ${CURL_INCLUDE_DIRS} - "include/tomlplusplus" - "include/sentry-native/include" - "include/curl/include") +set(BeamMP_Sources + include/TConsole.h src/TConsole.cpp + include/TServer.h src/TServer.cpp + include/Compat.h src/Compat.cpp + include/Common.h src/Common.cpp + include/Client.h src/Client.cpp + include/VehicleData.h src/VehicleData.cpp + include/TConfig.h src/TConfig.cpp + include/TLuaEngine.h src/TLuaEngine.cpp + include/TLuaPlugin.h src/TLuaPlugin.cpp + include/TResourceManager.h src/TResourceManager.cpp + include/THeartbeatThread.h src/THeartbeatThread.cpp + include/Http.h src/Http.cpp + include/TSentry.h src/TSentry.cpp + include/TPPSMonitor.h src/TPPSMonitor.cpp + include/TNetwork.h src/TNetwork.cpp + include/LuaAPI.h src/LuaAPI.cpp + include/TScopedTimer.h src/TScopedTimer.cpp + include/SignalHandling.h src/SignalHandling.cpp + include/ArgsParser.h src/ArgsParser.cpp + include/Environment.h +) -target_link_libraries(BeamMP-Server +set(BeamMP_Includes + "${CMAKE_CURRENT_SOURCE_DIR}/include" + "${CMAKE_CURRENT_SOURCE_DIR}/commandline" + "${CMAKE_CURRENT_SOURCE_DIR}/include" + ${LUA_INCLUDE_DIR} + ${CURL_INCLUDE_DIRS} + "include/tomlplusplus" + "include/sentry-native/include" + "include/curl/include" +) + +set(BeamMP_Definitions + SECRET_SENTRY_URL="${BEAMMP_SECRET_SENTRY_URL}" +) + +set(BeamMP_Libraries + doctest::doctest OpenSSL::SSL OpenSSL::Crypto sol2::sol2 @@ -145,8 +147,57 @@ target_link_libraries(BeamMP-Server ZLIB::ZLIB ${LUA_LIBRARIES} commandline - sentry) + sentry +) if (WIN32) - target_link_libraries(BeamMP-Server wsock32 ws2_32) + set(BeamMP_PlatformLibs wsock32 ws2_32) endif () + +# ------------------------ BEAMMP SERVER ----------------------------- +# ------------------------ BEAMMP SERVER TESTS ----------------------- + +add_executable(BeamMP-Server + src/main.cpp + ${BeamMP_Sources} +) + +target_compile_definitions(BeamMP-Server PRIVATE + ${BeamMP_Definitions} + DOCTEST_CONFIG_DISABLE +) + +target_include_directories(BeamMP-Server PUBLIC + ${BeamMP_Includes} +) + +target_link_libraries(BeamMP-Server + ${BeamMP_Libraries} + ${BeamMP_PlatformLibs} +) + +# ------------------------ BEAMMP SERVER TESTS ----------------------- + +option(BUILD_TESTS "Build BeamMP-Server tests" ON) + +if(BUILD_TESTS) + add_executable(BeamMP-Server-tests + test/test_main.cpp + ${BeamMP_Sources} + ) + + target_compile_definitions(BeamMP-Server-tests PRIVATE + ${BeamMP_Definitions} + DOCTEST_CONFIG_DISABLE + ) + + target_include_directories(BeamMP-Server-tests PUBLIC + ${BeamMP_Includes} + ) + + target_link_libraries(BeamMP-Server-tests + ${BeamMP_Libraries} + ${BeamMP_PlatformLibs} + ) +endif() + diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 6167b39..9e5a7ec 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -8,3 +8,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") +add_subdirectory("${PROJECT_SOURCE_DIR}/deps/doctest") diff --git a/deps/doctest b/deps/doctest new file mode 160000 index 0000000..7b98851 --- /dev/null +++ b/deps/doctest @@ -0,0 +1 @@ +Subproject commit 7b9885133108ae301ddd16e2651320f54cafeba7 diff --git a/test/test_main.cpp b/test/test_main.cpp new file mode 100644 index 0000000..0a3f254 --- /dev/null +++ b/test/test_main.cpp @@ -0,0 +1,2 @@ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include From bc1628afeb8f2b28123a40d132f534c6dfc0fa9d Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 11:51:26 +0200 Subject: [PATCH 054/184] fix some sentry and linking related issues --- CMakeLists.txt | 3 --- src/Common.cpp | 3 +++ src/main.cpp | 4 ---- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 204b76d..3d45c23 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,7 +125,6 @@ set(BeamMP_Sources set(BeamMP_Includes "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/commandline" - "${CMAKE_CURRENT_SOURCE_DIR}/include" ${LUA_INCLUDE_DIR} ${CURL_INCLUDE_DIRS} "include/tomlplusplus" @@ -155,7 +154,6 @@ if (WIN32) endif () # ------------------------ BEAMMP SERVER ----------------------------- -# ------------------------ BEAMMP SERVER TESTS ----------------------- add_executable(BeamMP-Server src/main.cpp @@ -188,7 +186,6 @@ if(BUILD_TESTS) target_compile_definitions(BeamMP-Server-tests PRIVATE ${BeamMP_Definitions} - DOCTEST_CONFIG_DISABLE ) target_include_directories(BeamMP-Server-tests PUBLIC diff --git a/src/Common.cpp b/src/Common.cpp index 23c483d..77933bb 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -12,6 +12,9 @@ #include "CustomAssert.h" #include "Http.h" +// global, yes, this is ugly, no, it cant be done another way +TSentry Sentry {}; + Application::TSettings Application::Settings = {}; void Application::RegisterShutdownHandler(const TShutdownHandler& Handler) { diff --git a/src/main.cpp b/src/main.cpp index 99d194f..5878784 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -44,10 +44,6 @@ EXAMPLES: 'MyWestCoastServerConfig.toml'. )"; -// this is provided by the build system, leave empty for source builds -// global, yes, this is ugly, no, it cant be done another way -TSentry Sentry {}; - struct MainArguments { int argc {}; char** argv {}; From 4f69ca1ad03e8541ef358c6b4652cb01b4bd0bce Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Mon, 23 May 2022 16:59:31 +0200 Subject: [PATCH 055/184] remove ssl code @jimkoen This was removed because, as useful and as much work as this was, we can't reasonably take responsibility for this. Instead, a server like this should *always* be localhost only, and if it's not, it should be behind an nginx reverse proxy anyways. We're removing the config options regarding this in one of the next commits. --- include/Http.h | 18 ----- src/Http.cpp | 197 +++---------------------------------------------- 2 files changed, 9 insertions(+), 206 deletions(-) diff --git a/include/Http.h b/include/Http.h index 3ff2cd5..35fb7b5 100644 --- a/include/Http.h +++ b/include/Http.h @@ -3,8 +3,6 @@ #include #include #include -#include -#include #include #include @@ -19,10 +17,6 @@ namespace fs = std::filesystem; -namespace Crypto { -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, const httplib::Headers& headers = {}); @@ -32,8 +26,6 @@ namespace Status { const std::string ErrorString = "-1"; namespace Server { - void SetupEnvironment(); - // todo: Add non TLS Server Instance, this one is TLS only class THttpServerInstance { public: THttpServerInstance(); @@ -46,15 +38,5 @@ namespace Server { private: std::thread mThread; }; - // todo: all of these functions are likely unsafe, - // todo: replace with something that's managed by a domain specific crypto library - class Tx509KeypairGenerator { - public: - static long GenerateRandomId(); - static bool EnsureTLSConfigExists(); - static X509* GenerateCertificate(EVP_PKEY& pkey); - static EVP_PKEY* GenerateKey(); - static void GenerateAndWriteToDisk(const fs::path& KeyFilePath, const fs::path& CertFilePath); - }; } } diff --git a/src/Http.cpp b/src/Http.cpp index aeb66d5..8edc249 100644 --- a/src/Http.cpp +++ b/src/Http.cpp @@ -7,17 +7,13 @@ #include "httplib.h" #include +#include #include -#include -#include -#include -#include #include -fs::path Http::Server::THttpServerInstance::KeyFilePath; -fs::path Http::Server::THttpServerInstance::CertFilePath; + // TODO: Add sentry error handling back -namespace json = rapidjson; +using json = nlohmann::json; std::string Http::GET(const std::string& host, int port, const std::string& target, unsigned int* status) { httplib::SSLClient client(host, port); @@ -146,147 +142,6 @@ std::string Http::Status::ToString(int Code) { } } -long Http::Server::Tx509KeypairGenerator::GenerateRandomId() { - std::random_device R; - std::default_random_engine E1(R()); - std::uniform_int_distribution UniformDist(0, ULONG_MAX); - return UniformDist(E1); -} - -// Http::Server::THttpServerInstance::THttpServerInstance() { } -EVP_PKEY* Http::Server::Tx509KeypairGenerator::GenerateKey() { - /** - * Allocate memory for the pkey - */ - EVP_PKEY* PKey = EVP_PKEY_new(); - if (PKey == nullptr) { - beammp_error("Could not allocate memory for X.509 private key (PKEY) generation."); - throw std::runtime_error { std::string { "X.509 PKEY allocation error" } }; - } - BIGNUM* E = BN_new(); - beammp_assert(E); // TODO: replace all these asserts with beammp_errors - unsigned char three = 3; - BIGNUM* EErr = BN_bin2bn(&three, sizeof(three), E); - beammp_assert(EErr); - RSA* Rsa = RSA_new(); - beammp_assert(Rsa); - int Ret = RSA_generate_key_ex(Rsa, Crypto::RSA_DEFAULT_KEYLENGTH, E, nullptr); - beammp_assert(Ret == 1); - BN_free(E); - if (!EVP_PKEY_assign_RSA(PKey, Rsa)) { - EVP_PKEY_free(PKey); - beammp_error(std::string("Could not generate " + std::to_string(Crypto::RSA_DEFAULT_KEYLENGTH) + "-bit RSA key.")); - throw std::runtime_error { std::string("X.509 RSA key generation error") }; - } - // todo: figure out if returning by reference instead of passing pointers is a security breach - return PKey; -} - -X509* Http::Server::Tx509KeypairGenerator::GenerateCertificate(EVP_PKEY& PKey) { - X509* X509 = X509_new(); - if (X509 == nullptr) { - X509_free(X509); - beammp_error("Could not allocate memory for X.509 certificate generation."); - throw std::runtime_error { std::string("X.509 certificate generation error") }; - } - - /**Set the metadata of the certificate*/ - ASN1_INTEGER_set(X509_get_serialNumber(X509), GenerateRandomId()); - - /**Set the cert validity to a year*/ - X509_gmtime_adj(X509_get_notBefore(X509), 0); - X509_gmtime_adj(X509_get_notAfter(X509), 31536000L); - - /**Set the public key of the cert*/ - X509_set_pubkey(X509, &PKey); - - X509_NAME* Name = X509_get_subject_name(X509); - - /**Set cert metadata*/ - X509_NAME_add_entry_by_txt(Name, "C", MBSTRING_ASC, (unsigned char*)"GB", -1, -1, 0); - X509_NAME_add_entry_by_txt(Name, "O", MBSTRING_ASC, (unsigned char*)"BeamMP Ltd.", -1, -1, 0); - X509_NAME_add_entry_by_txt(Name, "CN", MBSTRING_ASC, (unsigned char*)"localhost", -1, -1, 0); - - X509_set_issuer_name(X509, Name); - - // TODO: Hashing with sha256 might cause problems, check later - if (!X509_sign(X509, &PKey, EVP_sha1())) { - X509_free(X509); - beammp_error("Could not sign X.509 certificate."); - throw std::runtime_error { std::string("X.509 certificate signing error") }; - } - return X509; -} - -void Http::Server::Tx509KeypairGenerator::GenerateAndWriteToDisk(const fs::path& KeyFilePath, const fs::path& CertFilePath) { - // todo: generate directories for ssl keys - FILE* KeyFile = std::fopen(reinterpret_cast(KeyFilePath.c_str()), "wb"); - if (!KeyFile) { - beammp_error("Could not create file 'key.pem', check your permissions"); - throw std::runtime_error("Could not create file 'key.pem'"); - } - - EVP_PKEY* PKey = Http::Server::Tx509KeypairGenerator::GenerateKey(); - - bool WriteOpResult = PEM_write_PrivateKey(KeyFile, PKey, nullptr, nullptr, 0, nullptr, nullptr); - fclose(KeyFile); - - if (!WriteOpResult) { - beammp_error("Could not write to file 'key.pem', check your permissions"); - throw std::runtime_error("Could not write to file 'key.pem'"); - } - - FILE* CertFile = std::fopen(reinterpret_cast(CertFilePath.c_str()), "wb"); // x509 file - if (!CertFile) { - beammp_error("Could not create file 'cert.pem', check your permissions"); - throw std::runtime_error("Could not create file 'cert.pem'"); - } - - X509* x509 = Http::Server::Tx509KeypairGenerator::GenerateCertificate(*PKey); - WriteOpResult = PEM_write_X509(CertFile, x509); - fclose(CertFile); - - if (!WriteOpResult) { - beammp_error("Could not write to file 'cert.pem', check your permissions"); - throw std::runtime_error("Could not write to file 'cert.pem'"); - } - EVP_PKEY_free(PKey); - X509_free(x509); - return; -} - -bool Http::Server::Tx509KeypairGenerator::EnsureTLSConfigExists() { - if (fs::is_regular_file(Application::Settings.SSLKeyPath) - && fs::is_regular_file(Application::Settings.SSLCertPath)) { - return true; - } else { - return false; - } -} - -void Http::Server::SetupEnvironment() { - if (!Application::Settings.HTTPServerUseSSL) { - return; - } - auto parent = fs::path(Application::Settings.SSLKeyPath).parent_path(); - if (!fs::exists(parent)) - fs::create_directories(parent); - - Application::TSettings defaultSettings {}; - if (!Tx509KeypairGenerator::EnsureTLSConfigExists()) { - beammp_warn(std::string("No default TLS Key / Cert found. " - "IF YOU HAVE NOT MODIFIED THE SSLKeyPath OR SSLCertPath VALUES " - "THIS IS NORMAL ON FIRST STARTUP! BeamMP will generate it's own certs in the default directory " - "(Check for permissions or corrupted key-/certfile)")); - Tx509KeypairGenerator::GenerateAndWriteToDisk(defaultSettings.SSLKeyPath, defaultSettings.SSLCertPath); - Http::Server::THttpServerInstance::KeyFilePath = defaultSettings.SSLKeyPath; - Http::Server::THttpServerInstance::CertFilePath = defaultSettings.SSLCertPath; - } else { - Http::Server::THttpServerInstance::KeyFilePath = Application::Settings.SSLKeyPath; - Http::Server::THttpServerInstance::CertFilePath = Application::Settings.SSLCertPath; - } -} - Http::Server::THttpServerInstance::THttpServerInstance() { Application::SetSubsystemStatus("HTTPServer", Application::Status::Starting); mThread = std::thread(&Http::Server::THttpServerInstance::operator(), this); @@ -324,48 +179,14 @@ void Http::Server::THttpServerInstance::operator()() try { break; } } - res.set_content(SystemsBad == 0 ? "0" : "1", "text/plain"); + res.set_content( + json { + { "ok", SystemsBad == 0 }, + } + .dump(), + "application/json"); res.status = 200; }); - /* - HttpLibServerInstance->Get("/status", [](const httplib::Request&, httplib::Response& res) { - try { - json::Document response; - response.SetObject(); - rapidjson::Document::AllocatorType& Allocator = response.GetAllocator(); - // add to response - auto& Server = LuaAPI::MP::Engine->Server(); - size_t CarCount = 0; - size_t GuestCount = 0; - json::Value Array(rapidjson::kArrayType); - LuaAPI::MP::Engine->Server().ForEachClient([&](std::weak_ptr Client) -> bool { - if (!Client.expired()) { - auto Locked = Client.lock(); - CarCount += Locked->GetCarCount(); - GuestCount += Locked->IsGuest() ? 1 : 0; - json::Value Player(json::kObjectType); - Player.AddMember("name", json::StringRef(Locked->GetName().c_str()), Allocator); - Player.AddMember("id", Locked->GetID(), Allocator); - Array.PushBack(Player, Allocator); - } - return true; - }); - response.AddMember("players", Array, Allocator); - response.AddMember("player_count", Server.ClientCount(), Allocator); - response.AddMember("guest_count", GuestCount, Allocator); - response.AddMember("car_count", CarCount, Allocator); - - // compile & send response - json::StringBuffer sb; - json::Writer writer(sb); - response.Accept(writer); - res.set_content(sb.GetString(), "application/json"); - } catch (const std::exception& e) { - beammp_error("Exception in /status endpoint: " + std::string(e.what())); - res.status = 500; - } - }); - */ // magic endpoint HttpLibServerInstance->Get({ 0x2f, 0x6b, 0x69, 0x74, 0x74, 0x79 }, [](const httplib::Request&, httplib::Response& res) { res.set_content(std::string(Magic), "text/plain"); From a0876ed58cbfd47b20e19be54127d4a7e843abd0 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 11:59:36 +0200 Subject: [PATCH 056/184] update submodules, remove ssl crap --- CMakeLists.txt | 2 +- deps/cpp-httplib | 2 +- deps/fmt | 2 +- deps/json | 2 +- deps/sentry-native | 2 +- deps/sol2 | 2 +- deps/toml11 | 2 +- include/Http.h | 2 -- src/Http.cpp | 8 +------- src/main.cpp | 2 +- 10 files changed, 9 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d45c23..26401e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,7 +35,7 @@ include_directories("${PROJECT_SOURCE_DIR}/deps/cpp-httplib") include_directories("${PROJECT_SOURCE_DIR}/deps/json/single_include") include_directories("${PROJECT_SOURCE_DIR}/deps") -add_compile_definitions(CPPHTTPLIB_OPENSSL_SUPPORT) +add_compile_definitions(CPPHTTPLIB_OPENSSL_SUPPORT=1) # ------------------------ APPLE --------------------------------- if(APPLE) diff --git a/deps/cpp-httplib b/deps/cpp-httplib index b324921..47044c0 160000 --- a/deps/cpp-httplib +++ b/deps/cpp-httplib @@ -1 +1 @@ -Subproject commit b324921c1aeff2976544128e4bb2a0979a4aa595 +Subproject commit 47044c05a8587dff86ab90526daabfef61079490 diff --git a/deps/fmt b/deps/fmt index 17dda58..ce246aa 160000 --- a/deps/fmt +++ b/deps/fmt @@ -1 +1 @@ -Subproject commit 17dda58391fba627a56482f5d2652a396619bc26 +Subproject commit ce246aaf74cb7616777497da988804fa4c02bf2d diff --git a/deps/json b/deps/json index eb21824..31e2583 160000 --- a/deps/json +++ b/deps/json @@ -1 +1 @@ -Subproject commit eb2182414749825be086c825edb5229e5c28503d +Subproject commit 31e25839b6576d31bc32d5b7d08ba8a32b38f4c2 diff --git a/deps/sentry-native b/deps/sentry-native index 90966cc..0a498e3 160000 --- a/deps/sentry-native +++ b/deps/sentry-native @@ -1 +1 @@ -Subproject commit 90966cc1022b8155681b6899539b35466baccf2c +Subproject commit 0a498e3e40d37180464c8fd17058e7330c72eb7a diff --git a/deps/sol2 b/deps/sol2 index c068aef..d0eba0a 160000 --- a/deps/sol2 +++ b/deps/sol2 @@ -1 +1 @@ -Subproject commit c068aefbeddb3dd1f1fd38d42843ecb49a3b4cdb +Subproject commit d0eba0a7544b0b62e37f08972c0f431f3c0e96b4 diff --git a/deps/toml11 b/deps/toml11 index fda0a2b..e8f922a 160000 --- a/deps/toml11 +++ b/deps/toml11 @@ -1 +1 @@ -Subproject commit fda0a2b9abd16e356f777c40a675131821c71b00 +Subproject commit e8f922a1b874b110781dfb3d23d03d41ddb6666b diff --git a/include/Http.h b/include/Http.h index 35fb7b5..4850470 100644 --- a/include/Http.h +++ b/include/Http.h @@ -29,8 +29,6 @@ namespace Server { class THttpServerInstance { public: THttpServerInstance(); - static fs::path KeyFilePath; - static fs::path CertFilePath; protected: void operator()(); diff --git a/src/Http.cpp b/src/Http.cpp index 8edc249..072a8ec 100644 --- a/src/Http.cpp +++ b/src/Http.cpp @@ -151,13 +151,7 @@ Http::Server::THttpServerInstance::THttpServerInstance() { void Http::Server::THttpServerInstance::operator()() try { beammp_info("HTTP(S) Server started on port " + std::to_string(Application::Settings.HTTPServerPort)); std::unique_ptr HttpLibServerInstance; - if (Application::Settings.HTTPServerUseSSL) { - HttpLibServerInstance = std::make_unique( - reinterpret_cast(Http::Server::THttpServerInstance::CertFilePath.c_str()), - reinterpret_cast(Http::Server::THttpServerInstance::KeyFilePath.c_str())); - } else { - HttpLibServerInstance = std::make_unique(); - } + HttpLibServerInstance = std::make_unique(); // todo: make this IP agnostic so people can set their own IP HttpLibServerInstance->Get("/", [](const httplib::Request&, httplib::Response& res) { res.set_content("

Hello World!

BeamMP Server can now serve HTTP requests!

", "text/html"); diff --git a/src/main.cpp b/src/main.cpp index 5878784..9014ad7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,7 +17,7 @@ #include #include -#define CPPHTTPLIB_OPENSSL_SUPPORT 1 + static const std::string sCommandlineArguments = R"( USAGE: BeamMP-Server [arguments] From 895058f85f604e604602ecc3a18f97e1435a5015 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 12:01:27 +0200 Subject: [PATCH 057/184] revert sentry to 0.4.9 --- deps/sentry-native | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/sentry-native b/deps/sentry-native index 0a498e3..b3f2b6d 160000 --- a/deps/sentry-native +++ b/deps/sentry-native @@ -1 +1 @@ -Subproject commit 0a498e3e40d37180464c8fd17058e7330c72eb7a +Subproject commit b3f2b6ddac886616b40cdccbb05b81af14b6adc9 From ed8d8a64191c10db2611d0616ecb7f0c62714ba8 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 12:03:03 +0200 Subject: [PATCH 058/184] remove unused code --- src/main.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 9014ad7..49c0eb7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -160,7 +160,6 @@ int BeamMPServerMain(MainArguments Arguments) { Application::CheckForUpdates(); if (Application::Settings.HTTPServerEnabled) { - Http::Server::SetupEnvironment(); Http::Server::THttpServerInstance HttpServerInstance {}; } From 67b8565a4d6f0168a6757eb224fd749bc7bd43ec Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 12:06:36 +0200 Subject: [PATCH 059/184] update json --- deps/json | 2 +- deps/sentry-native | 2 +- deps/sol2 | 2 +- deps/toml11 | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/deps/json b/deps/json index 31e2583..ede6667 160000 --- a/deps/json +++ b/deps/json @@ -1 +1 @@ -Subproject commit 31e25839b6576d31bc32d5b7d08ba8a32b38f4c2 +Subproject commit ede66678580596028bcd6e18871a35a54bac01d7 diff --git a/deps/sentry-native b/deps/sentry-native index b3f2b6d..90966cc 160000 --- a/deps/sentry-native +++ b/deps/sentry-native @@ -1 +1 @@ -Subproject commit b3f2b6ddac886616b40cdccbb05b81af14b6adc9 +Subproject commit 90966cc1022b8155681b6899539b35466baccf2c diff --git a/deps/sol2 b/deps/sol2 index d0eba0a..c068aef 160000 --- a/deps/sol2 +++ b/deps/sol2 @@ -1 +1 @@ -Subproject commit d0eba0a7544b0b62e37f08972c0f431f3c0e96b4 +Subproject commit c068aefbeddb3dd1f1fd38d42843ecb49a3b4cdb diff --git a/deps/toml11 b/deps/toml11 index e8f922a..fda0a2b 160000 --- a/deps/toml11 +++ b/deps/toml11 @@ -1 +1 @@ -Subproject commit e8f922a1b874b110781dfb3d23d03d41ddb6666b +Subproject commit fda0a2b9abd16e356f777c40a675131821c71b00 From 00f156cb86ee98018310a44529f142d67af15584 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 13:02:09 +0200 Subject: [PATCH 060/184] start adding tests --- include/Common.h | 185 ++++++++++++++++++++++----------------- src/ArgsParser.cpp | 76 ++++++++++++++++ src/Common.cpp | 101 +++++++++++++++++++++ src/LuaAPI.cpp | 23 +++-- src/TResourceManager.cpp | 3 +- test/test_main.cpp | 22 ++++- 6 files changed, 323 insertions(+), 87 deletions(-) diff --git a/include/Common.h b/include/Common.h index 8b806d5..fe5c4d5 100644 --- a/include/Common.h +++ b/include/Common.h @@ -15,6 +15,8 @@ extern TSentry Sentry; #include #include +#include + #include "Compat.h" #include "TConsole.h" @@ -139,88 +141,115 @@ void RegisterThread(const std::string& str); #define _line std::to_string(__LINE__) #define _in_lambda (std::string(__func__) == "operator()") -// we would like the full function signature 'void a::foo() const' -// on windows this is __FUNCSIG__, on GCC it's __PRETTY_FUNCTION__, -// feel free to add more -#if defined(WIN32) -#define _function_name std::string(__FUNCSIG__) -#elif defined(__unix) || defined(__unix__) -#define _function_name std::string(__PRETTY_FUNCTION__) -#else -#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 -// each info/debug/warn... call instead of the 'filename:line' format. -#if defined(BMP_FULL_FUNCTION_NAMES) -#define _this_location (ThreadName() + _function_name + " ") -#else -#define _this_location (ThreadName() + _file_basename + ":" + _line + " ") -#endif -#define SU_RAW SSU_UNRAW - -#else // !defined(DEBUG) - -#define SU_RAW RAWIFY(SSU_UNRAW) -#define _this_location (ThreadName()) - -#endif // defined(DEBUG) - -#define beammp_warn(x) Application::Console().Write(_this_location + std::string("[WARN] ") + (x)) -#define beammp_info(x) Application::Console().Write(_this_location + std::string("[INFO] ") + (x)) -#define beammp_error(x) \ - do { \ - Application::Console().Write(_this_location + std::string("[ERROR] ") + (x)); \ - Sentry.AddErrorBreadcrumb((x), _file_basename, _line); \ - } while (false) -#define beammp_lua_error(x) \ - do { \ - Application::Console().Write(_this_location + std::string("[LUA ERROR] ") + (x)); \ - } while (false) -#define beammp_lua_warn(x) \ - do { \ - Application::Console().Write(_this_location + std::string("[LUA WARN] ") + (x)); \ - } while (false) -#define luaprint(x) Application::Console().Write(_this_location + std::string("[LUA] ") + (x)) -#define beammp_debug(x) \ - do { \ - if (Application::Settings.DebugModeEnabled) { \ - Application::Console().Write(_this_location + std::string("[DEBUG] ") + (x)); \ - } \ - } while (false) -#define beammp_event(x) \ - do { \ - if (Application::Settings.DebugModeEnabled) { \ - Application::Console().Write(_this_location + std::string("[EVENT] ") + (x)); \ - } \ - } while (false) // for those times when you just need to ignore something :^) // explicity disables a [[nodiscard]] warning #define beammp_ignore(x) (void)x -// trace() is a debug-build debug() -#if defined(DEBUG) -#define beammp_trace(x) \ - do { \ - if (Application::Settings.DebugModeEnabled) { \ - Application::Console().Write(_this_location + std::string("[TRACE] ") + (x)); \ - } \ - } while (false) -#else -#define beammp_trace(x) -#endif // defined(DEBUG) -#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__)) +// clang-format off +#ifdef DOCTEST_CONFIG_DISABLE + + // we would like the full function signature 'void a::foo() const' + // on windows this is __FUNCSIG__, on GCC it's __PRETTY_FUNCTION__, + // feel free to add more + #if defined(WIN32) + #define _function_name std::string(__FUNCSIG__) + #elif defined(__unix) || defined(__unix__) + #define _function_name std::string(__PRETTY_FUNCTION__) + #else + #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 + // each info/debug/warn... call instead of the 'filename:line' format. + #if defined(BMP_FULL_FUNCTION_NAMES) + #define _this_location (ThreadName() + _function_name + " ") + #else + #define _this_location (ThreadName() + _file_basename + ":" + _line + " ") + #endif + + #endif // defined(DEBUG) + + #define beammp_warn(x) Application::Console().Write(_this_location + std::string("[WARN] ") + (x)) + #define beammp_info(x) Application::Console().Write(_this_location + std::string("[INFO] ") + (x)) + #define beammp_error(x) \ + do { \ + Application::Console().Write(_this_location + std::string("[ERROR] ") + (x)); \ + Sentry.AddErrorBreadcrumb((x), _file_basename, _line); \ + } while (false) + #define beammp_lua_error(x) \ + do { \ + Application::Console().Write(_this_location + std::string("[LUA ERROR] ") + (x)); \ + } while (false) + #define beammp_lua_warn(x) \ + do { \ + Application::Console().Write(_this_location + std::string("[LUA WARN] ") + (x)); \ + } while (false) + #define luaprint(x) Application::Console().Write(_this_location + std::string("[LUA] ") + (x)) + #define beammp_debug(x) \ + do { \ + if (Application::Settings.DebugModeEnabled) { \ + Application::Console().Write(_this_location + std::string("[DEBUG] ") + (x)); \ + } \ + } while (false) + #define beammp_event(x) \ + do { \ + if (Application::Settings.DebugModeEnabled) { \ + Application::Console().Write(_this_location + std::string("[EVENT] ") + (x)); \ + } \ + } while (false) + // trace() is a debug-build debug() + #if defined(DEBUG) + #define beammp_trace(x) \ + do { \ + if (Application::Settings.DebugModeEnabled) { \ + Application::Console().Write(_this_location + std::string("[TRACE] ") + (x)); \ + } \ + } while (false) + #else + #define beammp_trace(x) + #endif // defined(DEBUG) + + #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__)) + +#else // DOCTEST_CONFIG_DISABLE + + #define beammp_error(x) /* x */ + #define beammp_lua_error(x) /* x */ + #define beammp_warn(x) /* x */ + #define beammp_lua_warn(x) /* x */ + #define beammp_info(x) /* x */ + #define beammp_event(x) /* x */ + #define beammp_debug(x) /* x */ + #define beammp_trace(x) /* x */ + #define luaprint(x) /* x */ + #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__)) + +#endif // DOCTEST_CONFIG_DISABLE + +#if defined(DEBUG) + #define SU_RAW SSU_UNRAW +#else + #define SU_RAW RAWIFY(SSU_UNRAW) + #define _this_location (ThreadName()) +#endif + +// clang-format on void LogChatMessage(const std::string& name, int id, const std::string& msg); diff --git a/src/ArgsParser.cpp b/src/ArgsParser.cpp index f00548f..f0d6257 100644 --- a/src/ArgsParser.cpp +++ b/src/ArgsParser.cpp @@ -1,6 +1,7 @@ #include "ArgsParser.h" #include "Common.h" #include +#include void ArgsParser::Parse(const std::vector& ArgList) { for (const auto& Arg : ArgList) { @@ -92,3 +93,78 @@ void ArgsParser::ConsumeLongFlag(const std::string& Arg) { beammp_warn("Argument '" + Name + "' was supplied but isn't a known argument, so it is likely being ignored."); } } + +TEST_CASE("ArgsParser") { + ArgsParser parser; + + SUBCASE("Simple args") { + parser.RegisterArgument({ "a" }, ArgsParser::Flags::NONE); + parser.RegisterArgument({ "hello" }, ArgsParser::Flags::NONE); + parser.Parse({ "--a", "--hello" }); + CHECK(parser.Verify()); + CHECK(parser.FoundArgument({ "a" })); + CHECK(parser.FoundArgument({ "hello" })); + CHECK(parser.FoundArgument({ "a", "hello" })); + CHECK(!parser.FoundArgument({ "b" })); + CHECK(!parser.FoundArgument({ "goodbye" })); + } + + SUBCASE("No args") { + parser.RegisterArgument({ "a" }, ArgsParser::Flags::NONE); + parser.RegisterArgument({ "hello" }, ArgsParser::Flags::NONE); + parser.Parse({}); + CHECK(parser.Verify()); + CHECK(!parser.FoundArgument({ "a" })); + CHECK(!parser.FoundArgument({ "hello" })); + CHECK(!parser.FoundArgument({ "a", "hello" })); + CHECK(!parser.FoundArgument({ "b" })); + CHECK(!parser.FoundArgument({ "goodbye" })); + CHECK(!parser.FoundArgument({ "" })); + } + + SUBCASE("Value args") { + parser.RegisterArgument({ "a" }, ArgsParser::Flags::HAS_VALUE); + parser.RegisterArgument({ "hello" }, ArgsParser::Flags::HAS_VALUE); + parser.Parse({ "--a=5", "--hello=world" }); + CHECK(parser.Verify()); + REQUIRE(parser.FoundArgument({ "a" })); + REQUIRE(parser.FoundArgument({ "hello" })); + CHECK(parser.GetValueOfArgument({ "a" }).has_value()); + CHECK(parser.GetValueOfArgument({ "a" }).value() == "5"); + CHECK(parser.GetValueOfArgument({ "hello" }).has_value()); + CHECK(parser.GetValueOfArgument({ "hello" }).value() == "world"); + } + + SUBCASE("Mixed value & no-value args") { + parser.RegisterArgument({ "a" }, ArgsParser::Flags::HAS_VALUE); + parser.RegisterArgument({ "hello" }, ArgsParser::Flags::NONE); + parser.Parse({ "--a=5", "--hello" }); + CHECK(parser.Verify()); + REQUIRE(parser.FoundArgument({ "a" })); + REQUIRE(parser.FoundArgument({ "hello" })); + CHECK(parser.GetValueOfArgument({ "a" }).has_value()); + CHECK(parser.GetValueOfArgument({ "a" }).value() == "5"); + CHECK(!parser.GetValueOfArgument({ "hello" }).has_value()); + } + + SUBCASE("Required args") { + SUBCASE("Two required, two present") { + parser.RegisterArgument({ "a" }, ArgsParser::Flags::REQUIRED); + parser.RegisterArgument({ "hello" }, ArgsParser::Flags::REQUIRED); + parser.Parse({ "--a", "--hello" }); + CHECK(parser.Verify()); + } + SUBCASE("Two required, one present") { + parser.RegisterArgument({ "a" }, ArgsParser::Flags::REQUIRED); + parser.RegisterArgument({ "hello" }, ArgsParser::Flags::REQUIRED); + parser.Parse({ "--a" }); + CHECK(!parser.Verify()); + } + SUBCASE("Two required, none present") { + parser.RegisterArgument({ "a" }, ArgsParser::Flags::REQUIRED); + parser.RegisterArgument({ "hello" }, ArgsParser::Flags::REQUIRED); + parser.Parse({ "--b" }); + CHECK(!parser.Verify()); + } + } +} diff --git a/src/Common.cpp b/src/Common.cpp index 77933bb..1df9a35 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -64,6 +64,23 @@ std::array Application::VersionStrToInts(const std::string& str) { return Version; } +TEST_CASE("Application::VersionStrToInts") { + auto v = Application::VersionStrToInts("1.2.3"); + CHECK(v[0] == 1); + CHECK(v[1] == 2); + CHECK(v[2] == 3); + + v = Application::VersionStrToInts("10.20.30"); + CHECK(v[0] == 10); + CHECK(v[1] == 20); + CHECK(v[2] == 30); + + v = Application::VersionStrToInts("100.200.255"); + CHECK(v[0] == 100); + CHECK(v[1] == 200); + CHECK(v[2] == 255); +} + bool Application::IsOutdated(const Version& Current, const Version& Newest) { if (Newest.major > Current.major) { return true; @@ -76,6 +93,49 @@ bool Application::IsOutdated(const Version& Current, const Version& Newest) { } } +TEST_CASE("Application::IsOutdated (version check)") { + SUBCASE("Same version") { + CHECK(!Application::IsOutdated({ 1, 2, 3 }, { 1, 2, 3 })); + } + // we need to use over 1-2 digits to test against lexical comparisons + SUBCASE("Patch outdated") { + for (size_t Patch = 0; Patch < 10; ++Patch) { + for (size_t Minor = 0; Minor < 10; ++Minor) { + for (size_t Major = 0; Major < 10; ++Major) { + CHECK(Application::IsOutdated({ Major, Minor, Patch }, { Major, Minor, Patch + 1 })); + } + } + } + } + SUBCASE("Minor outdated") { + for (size_t Patch = 0; Patch < 10; ++Patch) { + for (size_t Minor = 0; Minor < 10; ++Minor) { + for (size_t Major = 0; Major < 10; ++Major) { + CHECK(Application::IsOutdated({ Major, Minor, Patch }, { Major, Minor + 1, Patch })); + } + } + } + } + SUBCASE("Major outdated") { + for (size_t Patch = 0; Patch < 10; ++Patch) { + for (size_t Minor = 0; Minor < 10; ++Minor) { + for (size_t Major = 0; Major < 10; ++Major) { + CHECK(Application::IsOutdated({ Major, Minor, Patch }, { Major + 1, Minor, Patch })); + } + } + } + } + SUBCASE("All outdated") { + for (size_t Patch = 0; Patch < 10; ++Patch) { + for (size_t Minor = 0; Minor < 10; ++Minor) { + for (size_t Major = 0; Major < 10; ++Major) { + CHECK(Application::IsOutdated({ Major, Minor, Patch }, { Major + 1, Minor + 1, Patch + 1 })); + } + } + } + } +} + void Application::SetSubsystemStatus(const std::string& Subsystem, Status status) { switch (status) { case Status::Good: @@ -98,6 +158,15 @@ void Application::SetSubsystemStatus(const std::string& Subsystem, Status status mSystemStatusMap[Subsystem] = status; } +TEST_CASE("Application::SetSubsystemStatus") { + Application::SetSubsystemStatus("Test", Application::Status::Good); + auto Map = Application::GetSubsystemStatuses(); + CHECK(Map.at("Test") == Application::Status::Good); + Application::SetSubsystemStatus("Test", Application::Status::Bad); + Map = Application::GetSubsystemStatuses(); + CHECK(Map.at("Test") == Application::Status::Bad); +} + void Application::CheckForUpdates() { Application::SetSubsystemStatus("UpdateCheck", Application::Status::Starting); static bool FirstTime = true; @@ -155,6 +224,25 @@ std::string ThreadName(bool DebugModeOverride) { return ""; } +TEST_CASE("ThreadName") { + RegisterThread("MyThread"); + auto OrigDebug = Application::Settings.DebugModeEnabled; + + // ThreadName adds a space at the end, legacy but we need it still + SUBCASE("Debug mode enabled") { + Application::Settings.DebugModeEnabled = true; + CHECK(ThreadName(true) == "MyThread "); + CHECK(ThreadName(false) == "MyThread "); + } + SUBCASE("Debug mode disabled") { + Application::Settings.DebugModeEnabled = false; + CHECK(ThreadName(true) == "MyThread "); + CHECK(ThreadName(false) == ""); + } + // cleanup + Application::Settings.DebugModeEnabled = OrigDebug; +} + void RegisterThread(const std::string& str) { std::string ThreadId; #ifdef BEAMMP_WINDOWS @@ -172,6 +260,11 @@ void RegisterThread(const std::string& str) { threadNameMap[std::this_thread::get_id()] = str; } +TEST_CASE("RegisterThread") { + RegisterThread("MyThread"); + CHECK(threadNameMap.at(std::this_thread::get_id()) == "MyThread"); +} + Version::Version(uint8_t major, uint8_t minor, uint8_t patch) : major(major) , minor(minor) @@ -185,6 +278,12 @@ std::string Version::AsString() { return fmt::format("{:d}.{:d}.{:d}", major, minor, patch); } +TEST_CASE("Version::AsString") { + CHECK(Version { 0, 0, 0 }.AsString() == "0.0.0"); + CHECK(Version { 1, 2, 3 }.AsString() == "1.2.3"); + CHECK(Version { 255, 255, 255 }.AsString() == "255.255.255"); +} + void LogChatMessage(const std::string& name, int id, const std::string& msg) { if (Application::Settings.LogChat) { std::stringstream ss; @@ -196,7 +295,9 @@ void LogChatMessage(const std::string& name, int id, const std::string& msg) { ss << name << ""; } ss << msg; +#ifdef DOCTEST_CONFIG_DISABLE Application::Console().Write(ss.str()); +#endif } } diff --git a/src/LuaAPI.cpp b/src/LuaAPI.cpp index 4a5df29..7ac632a 100644 --- a/src/LuaAPI.cpp +++ b/src/LuaAPI.cpp @@ -176,50 +176,57 @@ void LuaAPI::MP::Set(int ConfigID, sol::object NewValue) { if (NewValue.is()) { Application::Settings.DebugModeEnabled = NewValue.as(); beammp_info(std::string("Set `Debug` to ") + (Application::Settings.DebugModeEnabled ? "true" : "false")); - } else + } else { beammp_lua_error("set invalid argument [2] expected boolean"); + } break; case 1: // private if (NewValue.is()) { Application::Settings.Private = NewValue.as(); beammp_info(std::string("Set `Private` to ") + (Application::Settings.Private ? "true" : "false")); - } else + } else { beammp_lua_error("set invalid argument [2] expected boolean"); + } break; case 2: // max cars if (NewValue.is()) { Application::Settings.MaxCars = NewValue.as(); beammp_info(std::string("Set `MaxCars` to ") + std::to_string(Application::Settings.MaxCars)); - } else + } else { beammp_lua_error("set invalid argument [2] expected integer"); + } break; case 3: // max players if (NewValue.is()) { Application::Settings.MaxPlayers = NewValue.as(); beammp_info(std::string("Set `MaxPlayers` to ") + std::to_string(Application::Settings.MaxPlayers)); - } else + } else { beammp_lua_error("set invalid argument [2] expected integer"); + } break; case 4: // Map if (NewValue.is()) { Application::Settings.MapName = NewValue.as(); beammp_info(std::string("Set `Map` to ") + Application::Settings.MapName); - } else + } else { beammp_lua_error("set invalid argument [2] expected string"); + } break; case 5: // Name if (NewValue.is()) { Application::Settings.ServerName = NewValue.as(); beammp_info(std::string("Set `Name` to ") + Application::Settings.ServerName); - } else + } else { beammp_lua_error("set invalid argument [2] expected string"); + } break; case 6: // Desc if (NewValue.is()) { Application::Settings.ServerDesc = NewValue.as(); beammp_info(std::string("Set `Description` to ") + Application::Settings.ServerDesc); - } else + } else { beammp_lua_error("set invalid argument [2] expected string"); + } break; default: beammp_warn("Invalid config ID \"" + std::to_string(ConfigID) + "\". Use `MP.Settings.*` enum for this."); @@ -255,7 +262,9 @@ void LuaAPI::MP::PrintRaw(sol::variadic_args Args) { ToPrint += LuaToString(static_cast(Arg)); ToPrint += "\t"; } +#ifdef DOCTEST_CONFIG_DISABLE Application::Console().WriteRaw(ToPrint); +#endif } int LuaAPI::PanicHandler(lua_State* State) { diff --git a/src/TResourceManager.cpp b/src/TResourceManager.cpp index afb0e6b..e12ef17 100644 --- a/src/TResourceManager.cpp +++ b/src/TResourceManager.cpp @@ -28,8 +28,9 @@ TResourceManager::TResourceManager() { } } - if (mModsLoaded) + if (mModsLoaded) { beammp_info("Loaded " + std::to_string(mModsLoaded) + " Mods"); + } Application::SetSubsystemStatus("ResourceManager", Application::Status::Good); } diff --git a/test/test_main.cpp b/test/test_main.cpp index 0a3f254..1aba0a1 100644 --- a/test/test_main.cpp +++ b/test/test_main.cpp @@ -1,2 +1,22 @@ -#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#define DOCTEST_CONFIG_IMPLEMENT #include + +#include + +int main(int argc, char** argv) { + doctest::Context context; + + Application::InitializeConsole(); + + context.applyCommandLine(argc, argv); + + int res = context.run(); // run + + if (context.shouldExit()) // important - query flags (and --exit) rely on the user doing this + return res; // propagate the result of the tests + + int client_stuff_return_code = 0; + // your program - if the testing framework is integrated in your production code + + return res + client_stuff_return_code; // the result from doctest is propagated here as well +} From 28c43a51eefd950d478f7664890b734beab73014 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 13:33:08 +0200 Subject: [PATCH 061/184] add some tests for LuaAPI FS, termios --- src/Compat.cpp | 18 +++++++++ src/Http.cpp | 6 +++ src/LuaAPI.cpp | 106 +++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 126 insertions(+), 4 deletions(-) diff --git a/src/Compat.cpp b/src/Compat.cpp index a1765b7..b5a5149 100644 --- a/src/Compat.cpp +++ b/src/Compat.cpp @@ -1,5 +1,8 @@ #include "Compat.h" +#include +#include + #ifndef WIN32 static struct termios old, current; @@ -20,6 +23,21 @@ void resetTermios(void) { tcsetattr(0, TCSANOW, &old); } +TEST_CASE("init and reset termios") { + struct termios original; + tcgetattr(0, &original); + SUBCASE("no echo") { + initTermios(false); + } + SUBCASE("yes echo") { + initTermios(true); + } + resetTermios(); + struct termios current; + tcgetattr(0, ¤t); + CHECK(std::memcmp(&original, ¤t, sizeof(struct termios)) == 0); +} + char getch_(int echo) { char ch; initTermios(echo); diff --git a/src/Http.cpp b/src/Http.cpp index 072a8ec..edc627e 100644 --- a/src/Http.cpp +++ b/src/Http.cpp @@ -142,6 +142,12 @@ std::string Http::Status::ToString(int Code) { } } +TEST_CASE("Http::Status::ToString") { + CHECK(Http::Status::ToString(200) == "OK"); + CHECK(Http::Status::ToString(696969) == "696969"); + CHECK(Http::Status::ToString(-1) == "Invalid Response Code"); +} + Http::Server::THttpServerInstance::THttpServerInstance() { Application::SetSubsystemStatus("HTTPServer", Application::Status::Starting); mThread = std::thread(&Http::Server::THttpServerInstance::operator(), this); diff --git a/src/LuaAPI.cpp b/src/LuaAPI.cpp index 7ac632a..2f3b7e5 100644 --- a/src/LuaAPI.cpp +++ b/src/LuaAPI.cpp @@ -102,6 +102,14 @@ void LuaAPI::Print(sol::variadic_args Args) { luaprint(ToPrint); } +TEST_CASE("LuaAPI::MP::GetServerVersion") { + const auto [ma, mi, pa] = LuaAPI::MP::GetServerVersion(); + const auto real = Application::ServerVersion(); + CHECK(ma == real.major); + CHECK(mi == real.minor); + CHECK(pa == real.patch); +} + static inline bool InternalTriggerClientEvent(int PlayerID, const std::string& EventName, const std::string& Data) { std::string Packet = "E:" + EventName + ":" + Data; if (PlayerID == -1) @@ -287,7 +295,7 @@ static std::pair FSWrapper(FnT Fn, ArgsT&&... Args) { std::pair LuaAPI::FS::CreateDirectory(const std::string& Path) { std::error_code errc; std::pair Result; - fs::create_directories(fs::relative(Path), errc); + fs::create_directories(Path, errc); Result.first = errc == std::error_code {}; if (!Result.first) { Result.second = errc.message(); @@ -295,6 +303,33 @@ std::pair LuaAPI::FS::CreateDirectory(const std::string& Path return Result; } +TEST_CASE("LuaAPI::FS::CreateDirectory") { + std::string TestDir = "beammp_test_dir"; + fs::remove_all(TestDir); + SUBCASE("Single level dir") { + const auto [Ok, Err] = LuaAPI::FS::CreateDirectory(TestDir); + CHECK(Ok); + CHECK(Err == ""); + CHECK(fs::exists(TestDir)); + } + SUBCASE("Multi level dir") { + const auto [Ok, Err] = LuaAPI::FS::CreateDirectory(TestDir + "/a/b/c"); + CHECK(Ok); + CHECK(Err == ""); + CHECK(fs::exists(TestDir + "/a/b/c")); + } + SUBCASE("Already exists") { + const auto [Ok, Err] = LuaAPI::FS::CreateDirectory(TestDir); + CHECK(Ok); + CHECK(Err == ""); + CHECK(fs::exists(TestDir)); + const auto [Ok2, Err2] = LuaAPI::FS::CreateDirectory(TestDir); + CHECK(Ok2); + CHECK(Err2 == ""); + } + fs::remove_all(TestDir); +} + std::pair LuaAPI::FS::Remove(const std::string& Path) { std::error_code errc; std::pair Result; @@ -306,10 +341,30 @@ std::pair LuaAPI::FS::Remove(const std::string& Path) { return Result; } +TEST_CASE("LuaAPI::FS::Remove") { + const std::string TestFileOrDir = "beammp_test_thing"; + SUBCASE("Remove existing directory") { + fs::create_directory(TestFileOrDir); + const auto [Ok, Err] = LuaAPI::FS::Remove(TestFileOrDir); + CHECK(Ok); + CHECK_EQ(Err, ""); + CHECK(!fs::exists(TestFileOrDir)); + } + SUBCASE("Remove non-existing directory") { + fs::remove_all(TestFileOrDir); + const auto [Ok, Err] = LuaAPI::FS::Remove(TestFileOrDir); + CHECK(Ok); + CHECK_EQ(Err, ""); + CHECK(!fs::exists(TestFileOrDir)); + } + // TODO: add tests for files + // TODO: add tests for files and folders without access permissions (failure) +} + std::pair LuaAPI::FS::Rename(const std::string& Path, const std::string& NewPath) { std::error_code errc; std::pair Result; - fs::rename(fs::relative(Path), fs::relative(NewPath), errc); + fs::rename(Path, NewPath, errc); Result.first = errc == std::error_code {}; if (!Result.first) { Result.second = errc.message(); @@ -317,10 +372,25 @@ std::pair LuaAPI::FS::Rename(const std::string& Path, const s return Result; } +TEST_CASE("LuaAPI::FS::Rename") { + const auto TestDir = "beammp_test_dir"; + const auto OtherTestDir = "beammp_test_dir_2"; + fs::remove_all(OtherTestDir); + fs::create_directory(TestDir); + const auto [Ok, Err] = LuaAPI::FS::Rename(TestDir, OtherTestDir); + CHECK(Ok); + CHECK_EQ(Err, ""); + CHECK(!fs::exists(TestDir)); + CHECK(fs::exists(OtherTestDir)); + + fs::remove_all(OtherTestDir); + fs::remove_all(TestDir); +} + std::pair LuaAPI::FS::Copy(const std::string& Path, const std::string& NewPath) { std::error_code errc; std::pair Result; - fs::copy(fs::relative(Path), fs::relative(NewPath), fs::copy_options::recursive, errc); + fs::copy(Path, NewPath, fs::copy_options::recursive, errc); Result.first = errc == std::error_code {}; if (!Result.first) { Result.second = errc.message(); @@ -328,8 +398,36 @@ std::pair LuaAPI::FS::Copy(const std::string& Path, const std return Result; } +TEST_CASE("LuaAPI::FS::Copy") { + const auto TestDir = "beammp_test_dir"; + const auto OtherTestDir = "beammp_test_dir_2"; + fs::remove_all(OtherTestDir); + fs::create_directory(TestDir); + const auto [Ok, Err] = LuaAPI::FS::Copy(TestDir, OtherTestDir); + CHECK(Ok); + CHECK_EQ(Err, ""); + CHECK(fs::exists(TestDir)); + CHECK(fs::exists(OtherTestDir)); + + fs::remove_all(OtherTestDir); + fs::remove_all(TestDir); +} + bool LuaAPI::FS::Exists(const std::string& Path) { - return fs::exists(fs::relative(Path)); + return fs::exists(Path); +} + +TEST_CASE("LuaAPI::FS::Exists") { + const auto TestDir = "beammp_test_dir"; + const auto OtherTestDir = "beammp_test_dir_2"; + fs::remove_all(OtherTestDir); + fs::create_directory(TestDir); + + CHECK(LuaAPI::FS::Exists(TestDir)); + CHECK(!LuaAPI::FS::Exists(OtherTestDir)); + + fs::remove_all(OtherTestDir); + fs::remove_all(TestDir); } std::string LuaAPI::FS::GetFilename(const std::string& Path) { From 2cf083a7e418a0751c8aeff95b2e4700b606ee19 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 13:34:29 +0200 Subject: [PATCH 062/184] run tests in github actions --- .github/workflows/cmake-linux.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index 3ddf653..5b605e3 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -37,6 +37,11 @@ jobs: shell: bash run: cmake --build . --config $BUILD_TYPE + - name: Run Tests + working-directory: ${{github.workspace}}/build-linux + shell: bash + run: ./BeamMP-Server-test + - name: Archive artifacts uses: actions/upload-artifact@v2 with: From 811ace19991c9b50417d86bf8018c3091384c748 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 13:36:02 +0200 Subject: [PATCH 063/184] fix Application::IsOutdated test case types --- src/Common.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Common.cpp b/src/Common.cpp index 1df9a35..2af1e20 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -99,36 +99,36 @@ TEST_CASE("Application::IsOutdated (version check)") { } // we need to use over 1-2 digits to test against lexical comparisons SUBCASE("Patch outdated") { - for (size_t Patch = 0; Patch < 10; ++Patch) { - for (size_t Minor = 0; Minor < 10; ++Minor) { - for (size_t Major = 0; Major < 10; ++Major) { + for (uint8_t Patch = 0; Patch < 10; ++Patch) { + for (uint8_t Minor = 0; Minor < 10; ++Minor) { + for (uint8_t Major = 0; Major < 10; ++Major) { CHECK(Application::IsOutdated({ Major, Minor, Patch }, { Major, Minor, Patch + 1 })); } } } } SUBCASE("Minor outdated") { - for (size_t Patch = 0; Patch < 10; ++Patch) { - for (size_t Minor = 0; Minor < 10; ++Minor) { - for (size_t Major = 0; Major < 10; ++Major) { + for (uint8_t Patch = 0; Patch < 10; ++Patch) { + for (uint8_t Minor = 0; Minor < 10; ++Minor) { + for (uint8_t Major = 0; Major < 10; ++Major) { CHECK(Application::IsOutdated({ Major, Minor, Patch }, { Major, Minor + 1, Patch })); } } } } SUBCASE("Major outdated") { - for (size_t Patch = 0; Patch < 10; ++Patch) { - for (size_t Minor = 0; Minor < 10; ++Minor) { - for (size_t Major = 0; Major < 10; ++Major) { + for (uint8_t Patch = 0; Patch < 10; ++Patch) { + for (uint8_t Minor = 0; Minor < 10; ++Minor) { + for (uint8_t Major = 0; Major < 10; ++Major) { CHECK(Application::IsOutdated({ Major, Minor, Patch }, { Major + 1, Minor, Patch })); } } } } SUBCASE("All outdated") { - for (size_t Patch = 0; Patch < 10; ++Patch) { - for (size_t Minor = 0; Minor < 10; ++Minor) { - for (size_t Major = 0; Major < 10; ++Major) { + for (uint8_t Patch = 0; Patch < 10; ++Patch) { + for (uint8_t Minor = 0; Minor < 10; ++Minor) { + for (uint8_t Major = 0; Major < 10; ++Major) { CHECK(Application::IsOutdated({ Major, Minor, Patch }, { Major + 1, Minor + 1, Patch + 1 })); } } From 22b63220c762668f30c29dea03ec2368bbf3808e Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 13:49:13 +0200 Subject: [PATCH 064/184] add more tests to LuaAPI::FS, minor fixes to LuaAPI::FS test config file creation, too --- src/LuaAPI.cpp | 28 ++++++++++++++++++++++++++++ src/TConfig.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/src/LuaAPI.cpp b/src/LuaAPI.cpp index 2f3b7e5..48b77d8 100644 --- a/src/LuaAPI.cpp +++ b/src/LuaAPI.cpp @@ -434,22 +434,50 @@ std::string LuaAPI::FS::GetFilename(const std::string& Path) { return fs::path(Path).filename().string(); } +TEST_CASE("LuaAPI::FS::GetFilename") { + CHECK(LuaAPI::FS::GetFilename("test.txt") == "test.txt"); + CHECK(LuaAPI::FS::GetFilename("/test.txt") == "test.txt"); + CHECK(LuaAPI::FS::GetFilename("place/test.txt") == "test.txt"); + CHECK(LuaAPI::FS::GetFilename("/some/../place/test.txt") == "test.txt"); +} + std::string LuaAPI::FS::GetExtension(const std::string& Path) { return fs::path(Path).extension().string(); } +TEST_CASE("LuaAPI::FS::GetExtension") { + CHECK(LuaAPI::FS::GetExtension("test.txt") == ".txt"); + CHECK(LuaAPI::FS::GetExtension("/test.txt") == ".txt"); + CHECK(LuaAPI::FS::GetExtension("place/test.txt") == ".txt"); + CHECK(LuaAPI::FS::GetExtension("/some/../place/test.txt") == ".txt"); + CHECK(LuaAPI::FS::GetExtension("/some/../place/test") == ""); + CHECK(LuaAPI::FS::GetExtension("/some/../place/test.a.b.c") == ".c"); + CHECK(LuaAPI::FS::GetExtension("/some/../place/test.") == "."); + CHECK(LuaAPI::FS::GetExtension("/some/../place/test.a.b.") == "."); +} + std::string LuaAPI::FS::GetParentFolder(const std::string& Path) { return fs::path(Path).parent_path().string(); } +TEST_CASE("LuaAPI::FS::GetParentFolder") { + CHECK(LuaAPI::FS::GetParentFolder("test.txt") == ""); + CHECK(LuaAPI::FS::GetParentFolder("/test.txt") == "/"); + CHECK(LuaAPI::FS::GetParentFolder("place/test.txt") == "place"); + CHECK(LuaAPI::FS::GetParentFolder("/some/../place/test.txt") == "/some/../place"); +} + +// TODO: add tests bool LuaAPI::FS::IsDirectory(const std::string& Path) { return fs::is_directory(Path); } +// TODO: add tests bool LuaAPI::FS::IsFile(const std::string& Path) { return fs::is_regular_file(Path); } +// TODO: add tests std::string LuaAPI::FS::ConcatPaths(sol::variadic_args Args) { fs::path Path; for (size_t i = 0; i < Args.size(); ++i) { diff --git a/src/TConfig.cpp b/src/TConfig.cpp index 0ffcbe8..04d6acd 100644 --- a/src/TConfig.cpp +++ b/src/TConfig.cpp @@ -32,6 +32,31 @@ static constexpr std::string_view StrSSLCertPath = "SSLCertPath"; static constexpr std::string_view StrHTTPServerPort = "HTTPServerPort"; static constexpr std::string_view StrHTTPServerIP = "HTTPServerIP"; +TEST_CASE("TConfig::TConfig") { + const std::string CfgFile = "beammp_server_testconfig.toml"; + fs::remove(CfgFile); + + TConfig Cfg(CfgFile); + + CHECK(fs::file_size(CfgFile) != 0); + + std::string buf; + { + buf.resize(fs::file_size(CfgFile)); + auto fp = std::fopen(CfgFile.c_str(), "r"); + std::fread(buf.data(), 1, buf.size(), fp); + std::fclose(fp); + } + INFO("file contents are:", buf); + + const auto table = toml::parse(CfgFile); + CHECK(table.at("General").is_table()); + CHECK(table.at("Misc").is_table()); + CHECK(table.at("HTTP").is_table()); + + fs::remove(CfgFile); +} + TConfig::TConfig(const std::string& ConfigFileName) : mConfigFileName(ConfigFileName) { Application::SetSubsystemStatus("Config", Application::Status::Starting); From fde5bc7fb63a9a01741ea459ac685229a65a9e34 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 13:50:50 +0200 Subject: [PATCH 065/184] remove tests from linux action --- .github/workflows/cmake-linux.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index 5b605e3..8c5cc16 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -36,12 +36,7 @@ jobs: working-directory: ${{github.workspace}}/build-linux shell: bash run: cmake --build . --config $BUILD_TYPE - - - name: Run Tests - working-directory: ${{github.workspace}}/build-linux - shell: bash - run: ./BeamMP-Server-test - + - name: Archive artifacts uses: actions/upload-artifact@v2 with: From 7224e90c68e0f23284f79087225ead5e72fb327d Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 13:56:58 +0200 Subject: [PATCH 066/184] add test workflow --- .github/workflows/cmake-linux.yml | 67 +++++++++++++++++++------------ 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index 8c5cc16..f0d1a98 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -10,35 +10,50 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - with: - submodules: 'recursive' + - uses: actions/checkout@v2 + with: + submodules: "recursive" - - name: Install Dependencies - env: - beammp_sentry_url: ${{ secrets.BEAMMP_SECRET_SENTRY_URL }} - run: | + - name: Install Dependencies + env: + beammp_sentry_url: ${{ secrets.BEAMMP_SECRET_SENTRY_URL }} + run: | echo ${#beammp_sentry_url} sudo apt-get update - sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev + sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev cmake - - name: Create Build Environment - run: cmake -E make_directory ${{github.workspace}}/build-linux + - name: Create Build Environment + run: cmake -E make_directory ${{github.workspace}}/build-linux - - name: Configure CMake - shell: bash - working-directory: ${{github.workspace}}/build-linux - env: - beammp_sentry_url: ${{ secrets.BEAMMP_SECRET_SENTRY_URL }} - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_COMPILER=g++-10 -DBEAMMP_SECRET_SENTRY_URL="$beammp_sentry_url" + - name: Configure CMake + shell: bash + working-directory: ${{github.workspace}}/build-linux + env: + beammp_sentry_url: ${{ secrets.BEAMMP_SECRET_SENTRY_URL }} + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_COMPILER=g++-10 -DBEAMMP_SECRET_SENTRY_URL="$beammp_sentry_url" - - name: Build - working-directory: ${{github.workspace}}/build-linux - shell: bash - run: cmake --build . --config $BUILD_TYPE - - - name: Archive artifacts - uses: actions/upload-artifact@v2 - with: - name: BeamMP-Server-linux - path: ${{github.workspace}}/build-linux/BeamMP-Server + - name: Build + working-directory: ${{github.workspace}}/build-linux + shell: bash + run: cmake --build . --config $BUILD_TYPE + + - name: Archive artifacts + uses: actions/upload-artifact@v2 + with: + name: BeamMP-Server-linux + path: ${{github.workspace}}/build-linux/BeamMP-Server + + run-tests: + needs: linux-build + runs-on: ubuntu-latest + + steps: + - name: Build + working-directory: ${{github.workspace}}/build-linux + shell: bash + run: cmake --build . --config $BUILD_TYPE BeamMP-Server-tests + + - name: Test + working-directory: ${{github.workspace}}/build-linux + shell: bash + run: ./BeamMP-Server-tests From 8df61fe44c835a3f5cd409d1f2d73e1a2ec8dc77 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 13:58:29 +0200 Subject: [PATCH 067/184] add g++ to workflow --- .github/workflows/cmake-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index f0d1a98..a8b97d1 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -20,7 +20,7 @@ jobs: run: | echo ${#beammp_sentry_url} sudo apt-get update - sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev cmake + sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev cmake g++-10 - name: Create Build Environment run: cmake -E make_directory ${{github.workspace}}/build-linux From c455d4855c13e232227c565d97e86bf9fd48ff27 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 14:40:56 +0200 Subject: [PATCH 068/184] possibly fix workflow --- .github/workflows/cmake-linux.yml | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index a8b97d1..4de77a6 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -7,7 +7,7 @@ env: jobs: linux-build: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 @@ -32,28 +32,39 @@ jobs: beammp_sentry_url: ${{ secrets.BEAMMP_SECRET_SENTRY_URL }} run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_COMPILER=g++-10 -DBEAMMP_SECRET_SENTRY_URL="$beammp_sentry_url" - - name: Build + - name: Build Server working-directory: ${{github.workspace}}/build-linux shell: bash - run: cmake --build . --config $BUILD_TYPE + run: cmake --build . --config $BUILD_TYPE BeamMP-Server - - name: Archive artifacts + - name: Build Tests + working-directory: ${{github.workspace}}/build-linux + shell: bash + run: cmake --build . --config $BUILD_TYPE BeamMP-Server-tests + + - name: Archive server artifact uses: actions/upload-artifact@v2 with: name: BeamMP-Server-linux path: ${{github.workspace}}/build-linux/BeamMP-Server + - name: Archive test artifact + uses: actions/upload-artifact@v2 + with: + name: BeamMP-Server-linux-tests + path: ${{github.workspace}}/build-linux/BeamMP-Server-tests + run-tests: needs: linux-build - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - - name: Build - working-directory: ${{github.workspace}}/build-linux - shell: bash - run: cmake --build . --config $BUILD_TYPE BeamMP-Server-tests + - uses: actions/download-artifact@master + with: + name: BeamMP-Server-linux-tests + path: ${{github.workspace}}/BeamMP-Server-tests - name: Test - working-directory: ${{github.workspace}}/build-linux + working-directory: ${{github.workspace}} shell: bash run: ./BeamMP-Server-tests From 303da19f4870b2c67eb4985e424e602219d7afc2 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 14:43:38 +0200 Subject: [PATCH 069/184] add -t to linux workflow --- .github/workflows/cmake-linux.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index 4de77a6..bb00d58 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -35,12 +35,12 @@ jobs: - name: Build Server working-directory: ${{github.workspace}}/build-linux shell: bash - run: cmake --build . --config $BUILD_TYPE BeamMP-Server + run: cmake --build . --config $BUILD_TYPE -t BeamMP-Server - name: Build Tests working-directory: ${{github.workspace}}/build-linux shell: bash - run: cmake --build . --config $BUILD_TYPE BeamMP-Server-tests + run: cmake --build . --config $BUILD_TYPE -t BeamMP-Server-tests - name: Archive server artifact uses: actions/upload-artifact@v2 From 1c3b1ecc76e3c6562dad8072dc262126dcc4b301 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 15:37:33 +0200 Subject: [PATCH 070/184] fix workflow test run path --- .github/workflows/cmake-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index bb00d58..0ad0e8f 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -62,7 +62,7 @@ jobs: - uses: actions/download-artifact@master with: name: BeamMP-Server-linux-tests - path: ${{github.workspace}}/BeamMP-Server-tests + path: ${{github.workspace}} - name: Test working-directory: ${{github.workspace}} From 3d7db6d0bc7008ea2ed204559fc6e7b3a696cad6 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 15:41:18 +0200 Subject: [PATCH 071/184] windows me waiting for MSVC to stop making me write properly, deepls retarded code: https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fmedia.istockphoto.com%2Fphotos%2Fskeleton-reaching-for-a-window-picture-id155154794%3Fk%3D6%26m%3D155154794%26s%3D612x612%26w%3D0%26h%3DKNIrAKdhAsI1FCoMtSLXd99ZnP9MO1zZj8VkThTvwVs%3D&f=1&nofb=1 --- src/Common.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Common.cpp b/src/Common.cpp index 2af1e20..11fe8f6 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -102,7 +102,7 @@ TEST_CASE("Application::IsOutdated (version check)") { for (uint8_t Patch = 0; Patch < 10; ++Patch) { for (uint8_t Minor = 0; Minor < 10; ++Minor) { for (uint8_t Major = 0; Major < 10; ++Major) { - CHECK(Application::IsOutdated({ Major, Minor, Patch }, { Major, Minor, Patch + 1 })); + CHECK(Application::IsOutdated({ uint8_t(Major), uint8_t(Minor), uint8_t(Patch) }, { uint8_t(Major), uint8_t(Minor), uint8_t(Patch + 1) })); } } } @@ -111,7 +111,7 @@ TEST_CASE("Application::IsOutdated (version check)") { for (uint8_t Patch = 0; Patch < 10; ++Patch) { for (uint8_t Minor = 0; Minor < 10; ++Minor) { for (uint8_t Major = 0; Major < 10; ++Major) { - CHECK(Application::IsOutdated({ Major, Minor, Patch }, { Major, Minor + 1, Patch })); + CHECK(Application::IsOutdated({ uint8_t(Major), uint8_t(Minor), uint8_t(Patch) }, { uint8_t(Major), uint8_t(Minor + 1), uint8_t(Patch) })); } } } @@ -120,7 +120,7 @@ TEST_CASE("Application::IsOutdated (version check)") { for (uint8_t Patch = 0; Patch < 10; ++Patch) { for (uint8_t Minor = 0; Minor < 10; ++Minor) { for (uint8_t Major = 0; Major < 10; ++Major) { - CHECK(Application::IsOutdated({ Major, Minor, Patch }, { Major + 1, Minor, Patch })); + CHECK(Application::IsOutdated({ uint8_t(Major), uint8_t(Minor), uint8_t(Patch) }, { uint8_t(Major + 1), uint8_t(Minor), uint8_t(Patch) })); } } } @@ -129,7 +129,7 @@ TEST_CASE("Application::IsOutdated (version check)") { for (uint8_t Patch = 0; Patch < 10; ++Patch) { for (uint8_t Minor = 0; Minor < 10; ++Minor) { for (uint8_t Major = 0; Major < 10; ++Major) { - CHECK(Application::IsOutdated({ Major, Minor, Patch }, { Major + 1, Minor + 1, Patch + 1 })); + CHECK(Application::IsOutdated({ uint8_t(Major), uint8_t(Minor), uint8_t(Patch) }, { uint8_t(Major + 1), uint8_t(Minor + 1), uint8_t(Patch + 1) })); } } } From b4f97a6da031ef7f655a4a960448ad75aea6d084 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 15:44:39 +0200 Subject: [PATCH 072/184] make tests executable before running :^) --- .github/workflows/cmake-linux.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index 0ad0e8f..743cced 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -67,4 +67,6 @@ jobs: - name: Test working-directory: ${{github.workspace}} shell: bash - run: ./BeamMP-Server-tests + run: | + chmod +x ./BeamMP-Server-tests + ./BeamMP-Server-tests From cc35d83834d2335b4d48b723c394aaad6bdb72f1 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 15:54:23 +0200 Subject: [PATCH 073/184] make linux workflow parallel, add runtime dependencies --- .github/workflows/cmake-linux.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index 743cced..3b552cf 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -35,12 +35,12 @@ jobs: - name: Build Server working-directory: ${{github.workspace}}/build-linux shell: bash - run: cmake --build . --config $BUILD_TYPE -t BeamMP-Server + run: cmake --build . --config $BUILD_TYPE -t BeamMP-Server --parallel - name: Build Tests working-directory: ${{github.workspace}}/build-linux shell: bash - run: cmake --build . --config $BUILD_TYPE -t BeamMP-Server-tests + run: cmake --build . --config $BUILD_TYPE -t BeamMP-Server-tests --parallel - name: Archive server artifact uses: actions/upload-artifact@v2 @@ -63,6 +63,12 @@ jobs: with: name: BeamMP-Server-linux-tests path: ${{github.workspace}} + + - name: Install Runtime Dependencies + shell: bash + run: | + sudo apt-get update + sudo apt-get install -y libz rapidjson liblua5.3 libssl libwebsocketpp libcurl4-openssl - name: Test working-directory: ${{github.workspace}} From 70e53c2a70918a752b227dfa59df8001454a4cf5 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 16:00:36 +0200 Subject: [PATCH 074/184] fix dependencies ubuntu's package naming is confusing me --- .github/workflows/cmake-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index 3b552cf..802b4ac 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -68,7 +68,7 @@ jobs: shell: bash run: | sudo apt-get update - sudo apt-get install -y libz rapidjson liblua5.3 libssl libwebsocketpp libcurl4-openssl + sudo apt-get install -y liblua5.3 openssl - name: Test working-directory: ${{github.workspace}} From 46b92b499289f01938eb09c41e168d713cfb0368 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 16:14:05 +0200 Subject: [PATCH 075/184] only run termios test if stdin is a tty --- src/Compat.cpp | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/Compat.cpp b/src/Compat.cpp index b5a5149..1374d3e 100644 --- a/src/Compat.cpp +++ b/src/Compat.cpp @@ -24,18 +24,20 @@ void resetTermios(void) { } TEST_CASE("init and reset termios") { - struct termios original; - tcgetattr(0, &original); - SUBCASE("no echo") { - initTermios(false); + if (isatty(STDIN_FILENO)) { + struct termios original; + tcgetattr(0, &original); + SUBCASE("no echo") { + initTermios(false); + } + SUBCASE("yes echo") { + initTermios(true); + } + resetTermios(); + struct termios current; + tcgetattr(0, ¤t); + CHECK(std::memcmp(&original, ¤t, sizeof(struct termios)) == 0); } - SUBCASE("yes echo") { - initTermios(true); - } - resetTermios(); - struct termios current; - tcgetattr(0, ¤t); - CHECK(std::memcmp(&original, ¤t, sizeof(struct termios)) == 0); } char getch_(int echo) { From f06f31c2a0b7a8b0740a377da2d668145de362c6 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 16:58:13 +0200 Subject: [PATCH 076/184] add moar tests!!! --- CMakeLists.txt | 1 + include/TLuaEngine.h | 2 +- src/TConsole.cpp | 26 ++++++++++++++++++++++++++ src/TLuaEngine.cpp | 7 +++++++ src/main.cpp | 8 ++++---- test/test_main.cpp | 2 +- 6 files changed, 40 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 26401e2..928c6bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,6 +62,7 @@ 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") + option(SANITIZE "Turns on thread and UB sanitizers" OFF) if (SANITIZE) message(STATUS "sanitize is ON") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined,thread") diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index 32e5bbf..706ecbf 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -73,7 +73,7 @@ private: std::unordered_map mFileTimes; }; -class TLuaEngine : IThreaded { +class TLuaEngine : IThreaded, public std::enable_shared_from_this { public: enum CallStrategy : int { BestEffort, diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 8bc98c6..c26f957 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -14,6 +14,17 @@ static inline bool StringStartsWith(const std::string& What, const std::string& return What.size() >= StartsWith.size() && What.substr(0, StartsWith.size()) == StartsWith; } +TEST_CASE("StringStartsWith") { + CHECK(StringStartsWith("Hello, World", "Hello")); + CHECK(StringStartsWith("Hello, World", "H")); + CHECK(StringStartsWith("Hello, World", "")); + CHECK(!StringStartsWith("Hello, World", "ello")); + CHECK(!StringStartsWith("Hello, World", "World")); + CHECK(StringStartsWith("", "")); + CHECK(!StringStartsWith("", "hello")); +} + +// Trims leading and trailing spaces, newlines, tabs, etc. static inline std::string TrimString(std::string S) { S.erase(S.begin(), std::find_if(S.begin(), S.end(), [](unsigned char ch) { return !std::isspace(ch); @@ -25,6 +36,21 @@ static inline std::string TrimString(std::string S) { return S; } +TEST_CASE("TrimString") { + CHECK(TrimString("hel lo") == "hel lo"); + CHECK(TrimString(" hel lo") == "hel lo"); + CHECK(TrimString(" hel lo ") == "hel lo"); + CHECK(TrimString("hel lo ") == "hel lo"); + CHECK(TrimString(" hel lo") == "hel lo"); + CHECK(TrimString("hel lo ") == "hel lo"); + CHECK(TrimString(" hel lo ") == "hel lo"); + CHECK(TrimString("\t\thel\nlo\n\n") == "hel\nlo"); + CHECK(TrimString("\n\thel\tlo\n\t") == "hel\tlo"); + CHECK(TrimString(" ") == ""); + CHECK(TrimString(" \t\n\r ") == ""); + CHECK(TrimString("") == ""); +} + std::string GetDate() { std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); time_t tt = std::chrono::system_clock::to_time_t(now); diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index e5885ec..db0f825 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -38,6 +38,12 @@ TLuaEngine::TLuaEngine() Start(); } +TEST_CASE("TLuaEngine ctor & dtor") { + Application::Settings.Resource = "beammp_server_test_resources"; + TLuaEngine engine; + Application::GracefullyShutdown(); +} + void TLuaEngine::operator()() { RegisterThread("LuaEngine"); Application::SetSubsystemStatus("LuaEngine", Application::Status::Good); @@ -269,6 +275,7 @@ std::shared_ptr TLuaEngine::EnqueueFunctionCall(TLuaStateId StateID, } void TLuaEngine::CollectAndInitPlugins() { + fs::create_directories(mResourceServerPath); for (const auto& Dir : fs::directory_iterator(mResourceServerPath)) { auto Path = Dir.path(); Path = fs::relative(Path); diff --git a/src/main.cpp b/src/main.cpp index 49c0eb7..74dbbfd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -133,9 +133,9 @@ int BeamMPServerMain(MainArguments Arguments) { TServer Server(Arguments.List); TConfig Config(ConfigPath); - TLuaEngine LuaEngine; - LuaEngine.SetServer(&Server); - Application::Console().InitializeLuaConsole(LuaEngine); + auto LuaEngine = std::make_shared(); + LuaEngine->SetServer(&Server); + Application::Console().InitializeLuaConsole(*LuaEngine); if (Config.Failed()) { beammp_info("Closing in 10 seconds"); @@ -155,7 +155,7 @@ int BeamMPServerMain(MainArguments Arguments) { TPPSMonitor PPSMonitor(Server); THeartbeatThread Heartbeat(ResourceManager, Server); TNetwork Network(Server, PPSMonitor, ResourceManager); - LuaEngine.SetNetwork(&Network); + LuaEngine->SetNetwork(&Network); PPSMonitor.SetNetwork(Network); Application::CheckForUpdates(); diff --git a/test/test_main.cpp b/test/test_main.cpp index 1aba0a1..01af393 100644 --- a/test/test_main.cpp +++ b/test/test_main.cpp @@ -6,7 +6,7 @@ int main(int argc, char** argv) { doctest::Context context; - Application::InitializeConsole(); + // Application::InitializeConsole(); context.applyCommandLine(argc, argv); From 36547d1e9eef093ebbfc8612c973df4a1f37c155 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 20:59:53 +0200 Subject: [PATCH 077/184] Move PluginMonitor out of TLuaEngine --- CMakeLists.txt | 1 + include/Common.h | 2 ++ include/TLuaEngine.h | 16 +-------- include/TPluginMonitor.h | 23 ++++++++++++ src/TLuaEngine.cpp | 71 ++---------------------------------- src/TPluginMonitor.cpp | 78 ++++++++++++++++++++++++++++++++++++++++ src/main.cpp | 3 ++ 7 files changed, 110 insertions(+), 84 deletions(-) create mode 100644 include/TPluginMonitor.h create mode 100644 src/TPluginMonitor.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 928c6bc..4df517e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -120,6 +120,7 @@ set(BeamMP_Sources include/TScopedTimer.h src/TScopedTimer.cpp include/SignalHandling.h src/SignalHandling.cpp include/ArgsParser.h src/ArgsParser.cpp + include/TPluginMonitor.h src/TPluginMonitor.cpp include/Environment.h ) diff --git a/include/Common.h b/include/Common.h index fe5c4d5..abf758a 100644 --- a/include/Common.h +++ b/include/Common.h @@ -16,6 +16,8 @@ extern TSentry Sentry; #include #include +#include +namespace fs = std::filesystem; #include "Compat.h" diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index 706ecbf..ee4e953 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -60,20 +60,7 @@ struct TLuaChunk { std::string PluginPath; }; -class TPluginMonitor : IThreaded { -public: - TPluginMonitor(const fs::path& Path, TLuaEngine& Engine, std::atomic_bool& Shutdown); - - void operator()(); - -private: - TLuaEngine& mEngine; - fs::path mPath; - std::atomic_bool& mShutdown; - std::unordered_map mFileTimes; -}; - -class TLuaEngine : IThreaded, public std::enable_shared_from_this { +class TLuaEngine : public std::enable_shared_from_this, IThreaded { public: enum CallStrategy : int { BestEffort, @@ -245,7 +232,6 @@ private: TNetwork* mNetwork; TServer* mServer; - TPluginMonitor mPluginMonitor; std::atomic_bool mShutdown { false }; fs::path mResourceServerPath; std::vector> mLuaPlugins; diff --git a/include/TPluginMonitor.h b/include/TPluginMonitor.h new file mode 100644 index 0000000..3d82c4f --- /dev/null +++ b/include/TPluginMonitor.h @@ -0,0 +1,23 @@ +#pragma once + +#include "Common.h" +#include "IThreaded.h" + +#include +#include +#include + +class TLuaEngine; + +class TPluginMonitor : IThreaded, public std::enable_shared_from_this { +public: + TPluginMonitor(const fs::path& Path, std::shared_ptr Engine); + + void operator()(); + +private: + std::shared_ptr mEngine; + fs::path mPath; + std::atomic_bool mShutdown; + std::unordered_map mFileTimes; +}; diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index db0f825..74b01c3 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -15,8 +15,7 @@ TLuaEngine* LuaAPI::MP::Engine; -TLuaEngine::TLuaEngine() - : mPluginMonitor(fs::path(Application::Settings.Resource) / "Server", *this, mShutdown) { +TLuaEngine::TLuaEngine() { Application::SetSubsystemStatus("LuaEngine", Application::Status::Starting); LuaAPI::MP::Engine = this; if (!fs::exists(Application::Settings.Resource)) { @@ -35,7 +34,7 @@ TLuaEngine::TLuaEngine() } Application::SetSubsystemStatus("LuaEngine", Application::Status::Shutdown); }); - Start(); + IThreaded::Start(); } TEST_CASE("TLuaEngine ctor & dtor") { @@ -988,69 +987,3 @@ bool TLuaEngine::TimedEvent::Expired() { void TLuaEngine::TimedEvent::Reset() { LastCompletion = std::chrono::high_resolution_clock::now(); } - -TPluginMonitor::TPluginMonitor(const fs::path& Path, TLuaEngine& Engine, std::atomic_bool& Shutdown) - : mEngine(Engine) - , mPath(Path) - , mShutdown(Shutdown) { - if (!fs::exists(mPath)) { - fs::create_directories(mPath); - } - for (const auto& Entry : fs::recursive_directory_iterator(mPath)) { - // TODO: trigger an event when a subfolder file changes - if (Entry.is_regular_file()) { - mFileTimes[Entry.path().string()] = fs::last_write_time(Entry.path()); - } - } - Start(); -} - -void TPluginMonitor::operator()() { - RegisterThread("PluginMonitor"); - beammp_info("PluginMonitor started"); - while (!mShutdown) { - std::this_thread::sleep_for(std::chrono::seconds(3)); - std::vector ToRemove; - for (const auto& Pair : mFileTimes) { - 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)"); - } - } -} diff --git a/src/TPluginMonitor.cpp b/src/TPluginMonitor.cpp new file mode 100644 index 0000000..5c940be --- /dev/null +++ b/src/TPluginMonitor.cpp @@ -0,0 +1,78 @@ +#include "TPluginMonitor.h" + +#include "TLuaEngine.h" + +TPluginMonitor::TPluginMonitor(const fs::path& Path, std::shared_ptr Engine) + : mEngine(Engine) + , mPath(Path) { + if (!fs::exists(mPath)) { + fs::create_directories(mPath); + } + for (const auto& Entry : fs::recursive_directory_iterator(mPath)) { + // TODO: trigger an event when a subfolder file changes + if (Entry.is_regular_file()) { + mFileTimes[Entry.path().string()] = fs::last_write_time(Entry.path()); + } + } + + Application::RegisterShutdownHandler([this] { + mShutdown = true; + if (mThread.joinable()) { + mThread.join(); + } + }); + + Start(); +} + +void TPluginMonitor::operator()() { + RegisterThread("PluginMonitor"); + beammp_info("PluginMonitor started"); + while (!mShutdown) { + std::vector ToRemove; + for (const auto& Pair : mFileTimes) { + 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 (size_t i = 0; i < 3 && !mShutdown; ++i) { + std::this_thread::sleep_for(std::chrono::seconds(i)); + } + } + 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)"); + } + } +} diff --git a/src/main.cpp b/src/main.cpp index 74dbbfd..f3e23f4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,6 +11,7 @@ #include "TLuaEngine.h" #include "TNetwork.h" #include "TPPSMonitor.h" +#include "TPluginMonitor.h" #include "TResourceManager.h" #include "TScopedTimer.h" #include "TServer.h" @@ -159,6 +160,8 @@ int BeamMPServerMain(MainArguments Arguments) { PPSMonitor.SetNetwork(Network); Application::CheckForUpdates(); + TPluginMonitor PluginMonitor(fs::path(Application::Settings.Resource) / "Server", LuaEngine); + if (Application::Settings.HTTPServerEnabled) { Http::Server::THttpServerInstance HttpServerInstance {}; } From 0621c0bf82b6b7ba43e71acea5166352de815fda Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 26 May 2022 21:13:18 +0200 Subject: [PATCH 078/184] rebase fixup --- CMakeLists.txt | 28 ++++++++++++---------------- deps/CMakeLists.txt | 8 ++------ include/TConfig.h | 2 +- include/TLuaEngine.h | 2 +- src/TSentry.cpp | 2 +- 5 files changed, 17 insertions(+), 25 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4df517e..703531a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,14 +26,14 @@ 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") -include_directories("${PROJECT_SOURCE_DIR}/deps/websocketpp") -include_directories("${PROJECT_SOURCE_DIR}/deps/commandline") -include_directories("${PROJECT_SOURCE_DIR}/deps/sol2/include") -include_directories("${PROJECT_SOURCE_DIR}/deps/cpp-httplib") -include_directories("${PROJECT_SOURCE_DIR}/deps/json/single_include") -include_directories("${PROJECT_SOURCE_DIR}/deps") +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/deps/asio/asio/include") +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/deps/rapidjson/include") +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/deps/websocketpp") +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/deps/commandline") +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/deps/sol2/include") +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/deps/cpp-httplib") +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/deps/json/single_include") +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/deps") add_compile_definitions(CPPHTTPLIB_OPENSSL_SUPPORT=1) @@ -50,13 +50,6 @@ elseif (WIN32) 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}) - 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") @@ -87,6 +80,9 @@ add_subdirectory("deps/sentry-native") # ------------------------ C++ SETUP --------------------------------- set(CMAKE_CXX_STANDARD 17) +if (MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj") +endif () # ------------------------ DEPENDENCIES ------------------------------ message(STATUS "Adding local source dependencies") @@ -171,7 +167,7 @@ target_include_directories(BeamMP-Server PUBLIC ${BeamMP_Includes} ) -target_link_libraries(BeamMP-Server +target_link_libraries(BeamMP-Server ${BeamMP_Libraries} ${BeamMP_PlatformLibs} ) diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 9e5a7ec..0c1ba10 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -1,11 +1,7 @@ -include_directories("${PROJECT_SOURCE_DIR}/deps/asio/asio/include") -include_directories("${PROJECT_SOURCE_DIR}/deps/rapidjson/include") -include_directories("${PROJECT_SOURCE_DIR}/deps/websocketpp") -include_directories("${PROJECT_SOURCE_DIR}/deps/commandline") -include_directories("${PROJECT_SOURCE_DIR}/deps/sol2/include") include_directories("${PROJECT_SOURCE_DIR}/deps") +include_directories("${PROJECT_SOURCE_DIR}/deps/commandline") add_subdirectory("${PROJECT_SOURCE_DIR}/deps/commandline") -add_subdirectory("${PROJECT_SOURCE_DIR}/deps/sol2") add_subdirectory("${PROJECT_SOURCE_DIR}/deps/fmt") +add_subdirectory("${PROJECT_SOURCE_DIR}/deps/sol2") add_subdirectory("${PROJECT_SOURCE_DIR}/deps/doctest") diff --git a/include/TConfig.h b/include/TConfig.h index fe43d5c..2349cc8 100644 --- a/include/TConfig.h +++ b/include/TConfig.h @@ -6,7 +6,7 @@ #include #define TOML11_PRESERVE_COMMENTS_BY_DEFAULT -#include // header-only version of TOML++ +#include // header-only version of TOML++ namespace fs = std::filesystem; diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index ee4e953..170907c 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/TSentry.cpp b/src/TSentry.cpp index da05c10..7f2b416 100644 --- a/src/TSentry.cpp +++ b/src/TSentry.cpp @@ -72,7 +72,7 @@ void TSentry::Log(SentryLevel level, const std::string& logger, const std::strin SetContext("threads", { { "thread-name", ThreadName(true) } }); auto Msg = sentry_value_new_message_event(sentry_level_t(level), logger.c_str(), text.c_str()); sentry_capture_event(Msg); - sentry_remove_transaction(); + sentry_set_transaction(nullptr); } void TSentry::LogError(const std::string& text, const std::string& file, const std::string& line) { From 25391fa0c714c8d10ca37e2d1a74f7c2aa9cefdc Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Sat, 4 Jun 2022 16:50:41 +0300 Subject: [PATCH 079/184] thank you toml11 maintainer for naming your header some super generic name that clashes with every single other toml library B) --- include/TConfig.h | 2 +- include/TLuaEngine.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/TConfig.h b/include/TConfig.h index 2349cc8..fe43d5c 100644 --- a/include/TConfig.h +++ b/include/TConfig.h @@ -6,7 +6,7 @@ #include #define TOML11_PRESERVE_COMMENTS_BY_DEFAULT -#include // header-only version of TOML++ +#include // header-only version of TOML++ namespace fs = std::filesystem; diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index 170907c..ee4e953 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include From 817bd4b5889e335dc51a8f513785191da43d5898 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Tue, 28 Jun 2022 03:20:26 +0200 Subject: [PATCH 080/184] start fixing mod download --- include/Common.h | 5 ++-- include/TNetwork.h | 1 + src/TNetwork.cpp | 65 +++++++++++++++++++++++++++++++++++++++------- 3 files changed, 60 insertions(+), 11 deletions(-) diff --git a/include/Common.h b/include/Common.h index abf758a..313df2b 100644 --- a/include/Common.h +++ b/include/Common.h @@ -135,8 +135,9 @@ std::string ThreadName(bool DebugModeOverride = false); void RegisterThread(const std::string& str); #define RegisterThreadAuto() RegisterThread(__func__) -#define KB 1024 -#define MB (KB * 1024) +#define KB 1024llu +#define MB (KB * 1024llu) +#define GB (MB * 1024llu) #define SSU_UNRAW SECRET_SENTRY_URL #define _file_basename std::filesystem::path(__FILE__).filename().string() diff --git a/include/TNetwork.h b/include/TNetwork.h index 528aef4..87e4235 100644 --- a/include/TNetwork.h +++ b/include/TNetwork.h @@ -48,4 +48,5 @@ private: void SendFile(TClient& c, const std::string& Name); static bool TCPSendRaw(TClient& C, SOCKET socket, char* Data, int32_t Size); static void SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std::string& Name); + static uint8_t* SendSplit(TClient& c, SOCKET Socket, uint8_t* DataPtr, size_t Size); }; diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index dfa8776..9c0e748 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -761,14 +761,63 @@ void TNetwork::SendFile(TClient& c, const std::string& UnsafeName) { } } +static std::pair SplitIntoChunks(size_t FullSize, size_t ChunkSize) { + if (FullSize < ChunkSize) { + return { 0, FullSize }; + } + size_t Count = FullSize / (FullSize / ChunkSize); + size_t LastChunkSize = FullSize - (Count * ChunkSize); + return { Count, LastChunkSize }; +} + +TEST_CASE("SplitIntoChunks") { + size_t FullSize; + size_t ChunkSize; + SUBCASE("Normal case") { + FullSize = 1234567; + ChunkSize = 1234; + } + SUBCASE("Zero original size") { + FullSize = 0; + ChunkSize = 100; + } + SUBCASE("Equal full size and chunk size") { + FullSize = 125; + ChunkSize = 125; + } + SUBCASE("Even split") { + FullSize = 10000; + ChunkSize = 100; + } + SUBCASE("Odd split") { + FullSize = 13; + ChunkSize = 2; + } + SUBCASE("Large sizes") { + FullSize = 10 * GB; + ChunkSize = 125 * MB; + } + auto [Count, LastSize] = SplitIntoChunks(FullSize, ChunkSize); + CHECK((Count * ChunkSize) + LastSize == FullSize); +} + +uint8_t* /* end ptr */ TNetwork::SendSplit(TClient& c, SOCKET Socket, uint8_t* DataPtr, size_t Size) { + if (TCPSendRaw(c, Socket, reinterpret_cast(DataPtr), Size)) { + return DataPtr + Size; + } else { + return nullptr; + } +} + 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); + auto Buf = f.rdbuf(); uint32_t Split = 125 * MB; - char* Data; + std::vector Data; if (Size > Split) - Data = new char[Split]; + Data.resize(Split); else - Data = new char[Size]; + Data.resize(Size); SOCKET TCPSock; if (D) TCPSock = c.GetDownSock(); @@ -779,8 +828,8 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std size_t Diff = Size - Sent; if (Diff > Split) { f.seekg(Sent, std::ios_base::beg); - f.read(Data, Split); - if (!TCPSendRaw(c, TCPSock, Data, Split)) { + f.read(reinterpret_cast(Data.data()), Split); + if (!TCPSendRaw(c, TCPSock, reinterpret_cast(Data.data()), Split)) { if (c.GetStatus() > -1) c.SetStatus(-1); break; @@ -788,8 +837,8 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std Sent += Split; } else { f.seekg(Sent, std::ios_base::beg); - f.read(Data, Diff); - if (!TCPSendRaw(c, TCPSock, Data, int32_t(Diff))) { + f.read(reinterpret_cast(Data.data()), Diff); + if (!TCPSendRaw(c, TCPSock, reinterpret_cast(Data.data()), int32_t(Diff))) { if (c.GetStatus() > -1) c.SetStatus(-1); break; @@ -797,8 +846,6 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std Sent += Diff; } } - delete[] Data; - f.close(); } bool TNetwork::TCPSendRaw(TClient& C, SOCKET socket, char* Data, int32_t Size) { From 6e46d5aca9bf7b0948403dd0a1cd56a630fa1a0b Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Tue, 28 Jun 2022 03:29:01 +0200 Subject: [PATCH 081/184] start building the `clear` command --- include/TConsole.h | 2 ++ src/TConsole.cpp | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/include/TConsole.h b/include/TConsole.h index 1f990b8..22a3b01 100644 --- a/include/TConsole.h +++ b/include/TConsole.h @@ -35,6 +35,7 @@ private: 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_Clear(const std::string&, const std::vector& args); void Command_Say(const std::string& FullCommand); bool EnsureArgsCount(const std::vector& args, size_t n); @@ -50,6 +51,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); } }, + { "clear", [this](const auto& a, const auto& b) { Command_Clear(a, b); } }, { "say", [this](const auto&, const auto&) { Command_Say(""); } }, // shouldn't actually be called }; diff --git a/src/TConsole.cpp b/src/TConsole.cpp index c26f957..d26c95e 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -227,7 +227,8 @@ void TConsole::Command_Help(const std::string&, const std::vector& 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)"; + status how the server is doing and what it's up to + clear clears the console window)"; Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString)); } @@ -240,6 +241,13 @@ std::string TConsole::ConcatArgs(const std::vector& args, char spac return Result; } +void TConsole::Command_Clear(const std::string&, const std::vector& args) { + if (!EnsureArgsCount(args, 0, size_t(-1))) { + return; + } + mCommandline.write("\x1b[;H\x1b[2J"); +} + void TConsole::Command_Kick(const std::string&, const std::vector& args) { if (!EnsureArgsCount(args, 1, size_t(-1))) { return; From dfa90da8af8974a847349f748e78389c565b05e9 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 30 Jun 2022 20:19:48 +0200 Subject: [PATCH 082/184] check if Resources/Server directory exists, and only create it if it doesnt While this doesn't make a difference with usual setups, it does if the server path exists but is a symlink. In that case, the create_directories call fails, and the server aborts. this fixes that. :^) --- src/TLuaEngine.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 74b01c3..050f416 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -274,7 +274,9 @@ std::shared_ptr TLuaEngine::EnqueueFunctionCall(TLuaStateId StateID, } void TLuaEngine::CollectAndInitPlugins() { - fs::create_directories(mResourceServerPath); + if (!fs::exists(mResourceServerPath)) { + fs::create_directories(mResourceServerPath); + } for (const auto& Dir : fs::directory_iterator(mResourceServerPath)) { auto Path = Dir.path(); Path = fs::relative(Path); From 9420d8a7a00913fc7477a3ffd45103becbadf713 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 30 Jun 2022 21:30:56 +0200 Subject: [PATCH 083/184] update changelog --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index da9eb18..d0314c1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ - 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 ':' +- FIXED a fatal exception on LuaEngine startup if Resources/Server is a symlink # v3.0.2 From 04bbdff6b74499010848ba3f39ef35d0de90e41a Mon Sep 17 00:00:00 2001 From: Mackenzie <41524393+Mack29446@users.noreply.github.com> Date: Tue, 12 Jul 2022 22:59:41 +0100 Subject: [PATCH 084/184] Add code from EvanMulawski --- src/TNetwork.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index 9c0e748..6c654cd 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -239,13 +239,26 @@ void TNetwork::HandleDownload(SOCKET TCPSock) { }); } +static int get_ip_str(const struct sockaddr *sa, char *strBuf, size_t strBufSize) { + switch(sa->sa_family) { + case AF_INET: + inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), strBuf, strBufSize); + break; + case AF_INET6: + inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), strBuf, strBufSize); + break; + default: + return 1; + } + return 0; +} + void TNetwork::Authentication(const TConnection& ClientConnection) { auto Client = CreateClient(ClientConnection.Socket); - char AddrBuf[64]; - // TODO: IPv6 would need this to be changed - auto str = inet_ntop(AF_INET, reinterpret_cast(&ClientConnection.SockAddr), AddrBuf, sizeof(ClientConnection.SockAddr)); - beammp_trace("This thread is ip " + std::string(str)); - Client->SetIdentifier("ip", str); + char AddrBuf[INET6_ADDRSTRLEN]; + get_ip_str(&ClientConnection.SockAddr, AddrBuf, sizeof(AddrBuf)); + beammp_trace("This thread is ip " + std::string(AddrBuf)); + Client->SetIdentifier("ip", AddrBuf); std::string Rc; // TODO: figure out why this is not default constructed beammp_info("Identifying new ClientConnection..."); From 0a8e7d8e502d68333ab86e6a4ef94cdbeefac126 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 14 Jul 2022 00:27:44 +0200 Subject: [PATCH 085/184] add TriggerLocalEvent --- include/TLuaEngine.h | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index ee4e953..f71b9df 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -119,7 +119,6 @@ public: [[nodiscard]] std::shared_ptr EnqueueFunctionCall(TLuaStateId StateID, const std::string& FunctionName, const std::vector& Args); void EnsureStateExists(TLuaStateId StateId, const std::string& Name, bool DontCallOnInit = false); void RegisterEvent(const std::string& EventName, TLuaStateId StateId, const std::string& FunctionName); - template /** * * @tparam ArgsT Template Arguments for the event (Metadata) todo: figure out what this means @@ -128,6 +127,7 @@ public: * @param Args * @return */ + template [[nodiscard]] std::vector> TriggerEvent(const std::string& EventName, TLuaStateId IgnoreId, ArgsT&&... Args) { std::unique_lock Lock(mLuaEventsMutex); beammp_event(EventName); @@ -147,6 +147,21 @@ public: } return Results; // } + template + [[nodiscard]] std::vector> TriggerLocalEvent(const TLuaStateId& StateId, const std::string& EventName, ArgsT&&... Args) { + std::unique_lock Lock(mLuaEventsMutex); + beammp_event(EventName + " in '" + StateId + "'"); + if (mLuaEvents.find(EventName) == mLuaEvents.end()) { // if no event handler is defined for 'EventName', return immediately + return {}; + } + std::vector> Results; + std::vector Arguments { TLuaArgTypes { std::forward(Args) }... }; + const auto Handlers = GetEventHandlersForState(EventName, StateId); + for (const auto& Handler : Handlers) { + Results.push_back(EnqueueFunctionCall(StateId, Handler, Arguments)); + } + return Results; + } std::set GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId); void CreateEventTimer(const std::string& EventName, TLuaStateId StateId, size_t IntervalMS, CallStrategy Strategy); void CancelEventTimers(const std::string& EventName, TLuaStateId StateId); From ad414ec5c9066d294b09dd18b7ac4bc08cc46abb Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 14 Jul 2022 00:29:56 +0200 Subject: [PATCH 086/184] call onInit on hot-reload, cleanup, remove warnings onInit is now called on hot-reload, for the whole plugin. Arguably, this is not expected behavior, since only one file is being reloaded, but this is the easiest way to do it, and the entire hot-reload process is only for development purposes. Open an issue if this breaks your stuff :^) --- include/TConfig.h | 2 +- src/TConfig.cpp | 4 ++-- src/TPluginMonitor.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/TConfig.h b/include/TConfig.h index fe43d5c..d9d6045 100644 --- a/include/TConfig.h +++ b/include/TConfig.h @@ -19,7 +19,7 @@ public: void FlushToFile(); private: - void CreateConfigFile(std::string_view name); + void CreateConfigFile(); void ParseFromFile(std::string_view name); void PrintDebug(); void TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, std::string& OutValue); diff --git a/src/TConfig.cpp b/src/TConfig.cpp index b9503b5..fc3a1d1 100644 --- a/src/TConfig.cpp +++ b/src/TConfig.cpp @@ -62,7 +62,7 @@ TConfig::TConfig(const std::string& ConfigFileName) Application::SetSubsystemStatus("Config", Application::Status::Starting); if (!fs::exists(mConfigFileName) || !fs::is_regular_file(mConfigFileName)) { beammp_info("No config file found! Generating one..."); - CreateConfigFile(mConfigFileName); + CreateConfigFile(); } if (!mFailed) { if (fs::exists("Server.cfg")) { @@ -138,7 +138,7 @@ void TConfig::FlushToFile() { std::fclose(File); } -void TConfig::CreateConfigFile(std::string_view name) { +void TConfig::CreateConfigFile() { // build from old config Server.cfg try { diff --git a/src/TPluginMonitor.cpp b/src/TPluginMonitor.cpp index 5c940be..4cae6a0 100644 --- a/src/TPluginMonitor.cpp +++ b/src/TPluginMonitor.cpp @@ -47,8 +47,8 @@ void TPluginMonitor::operator()() { 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); + mEngine->ReportErrors(mEngine->TriggerLocalEvent(StateID, "onInit")); } else { // TODO: trigger onFileChanged event beammp_trace("Change detected in file \"" + Pair.first + "\", event trigger not implemented yet"); From a2cc6291537464777bbc96454e15e624a4dbfcce Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 14 Jul 2022 01:18:50 +0200 Subject: [PATCH 087/184] add onFileChanged (fixes #116) --- include/Common.h | 1 + include/TPluginMonitor.h | 2 +- src/TPluginMonitor.cpp | 24 ++++++++++-------------- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/include/Common.h b/include/Common.h index 313df2b..28b1ffd 100644 --- a/include/Common.h +++ b/include/Common.h @@ -220,6 +220,7 @@ void RegisterThread(const std::string& str); #define beammp_errorf(...) beammp_error(fmt::format(__VA_ARGS__)) #define beammp_infof(...) beammp_info(fmt::format(__VA_ARGS__)) + #define beammp_debugf(...) beammp_debug(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__)) diff --git a/include/TPluginMonitor.h b/include/TPluginMonitor.h index 3d82c4f..a617987 100644 --- a/include/TPluginMonitor.h +++ b/include/TPluginMonitor.h @@ -18,6 +18,6 @@ public: private: std::shared_ptr mEngine; fs::path mPath; - std::atomic_bool mShutdown; + std::atomic_bool mShutdown { false }; std::unordered_map mFileTimes; }; diff --git a/src/TPluginMonitor.cpp b/src/TPluginMonitor.cpp index 4cae6a0..7c84fec 100644 --- a/src/TPluginMonitor.cpp +++ b/src/TPluginMonitor.cpp @@ -5,6 +5,7 @@ TPluginMonitor::TPluginMonitor(const fs::path& Path, std::shared_ptr Engine) : mEngine(Engine) , mPath(Path) { + Application::SetSubsystemStatus("PluginMonitor", Application::Status::Starting); if (!fs::exists(mPath)) { fs::create_directories(mPath); } @@ -28,16 +29,17 @@ TPluginMonitor::TPluginMonitor(const fs::path& Path, std::shared_ptr void TPluginMonitor::operator()() { RegisterThread("PluginMonitor"); beammp_info("PluginMonitor started"); + Application::SetSubsystemStatus("PluginMonitor", Application::Status::Good); while (!mShutdown) { std::vector ToRemove; for (const auto& Pair : mFileTimes) { try { auto CurrentTime = fs::last_write_time(Pair.first); - if (CurrentTime != Pair.second) { + 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"); + beammp_infof("File \"{}\" changed, reloading", Pair.first); // 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); @@ -49,30 +51,24 @@ void TPluginMonitor::operator()() { auto Res = mEngine->EnqueueScript(StateID, Chunk); mEngine->AddResultToCheck(Res); mEngine->ReportErrors(mEngine->TriggerLocalEvent(StateID, "onInit")); + mEngine->ReportErrors(mEngine->TriggerEvent("onFileChanged", "", Pair.first)); } 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); - } - }*/ + beammp_debugf("File \"{}\" changed, not reloading because it's in a subdirectory. Triggering 'onFileChanged' event instead", Pair.first); + mEngine->ReportErrors(mEngine->TriggerEvent("onFileChanged", "", Pair.first)); } } } catch (const std::exception& e) { ToRemove.push_back(Pair.first); } for (size_t i = 0; i < 3 && !mShutdown; ++i) { - std::this_thread::sleep_for(std::chrono::seconds(i)); + std::this_thread::sleep_for(std::chrono::seconds(1)); } } 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)"); + beammp_warnf("File \"{}\" couldn't be accessed, so it was removed from plugin hot reload monitor (probably got deleted)", File); } } + Application::SetSubsystemStatus("PluginMonitor", Application::Status::Shutdown); } From 51ccf313730107ee8072d046c1766e4fa5a35fb4 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 14 Jul 2022 01:38:35 +0200 Subject: [PATCH 088/184] add beammp_debugf --- include/Common.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/Common.h b/include/Common.h index 28b1ffd..805739c 100644 --- a/include/Common.h +++ b/include/Common.h @@ -240,6 +240,7 @@ void RegisterThread(const std::string& str); #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_debugf(...) beammp_debug(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__)) From 6a940609701138e1c01cba1e3835a6be59d0befe Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 14 Jul 2022 02:08:28 +0200 Subject: [PATCH 089/184] update changelog --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index eeced3e..6ee3ff8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,8 +4,11 @@ - 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 +- ADDED onFileChanged event, triggered when a server plugin file changes +- FIXED `ip` in MP.GetIdentifiers - FIXED issue with client->server events which contain ':' - FIXED a fatal exception on LuaEngine startup if Resources/Server is a symlink +- FIXED onInit not being called on hot-reload # v3.0.2 From fd7b11f436d159550ba12b726a37fb4b4116fe0d Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 20 Jul 2022 14:31:32 +0200 Subject: [PATCH 090/184] fix event loop timing issue The event loop tries to run no faster than every 10ms. If it detects that it goes faster, it would incorrectly calculate the difference, and then wait (what I assume was) way too long or too short. Either way, now it's fixed and it correctly works, even when introducing new lua states. --- Changelog.md | 2 ++ include/Common.h | 7 +++++++ include/THeartbeatThread.h | 1 - include/TLuaEngine.h | 6 ++---- include/TNetwork.h | 1 - include/TPPSMonitor.h | 3 +-- include/TPluginMonitor.h | 1 - src/Common.cpp | 22 +++++++++++++++++++++ src/THeartbeatThread.cpp | 3 +-- src/TLuaEngine.cpp | 40 +++++++++++++++++++------------------- src/TNetwork.cpp | 26 ++++++++++++------------- src/TPPSMonitor.cpp | 3 +-- src/TPluginMonitor.cpp | 5 ++--- src/main.cpp | 2 +- 14 files changed, 71 insertions(+), 51 deletions(-) diff --git a/Changelog.md b/Changelog.md index 6ee3ff8..aa104b3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,4 @@ + # v3.1.0 - ADDED Tab autocomplete in console, smart tab autocomplete (understands lua tables and types) in the lua console @@ -9,6 +10,7 @@ - FIXED issue with client->server events which contain ':' - FIXED a fatal exception on LuaEngine startup if Resources/Server is a symlink - FIXED onInit not being called on hot-reload +- FIXED incorrect timing calculation of Lua EventTimer loop # v3.0.2 diff --git a/include/Common.h b/include/Common.h index 805739c..c77ffd1 100644 --- a/include/Common.h +++ b/include/Common.h @@ -12,6 +12,7 @@ extern TSentry Sentry; #include #include #include +#include #include #include @@ -96,6 +97,8 @@ public: static void CheckForUpdates(); static std::array VersionStrToInts(const std::string& str); static bool IsOutdated(const Version& Current, const Version& Newest); + static bool IsShuttingDown(); + static void SleepSafeSeconds(size_t Seconds); static void InitializeConsole() { if (!mConsole) { @@ -121,10 +124,14 @@ public: static void SetSubsystemStatus(const std::string& Subsystem, Status status); private: + static void SetShutdown(bool Val); + static inline SystemStatusMap mSystemStatusMap {}; static inline std::mutex mSystemStatusMapMutex {}; static inline std::string mPPS; static inline std::unique_ptr mConsole; + static inline std::shared_mutex mShutdownMtx {}; + static inline bool mShutdown { false }; static inline std::mutex mShutdownHandlersMutex {}; static inline std::deque mShutdownHandlers {}; diff --git a/include/THeartbeatThread.h b/include/THeartbeatThread.h index 1f0ab14..1063be6 100644 --- a/include/THeartbeatThread.h +++ b/include/THeartbeatThread.h @@ -15,7 +15,6 @@ private: std::string GenerateCall(); std::string GetPlayers(); - bool mShutdown = false; TResourceManager& mResourceManager; TServer& mServer; }; diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index f71b9df..2041687 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -187,7 +187,7 @@ private: class StateThreadData : IThreaded { public: - StateThreadData(const std::string& Name, std::atomic_bool& Shutdown, TLuaStateId StateId, TLuaEngine& Engine); + StateThreadData(const std::string& Name, TLuaStateId StateId, TLuaEngine& Engine); StateThreadData(const StateThreadData&) = delete; ~StateThreadData() noexcept { beammp_debug("\"" + mStateId + "\" destroyed"); } [[nodiscard]] std::shared_ptr EnqueueScript(const TLuaChunk& Script); @@ -218,7 +218,6 @@ private: sol::table Lua_FS_ListDirectories(const std::string& Path); std::string mName; - std::atomic_bool& mShutdown; TLuaStateId mStateId; lua_State* mState; std::thread mThread; @@ -247,8 +246,7 @@ private: TNetwork* mNetwork; TServer* mServer; - std::atomic_bool mShutdown { false }; - fs::path mResourceServerPath; + const fs::path mResourceServerPath; std::vector> mLuaPlugins; std::unordered_map> mLuaStates; std::recursive_mutex mLuaStatesMutex; diff --git a/include/TNetwork.h b/include/TNetwork.h index 87e4235..6394306 100644 --- a/include/TNetwork.h +++ b/include/TNetwork.h @@ -32,7 +32,6 @@ private: TServer& mServer; TPPSMonitor& mPPSMonitor; SOCKET mUDPSock {}; - bool mShutdown { false }; TResourceManager& mResourceManager; std::thread mUDPThread; std::thread mTCPThread; diff --git a/include/TPPSMonitor.h b/include/TPPSMonitor.h index 508dfc5..0718f10 100644 --- a/include/TPPSMonitor.h +++ b/include/TPPSMonitor.h @@ -22,6 +22,5 @@ private: TServer& mServer; std::optional> mNetwork { std::nullopt }; - bool mShutdown { false }; int mInternalPPS { 0 }; -}; \ No newline at end of file +}; diff --git a/include/TPluginMonitor.h b/include/TPluginMonitor.h index a617987..2ed77bc 100644 --- a/include/TPluginMonitor.h +++ b/include/TPluginMonitor.h @@ -18,6 +18,5 @@ public: private: std::shared_ptr mEngine; fs::path mPath; - std::atomic_bool mShutdown { false }; std::unordered_map mFileTimes; }; diff --git a/src/Common.cpp b/src/Common.cpp index 11fe8f6..728bbf7 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -25,6 +25,7 @@ void Application::RegisterShutdownHandler(const TShutdownHandler& Handler) { } void Application::GracefullyShutdown() { + SetShutdown(true); static bool AlreadyShuttingDown = false; static uint8_t ShutdownAttempts = 0; if (AlreadyShuttingDown) { @@ -93,6 +94,22 @@ bool Application::IsOutdated(const Version& Current, const Version& Newest) { } } +bool Application::IsShuttingDown() { + std::shared_lock Lock(mShutdownMtx); + return mShutdown; +} + +void Application::SleepSafeSeconds(size_t Seconds) { + // Sleeps for 500 ms, checks if a shutdown occurred, and so forth + for (size_t i = 0; i < Seconds * 2; ++i) { + if (Application::IsShuttingDown()) { + return; + } else { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } + } +} + TEST_CASE("Application::IsOutdated (version check)") { SUBCASE("Same version") { CHECK(!Application::IsOutdated({ 1, 2, 3 }, { 1, 2, 3 })); @@ -158,6 +175,11 @@ void Application::SetSubsystemStatus(const std::string& Subsystem, Status status mSystemStatusMap[Subsystem] = status; } +void Application::SetShutdown(bool Val) { + std::unique_lock Lock(mShutdownMtx); + mShutdown = Val; +} + TEST_CASE("Application::SetSubsystemStatus") { Application::SetSubsystemStatus("Test", Application::Status::Good); auto Map = Application::GetSubsystemStatuses(); diff --git a/src/THeartbeatThread.cpp b/src/THeartbeatThread.cpp index 5d3d437..edd0ed2 100644 --- a/src/THeartbeatThread.cpp +++ b/src/THeartbeatThread.cpp @@ -20,7 +20,7 @@ void THeartbeatThread::operator()() { static std::chrono::high_resolution_clock::time_point LastNormalUpdateTime = std::chrono::high_resolution_clock::now(); bool isAuth = false; size_t UpdateReminderCounter = 0; - while (!mShutdown) { + while (!Application::IsShuttingDown()) { ++UpdateReminderCounter; Body = GenerateCall(); // a hot-change occurs when a setting has changed, to update the backend of that change. @@ -164,7 +164,6 @@ THeartbeatThread::THeartbeatThread(TResourceManager& ResourceManager, TServer& S Application::RegisterShutdownHandler([&] { Application::SetSubsystemStatus("Heartbeat", Application::Status::ShuttingDown); if (mThread.joinable()) { - mShutdown = true; mThread.join(); } Application::SetSubsystemStatus("Heartbeat", Application::Status::Shutdown); diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 050f416..a4e2d52 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -15,20 +15,18 @@ TLuaEngine* LuaAPI::MP::Engine; -TLuaEngine::TLuaEngine() { +TLuaEngine::TLuaEngine() + : mResourceServerPath(fs::path(Application::Settings.Resource) / "Server") { Application::SetSubsystemStatus("LuaEngine", Application::Status::Starting); LuaAPI::MP::Engine = this; if (!fs::exists(Application::Settings.Resource)) { fs::create_directory(Application::Settings.Resource); } - fs::path Path = fs::path(Application::Settings.Resource) / "Server"; - if (!fs::exists(Path)) { - fs::create_directory(Path); + if (!fs::exists(mResourceServerPath)) { + fs::create_directory(mResourceServerPath); } - mResourceServerPath = Path; Application::RegisterShutdownHandler([&] { Application::SetSubsystemStatus("LuaEngine", Application::Status::ShuttingDown); - mShutdown = true; if (mThread.joinable()) { mThread.join(); } @@ -59,7 +57,7 @@ void TLuaEngine::operator()() { auto ResultCheckThread = std::thread([&] { RegisterThread("ResultCheckThread"); - while (!mShutdown) { + while (!Application::IsShuttingDown()) { std::unique_lock Lock(mResultsToCheckMutex); mResultsToCheckCond.wait_for(Lock, std::chrono::milliseconds(20)); if (!mResultsToCheck.empty()) { @@ -79,10 +77,7 @@ void TLuaEngine::operator()() { }); // event loop auto Before = std::chrono::high_resolution_clock::now(); - while (!mShutdown) { - if (mLuaStates.size() == 0) { - std::this_thread::sleep_for(std::chrono::seconds(100)); - } + while (!Application::IsShuttingDown()) { { // Timed Events Scope std::unique_lock Lock(mTimedEventsMutex); for (auto& Timer : mTimedEvents) { @@ -108,12 +103,18 @@ void TLuaEngine::operator()() { } } } - const auto Expected = std::chrono::milliseconds(10); - if (auto Diff = std::chrono::high_resolution_clock::now() - Before; - Diff < Expected) { - std::this_thread::sleep_for(Expected - Diff); + if (mLuaStates.size() == 0) { + beammp_trace("No Lua states, event loop running extremely sparsely"); + Application::SleepSafeSeconds(10); } else { - beammp_trace("Event loop cannot keep up! Running " + std::to_string(Diff.count()) + "s behind"); + constexpr double NsFactor = 1000000.0; + constexpr double Expected = 10.0; // ms + const auto Diff = (std::chrono::high_resolution_clock::now() - Before).count() / NsFactor; + if (Diff < Expected) { + std::this_thread::sleep_for(std::chrono::nanoseconds(size_t((Expected - Diff) * NsFactor))); + } else { + beammp_tracef("Event loop cannot keep up! Running {}ms behind", Diff); + } } Before = std::chrono::high_resolution_clock::now(); } @@ -326,7 +327,7 @@ void TLuaEngine::EnsureStateExists(TLuaStateId StateId, const std::string& Name, std::unique_lock Lock(mLuaStatesMutex); if (mLuaStates.find(StateId) == mLuaStates.end()) { beammp_debug("Creating lua state for state id \"" + StateId + "\""); - auto DataPtr = std::make_unique(Name, mShutdown, StateId, *this); + auto DataPtr = std::make_unique(Name, StateId, *this); mLuaStates[StateId] = std::move(DataPtr); RegisterEvent("onInit", StateId, "onInit"); if (!DontCallOnInit) { @@ -614,9 +615,8 @@ sol::table TLuaEngine::StateThreadData::Lua_JsonDecode(const std::string& str) { return table; } -TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomic_bool& Shutdown, TLuaStateId StateId, TLuaEngine& Engine) +TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, TLuaStateId StateId, TLuaEngine& Engine) : mName(Name) - , mShutdown(Shutdown) , mStateId(StateId) , mState(luaL_newstate()) , mEngine(&Engine) { @@ -819,7 +819,7 @@ void TLuaEngine::StateThreadData::RegisterEvent(const std::string& EventName, co void TLuaEngine::StateThreadData::operator()() { RegisterThread("Lua:" + mStateId); - while (!mShutdown) { + while (!Application::IsShuttingDown()) { { // StateExecuteQueue Scope std::unique_lock Lock(mStateExecuteQueueMutex); if (!mStateExecuteQueue.empty()) { diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index 6c654cd..be9b773 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -25,7 +25,6 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R Application::RegisterShutdownHandler([&] { Application::SetSubsystemStatus("UDPNetwork", Application::Status::ShuttingDown); if (mUDPThread.joinable()) { - mShutdown = true; mUDPThread.detach(); } Application::SetSubsystemStatus("UDPNetwork", Application::Status::Shutdown); @@ -33,7 +32,6 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R Application::RegisterShutdownHandler([&] { Application::SetSubsystemStatus("TCPNetwork", Application::Status::ShuttingDown); if (mTCPThread.joinable()) { - mShutdown = true; mTCPThread.detach(); } Application::SetSubsystemStatus("TCPNetwork", Application::Status::Shutdown); @@ -68,7 +66,7 @@ void TNetwork::UDPServerMain() { Application::SetSubsystemStatus("UDPNetwork", Application::Status::Good); beammp_info(("Vehicle data network online on port ") + std::to_string(Application::Settings.Port) + (" with a Max of ") + std::to_string(Application::Settings.MaxPlayers) + (" Clients")); - while (!mShutdown) { + while (!Application::IsShuttingDown()) { try { sockaddr_in client {}; std::string Data = UDPRcvFromClient(client); // Receives any data from Socket @@ -152,7 +150,7 @@ void TNetwork::TCPServerMain() { beammp_info("Vehicle event network online"); do { try { - if (mShutdown) { + if (Application::IsShuttingDown()) { beammp_debug("shutdown during TCP wait for accept loop"); break; } @@ -239,16 +237,16 @@ void TNetwork::HandleDownload(SOCKET TCPSock) { }); } -static int get_ip_str(const struct sockaddr *sa, char *strBuf, size_t strBufSize) { - switch(sa->sa_family) { - case AF_INET: - inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), strBuf, strBufSize); - break; - case AF_INET6: - inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), strBuf, strBufSize); - break; - default: - return 1; +static int get_ip_str(const struct sockaddr* sa, char* strBuf, size_t strBufSize) { + switch (sa->sa_family) { + case AF_INET: + inet_ntop(AF_INET, &(((struct sockaddr_in*)sa)->sin_addr), strBuf, strBufSize); + break; + case AF_INET6: + inet_ntop(AF_INET6, &(((struct sockaddr_in6*)sa)->sin6_addr), strBuf, strBufSize); + break; + default: + return 1; } return 0; } diff --git a/src/TPPSMonitor.cpp b/src/TPPSMonitor.cpp index b5b4951..3edd17d 100644 --- a/src/TPPSMonitor.cpp +++ b/src/TPPSMonitor.cpp @@ -10,7 +10,6 @@ TPPSMonitor::TPPSMonitor(TServer& Server) Application::SetSubsystemStatus("PPSMonitor", Application::Status::ShuttingDown); if (mThread.joinable()) { beammp_debug("shutting down PPSMonitor"); - mShutdown = true; mThread.join(); beammp_debug("shut down PPSMonitor"); } @@ -27,7 +26,7 @@ void TPPSMonitor::operator()() { beammp_debug("PPSMonitor starting"); Application::SetSubsystemStatus("PPSMonitor", Application::Status::Good); std::vector> TimedOutClients; - while (!mShutdown) { + while (!Application::IsShuttingDown()) { std::this_thread::sleep_for(std::chrono::seconds(1)); int C = 0, V = 0; if (mServer.ClientCount() == 0) { diff --git a/src/TPluginMonitor.cpp b/src/TPluginMonitor.cpp index 7c84fec..9553f5d 100644 --- a/src/TPluginMonitor.cpp +++ b/src/TPluginMonitor.cpp @@ -17,7 +17,6 @@ TPluginMonitor::TPluginMonitor(const fs::path& Path, std::shared_ptr } Application::RegisterShutdownHandler([this] { - mShutdown = true; if (mThread.joinable()) { mThread.join(); } @@ -30,7 +29,7 @@ void TPluginMonitor::operator()() { RegisterThread("PluginMonitor"); beammp_info("PluginMonitor started"); Application::SetSubsystemStatus("PluginMonitor", Application::Status::Good); - while (!mShutdown) { + while (!Application::IsShuttingDown()) { std::vector ToRemove; for (const auto& Pair : mFileTimes) { try { @@ -61,7 +60,7 @@ void TPluginMonitor::operator()() { } catch (const std::exception& e) { ToRemove.push_back(Pair.first); } - for (size_t i = 0; i < 3 && !mShutdown; ++i) { + for (size_t i = 0; i < 3 && !Application::IsShuttingDown(); ++i) { std::this_thread::sleep_for(std::chrono::seconds(1)); } } diff --git a/src/main.cpp b/src/main.cpp index f3e23f4..4650570 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -69,7 +69,7 @@ int main(int argc, char** argv) { Sentry.LogException(e, _file_basename, _line); MainRet = -1; } - return MainRet; + std::exit(MainRet); } int BeamMPServerMain(MainArguments Arguments) { From 2ed92c4aa14aa606c4f5fbd9c44324df32c5e126 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 20 Jul 2022 14:42:07 +0200 Subject: [PATCH 091/184] fix plugin monitor eating a whole cpu core i must have messed this up when i changed the plugin monitor behavior recently --- src/TPluginMonitor.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/TPluginMonitor.cpp b/src/TPluginMonitor.cpp index 9553f5d..df575f1 100644 --- a/src/TPluginMonitor.cpp +++ b/src/TPluginMonitor.cpp @@ -60,10 +60,8 @@ void TPluginMonitor::operator()() { } catch (const std::exception& e) { ToRemove.push_back(Pair.first); } - for (size_t i = 0; i < 3 && !Application::IsShuttingDown(); ++i) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } } + Application::SleepSafeSeconds(3); for (const auto& File : ToRemove) { mFileTimes.erase(File); beammp_warnf("File \"{}\" couldn't be accessed, so it was removed from plugin hot reload monitor (probably got deleted)", File); From f21d3d038921f401ecb703ec89fef9129bdaf40b Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 20 Jul 2022 14:56:58 +0200 Subject: [PATCH 092/184] fix bug which caused Lua hot-reload not to report syntax errors --- Changelog.md | 1 + src/TPluginMonitor.cpp | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index aa104b3..5b87d71 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,6 +11,7 @@ - FIXED a fatal exception on LuaEngine startup if Resources/Server is a symlink - FIXED onInit not being called on hot-reload - FIXED incorrect timing calculation of Lua EventTimer loop +- FIXED bug which caused hot-reload not to report syntax errors # v3.0.2 diff --git a/src/TPluginMonitor.cpp b/src/TPluginMonitor.cpp index df575f1..3332ae1 100644 --- a/src/TPluginMonitor.cpp +++ b/src/TPluginMonitor.cpp @@ -48,9 +48,13 @@ void TPluginMonitor::operator()() { 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); - mEngine->AddResultToCheck(Res); - mEngine->ReportErrors(mEngine->TriggerLocalEvent(StateID, "onInit")); - mEngine->ReportErrors(mEngine->TriggerEvent("onFileChanged", "", Pair.first)); + Res->WaitUntilReady(); + if (Res->Error) { + beammp_lua_errorf("Error while hot-reloading \"{}\": {}", Pair.first, Res->ErrorMessage); + } else { + mEngine->ReportErrors(mEngine->TriggerLocalEvent(StateID, "onInit")); + mEngine->ReportErrors(mEngine->TriggerEvent("onFileChanged", "", Pair.first)); + } } else { // is in subfolder, dont reload, just trigger an event beammp_debugf("File \"{}\" changed, not reloading because it's in a subdirectory. Triggering 'onFileChanged' event instead", Pair.first); From 8b57f6e35af198164dee47bc879ee66ee639ff8b Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 20 Jul 2022 16:07:00 +0200 Subject: [PATCH 093/184] fix missing Lua error messages in ResultCheckThread --- Changelog.md | 1 + src/TLuaEngine.cpp | 14 ++++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Changelog.md b/Changelog.md index 5b87d71..e8f3a6a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,6 +12,7 @@ - FIXED onInit not being called on hot-reload - FIXED incorrect timing calculation of Lua EventTimer loop - FIXED bug which caused hot-reload not to report syntax errors +- FIXED missing error messages on some event handler calls # v3.0.2 diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index a4e2d52..7ca54f7 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -59,19 +59,21 @@ void TLuaEngine::operator()() { RegisterThread("ResultCheckThread"); while (!Application::IsShuttingDown()) { std::unique_lock Lock(mResultsToCheckMutex); - mResultsToCheckCond.wait_for(Lock, std::chrono::milliseconds(20)); + beammp_tracef("Results to check: {}", mResultsToCheck.size()); if (!mResultsToCheck.empty()) { mResultsToCheck.remove_if([](const std::shared_ptr& Ptr) -> bool { if (Ptr->Ready) { - return true; - } else if (Ptr->Error) { - if (Ptr->ErrorMessage != BeamMPFnNotFoundError) { - beammp_lua_error(Ptr->Function + ": " + Ptr->ErrorMessage); + if (Ptr->Error) { + if (Ptr->ErrorMessage != BeamMPFnNotFoundError) { + beammp_lua_error(Ptr->Function + ": " + Ptr->ErrorMessage); + } } return true; } return false; }); + } else { + mResultsToCheckCond.wait_for(Lock, std::chrono::milliseconds(20)); } } }); @@ -161,7 +163,7 @@ void TLuaEngine::AddResultToCheck(const std::shared_ptr& Result) { mResultsToCheckCond.notify_one(); } -std::unordered_map /* handlers */> TLuaEngine::Debug_GetEventsForState(TLuaStateId StateId) { +std::unordered_map /* handlers */> TLuaEngine::Debug_GetEventsForState(TLuaStateId StateId) { std::unordered_map> Result; std::unique_lock Lock(mLuaEventsMutex); for (const auto& EventNameToEventMap : mLuaEvents) { From 054016a099abc3b341cb911bc9b8254750ecede5 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 20 Jul 2022 16:08:56 +0200 Subject: [PATCH 094/184] remove debug print --- src/TLuaEngine.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 7ca54f7..1ac7fe3 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -59,7 +59,6 @@ void TLuaEngine::operator()() { RegisterThread("ResultCheckThread"); while (!Application::IsShuttingDown()) { std::unique_lock Lock(mResultsToCheckMutex); - beammp_tracef("Results to check: {}", mResultsToCheck.size()); if (!mResultsToCheck.empty()) { mResultsToCheck.remove_if([](const std::shared_ptr& Ptr) -> bool { if (Ptr->Ready) { From b009a37f35d4a59b71913a5bfe66c0df071af8b3 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 3 Aug 2022 13:59:11 +0200 Subject: [PATCH 095/184] update asio to 1-23-0 --- deps/asio | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/asio b/deps/asio index d038fb3..4915cfd 160000 --- a/deps/asio +++ b/deps/asio @@ -1 +1 @@ -Subproject commit d038fb3c2fb56fb91ff1d17b0715cff7887aa09e +Subproject commit 4915cfd8a1653c157a1480162ae5601318553eb8 From 1caa5e3517e4ef61c7fed06cc480bbd2c4a68a8d Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 3 Aug 2022 13:59:54 +0200 Subject: [PATCH 096/184] update commandline --- deps/commandline | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/commandline b/deps/commandline index d6b1c32..7b9f51d 160000 --- a/deps/commandline +++ b/deps/commandline @@ -1 +1 @@ -Subproject commit d6b1c32c8af6ad5306f9f001305b3be9928ae4bb +Subproject commit 7b9f51d6a0402500a8f84a2fb37223ac4309f2d7 From e634b4b6b780b8aecfeeecfaef2cb75e63fb9950 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 3 Aug 2022 14:00:26 +0200 Subject: [PATCH 097/184] update doctest --- deps/doctest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/doctest b/deps/doctest index 7b98851..b7c21ec 160000 --- a/deps/doctest +++ b/deps/doctest @@ -1 +1 @@ -Subproject commit 7b9885133108ae301ddd16e2651320f54cafeba7 +Subproject commit b7c21ec5ceeadb4951b00396fc1e4642dd347e5f From 60dd1e24726bda897363231fe2380f6367173198 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 3 Aug 2022 14:01:04 +0200 Subject: [PATCH 098/184] update json --- deps/json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/json b/deps/json index ede6667..69d7448 160000 --- a/deps/json +++ b/deps/json @@ -1 +1 @@ -Subproject commit ede66678580596028bcd6e18871a35a54bac01d7 +Subproject commit 69d744867f8847c91a126fa25e9a6a3d67b3be41 From 398f8d3fa40b4e0a15b549695546d2abebc3f791 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 3 Aug 2022 14:01:40 +0200 Subject: [PATCH 099/184] update sol2 --- deps/sol2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/sol2 b/deps/sol2 index c068aef..eba8662 160000 --- a/deps/sol2 +++ b/deps/sol2 @@ -1 +1 @@ -Subproject commit c068aefbeddb3dd1f1fd38d42843ecb49a3b4cdb +Subproject commit eba86625b707e3c8c99bbfc4624e51f42dc9e561 From 76a8f231ac04921f375fc045093268f6a6bb0b6e Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 3 Aug 2022 14:02:11 +0200 Subject: [PATCH 100/184] update httplib --- deps/cpp-httplib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/cpp-httplib b/deps/cpp-httplib index 47044c0..d92c314 160000 --- a/deps/cpp-httplib +++ b/deps/cpp-httplib @@ -1 +1 @@ -Subproject commit 47044c05a8587dff86ab90526daabfef61079490 +Subproject commit d92c31446687cfa336a6332b1015b4fe289fbdec From b443bec72ef7ec7e7a2615d142328e1b65562eb5 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 3 Aug 2022 18:13:25 +0200 Subject: [PATCH 101/184] update fmt --- deps/fmt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/fmt b/deps/fmt index ce246aa..c4ee726 160000 --- a/deps/fmt +++ b/deps/fmt @@ -1 +1 @@ -Subproject commit ce246aaf74cb7616777497da988804fa4c02bf2d +Subproject commit c4ee726532178e556d923372f29163bd206d7732 From e4db66782e3de2677bf1314b135b43dc6d2f3785 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 3 Aug 2022 18:13:49 +0200 Subject: [PATCH 102/184] update libzip --- deps/libzip | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/libzip b/deps/libzip index 76df02f..5532f9b 160000 --- a/deps/libzip +++ b/deps/libzip @@ -1 +1 @@ -Subproject commit 76df02f86b9746e139fd9fc934a70e3a21bbc557 +Subproject commit 5532f9baa0c44cc5435ad135686a4ea009075b9a From e4cbba59ef126d1a8468a6a22287b8d71a1ae9cd Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 3 Aug 2022 18:14:19 +0200 Subject: [PATCH 103/184] update sentry-native --- deps/sentry-native | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/sentry-native b/deps/sentry-native index 90966cc..87e67ad 160000 --- a/deps/sentry-native +++ b/deps/sentry-native @@ -1 +1 @@ -Subproject commit 90966cc1022b8155681b6899539b35466baccf2c +Subproject commit 87e67ad783a7ec4476b0eb4742bd40fe5a1e2435 From 54e02abad1e6cc1b20b8a7c335c908a3e8afa75f Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 3 Aug 2022 18:16:56 +0200 Subject: [PATCH 104/184] update toml11 --- deps/toml11 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/toml11 b/deps/toml11 index 1400dd2..c7627ff 160000 --- a/deps/toml11 +++ b/deps/toml11 @@ -1 +1 @@ -Subproject commit 1400dd223fb4297337266fcb5d04b700338aea71 +Subproject commit c7627ff6a1eb6f34fbd98369990a9442e2836c25 From 44b94c9e58838c3a49a877246f30df71c11622fd Mon Sep 17 00:00:00 2001 From: 20dka Date: Wed, 14 Sep 2022 01:36:16 +0200 Subject: [PATCH 105/184] add MP.GetPositionRaw(pid, vid) fix vehicles sometimes not deleting for all players --- Changelog.md | 2 + include/Client.h | 4 ++ include/Common.h | 4 ++ include/TLuaEngine.h | 1 + include/TServer.h | 1 + src/Client.cpp | 17 ++++++ src/TLuaEngine.cpp | 32 +++++++++++ src/TServer.cpp | 127 ++++++++++++++++++++++++++++++++++--------- 8 files changed, 163 insertions(+), 25 deletions(-) diff --git a/Changelog.md b/Changelog.md index e8f3a6a..f719f10 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ - ADDED MP.JsonEncode() and MP.JsonDecode(), which turn lua tables into json and vice-versa - ADDED FS.ListFiles and FS.ListDirectories - ADDED onFileChanged event, triggered when a server plugin file changes +- ADDED MP.GetPositionRaw(), which can be used to retrieve the latest position packet per player, per vehicle - FIXED `ip` in MP.GetIdentifiers - FIXED issue with client->server events which contain ':' - FIXED a fatal exception on LuaEngine startup if Resources/Server is a symlink @@ -13,6 +14,7 @@ - FIXED incorrect timing calculation of Lua EventTimer loop - FIXED bug which caused hot-reload not to report syntax errors - FIXED missing error messages on some event handler calls +- FIXED vehicles not deleting for all players if an edit was cancelled by Lua # v3.0.2 diff --git a/include/Client.h b/include/Client.h index d513659..114ff3e 100644 --- a/include/Client.h +++ b/include/Client.h @@ -39,11 +39,13 @@ public: void AddNewCar(int Ident, const std::string& Data); void SetCarData(int Ident, const std::string& Data); + void SetCarPosition(int Ident, const std::string& Data); TVehicleDataLockPair GetAllCars(); void SetName(const std::string& Name) { mName = Name; } void SetRoles(const std::string& Role) { mRole = Role; } void SetIdentifier(const std::string& key, const std::string& value) { mIdentifiers[key] = value; } std::string GetCarData(int Ident); + std::string GetCarPositionRaw(int Ident); void SetUDPAddr(sockaddr_in Addr) { mUDPAddress = Addr; } void SetDownSock(SOCKET CSock) { mSocket[1] = CSock; } void SetTCPSock(SOCKET CSock) { mSocket[0] = CSock; } @@ -93,7 +95,9 @@ private: std::unordered_map mIdentifiers; bool mIsGuest = false; mutable std::mutex mVehicleDataMutex; + mutable std::mutex mVehiclePositionMutex; TSetOfVehicleData mVehicleData; + SparseArray mVehiclePosition; std::string mName = "Unknown Client"; SOCKET mSocket[2] { SOCKET(0), SOCKET(0) }; sockaddr_in mUDPAddress {}; // is this initialization OK? yes it is diff --git a/include/Common.h b/include/Common.h index c77ffd1..37c891f 100644 --- a/include/Common.h +++ b/include/Common.h @@ -4,6 +4,7 @@ extern TSentry Sentry; #include +#include #include #include #include @@ -33,6 +34,9 @@ struct Version { std::string AsString(); }; +template +using SparseArray = std::unordered_map; + // static class handling application start, shutdown, etc. // yes, static classes, singletons, globals are all pretty // bad idioms. In this case we need a central way to access diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index 2041687..b3cf883 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -211,6 +211,7 @@ private: sol::table Lua_GetPlayers(); std::string Lua_GetPlayerName(int ID); sol::table Lua_GetPlayerVehicles(int ID); + std::pair Lua_GetPositionRaw(int PID, int VID); 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); diff --git a/include/TServer.h b/include/TServer.h index 2e51e12..698835a 100644 --- a/include/TServer.h +++ b/include/TServer.h @@ -38,4 +38,5 @@ private: static bool ShouldSpawn(TClient& c, const std::string& CarJson, int ID); static bool IsUnicycle(TClient& c, const std::string& CarJson); static void Apply(TClient& c, int VID, const std::string& pckt); + static void HandlePosition(TClient& c, std::string Packet); }; diff --git a/src/Client.cpp b/src/Client.cpp index 410d966..f93f825 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -47,6 +47,23 @@ TClient::TVehicleDataLockPair TClient::GetAllCars() { return { &mVehicleData, std::unique_lock(mVehicleDataMutex) }; } +std::string TClient::GetCarPositionRaw(int Ident) { + std::unique_lock lock(mVehiclePositionMutex); + try + { + return mVehiclePosition.at(Ident); + } + catch (const std::out_of_range& oor) { + return ""; + } + return ""; +} + +void TClient::SetCarPosition(int Ident, const std::string& Data) { + std::unique_lock lock(mVehiclePositionMutex); + mVehiclePosition[Ident] = Data; +} + std::string TClient::GetCarData(int Ident) { { // lock std::unique_lock lock(mVehicleDataMutex); diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 1ac7fe3..303f8bf 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -517,6 +517,35 @@ sol::table TLuaEngine::StateThreadData::Lua_GetPlayerVehicles(int ID) { return sol::lua_nil; } +std::pair TLuaEngine::StateThreadData::Lua_GetPositionRaw(int PID, int VID) { + std::pair Result; + auto MaybeClient = GetClient(mEngine->Server(), PID); + if (MaybeClient && !MaybeClient.value().expired()) { + auto Client = MaybeClient.value().lock(); + std::string VehiclePos = Client->GetCarPositionRaw(VID); + + if (VehiclePos.empty()) { + //return std::make_tuple(sol::lua_nil, sol::make_object(StateView, "Vehicle not found")); + Result.second = "Vehicle not found"; + return Result; + } + + sol::table t = Lua_JsonDecode(VehiclePos); + if (t == sol::lua_nil){ + Result.second = "Packet decode failed"; + } + //return std::make_tuple(Result, sol::make_object(StateView, sol::lua_nil)); + Result.first = t; + return Result; + } + else { + //return std::make_tuple(sol::lua_nil, sol::make_object(StateView, "Client expired")); + Result.second = "Client expired"; + return Result; + } +} + + sol::table TLuaEngine::StateThreadData::Lua_HttpCreateConnection(const std::string& host, uint16_t port) { auto table = mStateView.create_table(); constexpr const char* InternalClient = "__InternalClient"; @@ -673,6 +702,9 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, TLuaStateI MPTable.set_function("GetPlayerVehicles", [&](int ID) -> sol::table { return Lua_GetPlayerVehicles(ID); }); + MPTable.set_function("GetPositionRaw", [&](int PID, int VID) -> std::pair { + return Lua_GetPositionRaw(PID, VID); + }); MPTable.set_function("SendChatMessage", &LuaAPI::MP::SendChatMessage); MPTable.set_function("GetPlayers", [&]() -> sol::table { return Lua_GetPlayers(); diff --git a/src/TServer.cpp b/src/TServer.cpp index 48b2bbb..92e9061 100644 --- a/src/TServer.cpp +++ b/src/TServer.cpp @@ -15,6 +15,67 @@ #include "Json.h" +static std::optional> GetPidVid(std::string str) { + auto IDSep = str.find('-'); + std::string pid = str.substr(0, IDSep); + std::string vid = str.substr(IDSep + 1); + + if (pid.find_first_not_of("0123456789") == std::string::npos && vid.find_first_not_of("0123456789") == std::string::npos) { + try { + int PID = stoi(pid); + int VID = stoi(vid); + return {{ PID, VID }}; + } catch(const std::exception&) { + return std::nullopt; + } + } + return std::nullopt; +} + +TEST_CASE("GetPidVid") { + SUBCASE("Valid singledigit") { + const auto MaybePidVid = GetPidVid("0-1"); + CHECK(MaybePidVid); + auto [pid, vid] = MaybePidVid.value(); + + CHECK_EQ(pid, 0); + CHECK_EQ(vid, 1); + } + SUBCASE("Valid doubledigit") { + const auto MaybePidVid = GetPidVid("10-12"); + CHECK(MaybePidVid); + auto [pid, vid] = MaybePidVid.value(); + + CHECK_EQ(pid, 10); + CHECK_EQ(vid, 12); + } + SUBCASE("Empty string") { + const auto MaybePidVid = GetPidVid(""); + CHECK(!MaybePidVid); + } + SUBCASE("Invalid separator") { + const auto MaybePidVid = GetPidVid("0x0"); + CHECK(!MaybePidVid); + } + SUBCASE("Missing pid") { + const auto MaybePidVid = GetPidVid("-0"); + CHECK(!MaybePidVid); + } + SUBCASE("Missing vid") { + const auto MaybePidVid = GetPidVid("0-"); + CHECK(!MaybePidVid); + } + SUBCASE("Invalid pid") { + const auto MaybePidVid = GetPidVid("x-0"); + CHECK(!MaybePidVid); + } + SUBCASE("Invalid vid") { + const auto MaybePidVid = GetPidVid("0-x"); + CHECK(!MaybePidVid); + } +} + + TServer::TServer(const std::vector& Arguments) { beammp_info("BeamMP Server v" + Application::ServerVersionString()); Application::SetSubsystemStatus("Server", Application::Status::Starting); @@ -86,8 +147,8 @@ void TServer::GlobalParser(const std::weak_ptr& Client, std::string Pac std::any Res; char Code = Packet.at(0); - // V to Z - if (Code <= 90 && Code >= 86) { + // V to Y + if (Code <= 89 && Code >= 86) { PPSMonitor.IncrementInternalPPS(); Network.SendToAll(LockedClient.get(), Packet, false, false); return; @@ -145,6 +206,11 @@ void TServer::GlobalParser(const std::weak_ptr& Client, std::string Pac beammp_trace("got 'N' packet (" + std::to_string(Packet.size()) + ")"); Network.SendToAll(LockedClient.get(), Packet, false, true); return; + case 'Z': // position packet + PPSMonitor.IncrementInternalPPS(); + Network.SendToAll(LockedClient.get(), Packet, false, false); + + HandlePosition(*LockedClient, Packet); default: return; } @@ -223,13 +289,11 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ } } return; - case 'c': + case 'c': { beammp_trace(std::string(("got 'Oc' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")")); - pid = Data.substr(0, Data.find('-')); - vid = Data.substr(Data.find('-') + 1, Data.find(':', 1) - Data.find('-') - 1); - if (pid.find_first_not_of("0123456789") == std::string::npos && vid.find_first_not_of("0123456789") == std::string::npos) { - PID = stoi(pid); - VID = stoi(vid); + auto MaybePidVid = GetPidVid(Data.substr(0, Data.find(':', 1))); + if (MaybePidVid) { + std::tie(PID, VID) = MaybePidVid.value(); } if (PID != -1 && VID != -1 && PID == c.GetID()) { auto Futures = LuaAPI::MP::Engine->TriggerEvent("onVehicleEdited", "", c.GetID(), VID, Packet.substr(3)); @@ -250,20 +314,17 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ c.SetUnicycleID(-1); } std::string Destroy = "Od:" + std::to_string(c.GetID()) + "-" + std::to_string(VID); - if (!Network.Respond(c, Destroy, true)) { - // TODO: handle - } + Network.SendToAll(nullptr, Destroy, true, true); c.DeleteCar(VID); } } return; - case 'd': + } + case 'd': { beammp_trace(std::string(("got 'Od' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")")); - pid = Data.substr(0, Data.find('-')); - vid = Data.substr(Data.find('-') + 1); - if (pid.find_first_not_of("0123456789") == std::string::npos && vid.find_first_not_of("0123456789") == std::string::npos) { - PID = stoi(pid); - VID = stoi(vid); + auto MaybePidVid = GetPidVid(Data); + if (MaybePidVid) { + std::tie(PID, VID) = MaybePidVid.value(); } if (PID != -1 && VID != -1 && PID == c.GetID()) { if (c.GetUnicycleID() == VID) { @@ -276,15 +337,12 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ beammp_debug(c.GetName() + (" deleted car with ID ") + std::to_string(VID)); } return; - case 'r': + } + case 'r': { beammp_trace(std::string(("got 'Or' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")")); - Pos = int(Data.find('-')); - pid = Data.substr(0, Pos++); - vid = Data.substr(Pos, Data.find(':') - Pos); - - if (pid.find_first_not_of("0123456789") == std::string::npos && vid.find_first_not_of("0123456789") == std::string::npos) { - PID = stoi(pid); - VID = stoi(vid); + auto MaybePidVid = GetPidVid(Data); + if (MaybePidVid) { + std::tie(PID, VID) = MaybePidVid.value(); } if (PID != -1 && VID != -1 && PID == c.GetID()) { @@ -293,6 +351,7 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ Network.SendToAll(&c, Packet, false, true); } return; + } case 't': beammp_trace(std::string(("got 'Ot' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")")); Network.SendToAll(&c, Packet, false, true); @@ -365,3 +424,21 @@ void TServer::InsertClient(const std::shared_ptr& NewClient) { WriteLock Lock(mClientsMutex); // TODO why is there 30+ threads locked here (void)mClients.insert(NewClient); } + +void TServer::HandlePosition(TClient& c, std::string Packet) { + // Zp:serverVehicleID:data + std::string withoutCode = Packet.substr(3); + auto NameDataSep = withoutCode.find(':', 2); + std::string ServerVehicleID = withoutCode.substr(2, NameDataSep - 2); + std::string Data = withoutCode.substr(NameDataSep + 1); + + // parse veh ID + auto MaybePidVid = GetPidVid(ServerVehicleID); + if (MaybePidVid) { + int PID = -1; + int VID = -1; + std::tie(PID, VID) = MaybePidVid.value(); + + c.SetCarPosition(VID, Data); + } +} From a84d042a8abb34d23f813e8e9b3a3ec394a4cd8f Mon Sep 17 00:00:00 2001 From: 20dka Date: Wed, 14 Sep 2022 12:36:54 +0200 Subject: [PATCH 106/184] add error messages to some lua events --- Changelog.md | 1 + include/LuaAPI.h | 10 ++++----- src/LuaAPI.cpp | 57 +++++++++++++++++++++++++++++++++--------------- 3 files changed, 45 insertions(+), 23 deletions(-) diff --git a/Changelog.md b/Changelog.md index f719f10..4b5e126 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ - ADDED FS.ListFiles and FS.ListDirectories - ADDED onFileChanged event, triggered when a server plugin file changes - ADDED MP.GetPositionRaw(), which can be used to retrieve the latest position packet per player, per vehicle +- ADDED error messages to some lua functions - FIXED `ip` in MP.GetIdentifiers - FIXED issue with client->server events which contain ':' - FIXED a fatal exception on LuaEngine startup if Resources/Server is a symlink diff --git a/include/LuaAPI.h b/include/LuaAPI.h index 4871a0b..6d2af31 100644 --- a/include/LuaAPI.h +++ b/include/LuaAPI.h @@ -12,12 +12,12 @@ namespace MP { std::string GetOSName(); std::tuple GetServerVersion(); - bool TriggerClientEvent(int PlayerID, const std::string& EventName, const sol::object& Data); - bool TriggerClientEventJson(int PlayerID, const std::string& EventName, const sol::table& Data); + std::pair TriggerClientEvent(int PlayerID, const std::string& EventName, const sol::object& Data); + std::pair 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); - void RemoveVehicle(int PlayerID, int VehicleID); + std::pair DropPlayer(int ID, std::optional MaybeReason); + std::pair SendChatMessage(int ID, const std::string& Message); + std::pair RemoveVehicle(int PlayerID, int VehicleID); void Set(int ConfigID, sol::object NewValue); bool IsPlayerGuest(int ID); bool IsPlayerConnected(int ID); diff --git a/src/LuaAPI.cpp b/src/LuaAPI.cpp index 48b77d8..e26a1eb 100644 --- a/src/LuaAPI.cpp +++ b/src/LuaAPI.cpp @@ -110,72 +110,93 @@ TEST_CASE("LuaAPI::MP::GetServerVersion") { CHECK(pa == real.patch); } -static inline bool InternalTriggerClientEvent(int PlayerID, const std::string& EventName, const std::string& Data) { +static inline std::pair InternalTriggerClientEvent(int PlayerID, const std::string& EventName, const std::string& Data) { std::string Packet = "E:" + EventName + ":" + Data; - if (PlayerID == -1) + if (PlayerID == -1) { LuaAPI::MP::Engine->Network().SendToAll(nullptr, Packet, true, true); - else { + return {true, ""}; + } else { auto MaybeClient = GetClient(LuaAPI::MP::Engine->Server(), PlayerID); if (!MaybeClient || MaybeClient.value().expired()) { - beammp_lua_error("TriggerClientEvent invalid Player ID"); - return false; + beammp_lua_errorf("TriggerClientEvent invalid Player ID '{}'", PlayerID); + return {false, "Invalid Player ID"}; } 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)); + beammp_lua_errorf("Respond failed, dropping client {}", PlayerID); LuaAPI::MP::Engine->Network().ClientKick(*c, "Disconnected after failing to receive packets"); - return false; + return {false, "Respond failed, dropping client"}; + } + return {true, ""}; } - return true; } -bool LuaAPI::MP::TriggerClientEvent(int PlayerID, const std::string& EventName, const sol::object& DataObj) { +std::pair 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) { +std::pair LuaAPI::MP::DropPlayer(int ID, std::optional MaybeReason) { auto MaybeClient = GetClient(Engine->Server(), ID); if (!MaybeClient || MaybeClient.value().expired()) { - beammp_lua_error("Tried to drop client with id " + std::to_string(ID) + ", who doesn't exist"); - return; + beammp_lua_errorf("Tried to drop client with id {}, who doesn't exist", ID); + return {false, "Player does not exist"}; } auto c = MaybeClient.value().lock(); LuaAPI::MP::Engine->Network().ClientKick(*c, MaybeReason.value_or("No reason")); + return {true, ""}; } -void LuaAPI::MP::SendChatMessage(int ID, const std::string& Message) { +std::pair LuaAPI::MP::SendChatMessage(int ID, const std::string& Message) { + std::pair Result; std::string Packet = "C:Server: " + Message; if (ID == -1) { LogChatMessage(" (to everyone) ", -1, Message); Engine->Network().SendToAll(nullptr, Packet, true, true); + Result.first = true; } else { auto MaybeClient = GetClient(Engine->Server(), ID); if (MaybeClient && !MaybeClient.value().expired()) { auto c = MaybeClient.value().lock(); - if (!c->IsSynced()) - return; + if (!c->IsSynced()) { + Result.first = false; + Result.second = "Player still syncing data"; + return Result; + } LogChatMessage(" (to \"" + c->GetName() + "\")", -1, Message); Engine->Network().Respond(*c, Packet, true); + Result.first = true; } else { beammp_lua_error("SendChatMessage invalid argument [1] invalid ID"); + Result.first = false; + Result.second = "Invalid Player ID"; } + return Result; } + return Result; } -void LuaAPI::MP::RemoveVehicle(int PID, int VID) { +std::pair LuaAPI::MP::RemoveVehicle(int PID, int VID) { + std::pair Result; auto MaybeClient = GetClient(Engine->Server(), PID); if (!MaybeClient || MaybeClient.value().expired()) { beammp_lua_error("RemoveVehicle invalid Player ID"); - return; + Result.first = false; + Result.second = "Invalid Player ID"; + return Result; } auto c = MaybeClient.value().lock(); if (!c->GetCarData(VID).empty()) { std::string Destroy = "Od:" + std::to_string(PID) + "-" + std::to_string(VID); Engine->Network().SendToAll(nullptr, Destroy, true, true); c->DeleteCar(VID); + Result.first = true; + } else { + Result.first = false; + Result.second = "Vehicle does not exist"; } + return Result; } void LuaAPI::MP::Set(int ConfigID, sol::object NewValue) { @@ -645,6 +666,6 @@ 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) { +std::pair LuaAPI::MP::TriggerClientEventJson(int PlayerID, const std::string& EventName, const sol::table& Data) { return InternalTriggerClientEvent(PlayerID, EventName, JsonEncode(Data)); } From 4b242c26fca9e0b899519e4abe62291e1261ff35 Mon Sep 17 00:00:00 2001 From: 20dka Date: Wed, 14 Sep 2022 18:53:44 +0200 Subject: [PATCH 107/184] optimize function argument passing in GetPidVid, TServer::HandlePosition --- src/TServer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TServer.cpp b/src/TServer.cpp index 92e9061..b9d7d2d 100644 --- a/src/TServer.cpp +++ b/src/TServer.cpp @@ -15,7 +15,7 @@ #include "Json.h" -static std::optional> GetPidVid(std::string str) { +static std::optional> GetPidVid(const std::string& str) { auto IDSep = str.find('-'); std::string pid = str.substr(0, IDSep); std::string vid = str.substr(IDSep + 1); @@ -425,7 +425,7 @@ void TServer::InsertClient(const std::shared_ptr& NewClient) { (void)mClients.insert(NewClient); } -void TServer::HandlePosition(TClient& c, std::string Packet) { +void TServer::HandlePosition(TClient& c, const std::string& Packet) { // Zp:serverVehicleID:data std::string withoutCode = Packet.substr(3); auto NameDataSep = withoutCode.find(':', 2); From 71fbf7b7cc080d44ebbff4d8491258dd8eaddc59 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 3 Aug 2022 13:59:54 +0200 Subject: [PATCH 108/184] update commandline --- deps/commandline | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/commandline b/deps/commandline index d6b1c32..7b9f51d 160000 --- a/deps/commandline +++ b/deps/commandline @@ -1 +1 @@ -Subproject commit d6b1c32c8af6ad5306f9f001305b3be9928ae4bb +Subproject commit 7b9f51d6a0402500a8f84a2fb37223ac4309f2d7 From 6a166110f67d3662ba1a8ec1dfebdcbdcd593a9d Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 3 Aug 2022 14:00:26 +0200 Subject: [PATCH 109/184] update doctest --- deps/doctest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/doctest b/deps/doctest index 7b98851..b7c21ec 160000 --- a/deps/doctest +++ b/deps/doctest @@ -1 +1 @@ -Subproject commit 7b9885133108ae301ddd16e2651320f54cafeba7 +Subproject commit b7c21ec5ceeadb4951b00396fc1e4642dd347e5f From 023738c41b85c8229e563b7f29b2eb96e111f8ef Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 3 Aug 2022 14:01:04 +0200 Subject: [PATCH 110/184] update json --- deps/json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/json b/deps/json index ede6667..69d7448 160000 --- a/deps/json +++ b/deps/json @@ -1 +1 @@ -Subproject commit ede66678580596028bcd6e18871a35a54bac01d7 +Subproject commit 69d744867f8847c91a126fa25e9a6a3d67b3be41 From 071ba08dfeb0a100e82d2bcb5797ecc560528a4c Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 3 Aug 2022 14:01:40 +0200 Subject: [PATCH 111/184] update sol2 --- deps/sol2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/sol2 b/deps/sol2 index c068aef..eba8662 160000 --- a/deps/sol2 +++ b/deps/sol2 @@ -1 +1 @@ -Subproject commit c068aefbeddb3dd1f1fd38d42843ecb49a3b4cdb +Subproject commit eba86625b707e3c8c99bbfc4624e51f42dc9e561 From bc6b4e40e1b0d053ec8882275f0736b73c38459a Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 3 Aug 2022 14:02:11 +0200 Subject: [PATCH 112/184] update httplib --- deps/cpp-httplib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/cpp-httplib b/deps/cpp-httplib index 47044c0..d92c314 160000 --- a/deps/cpp-httplib +++ b/deps/cpp-httplib @@ -1 +1 @@ -Subproject commit 47044c05a8587dff86ab90526daabfef61079490 +Subproject commit d92c31446687cfa336a6332b1015b4fe289fbdec From 784bbd4f6bfa3e05ef2e6ec49d8d0d1cd1009f41 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 3 Aug 2022 18:13:25 +0200 Subject: [PATCH 113/184] update fmt --- deps/fmt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/fmt b/deps/fmt index ce246aa..c4ee726 160000 --- a/deps/fmt +++ b/deps/fmt @@ -1 +1 @@ -Subproject commit ce246aaf74cb7616777497da988804fa4c02bf2d +Subproject commit c4ee726532178e556d923372f29163bd206d7732 From ec9a280d3074b5a50fc6c03a279bfd369e63aea8 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 3 Aug 2022 18:13:49 +0200 Subject: [PATCH 114/184] update libzip --- deps/libzip | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/libzip b/deps/libzip index 76df02f..5532f9b 160000 --- a/deps/libzip +++ b/deps/libzip @@ -1 +1 @@ -Subproject commit 76df02f86b9746e139fd9fc934a70e3a21bbc557 +Subproject commit 5532f9baa0c44cc5435ad135686a4ea009075b9a From 1062e295827b33e62524385ab5d9bc37030f2458 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 3 Aug 2022 18:14:19 +0200 Subject: [PATCH 115/184] update sentry-native --- deps/sentry-native | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/sentry-native b/deps/sentry-native index 90966cc..87e67ad 160000 --- a/deps/sentry-native +++ b/deps/sentry-native @@ -1 +1 @@ -Subproject commit 90966cc1022b8155681b6899539b35466baccf2c +Subproject commit 87e67ad783a7ec4476b0eb4742bd40fe5a1e2435 From af5658d0e169c3f15a056b926d17406949261f2e Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 3 Aug 2022 18:16:56 +0200 Subject: [PATCH 116/184] update toml11 --- deps/toml11 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/toml11 b/deps/toml11 index 1400dd2..c7627ff 160000 --- a/deps/toml11 +++ b/deps/toml11 @@ -1 +1 @@ -Subproject commit 1400dd223fb4297337266fcb5d04b700338aea71 +Subproject commit c7627ff6a1eb6f34fbd98369990a9442e2836c25 From 23e9941704af79d4d1497a74702437eb057ace2f Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 14 Sep 2022 20:42:25 +0200 Subject: [PATCH 117/184] update linux and windows workflows to run on pr open, reopen, review submit --- .github/workflows/cmake-linux.yml | 15 ++++++++++----- .github/workflows/cmake-windows.yml | 24 ++++++++++++++---------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index 802b4ac..e624f72 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -1,6 +1,11 @@ name: CMake Linux Build -on: [push] +on: + push: + pull_request: + types: [opened, reopened] + pull_request_review: + types: [submitted] env: BUILD_TYPE: Release @@ -63,16 +68,16 @@ jobs: with: name: BeamMP-Server-linux-tests path: ${{github.workspace}} - + - name: Install Runtime Dependencies shell: bash run: | sudo apt-get update - sudo apt-get install -y liblua5.3 openssl + sudo apt-get install -y liblua5.3 openssl - name: Test working-directory: ${{github.workspace}} shell: bash run: | - chmod +x ./BeamMP-Server-tests - ./BeamMP-Server-tests + chmod +x ./BeamMP-Server-tests + ./BeamMP-Server-tests diff --git a/.github/workflows/cmake-windows.yml b/.github/workflows/cmake-windows.yml index b882766..e0be5ee 100644 --- a/.github/workflows/cmake-windows.yml +++ b/.github/workflows/cmake-windows.yml @@ -1,6 +1,11 @@ name: CMake Windows Build -on: [push] +on: + push: + pull_request: + types: [opened, reopened] + pull_request_review: + types: [submitted] env: BUILD_TYPE: Release @@ -8,20 +13,20 @@ env: jobs: windows-build: runs-on: windows-latest - + steps: - uses: actions/checkout@v2 with: - submodules: 'recursive' - + submodules: "recursive" + - name: Restore artifacts, or run vcpkg, build and cache artifacts uses: lukka/run-vcpkg@v7 id: runvcpkg with: - vcpkgArguments: 'lua zlib rapidjson openssl websocketpp curl' - vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg' - vcpkgGitCommitId: 'a106de33bbee694e3be6243718aa2a549a692832' - vcpkgTriplet: 'x64-windows-static' + vcpkgArguments: "lua zlib rapidjson openssl websocketpp curl" + vcpkgDirectory: "${{ runner.workspace }}/b/vcpkg" + vcpkgGitCommitId: "a106de33bbee694e3be6243718aa2a549a692832" + vcpkgTriplet: "x64-windows-static" - name: Create Build Environment run: cmake -E make_directory ${{github.workspace}}/build-windows @@ -49,10 +54,9 @@ jobs: shell: bash run: | cmake --build . --config Debug - + - name: Archive debug artifacts uses: actions/upload-artifact@v2 with: name: BeamMP-Server-debug.exe path: ${{github.workspace}}/build-windows/Debug/BeamMP-Server.exe - From 36a1da3218a1fed0e531de35f88421da79dc9f19 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 14 Sep 2022 20:59:07 +0200 Subject: [PATCH 118/184] fix TServer::HandlePosition declaration it was different from the implementation --- include/TServer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/TServer.h b/include/TServer.h index 698835a..cf7891a 100644 --- a/include/TServer.h +++ b/include/TServer.h @@ -38,5 +38,5 @@ private: static bool ShouldSpawn(TClient& c, const std::string& CarJson, int ID); static bool IsUnicycle(TClient& c, const std::string& CarJson); static void Apply(TClient& c, int VID, const std::string& pckt); - static void HandlePosition(TClient& c, std::string Packet); + static void HandlePosition(TClient& c, const std::string& Packet); }; From b71aa2db040b7477f3523e38000810444aea8f1a Mon Sep 17 00:00:00 2001 From: 20dka Date: Thu, 17 Mar 2022 00:46:55 +0100 Subject: [PATCH 119/184] advanced autocomplete for lua --- deps/commandline | 2 +- include/TLuaEngine.h | 9 ++++++ src/TConsole.cpp | 71 +++++++++++++++++++++++++++++++++----------- src/TLuaEngine.cpp | 60 +++++++++++++++++++++++++++++++++++++ 4 files changed, 124 insertions(+), 18 deletions(-) diff --git a/deps/commandline b/deps/commandline index 7b9f51d..d345985 160000 --- a/deps/commandline +++ b/deps/commandline @@ -1 +1 @@ -Subproject commit 7b9f51d6a0402500a8f84a2fb37223ac4309f2d7 +Subproject commit d3459852fe387a1911a8298d3b02f70cc7de5093 diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index b3cf883..049b554 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -96,6 +96,13 @@ public: std::unique_lock Lock(mLuaStatesMutex); return mLuaStates.size(); } + std::vector GetLuaStateNames() { + std::vector names{}; + for(auto const& [stateId, _ ] : mLuaStates) { + names.push_back(stateId); + } + return names; + } size_t GetTimedEventsCount() { std::unique_lock Lock(mTimedEventsMutex); return mTimedEvents.size(); @@ -172,6 +179,7 @@ public: static constexpr const char* BeamMPFnNotFoundError = "BEAMMP_FN_NOT_FOUND"; std::vector GetStateGlobalKeysForState(TLuaStateId StateId); + std::vector GetStateTableKeysForState(TLuaStateId StateId, std::vector keys); // Debugging functions (slow) std::unordered_map /* handlers */> Debug_GetEventsForState(TLuaStateId StateId); @@ -199,6 +207,7 @@ private: sol::state_view State() { return sol::state_view(mState); } std::vector GetStateGlobalKeys(); + std::vector GetStateTableKeys(const std::vector& keys); // Debug functions, slow std::queue>> Debug_GetStateExecuteQueue(); diff --git a/src/TConsole.cpp b/src/TConsole.cpp index d26c95e..e2a39b5 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -51,6 +51,18 @@ TEST_CASE("TrimString") { CHECK(TrimString("") == ""); } +// TODO: add unit tests to SplitString +static inline void SplitString(std::string const& str, const char delim, std::vector& out) { + size_t start; + size_t end = 0; + + while ((start = str.find_first_not_of(delim, end)) != std::string::npos) { + end = str.find(delim, start); + out.push_back(str.substr(start, end - start)); + } +} + + std::string GetDate() { std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); time_t tt = std::chrono::system_clock::to_time_t(now); @@ -618,45 +630,70 @@ 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&, std::string stub, int) { + mCommandline.on_autocomplete = [this](Commandline& c, std::string stub, int cursorPos) { 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] != '_') { + std::string prefix {}; // stores non-table part of input + for (size_t i = stub.length(); i > 0; i--) { //separate table from input + if (!std::isalnum(stub[i - 1]) && 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); + + // turn string into vector of keys + std::vector tablekeys; + + SplitString(stub, '.', tablekeys); + + // remove last key if incomplete + if (stub.rfind('.') != stub.size() - 1 && !tablekeys.empty()) { + tablekeys.pop_back(); + } + + auto keys = mLuaEngine->GetStateTableKeysForState(mStateId, tablekeys); + + for (const auto& key : keys) { // go through each bottom-level key + auto last_dot = stub.rfind('.'); + std::string last_atom; + if (last_dot != std::string::npos) { + last_atom = stub.substr(last_dot + 1); + } + std::string before_last_atom = stub.substr(0, last_dot + 1); // get last confirmed key + auto last = stub.substr(stub.rfind('.') + 1); + std::string::size_type n = key.find(last); if (n == 0) { - suggestions.push_back(prefix + key); - // beammp_warn(cmd_name); + suggestions.push_back(prefix + before_last_atom + key); } } } } 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); + if (stub.find("lua") == 0) { // starts with "lua" means we should suggest state names + std::string after_prefix = TrimString(stub.substr(3)); + auto stateNames = mLuaEngine->GetLuaStateNames(); + + for (const auto& name : stateNames) { + if (name.find(after_prefix) == 0) { + suggestions.push_back("lua " + name); + } + } + } else { + for (const auto& [cmd_name, cmd_fn] : mCommandMap) { + if (cmd_name.find(stub) == 0) { + suggestions.push_back(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."); } + std::sort(suggestions.begin(), suggestions.end()); return suggestions; }; } diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 303f8bf..b493296 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -221,6 +221,66 @@ std::vector TLuaEngine::StateThreadData::GetStateGlobalKeys() { return Result; } +std::vector TLuaEngine::GetStateTableKeysForState(TLuaStateId StateId, std::vector keys) { + std::unique_lock Lock(mLuaStatesMutex); + auto Result = mLuaStates.at(StateId)->GetStateTableKeys(keys); + return Result; +} + +std::vector TLuaEngine::StateThreadData::GetStateTableKeys(const std::vector& keys) { + auto globals = mStateView.globals(); + + sol::table current = globals; + std::vector Result {}; + + for (const auto& [key, value] : current) { + std::string s = key.as(); + if (value.get_type() == sol::type::function) { + s += "("; + } + Result.push_back(s); + } + + if (!keys.empty()) { + Result.clear(); + } + + for (size_t i = 0; i < keys.size(); ++i) { + auto obj = current.get(keys.at(i)); + if (obj.get_type() == sol::type::nil) { + // error + break; + } else if (i == keys.size() - 1) { + if (obj.get_type() == sol::type::table) { + for (const auto& [key, value] : obj.as()) { + std::string s = key.as(); + if (value.get_type() == sol::type::function) { + s += "("; + } + Result.push_back(s); + } + } else { + Result = { obj.as() }; + } + break; + } + if (obj.get_type() == sol::type::table) { + current = obj; + } else { + // error + break; + } + } + + return Result; +} + +/* + + _G.a.b.c.d. + +*/ + void TLuaEngine::WaitForAll(std::vector>& Results, const std::optional& Max) { for (const auto& Result : Results) { bool Cancelled = false; From a8333359ce227b6e4ac1a063fbbfe9ade9c4c02d Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Mon, 26 Sep 2022 00:17:23 +0200 Subject: [PATCH 120/184] update commandline to work with new autocomplete --- deps/commandline | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/commandline b/deps/commandline index d345985..7b9f51d 160000 --- a/deps/commandline +++ b/deps/commandline @@ -1 +1 @@ -Subproject commit d3459852fe387a1911a8298d3b02f70cc7de5093 +Subproject commit 7b9f51d6a0402500a8f84a2fb37223ac4309f2d7 From 6da9a921d0619094bafb2670f73b38f415b41206 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Mon, 26 Sep 2022 01:14:08 +0200 Subject: [PATCH 121/184] fix "init and reset termios" unit test it was testing via memory-equality, which is not valid. --- src/Compat.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Compat.cpp b/src/Compat.cpp index 1374d3e..53dfe0c 100644 --- a/src/Compat.cpp +++ b/src/Compat.cpp @@ -36,7 +36,14 @@ TEST_CASE("init and reset termios") { resetTermios(); struct termios current; tcgetattr(0, ¤t); - CHECK(std::memcmp(&original, ¤t, sizeof(struct termios)) == 0); + CHECK_EQ(std::memcmp(¤t.c_cc, &original.c_cc, sizeof(current.c_cc)), 0); + CHECK_EQ(current.c_cflag, original.c_cflag); + CHECK_EQ(current.c_iflag, original.c_iflag); + CHECK_EQ(current.c_ispeed, original.c_ispeed); + CHECK_EQ(current.c_lflag, original.c_lflag); + CHECK_EQ(current.c_line, original.c_line); + CHECK_EQ(current.c_oflag, original.c_oflag); + CHECK_EQ(current.c_ospeed, original.c_ospeed); } } From 24c98eb2b249c356f09f6f7fa3e48956cd011bb7 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Mon, 26 Sep 2022 12:02:05 +0200 Subject: [PATCH 122/184] add more compile-time diagnostics, implement fixes for them Before, a lot of common errors went unnoticed, due to insufficient compiler diagnostics. This commit fixes this by adding a lot of new diagnostics, and fixing the issues found by this. --- CMakeLists.txt | 85 ++++++++++++++++++++++++++++++++++----------- deps/CMakeLists.txt | 1 - include/Common.h | 28 +++++++-------- include/Http.h | 2 ++ src/Common.cpp | 5 +++ src/Compat.cpp | 6 ++-- src/Http.cpp | 3 +- src/LuaAPI.cpp | 29 ++++++++++------ src/TConfig.cpp | 2 +- src/TConsole.cpp | 24 +++++++------ src/TLuaEngine.cpp | 3 +- src/TNetwork.cpp | 21 ++++++----- src/TSentry.cpp | 2 +- src/TServer.cpp | 11 +++--- src/main.cpp | 4 +-- 15 files changed, 141 insertions(+), 85 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 41ad31d..13b4cce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,15 +26,6 @@ endif() set(HTTPLIB_REQUIRE_OPENSSL ON) set(SENTRY_BUILD_SHARED_LIBS OFF) -include_directories("${CMAKE_CURRENT_SOURCE_DIR}/deps/asio/asio/include") -include_directories("${CMAKE_CURRENT_SOURCE_DIR}/deps/rapidjson/include") -include_directories("${CMAKE_CURRENT_SOURCE_DIR}/deps/websocketpp") -include_directories("${CMAKE_CURRENT_SOURCE_DIR}/deps/commandline") -include_directories("${CMAKE_CURRENT_SOURCE_DIR}/deps/sol2/include") -include_directories("${CMAKE_CURRENT_SOURCE_DIR}/deps/cpp-httplib") -include_directories("${CMAKE_CURRENT_SOURCE_DIR}/deps/json/single_include") -include_directories("${CMAKE_CURRENT_SOURCE_DIR}/deps") - add_compile_definitions(CPPHTTPLIB_OPENSSL_SUPPORT=1) # ------------------------ APPLE --------------------------------- @@ -62,17 +53,15 @@ elseif (WIN32) STRING(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) # ------------------------ 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") option(SANITIZE "Turns on thread and UB sanitizers" OFF) if (SANITIZE) message(STATUS "sanitize is ON") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined,thread") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize={address,thread,undefined}") endif (SANITIZE) endif () -include_directories("include/sentry-native/include") set(BUILD_SHARED_LIBS OFF) # ------------------------ SENTRY --------------------------------- message(STATUS "Checking for Sentry URL") @@ -131,19 +120,57 @@ set(BeamMP_Sources ) set(BeamMP_Includes - "${CMAKE_CURRENT_SOURCE_DIR}/include" - "${CMAKE_CURRENT_SOURCE_DIR}/commandline" ${LUA_INCLUDE_DIR} ${CURL_INCLUDE_DIRS} - "include/tomlplusplus" - "include/sentry-native/include" - "include/curl/include" + "${CMAKE_CURRENT_SOURCE_DIR}/deps/cpp-httplib" + "${CMAKE_CURRENT_SOURCE_DIR}/deps/commandline" + "${CMAKE_CURRENT_SOURCE_DIR}/deps/json/single_include" + "${CMAKE_CURRENT_SOURCE_DIR}/deps/sol2/include" + "${CMAKE_CURRENT_SOURCE_DIR}/deps/rapidjson/include" + "${CMAKE_CURRENT_SOURCE_DIR}/deps/asio/asio/include" + "${CMAKE_CURRENT_SOURCE_DIR}/deps" ) set(BeamMP_Definitions SECRET_SENTRY_URL="${BEAMMP_SECRET_SENTRY_URL}" ) +if (UNIX) + set(BeamMP_CompileOptions + -Wall + -Wextra + -Wpedantic + + -Werror=uninitialized + -Werror=float-equal + -Werror=pointer-arith + -Werror=double-promotion + -Werror=write-strings + -Werror=cast-qual + -Werror=init-self + -Werror=cast-align + -Werror=unreachable-code + -Werror=strict-aliasing -fstrict-aliasing + -Werror=redundant-decls + -Werror=missing-declarations + -Werror=missing-field-initializers + -Werror=write-strings + -Werror=inline + -Werror=disabled-optimization + -Werror=ctor-dtor-privacy + -Werror=switch-enum + -Werror=switch-default + -Werror=old-style-cast + -Werror=overloaded-virtual + -Werror=zero-as-null-pointer-constant + -Werror=overloaded-virtual + -Werror=missing-include-dirs + -Werror=unused-result + + -fstack-protector + ) +endif() + set(BeamMP_Libraries doctest::doctest OpenSSL::SSL @@ -173,7 +200,15 @@ target_compile_definitions(BeamMP-Server PRIVATE DOCTEST_CONFIG_DISABLE ) -target_include_directories(BeamMP-Server PUBLIC +target_compile_options(BeamMP-Server PRIVATE + ${BeamMP_CompileOptions} +) + +target_include_directories(BeamMP-Server PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/include" +) + +target_include_directories(BeamMP-Server SYSTEM PRIVATE ${BeamMP_Includes} ) @@ -195,11 +230,19 @@ if(BUILD_TESTS) target_compile_definitions(BeamMP-Server-tests PRIVATE ${BeamMP_Definitions} ) - - target_include_directories(BeamMP-Server-tests PUBLIC - ${BeamMP_Includes} + + target_compile_options(BeamMP-Server-tests PRIVATE + ${BeamMP_CompileOptions} ) + target_include_directories(BeamMP-Server-tests PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/include" + ) + + target_include_directories(BeamMP-Server-tests SYSTEM PRIVATE + ${BeamMP_Includes} + ) + target_link_libraries(BeamMP-Server-tests ${BeamMP_Libraries} ${BeamMP_PlatformLibs} diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 0c1ba10..f439b19 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -1,5 +1,4 @@ include_directories("${PROJECT_SOURCE_DIR}/deps") -include_directories("${PROJECT_SOURCE_DIR}/deps/commandline") add_subdirectory("${PROJECT_SOURCE_DIR}/deps/commandline") add_subdirectory("${PROJECT_SOURCE_DIR}/deps/fmt") diff --git a/include/Common.h b/include/Common.h index 37c891f..3b0f205 100644 --- a/include/Common.h +++ b/include/Common.h @@ -4,7 +4,6 @@ extern TSentry Sentry; #include -#include #include #include #include @@ -15,14 +14,13 @@ extern TSentry Sentry; #include #include #include +#include #include #include #include namespace fs = std::filesystem; -#include "Compat.h" - #include "TConsole.h" struct Version { @@ -34,7 +32,7 @@ struct Version { std::string AsString(); }; -template +template using SparseArray = std::unordered_map; // static class handling application start, shutdown, etc. @@ -277,11 +275,11 @@ inline T Comp(const T& Data) { // obsolete C.fill(0); z_stream defstream; - defstream.zalloc = Z_NULL; - defstream.zfree = Z_NULL; - defstream.opaque = Z_NULL; - defstream.avail_in = (uInt)Data.size(); - defstream.next_in = (Bytef*)&Data[0]; + defstream.zalloc = nullptr; + defstream.zfree = nullptr; + defstream.opaque = nullptr; + defstream.avail_in = uInt(Data.size()); + defstream.next_in = const_cast(reinterpret_cast(&Data[0])); defstream.avail_out = Biggest; defstream.next_out = reinterpret_cast(C.data()); deflateInit(&defstream, Z_BEST_COMPRESSION); @@ -302,13 +300,13 @@ inline T DeComp(const T& Compressed) { // not needed C.fill(0); z_stream infstream; - infstream.zalloc = Z_NULL; - infstream.zfree = Z_NULL; - infstream.opaque = Z_NULL; + infstream.zalloc = nullptr; + infstream.zfree = nullptr; + infstream.opaque = nullptr; infstream.avail_in = Biggest; - infstream.next_in = (Bytef*)(&Compressed[0]); + infstream.next_in = const_cast(reinterpret_cast(&Compressed[0])); infstream.avail_out = Biggest; - infstream.next_out = (Bytef*)(C.data()); + infstream.next_out = const_cast(reinterpret_cast(C.data())); inflateInit(&infstream); inflate(&infstream, Z_SYNC_FLUSH); inflate(&infstream, Z_FINISH); @@ -323,5 +321,3 @@ inline T DeComp(const T& Compressed) { std::string GetPlatformAgnosticErrorString(); #define S_DSN SU_RAW - -void LogChatMessage(const std::string& name, int id, const std::string& msg); diff --git a/include/Http.h b/include/Http.h index 4850470..0898f2e 100644 --- a/include/Http.h +++ b/include/Http.h @@ -9,6 +9,8 @@ #if defined(BEAMMP_LINUX) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#pragma GCC diagnostic ignored "-Wcast-qual" +#pragma GCC diagnostic ignored "-Wold-style-cast" #endif #include #if defined(BEAMMP_LINUX) diff --git a/src/Common.cpp b/src/Common.cpp index 728bbf7..5a8b276 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -9,6 +9,7 @@ #include #include +#include "Compat.h" #include "CustomAssert.h" #include "Http.h" @@ -170,6 +171,8 @@ void Application::SetSubsystemStatus(const std::string& Subsystem, Status status case Status::Shutdown: beammp_trace("Subsystem '" + Subsystem + "': Shutdown"); break; + default: + beammp_assert_not_reachable(); } std::unique_lock Lock(mSystemStatusMapMutex); mSystemStatusMap[Subsystem] = status; @@ -347,5 +350,7 @@ std::string GetPlatformAgnosticErrorString() { } #elif defined(BEAMMP_LINUX) || defined(BEAMMP_APPLE) return std::strerror(errno); +#else + return "(no human-readable errors on this platform)"; #endif } diff --git a/src/Compat.cpp b/src/Compat.cpp index 53dfe0c..a843099 100644 --- a/src/Compat.cpp +++ b/src/Compat.cpp @@ -7,7 +7,7 @@ static struct termios old, current; -void initTermios(int echo) { +static void initTermios(int echo) { tcgetattr(0, &old); /* grab old terminal i/o settings */ current = old; /* make new settings same as old settings */ current.c_lflag &= ~ICANON; /* disable buffered i/o */ @@ -19,7 +19,7 @@ void initTermios(int echo) { tcsetattr(0, TCSANOW, ¤t); /* use these new terminal i/o settings now */ } -void resetTermios(void) { +static void resetTermios(void) { tcsetattr(0, TCSANOW, &old); } @@ -47,7 +47,7 @@ TEST_CASE("init and reset termios") { } } -char getch_(int echo) { +static char getch_(int echo) { char ch; initTermios(echo); read(STDIN_FILENO, &ch, 1); diff --git a/src/Http.cpp b/src/Http.cpp index edc627e..9f87ea4 100644 --- a/src/Http.cpp +++ b/src/Http.cpp @@ -4,7 +4,6 @@ #include "Common.h" #include "CustomAssert.h" #include "LuaAPI.h" -#include "httplib.h" #include #include @@ -177,6 +176,8 @@ void Http::Server::THttpServerInstance::operator()() try { case Application::Status::Bad: SystemsBad++; break; + default: + beammp_assert_not_reachable(); } } res.set_content( diff --git a/src/LuaAPI.cpp b/src/LuaAPI.cpp index e26a1eb..30a0b86 100644 --- a/src/LuaAPI.cpp +++ b/src/LuaAPI.cpp @@ -1,6 +1,7 @@ #include "LuaAPI.h" #include "Client.h" #include "Common.h" +#include "CustomAssert.h" #include "TLuaEngine.h" #include @@ -74,8 +75,10 @@ std::string LuaAPI::LuaToString(const sol::object Value, size_t Indent, bool Quo ss << "[[function: " << Value.as().pointer() << "]]"; return ss.str(); } + case sol::type::poly: + return ""; default: - return "((unprintable type))"; + return ""; } } @@ -114,21 +117,20 @@ static inline std::pair InternalTriggerClientEvent(int Player std::string Packet = "E:" + EventName + ":" + Data; if (PlayerID == -1) { LuaAPI::MP::Engine->Network().SendToAll(nullptr, Packet, true, true); - return {true, ""}; + return { true, "" }; } else { auto MaybeClient = GetClient(LuaAPI::MP::Engine->Server(), PlayerID); if (!MaybeClient || MaybeClient.value().expired()) { beammp_lua_errorf("TriggerClientEvent invalid Player ID '{}'", PlayerID); - return {false, "Invalid Player ID"}; + return { false, "Invalid Player ID" }; } auto c = MaybeClient.value().lock(); if (!LuaAPI::MP::Engine->Network().Respond(*c, Packet, true)) { beammp_lua_errorf("Respond failed, dropping client {}", PlayerID); LuaAPI::MP::Engine->Network().ClientKick(*c, "Disconnected after failing to receive packets"); - return {false, "Respond failed, dropping client"}; - + return { false, "Respond failed, dropping client" }; } - return {true, ""}; + return { true, "" }; } } @@ -141,11 +143,11 @@ std::pair LuaAPI::MP::DropPlayer(int ID, std::optionalServer(), ID); if (!MaybeClient || MaybeClient.value().expired()) { beammp_lua_errorf("Tried to drop client with id {}, who doesn't exist", ID); - return {false, "Player does not exist"}; + return { false, "Player does not exist" }; } auto c = MaybeClient.value().lock(); LuaAPI::MP::Engine->Network().ClientKick(*c, MaybeReason.value_or("No reason")); - return {true, ""}; + return { true, "" }; } std::pair LuaAPI::MP::SendChatMessage(int ID, const std::string& Message) { @@ -165,7 +167,10 @@ std::pair LuaAPI::MP::SendChatMessage(int ID, const std::stri return Result; } LogChatMessage(" (to \"" + c->GetName() + "\")", -1, Message); - Engine->Network().Respond(*c, Packet, true); + if (!Engine->Network().Respond(*c, Packet, true)) { + beammp_errorf("Failed to send chat message back to sender (id {}) - did the sender disconnect?", ID); + // TODO: should we return an error here? + } Result.first = true; } else { beammp_lua_error("SendChatMessage invalid argument [1] invalid ID"); @@ -521,7 +526,7 @@ static void JsonEncodeRecursive(nlohmann::json& json, const sol::object& left, c beammp_lua_error("json serialize will not go deeper than 100 nested tables, internal references assumed, aborted this path"); return; } - std::string key; + std::string key{}; switch (left.get_type()) { case sol::type::lua_nil: case sol::type::none: @@ -540,6 +545,8 @@ static void JsonEncodeRecursive(nlohmann::json& json, const sol::object& left, c case sol::type::number: key = std::to_string(left.as()); break; + default: + beammp_assert_not_reachable(); } nlohmann::json value; switch (right.get_type()) { @@ -582,6 +589,8 @@ static void JsonEncodeRecursive(nlohmann::json& json, const sol::object& left, c } break; } + default: + beammp_assert_not_reachable(); } if (is_array) { json.push_back(value); diff --git a/src/TConfig.cpp b/src/TConfig.cpp index fc3a1d1..1d0c795 100644 --- a/src/TConfig.cpp +++ b/src/TConfig.cpp @@ -167,7 +167,7 @@ void TConfig::TryReadValue(toml::value& Table, const std::string& Category, cons void TConfig::TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, int& OutValue) { if (Table[Category.c_str()][Key.data()].is_integer()) { - OutValue = Table[Category.c_str()][Key.data()].as_integer(); + OutValue = int(Table[Category.c_str()][Key.data()].as_integer()); } } diff --git a/src/TConsole.cpp b/src/TConsole.cpp index e2a39b5..5420d39 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -62,8 +62,7 @@ static inline void SplitString(std::string const& str, const char delim, std::ve } } - -std::string GetDate() { +static std::string GetDate() { std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); time_t tt = std::chrono::system_clock::to_time_t(now); auto local_tm = std::localtime(&tt); @@ -156,10 +155,10 @@ void TConsole::ChangeToLuaConsole(const std::string& LuaStateId) { mStateId = LuaStateId; mIsLuaConsole = true; if (mStateId != mDefaultStateId) { - Application::Console().WriteRaw("Attached to Lua state '" + mStateId + "'. For help, type `:help`. To detach, type `:detach`"); + Application::Console().WriteRaw("Attached to Lua state '" + mStateId + "'. For help, type `:help`. To detach, type `:exit`"); mCommandline.set_prompt("lua @" + LuaStateId + "> "); } else { - Application::Console().WriteRaw("Attached to Lua. For help, type `:help`. To detach, type `:detach`"); + Application::Console().WriteRaw("Attached to Lua. For help, type `:help`. To detach, type `:exit`"); mCommandline.set_prompt("lua> "); } mCachedRegularHistory = mCommandline.history(); @@ -271,9 +270,10 @@ void TConsole::Command_Kick(const std::string&, const std::vector& } beammp_trace("attempt to kick '" + Name + "' for '" + Reason + "'"); bool Kicked = false; + // TODO: this sucks, tolower is locale-dependent. 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); }); + std::for_each(Name1.begin(), Name1.end(), [](char& c) { c = char(std::tolower(char(c))); }); + std::for_each(Name2.begin(), Name2.end(), [](char& c) { c = char(std::tolower(char(c))); }); return StringStartsWith(Name1, Name2) || StringStartsWith(Name2, Name1); }; mLuaEngine->Server().ForEachClient([&](std::weak_ptr Client) -> bool { @@ -332,7 +332,9 @@ std::tuple> TConsole::ParseCommand(const s } ++Ptr; } - Arg = std::string(PrevPtr, Ptr - PrevPtr); + // this is required, otherwise we get negative int to unsigned cast in the next operations + beammp_assert(PrevPtr <= Ptr); + Arg = std::string(PrevPtr, std::string::size_type(Ptr - PrevPtr)); // remove quotes if enclosed in quotes for (char Quote : { '"', '\'', '`' }) { if (!Arg.empty() && Arg.at(0) == Quote && Arg.at(Arg.size() - 1) == Quote) { @@ -448,6 +450,8 @@ void TConsole::Command_Status(const std::string&, const std::vector SystemsShutdown++; SystemsShutdownList += NameStatusPair.first + ", "; break; + default: + beammp_assert_not_reachable(); } } // remove ", " at the end @@ -466,7 +470,7 @@ void TConsole::Command_Status(const std::string&, const std::vector << "\tConnected Players: " << ConnectedCount << "\n" << "\tGuests: " << GuestCount << "\n" << "\tCars: " << CarCount << "\n" - << "\tUptime: " << ElapsedTime << "ms (~" << size_t(ElapsedTime / 1000.0 / 60.0 / 60.0) << "h) \n" + << "\tUptime: " << ElapsedTime << "ms (~" << size_t(double(ElapsedTime) / 1000.0 / 60.0 / 60.0) << "h) \n" << "\tLua:\n" << "\t\tQueued results to check: " << mLuaEngine->GetResultsToCheckSize() << "\n" << "\t\tStates: " << mLuaEngine->GetLuaStateCount() << "\n" @@ -630,7 +634,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, int cursorPos) { + mCommandline.on_autocomplete = [this](Commandline&, std::string stub, int) { std::vector suggestions; try { if (mIsLuaConsole) { // if lua @@ -638,7 +642,7 @@ TConsole::TConsole() { beammp_info("Lua not started yet, please try again in a second"); } else { std::string prefix {}; // stores non-table part of input - for (size_t i = stub.length(); i > 0; i--) { //separate table from input + for (size_t i = stub.length(); i > 0; i--) { // separate table from input if (!std::isalnum(stub[i - 1]) && stub[i - 1] != '_' && stub[i - 1] != '.') { prefix = stub.substr(0, i); stub = stub.substr(i); diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index b493296..3f8b01a 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -7,7 +7,6 @@ #include #include -#include #include #include #include @@ -679,6 +678,8 @@ static void JsonDecodeRecursive(sol::state_view& StateView, sol::table& table, c return; case nlohmann::detail::value_t::discarded: return; + default: + beammp_assert_not_reachable(); } } diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index be9b773..ecdea11 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -57,7 +57,7 @@ void TNetwork::UDPServerMain() { serverAddr.sin_port = htons(uint16_t(Application::Settings.Port)); // Convert from little to big endian // Try and bind the socket to the IP and port - if (bind(mUDPSock, (sockaddr*)&serverAddr, sizeof(serverAddr)) != 0) { + if (bind(mUDPSock, reinterpret_cast(&serverAddr), sizeof(serverAddr)) != 0) { beammp_error("bind() failed: " + GetPlatformAgnosticErrorString()); std::this_thread::sleep_for(std::chrono::seconds(5)); exit(-1); // TODO: Wtf. @@ -134,7 +134,7 @@ void TNetwork::TCPServerMain() { 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) { + if (bind(Listener, reinterpret_cast(&addr), sizeof(addr)) < 0) { beammp_error("bind() failed, the server cannot operate and will shut down now. " "Error: " + GetPlatformAgnosticErrorString()); @@ -166,7 +166,7 @@ void TNetwork::TCPServerMain() { int ret = ::setsockopt(client.Socket, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast(&SendTimeoutMS), sizeof(SendTimeoutMS)); #else // POSIX struct timeval optval; - optval.tv_sec = (int)(SendTimeoutMS / 1000); + 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 @@ -237,13 +237,13 @@ void TNetwork::HandleDownload(SOCKET TCPSock) { }); } -static int get_ip_str(const struct sockaddr* sa, char* strBuf, size_t strBufSize) { +static int get_ip_str(const struct sockaddr* sa, char* strBuf, socklen_t strBufSize) { switch (sa->sa_family) { case AF_INET: - inet_ntop(AF_INET, &(((struct sockaddr_in*)sa)->sin_addr), strBuf, strBufSize); + inet_ntop(AF_INET, &reinterpret_cast(sa)->sin_addr, strBuf, strBufSize); break; case AF_INET6: - inet_ntop(AF_INET6, &(((struct sockaddr_in6*)sa)->sin6_addr), strBuf, strBufSize); + inet_ntop(AF_INET6, &reinterpret_cast(sa)->sin6_addr, strBuf, strBufSize); break; default: return 1; @@ -453,7 +453,7 @@ bool TNetwork::CheckBytes(TClient& c, int32_t BytesRcv) { } std::string TNetwork::TCPRcv(TClient& c) { - int32_t Header, BytesRcv = 0, Temp; + int32_t Header {}, BytesRcv = 0, Temp {}; if (c.GetStatus() < 0) return ""; @@ -470,7 +470,7 @@ std::string TNetwork::TCPRcv(TClient& c) { if (!CheckBytes(c, BytesRcv)) { return ""; } - if (Header < 100 * MB) { + if (Header < int32_t(100 * MB)) { Data.resize(Header); } else { ClientKick(c, "Header size limit exceeded"); @@ -822,7 +822,6 @@ uint8_t* /* end ptr */ TNetwork::SendSplit(TClient& c, SOCKET Socket, uint8_t* D 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); - auto Buf = f.rdbuf(); uint32_t Split = 125 * MB; std::vector Data; if (Size > Split) @@ -1018,7 +1017,7 @@ bool TNetwork::UDPSend(TClient& Client, std::string Data) const { size_t len = Data.size(); #endif // WIN32 - sendOk = sendto(mUDPSock, Data.c_str(), len, 0, (sockaddr*)&Addr, int(AddrSize)); + sendOk = sendto(mUDPSock, Data.c_str(), len, 0, reinterpret_cast(&Addr), int(AddrSize)); if (sendOk == -1) { beammp_debug("(UDP) sendto() failed: " + GetPlatformAgnosticErrorString()); if (Client.GetStatus() > -1) @@ -1039,7 +1038,7 @@ std::string TNetwork::UDPRcvFromClient(sockaddr_in& client) const { #ifdef WIN32 auto Rcv = recvfrom(mUDPSock, Ret.data(), int(Ret.size()), 0, (sockaddr*)&client, (int*)&clientLength); #else // unix - int64_t Rcv = recvfrom(mUDPSock, Ret.data(), Ret.size(), 0, (sockaddr*)&client, (socklen_t*)&clientLength); + int64_t Rcv = recvfrom(mUDPSock, Ret.data(), Ret.size(), 0, reinterpret_cast(&client), reinterpret_cast(&clientLength)); #endif // WIN32 if (Rcv == -1) { diff --git a/src/TSentry.cpp b/src/TSentry.cpp index 7f2b416..ceab947 100644 --- a/src/TSentry.cpp +++ b/src/TSentry.cpp @@ -6,7 +6,7 @@ #include TSentry::TSentry() { - if (std::strlen(S_DSN) == 0) { + if (std::strlen(S_DSN) == /* DISABLES CODE */ (0)) { mValid = false; } else { mValid = true; diff --git a/src/TServer.cpp b/src/TServer.cpp index b9d7d2d..291d2c3 100644 --- a/src/TServer.cpp +++ b/src/TServer.cpp @@ -22,10 +22,10 @@ static std::optional> GetPidVid(const std::string& str) { if (pid.find_first_not_of("0123456789") == std::string::npos && vid.find_first_not_of("0123456789") == std::string::npos) { try { - int PID = stoi(pid); - int VID = stoi(vid); - return {{ PID, VID }}; - } catch(const std::exception&) { + int PID = stoi(pid); + int VID = stoi(vid); + return { { PID, VID } }; + } catch (const std::exception&) { return std::nullopt; } } @@ -75,7 +75,6 @@ TEST_CASE("GetPidVid") { } } - TServer::TServer(const std::vector& Arguments) { beammp_info("BeamMP Server v" + Application::ServerVersionString()); Application::SetSubsystemStatus("Server", Application::Status::Starting); @@ -256,7 +255,7 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ std::string Packet = Pckt; char Code = Packet.at(1); int PID = -1; - int VID = -1, Pos; + int VID = -1; std::string Data = Packet.substr(3), pid, vid; switch (Code) { // Spawned Destroyed Switched/Moved NotFound Reset case 's': diff --git a/src/main.cpp b/src/main.cpp index 4650570..54f0f62 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,7 +2,6 @@ #include "ArgsParser.h" #include "Common.h" -#include "CustomAssert.h" #include "Http.h" #include "LuaAPI.h" #include "SignalHandling.h" @@ -13,7 +12,6 @@ #include "TPPSMonitor.h" #include "TPluginMonitor.h" #include "TResourceManager.h" -#include "TScopedTimer.h" #include "TServer.h" #include @@ -56,7 +54,7 @@ int BeamMPServerMain(MainArguments Arguments); int main(int argc, char** argv) { MainArguments Args { argc, argv, {}, argv[0] }; - Args.List.reserve(argc); + Args.List.reserve(size_t(argc)); for (int i = 1; i < argc; ++i) { Args.List.push_back(argv[i]); } From e638c25f70da89a5236604cbb76ff77146240e47 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Mon, 26 Sep 2022 12:03:57 +0200 Subject: [PATCH 123/184] change assertions to print the error in release builds this should make it easier to debug crashes in the server remotely in release builds when sentry is down. --- include/CustomAssert.h | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/include/CustomAssert.h b/include/CustomAssert.h index 73179f0..df3c4c6 100644 --- a/include/CustomAssert.h +++ b/include/CustomAssert.h @@ -58,16 +58,17 @@ inline void _assert([[maybe_unused]] const char* file, [[maybe_unused]] const ch #define beammp_assert(cond) _assert(__FILE__, __func__, __LINE__, #cond, (cond)) #define beammp_assert_not_reachable() _assert(__FILE__, __func__, __LINE__, "reached unreachable code", false) #else -// In release build, these macros turn into NOPs. The compiler will optimize these out. -#define beammp_assert(cond) \ - do { \ - bool result = (cond); \ - if (!result) { \ - Sentry.LogAssert(#cond, _file_basename, _line, __func__); \ - } \ +#define beammp_assert(cond) \ + do { \ + bool result = (cond); \ + if (!result) { \ + beammp_errorf("Assertion failed in '{}:{}': {}.", __func__, _line, #cond); \ + Sentry.LogAssert(#cond, _file_basename, _line, __func__); \ + } \ } while (false) -#define beammp_assert_not_reachable() \ - do { \ - Sentry.LogAssert("code is unreachable", _file_basename, _line, __func__); \ +#define beammp_assert_not_reachable() \ + do { \ + beammp_errorf("Assertion failed in '{}:{}': Unreachable code reached. This may result in a crash or undefined state of the program.", __func__, _line); \ + Sentry.LogAssert("code is unreachable", _file_basename, _line, __func__); \ } while (false) #endif // DEBUG From ae517b30c083bda72335cd01d142bc3e64b492a1 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Mon, 26 Sep 2022 12:04:42 +0200 Subject: [PATCH 124/184] fix bug which caused updates to only check the first URL --- src/Common.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Common.cpp b/src/Common.cpp index 5a8b276..5b153ca 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -198,7 +198,7 @@ void Application::CheckForUpdates() { // checks current version against latest version std::regex VersionRegex { R"(\d+\.\d+\.\d+\n*)" }; for (const auto& url : GetBackendUrlsInOrder()) { - auto Response = Http::GET(GetBackendUrlsInOrder().at(0), 443, "/v/s"); + auto Response = Http::GET(url, 443, "/v/s"); bool Matches = std::regex_match(Response, VersionRegex); if (Matches) { auto MyVersion = ServerVersion(); From b145c8159e9b58bb7f1d244a1220041626f80654 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Mon, 26 Sep 2022 12:12:16 +0200 Subject: [PATCH 125/184] update changelog to add HOME and END button, fix ip comment --- CMakeLists.txt | 2 -- Changelog.md | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 13b4cce..b870a2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -155,8 +155,6 @@ if (UNIX) -Werror=missing-declarations -Werror=missing-field-initializers -Werror=write-strings - -Werror=inline - -Werror=disabled-optimization -Werror=ctor-dtor-privacy -Werror=switch-enum -Werror=switch-default diff --git a/Changelog.md b/Changelog.md index 4b5e126..f03c517 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,7 +8,8 @@ - ADDED onFileChanged event, triggered when a server plugin file changes - ADDED MP.GetPositionRaw(), which can be used to retrieve the latest position packet per player, per vehicle - ADDED error messages to some lua functions -- FIXED `ip` in MP.GetIdentifiers +- ADDED HOME and END button working in console +- FIXED `ip` in MP.GetPlayerIdentifiers - FIXED issue with client->server events which contain ':' - FIXED a fatal exception on LuaEngine startup if Resources/Server is a symlink - FIXED onInit not being called on hot-reload From 988f19fd0086262fd8e08ae72bee5cca87398b76 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Mon, 26 Sep 2022 12:20:07 +0200 Subject: [PATCH 126/184] fix getch_ to explicitly ignore read() return value --- src/Compat.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compat.cpp b/src/Compat.cpp index a843099..c7d66c8 100644 --- a/src/Compat.cpp +++ b/src/Compat.cpp @@ -50,7 +50,7 @@ TEST_CASE("init and reset termios") { static char getch_(int echo) { char ch; initTermios(echo); - read(STDIN_FILENO, &ch, 1); + (void)read(STDIN_FILENO, &ch, 1); resetTermios(); return ch; } From 36e8ba16147fb1168709b8cb50d986bc9e51b6dc Mon Sep 17 00:00:00 2001 From: Lion Date: Mon, 26 Sep 2022 12:22:22 +0200 Subject: [PATCH 127/184] fix colon in changelog to be in proper code quotes --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index f03c517..9a0a7d1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,7 +10,7 @@ - ADDED error messages to some lua functions - ADDED HOME and END button working in console - FIXED `ip` in MP.GetPlayerIdentifiers -- FIXED issue with client->server events which contain ':' +- FIXED issue with client->server events which contain - FIXED a fatal exception on LuaEngine startup if Resources/Server is a symlink - FIXED onInit not being called on hot-reload - FIXED incorrect timing calculation of Lua EventTimer loop From 3c4737a145e6010dfeab180118e5fb0c22b6b05e Mon Sep 17 00:00:00 2001 From: Lion Date: Mon, 26 Sep 2022 12:23:30 +0200 Subject: [PATCH 128/184] fix colon in changelog once more, thanks to github's editor being bad --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 9a0a7d1..e21feff 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,7 +10,7 @@ - ADDED error messages to some lua functions - ADDED HOME and END button working in console - FIXED `ip` in MP.GetPlayerIdentifiers -- FIXED issue with client->server events which contain +- FIXED issue with client->server events which contain `:` - FIXED a fatal exception on LuaEngine startup if Resources/Server is a symlink - FIXED onInit not being called on hot-reload - FIXED incorrect timing calculation of Lua EventTimer loop From dbb01998ea531703af7d7eff66742ffe4ff4bb30 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Mon, 26 Sep 2022 12:24:27 +0200 Subject: [PATCH 129/184] fix ignored return value in getch_ --- src/Compat.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Compat.cpp b/src/Compat.cpp index c7d66c8..46efeda 100644 --- a/src/Compat.cpp +++ b/src/Compat.cpp @@ -50,7 +50,9 @@ TEST_CASE("init and reset termios") { static char getch_(int echo) { char ch; initTermios(echo); - (void)read(STDIN_FILENO, &ch, 1); + if (read(STDIN_FILENO, &ch, 1) < 0) { + // ignore, not much we can do + } resetTermios(); return ch; } From 1a9872db00dd9b70adf5e1a824a7321620684a55 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Tue, 27 Sep 2022 15:49:55 +0200 Subject: [PATCH 130/184] fix unhandled return value of fread in TConfig::TConfig unit test --- src/TConfig.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/TConfig.cpp b/src/TConfig.cpp index 1d0c795..8c93aa4 100644 --- a/src/TConfig.cpp +++ b/src/TConfig.cpp @@ -44,7 +44,10 @@ TEST_CASE("TConfig::TConfig") { { buf.resize(fs::file_size(CfgFile)); auto fp = std::fopen(CfgFile.c_str(), "r"); - std::fread(buf.data(), 1, buf.size(), fp); + auto res = std::fread(buf.data(), 1, buf.size(), fp); + if (res != buf.size()) { + // IGNORE? + } std::fclose(fp); } INFO("file contents are:", buf); From dd17b9542760fc18f93bf2e86bb0d18029af37de Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Tue, 27 Sep 2022 17:02:25 +0200 Subject: [PATCH 131/184] add changelog message about MP.TriggerClientEventJson --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index e21feff..3033a5f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,7 @@ - ADDED MP.GetPositionRaw(), which can be used to retrieve the latest position packet per player, per vehicle - ADDED error messages to some lua functions - ADDED HOME and END button working in console +- ADDED `MP.TriggerClientEventJson()` which takes a table as the data argument and sends it as JSON - FIXED `ip` in MP.GetPlayerIdentifiers - FIXED issue with client->server events which contain `:` - FIXED a fatal exception on LuaEngine startup if Resources/Server is a symlink From 10dff185e08f71e6f6957e91fd27fb1784b82f0c Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Tue, 27 Sep 2022 17:25:18 +0200 Subject: [PATCH 132/184] change changelog to correctly say "Util.Json*", not MP --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 3033a5f..45418eb 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,7 +3,7 @@ - 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 Util.JsonEncode() and Util.JsonDecode(), which turn lua tables into json and vice-versa - ADDED FS.ListFiles and FS.ListDirectories - ADDED onFileChanged event, triggered when a server plugin file changes - ADDED MP.GetPositionRaw(), which can be used to retrieve the latest position packet per player, per vehicle From a1d99c0203b327e955756922c9af9a83876d6af5 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Tue, 27 Sep 2022 23:07:30 +0200 Subject: [PATCH 133/184] update commandline library includes a fix for alpine --- deps/commandline | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/commandline b/deps/commandline index 7b9f51d..470cf2d 160000 --- a/deps/commandline +++ b/deps/commandline @@ -1 +1 @@ -Subproject commit 7b9f51d6a0402500a8f84a2fb37223ac4309f2d7 +Subproject commit 470cf2df4a6c94847b3a22868139095ae51902e6 From 9c6127a105728afca7b1b7a45d7380fc88d2070d Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Sat, 1 Oct 2022 19:10:21 +0200 Subject: [PATCH 134/184] fix bug which may cause a server to crash when a car is spawned thanks @Anonymous275 --- src/TServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TServer.cpp b/src/TServer.cpp index 291d2c3..afe0b44 100644 --- a/src/TServer.cpp +++ b/src/TServer.cpp @@ -264,7 +264,7 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ int CarID = c.GetOpenCarID(); beammp_debug(c.GetName() + (" created a car with ID ") + std::to_string(CarID)); - std::string CarJson = Packet.substr(5); + std::string CarJson = Packet.substr(6); Packet = "Os:" + c.GetRoles() + ":" + c.GetName() + ":" + std::to_string(c.GetID()) + "-" + std::to_string(CarID) + ":" + CarJson; auto Futures = LuaAPI::MP::Engine->TriggerEvent("onVehicleSpawn", "", c.GetID(), CarID, Packet.substr(3)); TLuaEngine::WaitForAll(Futures); From d63c84286e69cb7b40ba2e6bf250a8194d5586af Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Sat, 1 Oct 2022 22:25:45 +0200 Subject: [PATCH 135/184] replace logging functions with new fmt versions in ParseVehicle --- src/TServer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/TServer.cpp b/src/TServer.cpp index afe0b44..c27306a 100644 --- a/src/TServer.cpp +++ b/src/TServer.cpp @@ -259,10 +259,10 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ std::string Data = Packet.substr(3), pid, vid; switch (Code) { // Spawned Destroyed Switched/Moved NotFound Reset case 's': - beammp_trace(std::string(("got 'Os' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")")); + beammp_tracef("got 'Os' packet: '{}' ({})", Packet, Packet.size()); if (Data.at(0) == '0') { int CarID = c.GetOpenCarID(); - beammp_debug(c.GetName() + (" created a car with ID ") + std::to_string(CarID)); + beammp_debugf("'{}' created a car with ID {}", c.GetName(), CarID); std::string CarJson = Packet.substr(6); Packet = "Os:" + c.GetRoles() + ":" + c.GetName() + ":" + std::to_string(c.GetID()) + "-" + std::to_string(CarID) + ":" + CarJson; @@ -284,7 +284,7 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ if (!Network.Respond(c, Destroy, true)) { // TODO: handle } - beammp_debug(c.GetName() + (" (force : car limit/lua) removed ID ") + std::to_string(CarID)); + beammp_debugf("{} (force : car limit/lua) removed ID {}", c.GetName(), CarID); } } return; From 658b37acac9eb8cf40d21d759c4a36b4f312107b Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Mon, 3 Oct 2022 14:38:19 +0200 Subject: [PATCH 136/184] fix error sometimes not displaying when failing inside global event handler --- src/TLuaEngine.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 3f8b01a..aea827a 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -417,7 +417,6 @@ sol::table TLuaEngine::StateThreadData::Lua_TriggerGlobalEvent(const std::string if (Fn.valid()) { auto LuaResult = Fn(EventArgs); auto Result = std::make_shared(); - Result->Ready = true; if (LuaResult.valid()) { Result->Error = false; Result->Result = LuaResult; @@ -425,6 +424,7 @@ sol::table TLuaEngine::StateThreadData::Lua_TriggerGlobalEvent(const std::string Result->Error = true; Result->ErrorMessage = "Function result in TriggerGlobalEvent was invalid"; } + Result->Ready = true; Return.push_back(Result); } } @@ -948,15 +948,14 @@ void TLuaEngine::StateThreadData::operator()() { } sol::state_view StateView(mState); auto Res = StateView.safe_script(*S.first.Content, sol::script_pass_on_error, S.first.FileName); - S.second->Ready = true; if (Res.valid()) { S.second->Error = false; S.second->Result = std::move(Res); } else { S.second->Error = true; - sol::error Err = Res; - S.second->ErrorMessage = Err.what(); + S.second->ErrorMessage = std::string(sol::error(Res).what()); } + S.second->Ready = true; } } { // StateFunctionQueue Scope From 1f14de2e71f50c6263f94eca10ed462befa59190 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Mon, 3 Oct 2022 15:11:26 +0200 Subject: [PATCH 137/184] revert 9c6127a105728afca7b1b7a45d7380fc88d2070d and apply proper fix --- src/TServer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TServer.cpp b/src/TServer.cpp index c27306a..39a300c 100644 --- a/src/TServer.cpp +++ b/src/TServer.cpp @@ -250,7 +250,7 @@ bool TServer::ShouldSpawn(TClient& c, const std::string& CarJson, int ID) { } void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Network) { - if (Pckt.length() < 4) + if (Pckt.length() < 6) return; std::string Packet = Pckt; char Code = Packet.at(1); @@ -264,7 +264,7 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ int CarID = c.GetOpenCarID(); beammp_debugf("'{}' created a car with ID {}", c.GetName(), CarID); - std::string CarJson = Packet.substr(6); + std::string CarJson = Packet.substr(5); Packet = "Os:" + c.GetRoles() + ":" + c.GetName() + ":" + std::to_string(c.GetID()) + "-" + std::to_string(CarID) + ":" + CarJson; auto Futures = LuaAPI::MP::Engine->TriggerEvent("onVehicleSpawn", "", c.GetID(), CarID, Packet.substr(3)); TLuaEngine::WaitForAll(Futures); From cb0cb3079798b18d48aeb438d4f70daae237b54b Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Mon, 3 Oct 2022 15:12:59 +0200 Subject: [PATCH 138/184] fix windows compiler not understanding a CLEAR AND SIMPLE FUNCTION-STYLE CONSTRUCTOR CALL AHHHHHHH --- src/TLuaEngine.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index aea827a..c738a66 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -584,27 +584,25 @@ std::pair TLuaEngine::StateThreadData::Lua_GetPositionR std::string VehiclePos = Client->GetCarPositionRaw(VID); if (VehiclePos.empty()) { - //return std::make_tuple(sol::lua_nil, sol::make_object(StateView, "Vehicle not found")); + // return std::make_tuple(sol::lua_nil, sol::make_object(StateView, "Vehicle not found")); Result.second = "Vehicle not found"; return Result; } sol::table t = Lua_JsonDecode(VehiclePos); - if (t == sol::lua_nil){ + if (t == sol::lua_nil) { Result.second = "Packet decode failed"; } - //return std::make_tuple(Result, sol::make_object(StateView, sol::lua_nil)); + // return std::make_tuple(Result, sol::make_object(StateView, sol::lua_nil)); Result.first = t; return Result; - } - else { - //return std::make_tuple(sol::lua_nil, sol::make_object(StateView, "Client expired")); + } else { + // return std::make_tuple(sol::lua_nil, sol::make_object(StateView, "Client expired")); Result.second = "Client expired"; return Result; } } - sol::table TLuaEngine::StateThreadData::Lua_HttpCreateConnection(const std::string& host, uint16_t port) { auto table = mStateView.create_table(); constexpr const char* InternalClient = "__InternalClient"; @@ -953,7 +951,8 @@ void TLuaEngine::StateThreadData::operator()() { S.second->Result = std::move(Res); } else { S.second->Error = true; - S.second->ErrorMessage = std::string(sol::error(Res).what()); + sol::error Err = Res; + S.second->ErrorMessage = Err.what(); } S.second->Ready = true; } From 5d3dff3c883be11bfe47fc43675b2a0bc55a3117 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Mon, 3 Oct 2022 15:31:32 +0200 Subject: [PATCH 139/184] add identifiers (beammp id, ip) as an argument to onPlayerAuth --- Changelog.md | 1 + include/TLuaEngine.h | 3 ++- src/TLuaEngine.cpp | 10 ++++++++++ src/TNetwork.cpp | 2 +- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 45418eb..9aacbda 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,7 @@ - ADDED error messages to some lua functions - ADDED HOME and END button working in console - ADDED `MP.TriggerClientEventJson()` which takes a table as the data argument and sends it as JSON +- ADDED identifiers (beammp id, ip) to onPlayerAuth (4th argument) - FIXED `ip` in MP.GetPlayerIdentifiers - FIXED issue with client->server events which contain `:` - FIXED a fatal exception on LuaEngine startup if Resources/Server is a symlink diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index 049b554..de19f13 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -25,11 +25,12 @@ namespace fs = std::filesystem; /** * std::variant means, that TLuaArgTypes may be one of the Types listed as template args */ -using TLuaArgTypes = std::variant; +using TLuaArgTypes = std::variant>; static constexpr size_t TLuaArgTypes_String = 0; static constexpr size_t TLuaArgTypes_Int = 1; static constexpr size_t TLuaArgTypes_VariadicArgs = 2; static constexpr size_t TLuaArgTypes_Bool = 3; +static constexpr size_t TLuaArgTypes_StringStringMap = 4; class TLuaPlugin; diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index c738a66..87f13fa 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -4,6 +4,7 @@ #include "Http.h" #include "LuaAPI.h" #include "TLuaPlugin.h" +#include "sol/object.hpp" #include #include @@ -992,6 +993,15 @@ void TLuaEngine::StateThreadData::operator()() { case TLuaArgTypes_Bool: LuaArgs.push_back(sol::make_object(StateView, std::get(Arg))); break; + case TLuaArgTypes_StringStringMap: { + auto Map = std::get>(Arg); + auto Table = StateView.create_table(); + for (const auto& [k, v] : Map) { + Table[k] = v; + } + LuaArgs.push_back(sol::make_object(StateView, Table)); + break; + } default: beammp_error("Unknown argument type, passed as nil"); break; diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index ecdea11..588834d 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -355,7 +355,7 @@ void TNetwork::Authentication(const TConnection& ClientConnection) { return true; }); - auto Futures = LuaAPI::MP::Engine->TriggerEvent("onPlayerAuth", "", Client->GetName(), Client->GetRoles(), Client->IsGuest()); + auto Futures = LuaAPI::MP::Engine->TriggerEvent("onPlayerAuth", "", Client->GetName(), Client->GetRoles(), Client->IsGuest(), Client->GetIdentifiers()); TLuaEngine::WaitForAll(Futures); bool NotAllowed = std::any_of(Futures.begin(), Futures.end(), [](const std::shared_ptr& Result) { From 5f1d0030777d113ee2d14506733035a79ae89ad1 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Mon, 3 Oct 2022 17:06:32 +0200 Subject: [PATCH 140/184] fix various potential crashes in TServer::HandlePosition --- src/TServer.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/TServer.cpp b/src/TServer.cpp index 39a300c..8cba141 100644 --- a/src/TServer.cpp +++ b/src/TServer.cpp @@ -425,10 +425,24 @@ void TServer::InsertClient(const std::shared_ptr& NewClient) { } void TServer::HandlePosition(TClient& c, const std::string& Packet) { + if (Packet.size() < 3) { + // invalid packet + return; + } // Zp:serverVehicleID:data + // Zp:0:data std::string withoutCode = Packet.substr(3); auto NameDataSep = withoutCode.find(':', 2); + if (NameDataSep == std::string::npos || NameDataSep < 2) { + // invalid packet + return; + } + // FIXME: ensure that -2 does what it should... it seems weird. std::string ServerVehicleID = withoutCode.substr(2, NameDataSep - 2); + if (NameDataSep + 1 > withoutCode.size()) { + // invalid packet + return; + } std::string Data = withoutCode.substr(NameDataSep + 1); // parse veh ID @@ -436,6 +450,7 @@ void TServer::HandlePosition(TClient& c, const std::string& Packet) { if (MaybePidVid) { int PID = -1; int VID = -1; + // FIXME: check that the VID and PID are valid, so that we don't waste memory std::tie(PID, VID) = MaybePidVid.value(); c.SetCarPosition(VID, Data); From 30482d290adca9830056c1cdb0c038e004096e1a Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 5 Oct 2022 11:50:15 +0200 Subject: [PATCH 141/184] add boost 1.75 dependency this should be available on most platforms. boost allows us to simplify a LOT of code. --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index b870a2b..27ff731 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,6 +95,8 @@ include(FindOpenSSL) include(FindThreads) include(FindZLIB) +find_package(Boost 1.75 REQUIRED COMPONENTS system) + set(BeamMP_Sources include/TConsole.h src/TConsole.cpp include/TServer.h src/TServer.cpp @@ -170,6 +172,7 @@ if (UNIX) endif() set(BeamMP_Libraries + Boost::boost doctest::doctest OpenSSL::SSL OpenSSL::Crypto From 6e97a3cd6ece2dd06892291c3f08f1ee0dfc5be1 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 5 Oct 2022 12:14:25 +0200 Subject: [PATCH 142/184] switch udp networking to boost implementation --- CMakeLists.txt | 1 + include/BoostAliases.h | 5 +++ include/Client.h | 8 +++-- include/TNetwork.h | 10 ++++-- src/TNetwork.cpp | 74 +++++++++++++----------------------------- 5 files changed, 41 insertions(+), 57 deletions(-) create mode 100644 include/BoostAliases.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 27ff731..118e9b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -119,6 +119,7 @@ set(BeamMP_Sources include/ArgsParser.h src/ArgsParser.cpp include/TPluginMonitor.h src/TPluginMonitor.cpp include/Environment.h + include/BoostAliases.h ) set(BeamMP_Includes diff --git a/include/BoostAliases.h b/include/BoostAliases.h new file mode 100644 index 0000000..a42265b --- /dev/null +++ b/include/BoostAliases.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +using namespace boost::asio; diff --git a/include/Client.h b/include/Client.h index 114ff3e..fa70208 100644 --- a/include/Client.h +++ b/include/Client.h @@ -7,6 +7,7 @@ #include #include +#include "BoostAliases.h" #include "Common.h" #include "Compat.h" #include "VehicleData.h" @@ -46,14 +47,15 @@ public: void SetIdentifier(const std::string& key, const std::string& value) { mIdentifiers[key] = value; } std::string GetCarData(int Ident); std::string GetCarPositionRaw(int Ident); - void SetUDPAddr(sockaddr_in Addr) { mUDPAddress = Addr; } + void SetUDPAddr(const ip::udp::endpoint& Addr) { mUDPAddress = Addr; } void SetDownSock(SOCKET CSock) { mSocket[1] = CSock; } void SetTCPSock(SOCKET CSock) { mSocket[0] = CSock; } void SetStatus(int Status) { mStatus = Status; } // locks void DeleteCar(int Ident); [[nodiscard]] const std::unordered_map& GetIdentifiers() const { return mIdentifiers; } - [[nodiscard]] sockaddr_in GetUDPAddr() const { return mUDPAddress; } + [[nodiscard]] const ip::udp::endpoint& GetUDPAddr() const { return mUDPAddress; } + [[nodiscard]] ip::udp::endpoint& GetUDPAddr() { return mUDPAddress; } [[nodiscard]] SOCKET GetDownSock() const { return mSocket[1]; } [[nodiscard]] SOCKET GetTCPSock() const { return mSocket[0]; } [[nodiscard]] std::string GetRoles() const { return mRole; } @@ -100,7 +102,7 @@ private: SparseArray mVehiclePosition; std::string mName = "Unknown Client"; SOCKET mSocket[2] { SOCKET(0), SOCKET(0) }; - sockaddr_in mUDPAddress {}; // is this initialization OK? yes it is + ip::udp::endpoint mUDPAddress {}; int mUnicycleID = -1; std::string mRole; std::string mDID; diff --git a/include/TNetwork.h b/include/TNetwork.h index 6394306..f18f17f 100644 --- a/include/TNetwork.h +++ b/include/TNetwork.h @@ -1,8 +1,11 @@ #pragma once +#include "BoostAliases.h" #include "Compat.h" #include "TResourceManager.h" #include "TServer.h" +#include +#include struct TConnection; @@ -21,7 +24,7 @@ public: void Authentication(const TConnection& ClientConnection); [[nodiscard]] bool CheckBytes(TClient& c, int32_t BytesRcv); void SyncResources(TClient& c); - [[nodiscard]] bool UDPSend(TClient& Client, std::string Data) const; + [[nodiscard]] bool UDPSend(TClient& Client, std::string Data); void SendToAll(TClient* c, const std::string& Data, bool Self, bool Rel); void UpdatePlayer(TClient& Client); @@ -31,12 +34,13 @@ private: TServer& mServer; TPPSMonitor& mPPSMonitor; - SOCKET mUDPSock {}; + io_context mIoCtx; + ip::udp::socket mUDPSock; TResourceManager& mResourceManager; std::thread mUDPThread; std::thread mTCPThread; - std::string UDPRcvFromClient(sockaddr_in& client) const; + std::string UDPRcvFromClient(ip::udp::endpoint& ClientEndpoint); void HandleDownload(SOCKET TCPSock); void OnConnect(const std::weak_ptr& c); void TCPClient(const std::weak_ptr& c); diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index 588834d..3349e33 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -5,11 +5,15 @@ #include #include #include +#include +#include #include TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& ResourceManager) : mServer(Server) , mPPSMonitor(PPSMonitor) + , mIoCtx {} + , mUDPSock(mIoCtx) , mResourceManager(ResourceManager) { Application::SetSubsystemStatus("TCPNetwork", Application::Status::Starting); Application::SetSubsystemStatus("UDPNetwork", Application::Status::Starting); @@ -42,40 +46,25 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R void TNetwork::UDPServerMain() { RegisterThread("UDPServer"); -#if defined(BEAMMP_WINDOWS) - WSADATA data; - if (WSAStartup(514, &data)) { - beammp_error(("Can't start Winsock!")); - // return; - } -#endif // WINDOWS - mUDPSock = socket(AF_INET, SOCK_DGRAM, 0); - // Create a server hint structure for the server - sockaddr_in serverAddr {}; - serverAddr.sin_addr.s_addr = INADDR_ANY; // Any Local - serverAddr.sin_family = AF_INET; // Address format is IPv4 - serverAddr.sin_port = htons(uint16_t(Application::Settings.Port)); // Convert from little to big endian - - // Try and bind the socket to the IP and port - if (bind(mUDPSock, reinterpret_cast(&serverAddr), sizeof(serverAddr)) != 0) { + mUDPSock = ip::udp::socket(mIoCtx); + ip::udp::endpoint UdpListenEndpoint(ip::udp::v4(), Application::Settings.Port); + boost::system::error_code ec; + mUDPSock.bind(UdpListenEndpoint, ec); + if (ec) { beammp_error("bind() failed: " + GetPlatformAgnosticErrorString()); std::this_thread::sleep_for(std::chrono::seconds(5)); - exit(-1); // TODO: Wtf. - // return; + Application::GracefullyShutdown(); } Application::SetSubsystemStatus("UDPNetwork", Application::Status::Good); beammp_info(("Vehicle data network online on port ") + std::to_string(Application::Settings.Port) + (" with a Max of ") + std::to_string(Application::Settings.MaxPlayers) + (" Clients")); while (!Application::IsShuttingDown()) { try { - sockaddr_in client {}; + ip::udp::endpoint client {}; std::string Data = UDPRcvFromClient(client); // Receives any data from Socket size_t Pos = Data.find(':'); if (Data.empty() || Pos > 2) continue; - /*char clientIp[256]; - ZeroMemory(clientIp, 256); ///Code to get IP we don't need that yet - inet_ntop(AF_INET, &client.sin_addr, clientIp, 256);*/ uint8_t ID = uint8_t(Data.at(0)) - 1; mServer.ForEachClient([&](std::weak_ptr ClientPtr) -> bool { std::shared_ptr Client; @@ -995,7 +984,7 @@ void TNetwork::SendToAll(TClient* c, const std::string& Data, bool Self, bool Re return; } -bool TNetwork::UDPSend(TClient& Client, std::string Data) const { +bool TNetwork::UDPSend(TClient& Client, std::string Data) { if (!Client.IsConnected() || Client.GetStatus() < 0) { // this can happen if we try to send a packet to a client that is either // 1. not yet fully connected, or @@ -1003,28 +992,15 @@ bool TNetwork::UDPSend(TClient& Client, std::string Data) const { // this is fine can can be ignored :^) return true; } - sockaddr_in Addr = Client.GetUDPAddr(); - auto AddrSize = sizeof(Client.GetUDPAddr()); + const auto Addr = Client.GetUDPAddr(); if (Data.length() > 400) { std::string CMP(Comp(Data)); Data = "ABG:" + CMP; } -#ifdef WIN32 - int sendOk; - int len = static_cast(Data.size()); -#else - int64_t sendOk; - size_t len = Data.size(); -#endif // WIN32 - - sendOk = sendto(mUDPSock, Data.c_str(), len, 0, reinterpret_cast(&Addr), int(AddrSize)); - if (sendOk == -1) { - beammp_debug("(UDP) sendto() failed: " + GetPlatformAgnosticErrorString()); - if (Client.GetStatus() > -1) - Client.SetStatus(-1); - return false; - } else if (sendOk == 0) { - beammp_debug(("(UDP) sendto() returned 0")); + boost::system::error_code ec; + mUDPSock.send_to(buffer(Data), Addr, 0, ec); + if (ec) { + beammp_debugf("UDP sendto() failed: {}", ec.what()); if (Client.GetStatus() > -1) Client.SetStatus(-1); return false; @@ -1032,18 +1008,14 @@ bool TNetwork::UDPSend(TClient& Client, std::string Data) const { return true; } -std::string TNetwork::UDPRcvFromClient(sockaddr_in& client) const { - size_t clientLength = sizeof(client); +std::string TNetwork::UDPRcvFromClient(ip::udp::endpoint& ClientEndpoint) { std::array Ret {}; -#ifdef WIN32 - auto Rcv = recvfrom(mUDPSock, Ret.data(), int(Ret.size()), 0, (sockaddr*)&client, (int*)&clientLength); -#else // unix - int64_t Rcv = recvfrom(mUDPSock, Ret.data(), Ret.size(), 0, reinterpret_cast(&client), reinterpret_cast(&clientLength)); -#endif // WIN32 - - if (Rcv == -1) { - beammp_error("(UDP) Error receiving from client! recvfrom() failed: " + GetPlatformAgnosticErrorString()); + boost::system::error_code ec; + const auto Rcv = mUDPSock.receive_from(mutable_buffer(Ret.data(), Ret.size()), ClientEndpoint, 0, ec); + if (ec) { + beammp_errorf("UDP recvfrom() failed: {}", ec.what()); return ""; } + // FIXME: This breaks binary data due to \0. return std::string(Ret.begin(), Ret.begin() + Rcv); } From 7446526a191ff2ab49ffd0e601dee0677dfa47c3 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 5 Oct 2022 13:06:36 +0200 Subject: [PATCH 143/184] fix binding of udp server socket it was not binding properly because it wasn't open()ed, i guess --- src/TNetwork.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index 3349e33..5bed070 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -46,12 +47,17 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R void TNetwork::UDPServerMain() { RegisterThread("UDPServer"); - mUDPSock = ip::udp::socket(mIoCtx); - ip::udp::endpoint UdpListenEndpoint(ip::udp::v4(), Application::Settings.Port); + ip::udp::endpoint UdpListenEndpoint(ip::address::from_string("0.0.0.0"), Application::Settings.Port); boost::system::error_code ec; + mUDPSock.open(UdpListenEndpoint.protocol(), ec); + if (ec) { + beammp_error("open() failed: " + ec.what()); + std::this_thread::sleep_for(std::chrono::seconds(5)); + Application::GracefullyShutdown(); + } mUDPSock.bind(UdpListenEndpoint, ec); if (ec) { - beammp_error("bind() failed: " + GetPlatformAgnosticErrorString()); + beammp_error("bind() failed: " + ec.what()); std::this_thread::sleep_for(std::chrono::seconds(5)); Application::GracefullyShutdown(); } @@ -1011,7 +1017,9 @@ bool TNetwork::UDPSend(TClient& Client, std::string Data) { std::string TNetwork::UDPRcvFromClient(ip::udp::endpoint& ClientEndpoint) { std::array Ret {}; boost::system::error_code ec; + beammp_debugf("receiving data from {}:{}", ClientEndpoint.address().to_string(), ClientEndpoint.port()); const auto Rcv = mUDPSock.receive_from(mutable_buffer(Ret.data(), Ret.size()), ClientEndpoint, 0, ec); + beammp_debugf("received {} bytes from {}:{}", Rcv, ClientEndpoint.address().to_string(), ClientEndpoint.port()); if (ec) { beammp_errorf("UDP recvfrom() failed: {}", ec.what()); return ""; From 7d2e4d4581bc2c9fd39e56b1c7d81f4c7b2c81a4 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 5 Oct 2022 15:44:32 +0200 Subject: [PATCH 144/184] replace tcp networking with boost::asio tcp networking --- include/Client.h | 34 ++- include/Common.h | 2 +- include/TNetwork.h | 34 +-- include/TServer.h | 19 +- src/Client.cpp | 30 +- src/LuaAPI.cpp | 12 +- src/THeartbeatThread.cpp | 2 +- src/TNetwork.cpp | 642 +++++++++++++++++---------------------- src/TServer.cpp | 66 ++-- 9 files changed, 388 insertions(+), 453 deletions(-) diff --git a/include/Client.h b/include/Client.h index fa70208..b8858d1 100644 --- a/include/Client.h +++ b/include/Client.h @@ -20,9 +20,8 @@ class TServer; #endif // WINDOWS struct TConnection final { - SOCKET Socket; - struct sockaddr SockAddr; - socklen_t SockAddrLen; + ip::tcp::socket Socket; + ip::tcp::endpoint SockAddr; }; class TClient final { @@ -34,8 +33,9 @@ public: std::unique_lock Lock; }; - explicit TClient(TServer& Server); + TClient(TServer& Server, ip::tcp::socket&& Socket); TClient(const TClient&) = delete; + ~TClient(); TClient& operator=(const TClient&) = delete; void AddNewCar(int Ident, const std::string& Data); @@ -48,16 +48,19 @@ public: std::string GetCarData(int Ident); std::string GetCarPositionRaw(int Ident); void SetUDPAddr(const ip::udp::endpoint& Addr) { mUDPAddress = Addr; } - void SetDownSock(SOCKET CSock) { mSocket[1] = CSock; } - void SetTCPSock(SOCKET CSock) { mSocket[0] = CSock; } - void SetStatus(int Status) { mStatus = Status; } + void SetDownSock(ip::tcp::socket&& CSock) { mDownSocket = std::move(CSock); } + void SetTCPSock(ip::tcp::socket&& CSock) { mSocket = std::move(CSock); } + void Disconnect(std::string_view Reason); + bool IsDisconnected() const { return !mSocket.is_open(); } // locks void DeleteCar(int Ident); [[nodiscard]] const std::unordered_map& GetIdentifiers() const { return mIdentifiers; } [[nodiscard]] const ip::udp::endpoint& GetUDPAddr() const { return mUDPAddress; } [[nodiscard]] ip::udp::endpoint& GetUDPAddr() { return mUDPAddress; } - [[nodiscard]] SOCKET GetDownSock() const { return mSocket[1]; } - [[nodiscard]] SOCKET GetTCPSock() const { return mSocket[0]; } + [[nodiscard]] ip::tcp::socket& GetDownSock() { return mDownSocket; } + [[nodiscard]] const ip::tcp::socket& GetDownSock() const { return mDownSocket; } + [[nodiscard]] ip::tcp::socket& GetTCPSock() { return mSocket; } + [[nodiscard]] const ip::tcp::socket& GetTCPSock() const { return mSocket; } [[nodiscard]] std::string GetRoles() const { return mRole; } [[nodiscard]] std::string GetName() const { return mName; } void SetUnicycleID(int ID) { mUnicycleID = ID; } @@ -65,7 +68,6 @@ public: [[nodiscard]] int GetOpenCarID() const; [[nodiscard]] int GetCarCount() const; void ClearCars(); - [[nodiscard]] int GetStatus() const { return mStatus; } [[nodiscard]] int GetID() const { return mID; } [[nodiscard]] int GetUnicycleID() const { return mUnicycleID; } [[nodiscard]] bool IsConnected() const { return mIsConnected; } @@ -75,9 +77,9 @@ public: void SetIsGuest(bool NewIsGuest) { mIsGuest = NewIsGuest; } void SetIsSynced(bool NewIsSynced) { mIsSynced = NewIsSynced; } void SetIsSyncing(bool NewIsSyncing) { mIsSyncing = NewIsSyncing; } - void EnqueuePacket(const std::string& Packet); - [[nodiscard]] std::queue& MissedPacketQueue() { return mPacketsSync; } - [[nodiscard]] const std::queue& MissedPacketQueue() const { return mPacketsSync; } + void EnqueuePacket(const std::vector& Packet); + [[nodiscard]] std::queue>& MissedPacketQueue() { return mPacketsSync; } + [[nodiscard]] const std::queue>& MissedPacketQueue() const { return mPacketsSync; } [[nodiscard]] size_t MissedPacketQueueSize() const { return mPacketsSync.size(); } [[nodiscard]] std::mutex& MissedPacketQueueMutex() const { return mMissedPacketsMutex; } void SetIsConnected(bool NewIsConnected) { mIsConnected = NewIsConnected; } @@ -93,7 +95,7 @@ private: bool mIsSynced = false; bool mIsSyncing = false; mutable std::mutex mMissedPacketsMutex; - std::queue mPacketsSync; + std::queue> mPacketsSync; std::unordered_map mIdentifiers; bool mIsGuest = false; mutable std::mutex mVehicleDataMutex; @@ -101,12 +103,12 @@ private: TSetOfVehicleData mVehicleData; SparseArray mVehiclePosition; std::string mName = "Unknown Client"; - SOCKET mSocket[2] { SOCKET(0), SOCKET(0) }; + ip::tcp::socket mSocket; + ip::tcp::socket mDownSocket; ip::udp::endpoint mUDPAddress {}; int mUnicycleID = -1; std::string mRole; std::string mDID; - int mStatus = 0; int mID = -1; std::chrono::time_point mLastPingTime; }; diff --git a/include/Common.h b/include/Common.h index 3b0f205..06b24c8 100644 --- a/include/Common.h +++ b/include/Common.h @@ -80,7 +80,7 @@ public: static TConsole& Console() { return *mConsole; } static std::string ServerVersionString(); static const Version& ServerVersion() { return mVersion; } - static std::string ClientVersionString() { return "2.0"; } + static uint8_t ClientMajorVersion() { return 2; } static std::string PPS() { return mPPS; } static void SetPPS(const std::string& NewPPS) { mPPS = NewPPS; } diff --git a/include/TNetwork.h b/include/TNetwork.h index f18f17f..3b4980e 100644 --- a/include/TNetwork.h +++ b/include/TNetwork.h @@ -13,19 +13,18 @@ class TNetwork { public: TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& ResourceManager); - [[nodiscard]] bool TCPSend(TClient& c, const std::string& Data, bool IsSync = false); - [[nodiscard]] bool SendLarge(TClient& c, std::string Data, bool isSync = false); - [[nodiscard]] bool Respond(TClient& c, const std::string& MSG, bool Rel, bool isSync = false); - std::shared_ptr CreateClient(SOCKET TCPSock); - std::string TCPRcv(TClient& c); + [[nodiscard]] bool TCPSend(TClient& c, const std::vector& Data, bool IsSync = false); + [[nodiscard]] bool SendLarge(TClient& c, std::vector Data, bool isSync = false); + [[nodiscard]] bool Respond(TClient& c, const std::vector& MSG, bool Rel, bool isSync = false); + std::shared_ptr CreateClient(ip::tcp::socket&& TCPSock); + std::vector TCPRcv(TClient& c); void ClientKick(TClient& c, const std::string& R); [[nodiscard]] bool SyncClient(const std::weak_ptr& c); - void Identify(const TConnection& client); - void Authentication(const TConnection& ClientConnection); - [[nodiscard]] bool CheckBytes(TClient& c, int32_t BytesRcv); + void Identify(TConnection&& client); + std::shared_ptr Authentication(TConnection&& ClientConnection); void SyncResources(TClient& c); - [[nodiscard]] bool UDPSend(TClient& Client, std::string Data); - void SendToAll(TClient* c, const std::string& Data, bool Self, bool Rel); + [[nodiscard]] bool UDPSend(TClient& Client, std::vector Data); + void SendToAll(TClient* c, const std::vector& Data, bool Self, bool Rel); void UpdatePlayer(TClient& Client); private: @@ -34,22 +33,23 @@ private: TServer& mServer; TPPSMonitor& mPPSMonitor; - io_context mIoCtx; ip::udp::socket mUDPSock; TResourceManager& mResourceManager; std::thread mUDPThread; std::thread mTCPThread; - std::string UDPRcvFromClient(ip::udp::endpoint& ClientEndpoint); - void HandleDownload(SOCKET TCPSock); + std::vector UDPRcvFromClient(ip::udp::endpoint& ClientEndpoint); + void HandleDownload(TConnection&& TCPSock); void OnConnect(const std::weak_ptr& c); void TCPClient(const std::weak_ptr& c); void Looper(const std::weak_ptr& c); int OpenID(); - void OnDisconnect(const std::weak_ptr& ClientPtr, bool kicked); - void Parse(TClient& c, const std::string& Packet); + void OnDisconnect(const std::weak_ptr& ClientPtr); + void Parse(TClient& c, const std::vector& Packet); void SendFile(TClient& c, const std::string& Name); - static bool TCPSendRaw(TClient& C, SOCKET socket, char* Data, int32_t Size); + static bool TCPSendRaw(TClient& C, ip::tcp::socket& socket, const uint8_t* Data, size_t Size); static void SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std::string& Name); - static uint8_t* SendSplit(TClient& c, SOCKET Socket, uint8_t* DataPtr, size_t Size); + static const uint8_t* SendSplit(TClient& c, ip::tcp::socket& Socket, const uint8_t* DataPtr, size_t Size); }; + +std::vector StringToVector(const std::string& Str); diff --git a/include/TServer.h b/include/TServer.h index cf7891a..0967381 100644 --- a/include/TServer.h +++ b/include/TServer.h @@ -8,6 +8,8 @@ #include #include +#include "BoostAliases.h" + class TClient; class TNetwork; class TPPSMonitor; @@ -19,19 +21,22 @@ public: TServer(const std::vector& Arguments); void InsertClient(const std::shared_ptr& Ptr); - std::weak_ptr InsertNewClient(); void RemoveClient(const std::weak_ptr&); // in Fn, return true to continue, return false to break void ForEachClient(const std::function)>& Fn); size_t ClientCount() const; - static void GlobalParser(const std::weak_ptr& Client, std::string Packet, TPPSMonitor& PPSMonitor, TNetwork& Network); + static void GlobalParser(const std::weak_ptr& Client, std::vector&& Packet, TPPSMonitor& PPSMonitor, TNetwork& Network); static void HandleEvent(TClient& c, const std::string& Data); RWMutex& GetClientMutex() const { return mClientsMutex; } - const TScopedTimer UptimeTimer; + + // asio io context + io_context& IoCtx() { return mIoCtx; } + private: + io_context mIoCtx {}; TClientSet mClients; mutable RWMutex mClientsMutex; static void ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Network); @@ -40,3 +45,11 @@ private: static void Apply(TClient& c, int VID, const std::string& pckt); static void HandlePosition(TClient& c, const std::string& Packet); }; + +struct BufferView { + uint8_t* Data { nullptr }; + size_t Size { 0 }; + const uint8_t* data() const { return Data; } + uint8_t* data() { return Data; } + size_t size() const { return Size; } +}; diff --git a/src/Client.cpp b/src/Client.cpp index f93f825..991f0b7 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -2,6 +2,7 @@ #include "CustomAssert.h" #include "TServer.h" +#include #include #include @@ -49,16 +50,27 @@ TClient::TVehicleDataLockPair TClient::GetAllCars() { std::string TClient::GetCarPositionRaw(int Ident) { std::unique_lock lock(mVehiclePositionMutex); - try - { + try { return mVehiclePosition.at(Ident); - } - catch (const std::out_of_range& oor) { + } catch (const std::out_of_range& oor) { return ""; } return ""; } +void TClient::Disconnect(std::string_view Reason) { + beammp_debugf("Disconnecting client {} for reason: {}", GetID(), Reason); + boost::system::error_code ec; + mSocket.shutdown(socket_base::shutdown_both, ec); + if (ec) { + beammp_warnf("Failed to shutdown client socket: {}", ec.what()); + } + mSocket.close(ec); + if (ec) { + beammp_warnf("Failed to close client socket: {}", ec.what()); + } +} + void TClient::SetCarPosition(int Ident, const std::string& Data) { std::unique_lock lock(mVehiclePositionMutex); mVehiclePosition[Ident] = Data; @@ -98,16 +110,22 @@ TServer& TClient::Server() const { return mServer; } -void TClient::EnqueuePacket(const std::string& Packet) { +void TClient::EnqueuePacket(const std::vector& Packet) { std::unique_lock Lock(mMissedPacketsMutex); mPacketsSync.push(Packet); } -TClient::TClient(TServer& Server) +TClient::TClient(TServer& Server, ip::tcp::socket&& Socket) : mServer(Server) + , mSocket(std::move(Socket)) + , mDownSocket(ip::tcp::socket(Server.IoCtx())) , mLastPingTime(std::chrono::high_resolution_clock::now()) { } +TClient::~TClient() { + beammp_debugf("client destroyed: {} ('{}')", this->GetID(), this->GetName()); +} + void TClient::UpdatePingTime() { mLastPingTime = std::chrono::high_resolution_clock::now(); } diff --git a/src/LuaAPI.cpp b/src/LuaAPI.cpp index 30a0b86..08dec7e 100644 --- a/src/LuaAPI.cpp +++ b/src/LuaAPI.cpp @@ -116,7 +116,7 @@ TEST_CASE("LuaAPI::MP::GetServerVersion") { static inline std::pair InternalTriggerClientEvent(int PlayerID, const std::string& EventName, const std::string& Data) { std::string Packet = "E:" + EventName + ":" + Data; if (PlayerID == -1) { - LuaAPI::MP::Engine->Network().SendToAll(nullptr, Packet, true, true); + LuaAPI::MP::Engine->Network().SendToAll(nullptr, StringToVector(Packet), true, true); return { true, "" }; } else { auto MaybeClient = GetClient(LuaAPI::MP::Engine->Server(), PlayerID); @@ -125,7 +125,7 @@ static inline std::pair InternalTriggerClientEvent(int Player return { false, "Invalid Player ID" }; } auto c = MaybeClient.value().lock(); - if (!LuaAPI::MP::Engine->Network().Respond(*c, Packet, true)) { + if (!LuaAPI::MP::Engine->Network().Respond(*c, StringToVector(Packet), true)) { beammp_lua_errorf("Respond failed, dropping client {}", PlayerID); LuaAPI::MP::Engine->Network().ClientKick(*c, "Disconnected after failing to receive packets"); return { false, "Respond failed, dropping client" }; @@ -155,7 +155,7 @@ std::pair LuaAPI::MP::SendChatMessage(int ID, const std::stri std::string Packet = "C:Server: " + Message; if (ID == -1) { LogChatMessage(" (to everyone) ", -1, Message); - Engine->Network().SendToAll(nullptr, Packet, true, true); + Engine->Network().SendToAll(nullptr, StringToVector(Packet), true, true); Result.first = true; } else { auto MaybeClient = GetClient(Engine->Server(), ID); @@ -167,7 +167,7 @@ std::pair LuaAPI::MP::SendChatMessage(int ID, const std::stri return Result; } LogChatMessage(" (to \"" + c->GetName() + "\")", -1, Message); - if (!Engine->Network().Respond(*c, Packet, true)) { + if (!Engine->Network().Respond(*c, StringToVector(Packet), true)) { beammp_errorf("Failed to send chat message back to sender (id {}) - did the sender disconnect?", ID); // TODO: should we return an error here? } @@ -194,7 +194,7 @@ std::pair LuaAPI::MP::RemoveVehicle(int PID, int VID) { auto c = MaybeClient.value().lock(); if (!c->GetCarData(VID).empty()) { std::string Destroy = "Od:" + std::to_string(PID) + "-" + std::to_string(VID); - Engine->Network().SendToAll(nullptr, Destroy, true, true); + Engine->Network().SendToAll(nullptr, StringToVector(Destroy), true, true); c->DeleteCar(VID); Result.first = true; } else { @@ -526,7 +526,7 @@ static void JsonEncodeRecursive(nlohmann::json& json, const sol::object& left, c beammp_lua_error("json serialize will not go deeper than 100 nested tables, internal references assumed, aborted this path"); return; } - std::string key{}; + std::string key {}; switch (left.get_type()) { case sol::type::lua_nil: case sol::type::none: diff --git a/src/THeartbeatThread.cpp b/src/THeartbeatThread.cpp index edd0ed2..96504a8 100644 --- a/src/THeartbeatThread.cpp +++ b/src/THeartbeatThread.cpp @@ -148,7 +148,7 @@ std::string THeartbeatThread::GenerateCall() { << "&map=" << Application::Settings.MapName << "&private=" << (Application::Settings.Private ? "true" : "false") << "&version=" << Application::ServerVersionString() - << "&clientversion=" << Application::ClientVersionString() + << "&clientversion=" << Application::ClientMajorVersion() << "&name=" << Application::Settings.ServerName << "&modlist=" << mResourceManager.TrimmedList() << "&modstotalsize=" << mResourceManager.MaxModSize() diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index 5bed070..35ace21 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -1,7 +1,9 @@ #include "TNetwork.h" #include "Client.h" +#include "Common.h" #include "LuaAPI.h" #include "TLuaEngine.h" +#include "nlohmann/json.hpp" #include #include #include @@ -10,11 +12,23 @@ #include #include +std::vector StringToVector(const std::string& Str) { + return std::vector(Str.data(), Str.data() + Str.size()); +} + +static void CompressProperly(std::vector& Data) { + constexpr std::string_view ABG = "ABG:"; + auto CombinedData = std::vector(ABG.begin(), ABG.end()); + auto CompData = Comp(Data); + CombinedData.resize(ABG.size() + CompData.size()); + std::copy(CompData.begin(), CompData.end(), CombinedData.begin() + ABG.size()); + Data = CombinedData; +} + TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& ResourceManager) : mServer(Server) , mPPSMonitor(PPSMonitor) - , mIoCtx {} - , mUDPSock(mIoCtx) + , mUDPSock(Server.IoCtx()) , mResourceManager(ResourceManager) { Application::SetSubsystemStatus("TCPNetwork", Application::Status::Starting); Application::SetSubsystemStatus("UDPNetwork", Application::Status::Starting); @@ -67,9 +81,9 @@ void TNetwork::UDPServerMain() { while (!Application::IsShuttingDown()) { try { ip::udp::endpoint client {}; - std::string Data = UDPRcvFromClient(client); // Receives any data from Socket - size_t Pos = Data.find(':'); - if (Data.empty() || Pos > 2) + std::vector Data = UDPRcvFromClient(client); // Receives any data from Socket + auto Pos = std::find(Data.begin(), Data.end(), ':'); + if (Data.empty() || Pos > Data.begin() + 2) continue; uint8_t ID = uint8_t(Data.at(0)) - 1; mServer.ForEachClient([&](std::weak_ptr ClientPtr) -> bool { @@ -85,7 +99,8 @@ void TNetwork::UDPServerMain() { if (Client->GetID() == ID) { Client->SetUDPAddr(client); Client->SetIsConnected(true); - TServer::GlobalParser(ClientPtr, Data.substr(2), mPPSMonitor, *this); + Data.erase(Data.begin(), Data.begin() + 2); + TServer::GlobalParser(ClientPtr, std::move(Data), mPPSMonitor, *this); } return true; @@ -98,47 +113,30 @@ void TNetwork::UDPServerMain() { void TNetwork::TCPServerMain() { RegisterThread("TCPServer"); -#if defined(BEAMMP_WINDOWS) - WSADATA wsaData; - if (WSAStartup(514, &wsaData)) { - beammp_error("Can't start Winsock! Shutting down"); - Application::GracefullyShutdown(); + + ip::tcp::endpoint ListenEp(ip::address::from_string("0.0.0.0"), Application::Settings.Port); + ip::tcp::socket Listener(mServer.IoCtx()); + boost::system::error_code ec; + Listener.open(ListenEp.protocol(), ec); + if (ec) { + beammp_errorf("Failed to open socket: {}", ec.what()); + return; } -#endif // WINDOWS - TConnection client {}; - 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."); - Application::GracefullyShutdown(); + socket_base::linger LingerOpt {}; + LingerOpt.enabled(false); + Listener.set_option(LingerOpt, ec); + if (ec) { + beammp_errorf("Failed to set up listening socket to not linger / reuse address. " + "This may cause the socket to refuse to bind(). Error: {}", + ec.what()); } -#if defined(BEAMMP_WINDOWS) - const char optval = 0; - int ret = ::setsockopt(Listener, SOL_SOCKET, SO_DONTLINGER, &optval, sizeof(optval)); -#elif defined(BEAMMP_LINUX) || defined(BEAMMP_APPLE) - int optval = true; - int ret = ::setsockopt(Listener, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&optval), sizeof(optval)); -#endif - // 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, reinterpret_cast(&addr), sizeof(addr)) < 0) { - beammp_error("bind() failed, the server cannot operate and will shut down now. " - "Error: " - + GetPlatformAgnosticErrorString()); - Application::GracefullyShutdown(); - } - if (listen(Listener, SOMAXCONN) < 0) { - beammp_error("listen() failed, which is needed for the server to operate. " - "Shutting down. Error: " - + GetPlatformAgnosticErrorString()); + + ip::tcp::acceptor Acceptor(mServer.IoCtx(), ListenEp); + Acceptor.listen(socket_base::max_listen_connections, ec); + if (ec) { + beammp_errorf("listen() failed, which is needed for the server to operate. " + "Shutting down. Error: {}", + ec.what()); Application::GracefullyShutdown(); } Application::SetSubsystemStatus("TCPNetwork", Application::Status::Good); @@ -149,39 +147,22 @@ void TNetwork::TCPServerMain() { beammp_debug("shutdown during TCP wait for accept loop"); break; } - client.SockAddrLen = sizeof(client.SockAddr); - client.Socket = accept(Listener, &client.SockAddr, &client.SockAddrLen); - if (client.Socket == -1) { - beammp_warn(("Got an invalid client socket on connect! Skipping...")); - continue; + ip::tcp::endpoint ClientEp; + ip::tcp::socket ClientSocket = Acceptor.accept(ClientEp, ec); + if (ec) { + beammp_errorf("failed to accept: {}", ec.what()); } - // set timeout - size_t SendTimeoutMS = 30 * 1000; -#if defined(BEAMMP_WINDOWS) - int ret = ::setsockopt(client.Socket, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast(&SendTimeoutMS), sizeof(SendTimeoutMS)); -#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()); + ClientSocket.set_option(boost::asio::detail::socket_option::integer { 30 * 1000 }, ec); + if (!ec) { + beammp_errorf("failed to set send timeout on client socket: {}", ec.what()); } - std::thread ID(&TNetwork::Identify, this, client); + TConnection Conn { std::move(ClientSocket), ClientEp }; + std::thread ID(&TNetwork::Identify, this, std::move(Conn)); ID.detach(); // TODO: Add to a queue and attempt to join periodically } catch (const std::exception& e) { beammp_error("fatal: " + std::string(e.what())); } - } while (client.Socket != BEAMMP_INVALID_SOCKET); - - beammp_debug("all ok, arrived at " + std::string(__func__) + ":" + std::to_string(__LINE__)); - - CloseSocketProper(client.Socket); -#ifdef BEAMMP_WINDOWS - CloseSocketProper(client.Socket); - WSACleanup(); -#endif // WINDOWS + } while (!Application::IsShuttingDown()); } #undef GetObject // Fixes Windows @@ -189,34 +170,38 @@ void TNetwork::TCPServerMain() { #include "Json.h" namespace json = rapidjson; -void TNetwork::Identify(const TConnection& client) { +void TNetwork::Identify(TConnection&& RawConnection) { RegisterThreadAuto(); char Code; - if (recv(client.Socket, &Code, 1, 0) != 1) { - CloseSocketProper(client.Socket); + + boost::system::error_code ec; + read(RawConnection.Socket, buffer(&Code, 1), ec); + if (ec) { + // TODO: is this right?! + RawConnection.Socket.shutdown(socket_base::shutdown_both); return; } + std::shared_ptr Client { nullptr }; if (Code == 'C') { - Authentication(client); + Client = Authentication(std::move(RawConnection)); } else if (Code == 'D') { - HandleDownload(client.Socket); + HandleDownload(std::move(RawConnection)); } else if (Code == 'P') { -#if defined(BEAMMP_LINUX) || defined(BEAMMP_APPLE) - send(client.Socket, "P", 1, MSG_NOSIGNAL); -#else - send(client.Socket, "P", 1, 0); -#endif - CloseSocketProper(client.Socket); + boost::system::error_code ec; + write(RawConnection.Socket, buffer("P"), ec); return; } else { - CloseSocketProper(client.Socket); + beammp_errorf("Invalid code got in Identify: '{}'", Code); } } -void TNetwork::HandleDownload(SOCKET TCPSock) { +void TNetwork::HandleDownload(TConnection&& Conn) { char D; - if (recv(TCPSock, &D, 1, 0) != 1) { - CloseSocketProper(TCPSock); + boost::system::error_code ec; + read(Conn.Socket, buffer(&D, 1), ec); + if (ec) { + Conn.Socket.shutdown(socket_base::shutdown_both, ec); + // ignore ec return; } auto ID = uint8_t(D); @@ -225,110 +210,78 @@ void TNetwork::HandleDownload(SOCKET TCPSock) { if (!ClientPtr.expired()) { auto c = ClientPtr.lock(); if (c->GetID() == ID) { - c->SetDownSock(TCPSock); + c->SetDownSock(std::move(Conn.Socket)); } } return true; }); } -static int get_ip_str(const struct sockaddr* sa, char* strBuf, socklen_t strBufSize) { - switch (sa->sa_family) { - case AF_INET: - inet_ntop(AF_INET, &reinterpret_cast(sa)->sin_addr, strBuf, strBufSize); - break; - case AF_INET6: - inet_ntop(AF_INET6, &reinterpret_cast(sa)->sin6_addr, strBuf, strBufSize); - break; - default: - return 1; - } - return 0; -} +std::shared_ptr TNetwork::Authentication(TConnection&& RawConnection) { + auto Client = CreateClient(std::move(RawConnection.Socket)); + Client->SetIdentifier("ip", RawConnection.SockAddr.address().to_string()); + beammp_tracef("This thread is ip {}", RawConnection.SockAddr.address().to_string()); -void TNetwork::Authentication(const TConnection& ClientConnection) { - auto Client = CreateClient(ClientConnection.Socket); - char AddrBuf[INET6_ADDRSTRLEN]; - get_ip_str(&ClientConnection.SockAddr, AddrBuf, sizeof(AddrBuf)); - beammp_trace("This thread is ip " + std::string(AddrBuf)); - Client->SetIdentifier("ip", AddrBuf); - - std::string Rc; // TODO: figure out why this is not default constructed beammp_info("Identifying new ClientConnection..."); - Rc = TCPRcv(*Client); + auto Data = TCPRcv(*Client); - if (Rc.size() > 3 && Rc.substr(0, 2) == "VC") { - Rc = Rc.substr(2); - if (Rc.length() > 4 || Rc != Application::ClientVersionString()) { + constexpr std::string_view VC = "VC"; + if (Data.size() > 3 && std::equal(Data.begin(), Data.begin() + VC.size(), VC.begin(), VC.end())) { + std::string ClientVersionStr(reinterpret_cast(Data.data() + 2), Data.size() - 2); + Version ClientVersion = Application::VersionStrToInts(ClientVersionStr); + if (ClientVersion.major != Application::ClientMajorVersion()) { + beammp_errorf("Client tried to connect with version '{}', but only versions '{}.x.x' is allowed", + ClientVersion.AsString(), Application::ClientMajorVersion()); ClientKick(*Client, "Outdated Version!"); - return; + return nullptr; } } else { - ClientKick(*Client, "Invalid version header!"); - return; + ClientKick(*Client, fmt::format("Invalid version header: '{}' ({})", std::string(reinterpret_cast(Data.data()), Data.size()), Data.size())); + return nullptr; } - if (!TCPSend(*Client, "S")) { + if (!TCPSend(*Client, StringToVector("S"))) { // TODO: handle } - Rc = TCPRcv(*Client); + Data = TCPRcv(*Client); - if (Rc.size() > 50) { - ClientKick(*Client, "Invalid Key!"); - return; + if (Data.size() > 50) { + ClientKick(*Client, "Invalid Key (too long)!"); + return nullptr; } - auto RequestString = R"({"key":")" + Rc + "\"}"; - + nlohmann::json AuthReq { + { "key", std::string(reinterpret_cast(Data.data()), Data.size()) } + }; auto Target = "/pkToUser"; unsigned int ResponseCode = 0; - if (!Rc.empty()) { - Rc = Http::POST(Application::GetBackendUrlForAuth(), 443, Target, RequestString, "application/json", &ResponseCode); - } + const auto AuthResStr = Http::POST(Application::GetBackendUrlForAuth(), 443, Target, AuthReq.dump(), "application/json", &ResponseCode); - json::Document AuthResponse; - AuthResponse.Parse(Rc.c_str()); - if (Rc == Http::ErrorString || AuthResponse.HasParseError()) { + try { + nlohmann::json AuthRes = nlohmann::json::parse(AuthResStr); + + if (AuthRes["username"].is_string() && AuthRes["roles"].is_string() + && AuthRes["guest"].is_boolean() && AuthRes["identifiers"].is_array()) { + + Client->SetName(AuthRes["username"]); + Client->SetRoles(AuthRes["roles"]); + Client->SetIsGuest(AuthRes["guest"]); + for (const auto& ID : AuthRes["identifier"]) { + auto Raw = std::string(ID); + auto SepIndex = Raw.find(':'); + Client->SetIdentifier(Raw.substr(0, SepIndex), Raw.substr(SepIndex + 1)); + } + } else { + beammp_error("Invalid authentication data received from authentication backend"); + ClientKick(*Client, "Invalid authentication data!"); + return nullptr; + } + } catch (const std::exception& e) { + beammp_errorf("Client sent invalid key: {}", e.what()); + // TODO: we should really clarify that this was a backend response or parsing error ClientKick(*Client, "Invalid key! Please restart your game."); - return; - } - - if (!AuthResponse.IsObject()) { - if (Rc == "0") { - auto Lock = Sentry.CreateExclusiveContext(); - Sentry.SetContext("auth", - { { "response-body", Rc }, - { "key", RequestString } }); - Sentry.SetTransaction(Application::GetBackendUrlForAuth() + Target); - Sentry.Log(SentryLevel::Info, "default", "backend returned 0 instead of json (" + std::to_string(ResponseCode) + ")"); - } else { // Rc != "0" - ClientKick(*Client, "Backend returned invalid auth response format."); - beammp_error("Backend returned invalid auth response format. This should never happen."); - auto Lock = Sentry.CreateExclusiveContext(); - Sentry.SetContext("auth", - { { "response-body", Rc }, - { "key", RequestString } }); - Sentry.SetTransaction(Application::GetBackendUrlForAuth() + Target); - Sentry.Log(SentryLevel::Error, "default", "unexpected backend response (" + std::to_string(ResponseCode) + ")"); - } - return; - } - - if (AuthResponse["username"].IsString() && AuthResponse["roles"].IsString() - && AuthResponse["guest"].IsBool() && AuthResponse["identifiers"].IsArray()) { - - Client->SetName(AuthResponse["username"].GetString()); - Client->SetRoles(AuthResponse["roles"].GetString()); - Client->SetIsGuest(AuthResponse["guest"].GetBool()); - for (const auto& ID : AuthResponse["identifiers"].GetArray()) { - auto Raw = std::string(ID.GetString()); - auto SepIndex = Raw.find(':'); - Client->SetIdentifier(Raw.substr(0, SepIndex), Raw.substr(SepIndex + 1)); - } - } else { - ClientKick(*Client, "Invalid authentication data!"); - return; + return nullptr; } beammp_debug("Name -> " + Client->GetName() + ", Guest -> " + std::to_string(Client->IsGuest()) + ", Roles -> " + Client->GetRoles()); @@ -342,8 +295,7 @@ void TNetwork::Authentication(const TConnection& ClientConnection) { return true; } if (Cl->GetName() == Client->GetName() && Cl->IsGuest() == Client->IsGuest()) { - CloseSocketProper(Cl->GetTCPSock()); - Cl->SetStatus(-2); + Cl->Disconnect("Stale Client (not a real player)"); return false; } @@ -368,27 +320,28 @@ void TNetwork::Authentication(const TConnection& ClientConnection) { if (NotAllowed) { ClientKick(*Client, "you are not allowed on the server!"); - return; + return {}; } else if (NotAllowedWithReason) { ClientKick(*Client, Reason); - return; + return {}; } if (mServer.ClientCount() < size_t(Application::Settings.MaxPlayers)) { beammp_info("Identification success"); mServer.InsertClient(Client); TCPClient(Client); - } else + } else { ClientKick(*Client, "Server full!"); + } + return Client; } -std::shared_ptr TNetwork::CreateClient(SOCKET TCPSock) { - auto c = std::make_shared(mServer); - c->SetTCPSock(TCPSock); +std::shared_ptr TNetwork::CreateClient(ip::tcp::socket&& TCPSock) { + auto c = std::make_shared(mServer, std::move(TCPSock)); return c; } -bool TNetwork::TCPSend(TClient& c, const std::string& Data, bool IsSync) { +bool TNetwork::TCPSend(TClient& c, const std::vector& Data, bool IsSync) { if (!IsSync) { if (c.IsSyncing()) { if (!Data.empty()) { @@ -400,120 +353,101 @@ bool TNetwork::TCPSend(TClient& c, const std::string& Data, bool IsSync) { } } - int32_t Size, Sent; - std::string Send(4, 0); - Size = int32_t(Data.size()); - memcpy(&Send[0], &Size, sizeof(Size)); - Send += Data; - Sent = 0; - Size += 4; - do { -#if defined(BEAMMP_WINDOWS) - int32_t Temp = send(c.GetTCPSock(), &Send[Sent], Size - Sent, 0); -#elif defined(BEAMMP_LINUX) || defined(BEAMMP_APPLE) - int32_t Temp = send(c.GetTCPSock(), &Send[Sent], Size - Sent, MSG_NOSIGNAL); -#endif - if (Temp == 0) { - beammp_debug("send() == 0: " + GetPlatformAgnosticErrorString()); - if (c.GetStatus() > -1) - c.SetStatus(-1); - return false; - } else if (Temp < 0) { - beammp_debug("send() < 0: " + GetPlatformAgnosticErrorString()); // TODO fix it was spamming yet everyone stayed on the server - if (c.GetStatus() > -1) - c.SetStatus(-1); - CloseSocketProper(c.GetTCPSock()); - return false; - } - Sent += Temp; - c.UpdatePingTime(); - } while (Sent < Size); + auto& Sock = c.GetTCPSock(); + + /* + * our TCP protocol sends a header of 4 bytes, followed by the data. + * + * [][][][][][]...[] + * ^------^^---...-^ + * size data + */ + + const auto Size = int32_t(Data.size()); + std::vector ToSend; + ToSend.resize(Data.size() + sizeof(Size)); + std::memcpy(ToSend.data(), &Size, sizeof(Size)); + std::memcpy(ToSend.data() + sizeof(Size), Data.data(), Data.size()); + boost::system::error_code ec; + write(Sock, buffer(ToSend), ec); + if (ec) { + beammp_debugf("write(): {}", ec.what()); + c.Disconnect("write() failed"); + return false; + } + c.UpdatePingTime(); return true; } -bool TNetwork::CheckBytes(TClient& c, int32_t BytesRcv) { - if (BytesRcv == 0) { - beammp_trace("(TCP) Connection closing..."); - if (c.GetStatus() > -1) - c.SetStatus(-1); - return false; - } else if (BytesRcv < 0) { - beammp_debug("(TCP) recv() failed: " + GetPlatformAgnosticErrorString()); - if (c.GetStatus() > -1) - c.SetStatus(-1); - CloseSocketProper(c.GetTCPSock()); - return false; +std::vector TNetwork::TCPRcv(TClient& c) { + if (c.IsDisconnected()) { + beammp_error("Client disconnected, cancelling TCPRcv"); + return {}; } - return true; -} -std::string TNetwork::TCPRcv(TClient& c) { - int32_t Header {}, BytesRcv = 0, Temp {}; - if (c.GetStatus() < 0) - return ""; + int32_t Header {}; + auto& Sock = c.GetTCPSock(); - std::vector Data(sizeof(Header)); - do { - Temp = recv(c.GetTCPSock(), &Data[BytesRcv], 4 - BytesRcv, 0); - if (!CheckBytes(c, Temp)) { - return ""; - } - BytesRcv += Temp; - } while (size_t(BytesRcv) < sizeof(Header)); - memcpy(&Header, &Data[0], sizeof(Header)); - - if (!CheckBytes(c, BytesRcv)) { - return ""; + boost::system::error_code ec; + std::array HeaderData; + read(Sock, buffer(HeaderData), ec); + if (ec) { + // TODO: handle this case (read failed) + beammp_debugf("TCPRcv: Reading header failed: {}", ec.what()); + return {}; } + Header = *reinterpret_cast(HeaderData.data()); + beammp_tracef("Expecting to read {} bytes", Header); + + std::vector Data; + // TODO: This is arbitrary, this needs to be handled another way if (Header < int32_t(100 * MB)) { Data.resize(Header); } else { ClientKick(c, "Header size limit exceeded"); beammp_warn("Client " + c.GetName() + " (" + std::to_string(c.GetID()) + ") sent header of >100MB - assuming malicious intent and disconnecting the client."); - return ""; + return {}; + } + auto N = read(Sock, buffer(Data), ec); + if (ec) { + // TODO: handle this case properly + beammp_debugf("TCPRcv: Reading data failed: {}", ec.what()); + return {}; } - BytesRcv = 0; - do { - Temp = recv(c.GetTCPSock(), &Data[BytesRcv], Header - BytesRcv, 0); - if (!CheckBytes(c, Temp)) { - return ""; - } - BytesRcv += Temp; - } while (BytesRcv < Header); - std::string Ret(Data.data(), Header); - if (Ret.substr(0, 4) == "ABG:") { - Ret = DeComp(Ret.substr(4)); + if (N != Header) { + beammp_errorf("Expected to read {} bytes, instead got {}", Header, N); + } + + constexpr std::string_view ABG = "ABG:"; + if (Data.size() >= ABG.size() && std::equal(Data.begin(), Data.begin() + ABG.size(), ABG.begin(), ABG.end())) { + Data.erase(Data.begin(), Data.begin() + ABG.size()); + return DeComp(Data); + } else { + return Data; } - return Ret; } void TNetwork::ClientKick(TClient& c, const std::string& R) { beammp_info("Client kicked: " + R); - if (!TCPSend(c, "K" + R)) { + if (!TCPSend(c, StringToVector("K" + R))) { beammp_warn("tried to kick player '" + c.GetName() + "' (id " + std::to_string(c.GetID()) + "), but was already disconnected"); } - c.SetStatus(-2); - - if (c.GetTCPSock()) - CloseSocketProper(c.GetTCPSock()); - - if (c.GetDownSock()) - CloseSocketProper(c.GetDownSock()); + c.Disconnect("Kicked"); } void TNetwork::Looper(const std::weak_ptr& c) { RegisterThreadAuto(); while (!c.expired()) { auto Client = c.lock(); - if (Client->GetStatus() < 0) { - beammp_debug("client status < 0, breaking client loop"); + if (Client->IsDisconnected()) { + beammp_debug("client is disconnected, breaking client loop"); break; } if (!Client->IsSyncing() && Client->IsSynced() && Client->MissedPacketQueueSize() != 0) { // debug("sending " + std::to_string(Client->MissedPacketQueueSize()) + " queued packets"); while (Client->MissedPacketQueueSize() > 0) { - std::string QData {}; + std::vector QData {}; { // locked context std::unique_lock lock(Client->MissedPacketQueueMutex()); if (Client->MissedPacketQueueSize() <= 0) { @@ -524,15 +458,15 @@ void TNetwork::Looper(const std::weak_ptr& c) { } // end locked context // beammp_debug("sending a missed packet: " + QData); if (!TCPSend(*Client, QData, true)) { - if (Client->GetStatus() > -1) - Client->SetStatus(-1); + if (!Client->IsDisconnected()) + Client->Disconnect("Failed to TCPSend while clearing the missed packet queue"); { std::unique_lock lock(Client->MissedPacketQueueMutex()); while (!Client->MissedPacketQueue().empty()) { Client->MissedPacketQueue().pop(); } } - CloseSocketProper(Client->GetTCPSock()); + Client->Disconnect("WHY THE FUCK NOT"); break; } } @@ -544,7 +478,7 @@ void TNetwork::Looper(const std::weak_ptr& c) { void TNetwork::TCPClient(const std::weak_ptr& c) { // TODO: the c.expired() might cause issues here, remove if you end up here with your debugger - if (c.expired() || c.lock()->GetTCPSock() == -1) { + if (c.expired() || !c.lock()->GetTCPSock().is_open()) { mServer.RemoveClient(c); return; } @@ -557,24 +491,23 @@ void TNetwork::TCPClient(const std::weak_ptr& c) { if (c.expired()) break; auto Client = c.lock(); - if (Client->GetStatus() < 0) { + if (Client->IsDisconnected()) { beammp_debug("client status < 0, breaking client loop"); break; } auto res = TCPRcv(*Client); - if (res == "") { - beammp_debug("TCPRcv error, break client loop"); - break; + if (res.empty()) { + beammp_debug("TCPRcv empty, ignoring"); } - TServer::GlobalParser(c, res, mPPSMonitor, *this); + TServer::GlobalParser(c, std::move(res), mPPSMonitor, *this); } if (QueueSync.joinable()) QueueSync.join(); if (!c.expired()) { auto Client = c.lock(); - OnDisconnect(c, Client->GetStatus() == -2); + OnDisconnect(c); } else { beammp_warn("client expired in TCPClient, should never happen"); } @@ -591,11 +524,11 @@ void TNetwork::UpdatePlayer(TClient& Client) { return true; }); Packet = Packet.substr(0, Packet.length() - 1); - Client.EnqueuePacket(Packet); + Client.EnqueuePacket(StringToVector(Packet)); //(void)Respond(Client, Packet, true); } -void TNetwork::OnDisconnect(const std::weak_ptr& ClientPtr, bool kicked) { +void TNetwork::OnDisconnect(const std::weak_ptr& ClientPtr) { beammp_assert(!ClientPtr.expired()); auto LockedClientPtr = ClientPtr.lock(); TClient& c = *LockedClientPtr; @@ -608,20 +541,14 @@ void TNetwork::OnDisconnect(const std::weak_ptr& ClientPtr, bool kicked } // End Vehicle Data Lock Scope for (auto& v : VehicleData) { Packet = "Od:" + std::to_string(c.GetID()) + "-" + std::to_string(v.ID()); - SendToAll(&c, Packet, false, true); + SendToAll(&c, StringToVector(Packet), false, true); } - if (kicked) - Packet = ("L") + c.GetName() + (" was kicked!"); - else - Packet = ("L") + c.GetName() + (" left the server!"); - SendToAll(&c, Packet, false, true); + Packet = ("L") + c.GetName() + (" left the server!"); + SendToAll(&c, StringToVector(Packet), false, true); Packet.clear(); auto Futures = LuaAPI::MP::Engine->TriggerEvent("onPlayerDisconnect", "", c.GetID()); LuaAPI::MP::Engine->ReportErrors(Futures); - if (c.GetTCPSock()) - CloseSocketProper(c.GetTCPSock()); - if (c.GetDownSock()) - CloseSocketProper(c.GetDownSock()); + c.Disconnect("Already Disconnected (OnDisconnect)"); mServer.RemoveClient(ClientPtr); } @@ -653,44 +580,39 @@ void TNetwork::OnConnect(const std::weak_ptr& c) { beammp_info("Assigned ID " + std::to_string(LockedClient->GetID()) + " to " + LockedClient->GetName()); LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onPlayerConnecting", "", LockedClient->GetID())); SyncResources(*LockedClient); - if (LockedClient->GetStatus() < 0) + if (LockedClient->IsDisconnected()) return; - (void)Respond(*LockedClient, "M" + Application::Settings.MapName, true); // Send the Map on connect + (void)Respond(*LockedClient, StringToVector("M" + Application::Settings.MapName), true); // Send the Map on connect beammp_info(LockedClient->GetName() + " : Connected"); LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onPlayerJoining", "", LockedClient->GetID())); } void TNetwork::SyncResources(TClient& c) { -#ifndef DEBUG - try { -#endif - if (!TCPSend(c, "P" + std::to_string(c.GetID()))) { - // TODO handle - } - std::string Data; - while (c.GetStatus() > -1) { - Data = TCPRcv(c); - if (Data == "Done") - break; - Parse(c, Data); - } -#ifndef DEBUG - } catch (std::exception& e) { - beammp_error("Exception! : " + std::string(e.what())); - c.SetStatus(-1); + if (!TCPSend(c, StringToVector("P" + std::to_string(c.GetID())))) { + // TODO handle + } + std::vector Data; + while (!c.IsDisconnected()) { + Data = TCPRcv(c); + if (Data.empty()) { + break; + } + constexpr std::string_view Done = "Done"; + if (std::equal(Data.begin(), Data.end(), Done.begin(), Done.end())) + break; + Parse(c, Data); } -#endif } -void TNetwork::Parse(TClient& c, const std::string& Packet) { +void TNetwork::Parse(TClient& c, const std::vector& Packet) { if (Packet.empty()) return; char Code = Packet.at(0), SubCode = 0; - if (Packet.length() > 1) + if (Packet.size() > 1) SubCode = Packet.at(1); switch (Code) { case 'f': - SendFile(c, Packet.substr(1)); + SendFile(c, std::string(reinterpret_cast(Packet.data() + 1), Packet.size() - 1)); return; case 'S': if (SubCode == 'R') { @@ -698,7 +620,7 @@ void TNetwork::Parse(TClient& c, const std::string& Packet) { std::string ToSend = mResourceManager.FileList() + mResourceManager.FileSizes(); if (ToSend.empty()) ToSend = "-"; - if (!TCPSend(c, ToSend)) { + if (!TCPSend(c, StringToVector(ToSend))) { // TODO: error } } @@ -712,7 +634,7 @@ void TNetwork::SendFile(TClient& c, const std::string& UnsafeName) { beammp_info(c.GetName() + " requesting : " + UnsafeName.substr(UnsafeName.find_last_of('/'))); if (!fs::path(UnsafeName).has_filename()) { - if (!TCPSend(c, "CO")) { + if (!TCPSend(c, StringToVector("CO"))) { // TODO: handle } beammp_warn("File " + UnsafeName + " is not a file!"); @@ -722,28 +644,28 @@ void TNetwork::SendFile(TClient& c, const std::string& UnsafeName) { FileName = Application::Settings.Resource + "/Client/" + FileName; if (!std::filesystem::exists(FileName)) { - if (!TCPSend(c, "CO")) { + if (!TCPSend(c, StringToVector("CO"))) { // TODO: handle } beammp_warn("File " + UnsafeName + " could not be accessed!"); return; } - if (!TCPSend(c, "AG")) { + if (!TCPSend(c, StringToVector("AG"))) { // TODO: handle } /// Wait for connections int T = 0; - while (c.GetDownSock() < 1 && T < 50) { + while (!c.GetDownSock().is_open() && T < 50) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); T++; } - if (c.GetDownSock() < 1) { + if (!c.GetDownSock().is_open()) { beammp_error("Client doesn't have a download socket!"); - if (c.GetStatus() > -1) - c.SetStatus(-1); + if (!c.IsDisconnected()) + c.Disconnect("Missing download socket"); return; } @@ -807,8 +729,8 @@ TEST_CASE("SplitIntoChunks") { CHECK((Count * ChunkSize) + LastSize == FullSize); } -uint8_t* /* end ptr */ TNetwork::SendSplit(TClient& c, SOCKET Socket, uint8_t* DataPtr, size_t Size) { - if (TCPSendRaw(c, Socket, reinterpret_cast(DataPtr), Size)) { +const uint8_t* /* end ptr */ TNetwork::SendSplit(TClient& c, ip::tcp::socket& Socket, const uint8_t* DataPtr, size_t Size) { + if (TCPSendRaw(c, Socket, DataPtr, Size)) { return DataPtr + Size; } else { return nullptr; @@ -823,29 +745,28 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std Data.resize(Split); else Data.resize(Size); - SOCKET TCPSock; + ip::tcp::socket* TCPSock { nullptr }; if (D) - TCPSock = c.GetDownSock(); + TCPSock = &c.GetDownSock(); else - TCPSock = c.GetTCPSock(); - beammp_debug("Split load Socket " + std::to_string(TCPSock)); - while (c.GetStatus() > -1 && Sent < Size) { + TCPSock = &c.GetTCPSock(); + while (!c.IsDisconnected() && Sent < Size) { size_t Diff = Size - Sent; if (Diff > Split) { f.seekg(Sent, std::ios_base::beg); f.read(reinterpret_cast(Data.data()), Split); - if (!TCPSendRaw(c, TCPSock, reinterpret_cast(Data.data()), Split)) { - if (c.GetStatus() > -1) - c.SetStatus(-1); + if (!TCPSendRaw(c, *TCPSock, Data.data(), Split)) { + if (!c.IsDisconnected()) + c.Disconnect("TCPSendRaw failed in mod download (1)"); break; } Sent += Split; } else { f.seekg(Sent, std::ios_base::beg); f.read(reinterpret_cast(Data.data()), Diff); - if (!TCPSendRaw(c, TCPSock, reinterpret_cast(Data.data()), int32_t(Diff))) { - if (c.GetStatus() > -1) - c.SetStatus(-1); + if (!TCPSendRaw(c, *TCPSock, Data.data(), int32_t(Diff))) { + if (!c.IsDisconnected()) + c.Disconnect("TCPSendRaw failed in mod download (2)"); break; } Sent += Diff; @@ -853,37 +774,28 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std } } -bool TNetwork::TCPSendRaw(TClient& C, SOCKET socket, char* Data, int32_t Size) { - intmax_t Sent = 0; - do { -#if defined(BEAMMP_LINUX) || defined(BEAMMP_APPLE) - intmax_t Temp = send(socket, &Data[Sent], int(Size - Sent), MSG_NOSIGNAL); -#else - intmax_t Temp = send(socket, &Data[Sent], int(Size - Sent), 0); -#endif - if (Temp < 1) { - beammp_info("Socket Closed! " + std::to_string(socket)); - CloseSocketProper(socket); - return false; - } - Sent += Temp; - C.UpdatePingTime(); - } while (Sent < Size); +bool TNetwork::TCPSendRaw(TClient& C, ip::tcp::socket& socket, const uint8_t* Data, size_t Size) { + boost::system::error_code ec; + write(socket, buffer(Data, Size), ec); + if (ec) { + beammp_errorf("Failed to send raw data to client: {}", ec.what()); + return false; + } + C.UpdatePingTime(); return true; } -bool TNetwork::SendLarge(TClient& c, std::string Data, bool isSync) { - if (Data.length() > 400) { - std::string CMP(Comp(Data)); - Data = "ABG:" + CMP; +bool TNetwork::SendLarge(TClient& c, std::vector Data, bool isSync) { + if (Data.size() > 400) { + CompressProperly(Data); } return TCPSend(c, Data, isSync); } -bool TNetwork::Respond(TClient& c, const std::string& MSG, bool Rel, bool isSync) { +bool TNetwork::Respond(TClient& c, const std::vector& MSG, bool Rel, bool isSync) { char C = MSG.at(0); if (Rel || C == 'W' || C == 'Y' || C == 'V' || C == 'E') { - if (C == 'O' || C == 'T' || MSG.length() > 1000) { + if (C == 'O' || C == 'T' || MSG.size() > 1000) { return SendLarge(c, MSG, isSync); } else { return TCPSend(c, MSG, isSync); @@ -902,11 +814,11 @@ bool TNetwork::SyncClient(const std::weak_ptr& c) { return true; // Syncing, later set isSynced // after syncing is done, we apply all packets they missed - if (!Respond(*LockedClient, ("Sn") + LockedClient->GetName(), true)) { + if (!Respond(*LockedClient, StringToVector("Sn" + LockedClient->GetName()), true)) { return false; } // ignore error - (void)SendToAll(LockedClient.get(), ("JWelcome ") + LockedClient->GetName() + "!", false, true); + (void)SendToAll(LockedClient.get(), StringToVector("JWelcome " + LockedClient->GetName() + "!"), false, true); LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onPlayerJoin", "", LockedClient->GetID())); LockedClient->SetIsSyncing(true); @@ -928,12 +840,12 @@ bool TNetwork::SyncClient(const std::weak_ptr& c) { } // End Vehicle Data Lock Scope if (client != LockedClient) { for (auto& v : VehicleData) { - if (LockedClient->GetStatus() < 0) { + if (LockedClient->IsDisconnected()) { Return = true; res = false; return false; } - res = Respond(*LockedClient, v.Data(), true, true); + res = Respond(*LockedClient, StringToVector(v.Data()), true, true); } } @@ -948,7 +860,7 @@ bool TNetwork::SyncClient(const std::weak_ptr& c) { return true; } -void TNetwork::SendToAll(TClient* c, const std::string& Data, bool Self, bool Rel) { +void TNetwork::SendToAll(TClient* c, const std::vector& Data, bool Self, bool Rel) { if (!Self) beammp_assert(c); char C = Data.at(0); @@ -965,10 +877,11 @@ void TNetwork::SendToAll(TClient* c, const std::string& Data, bool Self, bool Re if (Self || Client.get() != c) { if (Client->IsSynced() || Client->IsSyncing()) { if (Rel || C == 'W' || C == 'Y' || C == 'V' || C == 'E') { - if (C == 'O' || C == 'T' || Data.length() > 1000) { - if (Data.length() > 400) { - std::string CMP(Comp(Data)); - Client->EnqueuePacket("ABG:" + CMP); + if (C == 'O' || C == 'T' || Data.size() > 1000) { + if (Data.size() > 400) { + auto CompressedData = Data; + CompressProperly(CompressedData); + Client->EnqueuePacket(CompressedData); } else { Client->EnqueuePacket(Data); } @@ -990,8 +903,8 @@ void TNetwork::SendToAll(TClient* c, const std::string& Data, bool Self, bool Re return; } -bool TNetwork::UDPSend(TClient& Client, std::string Data) { - if (!Client.IsConnected() || Client.GetStatus() < 0) { +bool TNetwork::UDPSend(TClient& Client, std::vector Data) { + if (!Client.IsConnected() || Client.IsDisconnected()) { // this can happen if we try to send a packet to a client that is either // 1. not yet fully connected, or // 2. disconnected and not yet fully removed @@ -999,22 +912,21 @@ bool TNetwork::UDPSend(TClient& Client, std::string Data) { return true; } const auto Addr = Client.GetUDPAddr(); - if (Data.length() > 400) { - std::string CMP(Comp(Data)); - Data = "ABG:" + CMP; + if (Data.size() > 400) { + CompressProperly(Data); } boost::system::error_code ec; mUDPSock.send_to(buffer(Data), Addr, 0, ec); if (ec) { beammp_debugf("UDP sendto() failed: {}", ec.what()); - if (Client.GetStatus() > -1) - Client.SetStatus(-1); + if (!Client.IsDisconnected()) + Client.Disconnect("UDP send failed"); return false; } return true; } -std::string TNetwork::UDPRcvFromClient(ip::udp::endpoint& ClientEndpoint) { +std::vector TNetwork::UDPRcvFromClient(ip::udp::endpoint& ClientEndpoint) { std::array Ret {}; boost::system::error_code ec; beammp_debugf("receiving data from {}:{}", ClientEndpoint.address().to_string(), ClientEndpoint.port()); @@ -1022,8 +934,8 @@ std::string TNetwork::UDPRcvFromClient(ip::udp::endpoint& ClientEndpoint) { beammp_debugf("received {} bytes from {}:{}", Rcv, ClientEndpoint.address().to_string(), ClientEndpoint.port()); if (ec) { beammp_errorf("UDP recvfrom() failed: {}", ec.what()); - return ""; + return {}; } // FIXME: This breaks binary data due to \0. - return std::string(Ret.begin(), Ret.begin() + Rcv); + return std::vector(Ret.begin(), Ret.end()); } diff --git a/src/TServer.cpp b/src/TServer.cpp index 8cba141..690ec50 100644 --- a/src/TServer.cpp +++ b/src/TServer.cpp @@ -4,6 +4,7 @@ #include "TNetwork.h" #include "TPPSMonitor.h" #include +#include #include #include @@ -102,13 +103,6 @@ void TServer::RemoveClient(const std::weak_ptr& WeakClientPtr) { } } -std::weak_ptr TServer::InsertNewClient() { - beammp_debug("inserting new client (" + std::to_string(ClientCount()) + ")"); - WriteLock Lock(mClientsMutex); - auto [Iter, Replaced] = mClients.insert(std::make_shared(*this)); - return *Iter; -} - void TServer::ForEachClient(const std::function)>& Fn) { decltype(mClients) Clients; { @@ -127,12 +121,11 @@ size_t TServer::ClientCount() const { return mClients.size(); } -void TServer::GlobalParser(const std::weak_ptr& Client, std::string Packet, TPPSMonitor& PPSMonitor, TNetwork& Network) { - if (Packet.find("Zp") != std::string::npos && Packet.size() > 500) { - // abort(); - } - if (Packet.substr(0, 4) == "ABG:") { - Packet = DeComp(Packet.substr(4)); +void TServer::GlobalParser(const std::weak_ptr& Client, std::vector&& Packet, TPPSMonitor& PPSMonitor, TNetwork& Network) { + constexpr std::string_view ABG = "ABG:"; + if (Packet.size() >= ABG.size() && std::equal(Packet.begin(), Packet.begin() + ABG.size(), ABG.begin(), ABG.end())) { + Packet.erase(Packet.begin(), Packet.begin() + ABG.size()); + Packet = DeComp(Packet); } if (Packet.empty()) { return; @@ -146,6 +139,8 @@ void TServer::GlobalParser(const std::weak_ptr& Client, std::string Pac std::any Res; char Code = Packet.at(0); + std::string StringPacket(reinterpret_cast(Packet.data()), Packet.size()); + // V to Y if (Code <= 89 && Code >= 86) { PPSMonitor.IncrementInternalPPS(); @@ -154,38 +149,34 @@ void TServer::GlobalParser(const std::weak_ptr& Client, std::string Pac } switch (Code) { case 'H': // initial connection - beammp_trace(std::string("got 'H' packet: '") + Packet + "' (" + std::to_string(Packet.size()) + ")"); if (!Network.SyncClient(Client)) { // TODO handle } return; case 'p': - if (!Network.Respond(*LockedClient, ("p"), false)) { + if (!Network.Respond(*LockedClient, StringToVector("p"), false)) { // failed to send - if (LockedClient->GetStatus() > -1) { - LockedClient->SetStatus(-1); - } + LockedClient->Disconnect("Failed to send ping"); } else { Network.UpdatePlayer(*LockedClient); } return; case 'O': - if (Packet.length() > 1000) { - beammp_debug(("Received data from: ") + LockedClient->GetName() + (" Size: ") + std::to_string(Packet.length())); + if (Packet.size() > 1000) { + beammp_debug(("Received data from: ") + LockedClient->GetName() + (" Size: ") + std::to_string(Packet.size())); } - ParseVehicle(*LockedClient, Packet, Network); + ParseVehicle(*LockedClient, StringPacket, Network); return; case 'J': - beammp_trace(std::string(("got 'J' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")")); Network.SendToAll(LockedClient.get(), Packet, false, true); return; case 'C': { - beammp_trace(std::string(("got 'C' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")")); - if (Packet.length() < 4 || Packet.find(':', 3) == std::string::npos) + if (Packet.size() < 4 || std::find(Packet.begin() + 3, Packet.end(), ':') == Packet.end()) break; - auto Futures = LuaAPI::MP::Engine->TriggerEvent("onChatMessage", "", LockedClient->GetID(), LockedClient->GetName(), Packet.substr(Packet.find(':', 3) + 2)); + const auto PacketAsString = std::string(reinterpret_cast(Packet.data()), Packet.size()); + auto Futures = LuaAPI::MP::Engine->TriggerEvent("onChatMessage", "", LockedClient->GetID(), LockedClient->GetName(), PacketAsString.substr(PacketAsString.find(':', 3) + 2)); TLuaEngine::WaitForAll(Futures); - LogChatMessage(LockedClient->GetName(), LockedClient->GetID(), Packet.substr(Packet.find(':', 3) + 1)); + LogChatMessage(LockedClient->GetName(), LockedClient->GetID(), PacketAsString.substr(PacketAsString.find(':', 3) + 1)); if (std::any_of(Futures.begin(), Futures.end(), [](const std::shared_ptr& Elem) { return !Elem->Error @@ -198,8 +189,7 @@ void TServer::GlobalParser(const std::weak_ptr& Client, std::string Pac return; } case 'E': - beammp_trace(std::string(("got 'E' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")")); - HandleEvent(*LockedClient, Packet); + HandleEvent(*LockedClient, StringPacket); return; case 'N': beammp_trace("got 'N' packet (" + std::to_string(Packet.size()) + ")"); @@ -209,7 +199,7 @@ void TServer::GlobalParser(const std::weak_ptr& Client, std::string Pac PPSMonitor.IncrementInternalPPS(); Network.SendToAll(LockedClient.get(), Packet, false, false); - HandlePosition(*LockedClient, Packet); + HandlePosition(*LockedClient, StringPacket); default: return; } @@ -275,13 +265,13 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ if (ShouldSpawn(c, CarJson, CarID) && !ShouldntSpawn) { c.AddNewCar(CarID, Packet); - Network.SendToAll(nullptr, Packet, true, true); + Network.SendToAll(nullptr, StringToVector(Packet), true, true); } else { - if (!Network.Respond(c, Packet, true)) { + if (!Network.Respond(c, StringToVector(Packet), true)) { // TODO: handle } std::string Destroy = "Od:" + std::to_string(c.GetID()) + "-" + std::to_string(CarID); - if (!Network.Respond(c, Destroy, true)) { + if (!Network.Respond(c, StringToVector(Destroy), true)) { // TODO: handle } beammp_debugf("{} (force : car limit/lua) removed ID {}", c.GetName(), CarID); @@ -306,14 +296,14 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ FoundPos = FoundPos == std::string::npos ? 0 : FoundPos; // attempt at sanitizing this if ((c.GetUnicycleID() != VID || IsUnicycle(c, Packet.substr(FoundPos))) && !ShouldntAllow) { - Network.SendToAll(&c, Packet, false, true); + Network.SendToAll(&c, StringToVector(Packet), false, true); Apply(c, VID, Packet); } else { if (c.GetUnicycleID() == VID) { c.SetUnicycleID(-1); } std::string Destroy = "Od:" + std::to_string(c.GetID()) + "-" + std::to_string(VID); - Network.SendToAll(nullptr, Destroy, true, true); + Network.SendToAll(nullptr, StringToVector(Destroy), true, true); c.DeleteCar(VID); } } @@ -329,7 +319,7 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ if (c.GetUnicycleID() == VID) { c.SetUnicycleID(-1); } - Network.SendToAll(nullptr, Packet, true, true); + Network.SendToAll(nullptr, StringToVector(Packet), true, true); // TODO: should this trigger on all vehicle deletions? LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onVehicleDeleted", "", c.GetID(), VID)); c.DeleteCar(VID); @@ -347,16 +337,16 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ if (PID != -1 && VID != -1 && PID == c.GetID()) { Data = Data.substr(Data.find('{')); LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onVehicleReset", "", c.GetID(), VID, Data)); - Network.SendToAll(&c, Packet, false, true); + Network.SendToAll(&c, StringToVector(Packet), false, true); } return; } case 't': beammp_trace(std::string(("got 'Ot' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")")); - Network.SendToAll(&c, Packet, false, true); + Network.SendToAll(&c, StringToVector(Packet), false, true); return; case 'm': - Network.SendToAll(&c, Packet, true, true); + Network.SendToAll(&c, StringToVector(Packet), true, true); return; default: beammp_trace(std::string(("possibly not implemented: '") + Packet + ("' (") + std::to_string(Packet.size()) + (")"))); From 231b13a0e760ae24a85698c9d5ae409d1a9830a7 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 5 Oct 2022 18:11:27 +0200 Subject: [PATCH 145/184] fix a ghost client bug --- include/Compat.h | 31 ------------------------------- src/TNetwork.cpp | 8 ++++---- 2 files changed, 4 insertions(+), 35 deletions(-) diff --git a/include/Compat.h b/include/Compat.h index 1a3cc63..fd4de59 100644 --- a/include/Compat.h +++ b/include/Compat.h @@ -5,54 +5,23 @@ // ======================= UNIX ======================== #ifdef BEAMMP_LINUX -#include #include -#include #include #include -using SOCKET = int; -using DWORD = unsigned long; -using PDWORD = unsigned long*; -using LPDWORD = unsigned long*; char _getch(); -inline void CloseSocketProper(int TheSocket) { - shutdown(TheSocket, SHUT_RDWR); - close(TheSocket); -} #endif // unix // ======================= APPLE ======================== #ifdef BEAMMP_APPLE -#include #include -#include #include #include -using SOCKET = int; -using DWORD = unsigned long; -using PDWORD = unsigned long*; -using LPDWORD = unsigned long*; char _getch(); -inline void CloseSocketProper(int TheSocket) { - shutdown(TheSocket, SHUT_RDWR); - close(TheSocket); -} #endif // unix // ======================= WINDOWS ======================= #ifdef BEAMMP_WINDOWS #include -#include -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/TNetwork.cpp b/src/TNetwork.cpp index 35ace21..8a2ec52 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -397,7 +397,6 @@ std::vector TNetwork::TCPRcv(TClient& c) { return {}; } Header = *reinterpret_cast(HeaderData.data()); - beammp_tracef("Expecting to read {} bytes", Header); std::vector Data; // TODO: This is arbitrary, this needs to be handled another way @@ -498,10 +497,13 @@ void TNetwork::TCPClient(const std::weak_ptr& c) { auto res = TCPRcv(*Client); if (res.empty()) { - beammp_debug("TCPRcv empty, ignoring"); + beammp_debug("TCPRcv empty"); + Client->Disconnect("TCPRcv failed"); + break; } TServer::GlobalParser(c, std::move(res), mPPSMonitor, *this); } + if (QueueSync.joinable()) QueueSync.join(); @@ -929,9 +931,7 @@ bool TNetwork::UDPSend(TClient& Client, std::vector Data) { std::vector TNetwork::UDPRcvFromClient(ip::udp::endpoint& ClientEndpoint) { std::array Ret {}; boost::system::error_code ec; - beammp_debugf("receiving data from {}:{}", ClientEndpoint.address().to_string(), ClientEndpoint.port()); const auto Rcv = mUDPSock.receive_from(mutable_buffer(Ret.data(), Ret.size()), ClientEndpoint, 0, ec); - beammp_debugf("received {} bytes from {}:{}", Rcv, ClientEndpoint.address().to_string(), ClientEndpoint.port()); if (ec) { beammp_errorf("UDP recvfrom() failed: {}", ec.what()); return {}; From 6249397fb5a92be89f1c8864b3fec2cc533a0eca Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 5 Oct 2022 20:50:28 +0200 Subject: [PATCH 146/184] add libboost-all-dev to github actions it's a new dependency --- .github/workflows/cmake-linux.yml | 2 +- .github/workflows/release-build.yml | 2 +- CMakeLists.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index e624f72..a801f66 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -25,7 +25,7 @@ jobs: run: | echo ${#beammp_sentry_url} sudo apt-get update - sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev cmake g++-10 + sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev cmake g++-10 libboost-all-dev - name: Create Build Environment run: cmake -E make_directory ${{github.workspace}}/build-linux diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index 2f8e3f3..a870080 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -42,7 +42,7 @@ jobs: - name: Install Dependencies run: | sudo apt-get update - sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev + sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev libboost-all-dev - name: Create Build Environment run: cmake -E make_directory ${{github.workspace}}/build-linux diff --git a/CMakeLists.txt b/CMakeLists.txt index 118e9b7..aa6fba2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,7 +95,7 @@ include(FindOpenSSL) include(FindThreads) include(FindZLIB) -find_package(Boost 1.75 REQUIRED COMPONENTS system) +find_package(Boost 1.70 REQUIRED COMPONENTS system) set(BeamMP_Sources include/TConsole.h src/TConsole.cpp From fc0a509bd9574c9fe0f0a100acf88ab1a4cbcefd Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 5 Oct 2022 21:15:11 +0200 Subject: [PATCH 147/184] fix clientversion parameter in heartbeat --- src/THeartbeatThread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/THeartbeatThread.cpp b/src/THeartbeatThread.cpp index 96504a8..c1ad639 100644 --- a/src/THeartbeatThread.cpp +++ b/src/THeartbeatThread.cpp @@ -148,7 +148,7 @@ std::string THeartbeatThread::GenerateCall() { << "&map=" << Application::Settings.MapName << "&private=" << (Application::Settings.Private ? "true" : "false") << "&version=" << Application::ServerVersionString() - << "&clientversion=" << Application::ClientMajorVersion() + << "&clientversion=" << std::to_string(Application::ClientMajorVersion()) + ".0" // FIXME: Wtf. << "&name=" << Application::Settings.ServerName << "&modlist=" << mResourceManager.TrimmedList() << "&modstotalsize=" << mResourceManager.MaxModSize() From 95ae0f5d03e20689aaa4cfbcbd1d0e4ed7771ce6 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 5 Oct 2022 22:17:56 +0200 Subject: [PATCH 148/184] fix 'Od' and 'Or' packets not being broadcast --- src/TServer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TServer.cpp b/src/TServer.cpp index 690ec50..7e2a3d7 100644 --- a/src/TServer.cpp +++ b/src/TServer.cpp @@ -311,7 +311,7 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ } case 'd': { beammp_trace(std::string(("got 'Od' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")")); - auto MaybePidVid = GetPidVid(Data); + auto MaybePidVid = GetPidVid(Data.substr(0, Data.find(':', 1))); if (MaybePidVid) { std::tie(PID, VID) = MaybePidVid.value(); } @@ -329,7 +329,7 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ } case 'r': { beammp_trace(std::string(("got 'Or' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")")); - auto MaybePidVid = GetPidVid(Data); + auto MaybePidVid = GetPidVid(Data.substr(0, Data.find(':', 1))); if (MaybePidVid) { std::tie(PID, VID) = MaybePidVid.value(); } From c42a523532a695c6c44119cca0e601e7a687e32d Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 6 Oct 2022 00:24:13 +0200 Subject: [PATCH 149/184] remove SO_SNDTIMEO for now --- src/TNetwork.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index 8a2ec52..f472f81 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -152,7 +152,7 @@ void TNetwork::TCPServerMain() { if (ec) { beammp_errorf("failed to accept: {}", ec.what()); } - ClientSocket.set_option(boost::asio::detail::socket_option::integer { 30 * 1000 }, ec); + //ClientSocket.set_option(boost::asio::detail::socket_option::integer { 30 * 1000 }, ec); if (!ec) { beammp_errorf("failed to set send timeout on client socket: {}", ec.what()); } @@ -457,15 +457,11 @@ void TNetwork::Looper(const std::weak_ptr& c) { } // end locked context // beammp_debug("sending a missed packet: " + QData); if (!TCPSend(*Client, QData, true)) { - if (!Client->IsDisconnected()) - Client->Disconnect("Failed to TCPSend while clearing the missed packet queue"); - { - std::unique_lock lock(Client->MissedPacketQueueMutex()); - while (!Client->MissedPacketQueue().empty()) { - Client->MissedPacketQueue().pop(); - } + Client->Disconnect("Failed to TCPSend while clearing the missed packet queue"); + std::unique_lock lock(Client->MissedPacketQueueMutex()); + while (!Client->MissedPacketQueue().empty()) { + Client->MissedPacketQueue().pop(); } - Client->Disconnect("WHY THE FUCK NOT"); break; } } From 917e3f98ab05e6a30c4ec178c8aabdcaa9b67e1a Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 6 Oct 2022 00:36:57 +0200 Subject: [PATCH 150/184] fix github actions dependencies for linux to use proper boost version --- .github/workflows/cmake-linux.yml | 2 +- .github/workflows/release-build.yml | 2 +- src/TNetwork.cpp | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index a801f66..8cf22ca 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -25,7 +25,7 @@ jobs: run: | echo ${#beammp_sentry_url} sudo apt-get update - sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev cmake g++-10 libboost-all-dev + sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev cmake g++-10 libboost-dev - name: Create Build Environment run: cmake -E make_directory ${{github.workspace}}/build-linux diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index a870080..0b6930b 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -42,7 +42,7 @@ jobs: - name: Install Dependencies run: | sudo apt-get update - sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev libboost-all-dev + sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev libboost-dev - name: Create Build Environment run: cmake -E make_directory ${{github.workspace}}/build-linux diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index f472f81..f95e76e 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -152,7 +152,6 @@ void TNetwork::TCPServerMain() { if (ec) { beammp_errorf("failed to accept: {}", ec.what()); } - //ClientSocket.set_option(boost::asio::detail::socket_option::integer { 30 * 1000 }, ec); if (!ec) { beammp_errorf("failed to set send timeout on client socket: {}", ec.what()); } From c69418ea5ed60a32bf4debcc093a698220ebeda7 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 6 Oct 2022 00:40:39 +0200 Subject: [PATCH 151/184] add boost_system dependency for linux gh actions --- .github/workflows/cmake-linux.yml | 2 +- .github/workflows/release-build.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index 8cf22ca..7f23f9a 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -25,7 +25,7 @@ jobs: run: | echo ${#beammp_sentry_url} sudo apt-get update - sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev cmake g++-10 libboost-dev + sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev cmake g++-10 libboost-dev libboost-system-dev - name: Create Build Environment run: cmake -E make_directory ${{github.workspace}}/build-linux diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index 0b6930b..c313a66 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -42,7 +42,7 @@ jobs: - name: Install Dependencies run: | sudo apt-get update - sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev libboost-dev + sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev libboost-dev libboost-system-dev - name: Create Build Environment run: cmake -E make_directory ${{github.workspace}}/build-linux From e5951928297574313299bf75c79b315261cd91dd Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 6 Oct 2022 00:43:17 +0200 Subject: [PATCH 152/184] rename header to boost errc --- src/Client.cpp | 2 +- src/TNetwork.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Client.cpp b/src/Client.cpp index 991f0b7..33af65a 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -2,7 +2,7 @@ #include "CustomAssert.h" #include "TServer.h" -#include +#include #include #include diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index f95e76e..af3bcef 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include std::vector StringToVector(const std::string& Str) { From 88f5db514ffd6427316dec9f417c7e9901c4426d Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 6 Oct 2022 00:46:35 +0200 Subject: [PATCH 153/184] remove unused headers --- src/Client.cpp | 1 - src/TNetwork.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Client.cpp b/src/Client.cpp index 33af65a..ee01f4d 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -2,7 +2,6 @@ #include "CustomAssert.h" #include "TServer.h" -#include #include #include diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index af3bcef..5c1d1ce 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include std::vector StringToVector(const std::string& Str) { From cc6b7846b29ee87903a17363f7d22e363cb11512 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 6 Oct 2022 00:51:51 +0200 Subject: [PATCH 154/184] add system include --- include/BoostAliases.h | 1 + src/Client.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/include/BoostAliases.h b/include/BoostAliases.h index a42265b..a9ef413 100644 --- a/include/BoostAliases.h +++ b/include/BoostAliases.h @@ -1,5 +1,6 @@ #pragma once #include +#include using namespace boost::asio; diff --git a/src/Client.cpp b/src/Client.cpp index ee01f4d..2d04551 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -4,6 +4,7 @@ #include "TServer.h" #include #include +#include void TClient::DeleteCar(int Ident) { std::unique_lock lock(mVehicleDataMutex); From b25f4a875c2fefa3d31d997822f5f155fa23e829 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 6 Oct 2022 00:54:57 +0200 Subject: [PATCH 155/184] run on latest ubuntu --- .github/workflows/cmake-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index 7f23f9a..ec0ff47 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -12,7 +12,7 @@ env: jobs: linux-build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 From ed872f730dbf53a72b17c0de4282128f08e3ce48 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 6 Oct 2022 00:57:31 +0200 Subject: [PATCH 156/184] link against boost::system --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index aa6fba2..c4f5ade 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -174,6 +174,7 @@ endif() set(BeamMP_Libraries Boost::boost + Boost::system doctest::doctest OpenSSL::SSL OpenSSL::Crypto From 93b2559120c214885c7a87110bffd02c59ed76ca Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 6 Oct 2022 01:01:55 +0200 Subject: [PATCH 157/184] switch to boost 1.74 --- .github/workflows/cmake-linux.yml | 4 ++-- .github/workflows/release-build.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index ec0ff47..b866387 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -12,7 +12,7 @@ env: jobs: linux-build: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v2 @@ -25,7 +25,7 @@ jobs: run: | echo ${#beammp_sentry_url} sudo apt-get update - sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev cmake g++-10 libboost-dev libboost-system-dev + sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev cmake g++-10 libboost1.74-all-dev libboost1.74-dev - name: Create Build Environment run: cmake -E make_directory ${{github.workspace}}/build-linux diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index c313a66..e2ec164 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -32,7 +32,7 @@ jobs: upload-release-files-linux: name: Upload Linux Release Files - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: create-release steps: - uses: actions/checkout@v2 @@ -42,7 +42,7 @@ jobs: - name: Install Dependencies run: | sudo apt-get update - sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev libboost-dev libboost-system-dev + sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev libboost-dev libboost1.74-all-dev libboost1.74-dev - name: Create Build Environment run: cmake -E make_directory ${{github.workspace}}/build-linux From 67d02d4cf2bb3f0542d2f9f7a04bbacb6bead2af Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 6 Oct 2022 01:29:00 +0200 Subject: [PATCH 158/184] remove unused error check --- src/TNetwork.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index 5c1d1ce..52f2b0e 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -151,9 +151,6 @@ void TNetwork::TCPServerMain() { if (ec) { beammp_errorf("failed to accept: {}", ec.what()); } - if (!ec) { - beammp_errorf("failed to set send timeout on client socket: {}", ec.what()); - } TConnection Conn { std::move(ClientSocket), ClientEp }; std::thread ID(&TNetwork::Identify, this, std::move(Conn)); ID.detach(); // TODO: Add to a queue and attempt to join periodically From 7d1318653caf10e9ae43ea61180f1f106d11be5f Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 6 Oct 2022 01:31:13 +0200 Subject: [PATCH 159/184] fix boost::system::error_code --- include/BoostAliases.h | 2 +- src/Client.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/include/BoostAliases.h b/include/BoostAliases.h index a9ef413..0348350 100644 --- a/include/BoostAliases.h +++ b/include/BoostAliases.h @@ -1,6 +1,6 @@ #pragma once #include -#include +#include using namespace boost::asio; diff --git a/src/Client.cpp b/src/Client.cpp index 2d04551..ee01f4d 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -4,7 +4,6 @@ #include "TServer.h" #include #include -#include void TClient::DeleteCar(int Ident) { std::unique_lock lock(mVehicleDataMutex); From 4320a91e5ced9aab6f54796bc178b0bcd831b078 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 6 Oct 2022 01:35:50 +0200 Subject: [PATCH 160/184] use message() instead of what() for ec --- src/Client.cpp | 4 ++-- src/TNetwork.cpp | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Client.cpp b/src/Client.cpp index ee01f4d..c2c3fac 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -62,11 +62,11 @@ void TClient::Disconnect(std::string_view Reason) { boost::system::error_code ec; mSocket.shutdown(socket_base::shutdown_both, ec); if (ec) { - beammp_warnf("Failed to shutdown client socket: {}", ec.what()); + beammp_warnf("Failed to shutdown client socket: {}", ec.message()); } mSocket.close(ec); if (ec) { - beammp_warnf("Failed to close client socket: {}", ec.what()); + beammp_warnf("Failed to close client socket: {}", ec.message()); } } diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index 52f2b0e..d6fd1fe 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -64,13 +64,13 @@ void TNetwork::UDPServerMain() { boost::system::error_code ec; mUDPSock.open(UdpListenEndpoint.protocol(), ec); if (ec) { - beammp_error("open() failed: " + ec.what()); + beammp_error("open() failed: " + ec.message()); std::this_thread::sleep_for(std::chrono::seconds(5)); Application::GracefullyShutdown(); } mUDPSock.bind(UdpListenEndpoint, ec); if (ec) { - beammp_error("bind() failed: " + ec.what()); + beammp_error("bind() failed: " + ec.message()); std::this_thread::sleep_for(std::chrono::seconds(5)); Application::GracefullyShutdown(); } @@ -118,7 +118,7 @@ void TNetwork::TCPServerMain() { boost::system::error_code ec; Listener.open(ListenEp.protocol(), ec); if (ec) { - beammp_errorf("Failed to open socket: {}", ec.what()); + beammp_errorf("Failed to open socket: {}", ec.message()); return; } socket_base::linger LingerOpt {}; @@ -127,7 +127,7 @@ void TNetwork::TCPServerMain() { if (ec) { beammp_errorf("Failed to set up listening socket to not linger / reuse address. " "This may cause the socket to refuse to bind(). Error: {}", - ec.what()); + ec.message()); } ip::tcp::acceptor Acceptor(mServer.IoCtx(), ListenEp); @@ -135,7 +135,7 @@ void TNetwork::TCPServerMain() { if (ec) { beammp_errorf("listen() failed, which is needed for the server to operate. " "Shutting down. Error: {}", - ec.what()); + ec.message()); Application::GracefullyShutdown(); } Application::SetSubsystemStatus("TCPNetwork", Application::Status::Good); @@ -149,7 +149,7 @@ void TNetwork::TCPServerMain() { ip::tcp::endpoint ClientEp; ip::tcp::socket ClientSocket = Acceptor.accept(ClientEp, ec); if (ec) { - beammp_errorf("failed to accept: {}", ec.what()); + beammp_errorf("failed to accept: {}", ec.message()); } TConnection Conn { std::move(ClientSocket), ClientEp }; std::thread ID(&TNetwork::Identify, this, std::move(Conn)); @@ -366,7 +366,7 @@ bool TNetwork::TCPSend(TClient& c, const std::vector& Data, bool IsSync boost::system::error_code ec; write(Sock, buffer(ToSend), ec); if (ec) { - beammp_debugf("write(): {}", ec.what()); + beammp_debugf("write(): {}", ec.message()); c.Disconnect("write() failed"); return false; } @@ -388,7 +388,7 @@ std::vector TNetwork::TCPRcv(TClient& c) { read(Sock, buffer(HeaderData), ec); if (ec) { // TODO: handle this case (read failed) - beammp_debugf("TCPRcv: Reading header failed: {}", ec.what()); + beammp_debugf("TCPRcv: Reading header failed: {}", ec.message()); return {}; } Header = *reinterpret_cast(HeaderData.data()); @@ -405,7 +405,7 @@ std::vector TNetwork::TCPRcv(TClient& c) { auto N = read(Sock, buffer(Data), ec); if (ec) { // TODO: handle this case properly - beammp_debugf("TCPRcv: Reading data failed: {}", ec.what()); + beammp_debugf("TCPRcv: Reading data failed: {}", ec.message()); return {}; } @@ -771,7 +771,7 @@ bool TNetwork::TCPSendRaw(TClient& C, ip::tcp::socket& socket, const uint8_t* Da boost::system::error_code ec; write(socket, buffer(Data, Size), ec); if (ec) { - beammp_errorf("Failed to send raw data to client: {}", ec.what()); + beammp_errorf("Failed to send raw data to client: {}", ec.message()); return false; } C.UpdatePingTime(); @@ -911,7 +911,7 @@ bool TNetwork::UDPSend(TClient& Client, std::vector Data) { boost::system::error_code ec; mUDPSock.send_to(buffer(Data), Addr, 0, ec); if (ec) { - beammp_debugf("UDP sendto() failed: {}", ec.what()); + beammp_debugf("UDP sendto() failed: {}", ec.message()); if (!Client.IsDisconnected()) Client.Disconnect("UDP send failed"); return false; @@ -924,7 +924,7 @@ std::vector TNetwork::UDPRcvFromClient(ip::udp::endpoint& ClientEndpoin boost::system::error_code ec; const auto Rcv = mUDPSock.receive_from(mutable_buffer(Ret.data(), Ret.size()), ClientEndpoint, 0, ec); if (ec) { - beammp_errorf("UDP recvfrom() failed: {}", ec.what()); + beammp_errorf("UDP recvfrom() failed: {}", ec.message()); return {}; } // FIXME: This breaks binary data due to \0. From 2678234d678bb517703965262f77c94bfcca5b69 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 6 Oct 2022 01:40:10 +0200 Subject: [PATCH 161/184] dont check for -Werror=zero-as-null-pointer-constant --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c4f5ade..e73fc8f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -163,12 +163,12 @@ if (UNIX) -Werror=switch-default -Werror=old-style-cast -Werror=overloaded-virtual - -Werror=zero-as-null-pointer-constant -Werror=overloaded-virtual -Werror=missing-include-dirs -Werror=unused-result -fstack-protector + -Wzero-as-null-pointer-constant ) endif() From 064e71e59fc4408cfc71c2dcd7425299479877e8 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 6 Oct 2022 02:14:58 +0200 Subject: [PATCH 162/184] fix client version check --- src/TNetwork.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index d6fd1fe..42acaf7 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -224,7 +224,7 @@ std::shared_ptr TNetwork::Authentication(TConnection&& RawConnection) { constexpr std::string_view VC = "VC"; if (Data.size() > 3 && std::equal(Data.begin(), Data.begin() + VC.size(), VC.begin(), VC.end())) { std::string ClientVersionStr(reinterpret_cast(Data.data() + 2), Data.size() - 2); - Version ClientVersion = Application::VersionStrToInts(ClientVersionStr); + Version ClientVersion = Application::VersionStrToInts(ClientVersionStr + ".0"); if (ClientVersion.major != Application::ClientMajorVersion()) { beammp_errorf("Client tried to connect with version '{}', but only versions '{}.x.x' is allowed", ClientVersion.AsString(), Application::ClientMajorVersion()); From 54730d2baf74b7d8ca54a0409c281663a6498e17 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 6 Oct 2022 02:26:05 +0200 Subject: [PATCH 163/184] remove heartbeat spam logging --- src/THeartbeatThread.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/THeartbeatThread.cpp b/src/THeartbeatThread.cpp index c1ad639..a1c6933 100644 --- a/src/THeartbeatThread.cpp +++ b/src/THeartbeatThread.cpp @@ -42,8 +42,6 @@ void THeartbeatThread::operator()() { Body += "&pps=" + Application::PPS(); - beammp_trace("heartbeat body: '" + Body + "'"); - auto SentryReportError = [&](const std::string& transaction, int status) { auto Lock = Sentry.CreateExclusiveContext(); Sentry.SetContext("heartbeat", @@ -61,7 +59,6 @@ void THeartbeatThread::operator()() { 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()) { if (!Application::Settings.Private) { From 98f77e157f86073378a5f817c599d061cfd13d84 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 6 Oct 2022 02:28:20 +0200 Subject: [PATCH 164/184] add WIN32_STATIC_RUNTIME option to cmake --- CMakeLists.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e73fc8f..34cac15 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,11 +46,14 @@ if(APPLE) link_directories(/usr/local/opt/openssl@1.1/lib) endif() # ------------------------ WINDOWS --------------------------------- +option(WIN32_STATIC_RUNTIME "Build statically-linked runtime on windows (don't touch unless you know what you're doing)" ON) 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}) + if (WIN32_STATIC_RUNTIME) + 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() # ------------------------ LINUX --------------------------------- elseif (UNIX) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g") From 94c0547a35ffe4fa97bc5193118d5cc4beb4fec5 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 6 Oct 2022 19:02:04 +0200 Subject: [PATCH 165/184] fix crash when the client disconnects while sending first identify setting --- src/TNetwork.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index 42acaf7..b13e090 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -173,7 +173,7 @@ void TNetwork::Identify(TConnection&& RawConnection) { read(RawConnection.Socket, buffer(&Code, 1), ec); if (ec) { // TODO: is this right?! - RawConnection.Socket.shutdown(socket_base::shutdown_both); + RawConnection.Socket.shutdown(socket_base::shutdown_both, ec); return; } std::shared_ptr Client { nullptr }; From 75ff9f75712aca3eeab54a5d17fca06b1e95e79f Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 13 Oct 2022 09:18:38 +0200 Subject: [PATCH 166/184] remove "backend response failed to parse as valid json" --- Changelog.md | 1 + src/THeartbeatThread.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 9aacbda..e4d9f3f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -19,6 +19,7 @@ - FIXED bug which caused hot-reload not to report syntax errors - FIXED missing error messages on some event handler calls - FIXED vehicles not deleting for all players if an edit was cancelled by Lua +- REMOVED "Backend response failed to parse as valid json" message # v3.0.2 diff --git a/src/THeartbeatThread.cpp b/src/THeartbeatThread.cpp index a1c6933..29511a4 100644 --- a/src/THeartbeatThread.cpp +++ b/src/THeartbeatThread.cpp @@ -62,7 +62,7 @@ void THeartbeatThread::operator()() { Doc.Parse(T.data(), T.size()); if (Doc.HasParseError() || !Doc.IsObject()) { if (!Application::Settings.Private) { - beammp_error("Backend response failed to parse as valid json"); + beammp_trace("Backend response failed to parse as valid json"); beammp_trace("Response was: `" + T + "`"); } Sentry.SetContext("JSON Response", { { "reponse", T } }); From 87965433c21412ede5a142051d1fcd7d71f2a299 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Sat, 15 Oct 2022 23:08:50 +0200 Subject: [PATCH 167/184] change log levels of common warnings and errors to debug this hides a lot of the "standard" errors we get behind the debug flag. for example, disconnecting a disconnected player would be such an error --- src/Client.cpp | 4 ++-- src/TNetwork.cpp | 4 ++-- src/TServer.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Client.cpp b/src/Client.cpp index c2c3fac..b03c99a 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -62,11 +62,11 @@ void TClient::Disconnect(std::string_view Reason) { boost::system::error_code ec; mSocket.shutdown(socket_base::shutdown_both, ec); if (ec) { - beammp_warnf("Failed to shutdown client socket: {}", ec.message()); + beammp_debugf("Failed to shutdown client socket: {}", ec.message()); } mSocket.close(ec); if (ec) { - beammp_warnf("Failed to close client socket: {}", ec.message()); + beammp_debugf("Failed to close client socket: {}", ec.message()); } } diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index b13e090..ef92c8c 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -273,7 +273,7 @@ std::shared_ptr TNetwork::Authentication(TConnection&& RawConnection) { return nullptr; } } catch (const std::exception& e) { - beammp_errorf("Client sent invalid key: {}", e.what()); + beammp_errorf("Client sent invalid key. Error was: {}", e.what()); // TODO: we should really clarify that this was a backend response or parsing error ClientKick(*Client, "Invalid key! Please restart your game."); return nullptr; @@ -425,7 +425,7 @@ std::vector TNetwork::TCPRcv(TClient& c) { void TNetwork::ClientKick(TClient& c, const std::string& R) { beammp_info("Client kicked: " + R); if (!TCPSend(c, StringToVector("K" + R))) { - beammp_warn("tried to kick player '" + c.GetName() + "' (id " + std::to_string(c.GetID()) + "), but was already disconnected"); + beammp_debugf("tried to kick player '{}' (id {}), but was already connected", c.GetName(), c.GetID()); } c.Disconnect("Kicked"); } diff --git a/src/TServer.cpp b/src/TServer.cpp index 7e2a3d7..f160e6a 100644 --- a/src/TServer.cpp +++ b/src/TServer.cpp @@ -225,7 +225,7 @@ bool TServer::IsUnicycle(TClient& c, const std::string& CarJson) { return true; } } catch (const std::exception& e) { - beammp_error("Failed to parse vehicle data as json for client " + std::to_string(c.GetID()) + ": '" + CarJson + "'"); + beammp_warn("Failed to parse vehicle data as json for client " + std::to_string(c.GetID()) + ": '" + CarJson + "'."); } return false; } From 331a597ec762fc05d39da732720961460eca3830 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Sat, 15 Oct 2022 23:19:33 +0200 Subject: [PATCH 168/184] add info about new networking to changelog --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index e4d9f3f..9505405 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,6 +11,8 @@ - ADDED HOME and END button working in console - ADDED `MP.TriggerClientEventJson()` which takes a table as the data argument and sends it as JSON - ADDED identifiers (beammp id, ip) to onPlayerAuth (4th argument) +- ADDED more network debug logging +- CHANGED all networking to be more stable, performant, and safe - FIXED `ip` in MP.GetPlayerIdentifiers - FIXED issue with client->server events which contain `:` - FIXED a fatal exception on LuaEngine startup if Resources/Server is a symlink From 92632b53b5b4f619ec09d800d2db1a31a61a35be Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Sat, 15 Oct 2022 23:30:09 +0200 Subject: [PATCH 169/184] fix binary data breaking in UDPRcvFromClient --- src/TNetwork.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index ef92c8c..6adf958 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -927,6 +927,6 @@ std::vector TNetwork::UDPRcvFromClient(ip::udp::endpoint& ClientEndpoin beammp_errorf("UDP recvfrom() failed: {}", ec.message()); return {}; } - // FIXME: This breaks binary data due to \0. - return std::vector(Ret.begin(), Ret.end()); + beammp_assert(Rcv <= Ret.size()); + return std::vector(Ret.begin(), Ret.begin() + Rcv); } From 466845b3141fe74ebfe95b274e438e200940a45f Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Sun, 16 Oct 2022 00:05:02 +0200 Subject: [PATCH 170/184] add udp binary data fix to Changelog --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 9505405..9ed32e4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -21,6 +21,7 @@ - FIXED bug which caused hot-reload not to report syntax errors - FIXED missing error messages on some event handler calls - FIXED vehicles not deleting for all players if an edit was cancelled by Lua +- FIXED server not handling binary UDP packets properly - REMOVED "Backend response failed to parse as valid json" message # v3.0.2 From fd51336a91c30873a8a7807a377021c48ab8decc Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Mon, 17 Oct 2022 12:01:08 +0200 Subject: [PATCH 171/184] update vcpkg --- .github/workflows/cmake-windows.yml | 2 +- .github/workflows/release-build.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake-windows.yml b/.github/workflows/cmake-windows.yml index e0be5ee..131250c 100644 --- a/.github/workflows/cmake-windows.yml +++ b/.github/workflows/cmake-windows.yml @@ -25,7 +25,7 @@ jobs: with: vcpkgArguments: "lua zlib rapidjson openssl websocketpp curl" vcpkgDirectory: "${{ runner.workspace }}/b/vcpkg" - vcpkgGitCommitId: "a106de33bbee694e3be6243718aa2a549a692832" + vcpkgGitCommitId: "06b5f4a769d848d1a20fa0acd556019728b56273" vcpkgTriplet: "x64-windows-static" - name: Create Build Environment diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index e2ec164..9b9733d 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -85,7 +85,7 @@ jobs: with: vcpkgArguments: 'lua zlib rapidjson openssl websocketpp curl' vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg' - vcpkgGitCommitId: 'a106de33bbee694e3be6243718aa2a549a692832' + vcpkgGitCommitId: '06b5f4a769d848d1a20fa0acd556019728b56273' vcpkgTriplet: 'x64-windows-static' - name: Create Build Environment From c6c2efb0b1ac8bb326a948c85b69bb7beb117ed4 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Mon, 17 Oct 2022 12:09:36 +0200 Subject: [PATCH 172/184] revert "update linux and windows workflows to run on pr open, reopen, review submit" This reverts commit 23e9941704af79d4d1497a74702437eb057ace2f. --- .github/workflows/cmake-linux.yml | 15 +++++---------- .github/workflows/cmake-windows.yml | 22 +++++++++------------- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index b866387..36875db 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -1,11 +1,6 @@ name: CMake Linux Build -on: - push: - pull_request: - types: [opened, reopened] - pull_request_review: - types: [submitted] +on: [push] env: BUILD_TYPE: Release @@ -68,16 +63,16 @@ jobs: with: name: BeamMP-Server-linux-tests path: ${{github.workspace}} - + - name: Install Runtime Dependencies shell: bash run: | sudo apt-get update - sudo apt-get install -y liblua5.3 openssl + sudo apt-get install -y liblua5.3 openssl - name: Test working-directory: ${{github.workspace}} shell: bash run: | - chmod +x ./BeamMP-Server-tests - ./BeamMP-Server-tests + chmod +x ./BeamMP-Server-tests + ./BeamMP-Server-tests diff --git a/.github/workflows/cmake-windows.yml b/.github/workflows/cmake-windows.yml index 131250c..6d0624e 100644 --- a/.github/workflows/cmake-windows.yml +++ b/.github/workflows/cmake-windows.yml @@ -1,11 +1,6 @@ name: CMake Windows Build -on: - push: - pull_request: - types: [opened, reopened] - pull_request_review: - types: [submitted] +on: [push] env: BUILD_TYPE: Release @@ -13,20 +8,20 @@ env: jobs: windows-build: runs-on: windows-latest - + steps: - uses: actions/checkout@v2 with: - submodules: "recursive" - + submodules: 'recursive' + - name: Restore artifacts, or run vcpkg, build and cache artifacts uses: lukka/run-vcpkg@v7 id: runvcpkg with: - vcpkgArguments: "lua zlib rapidjson openssl websocketpp curl" - vcpkgDirectory: "${{ runner.workspace }}/b/vcpkg" + vcpkgArguments: 'lua zlib rapidjson openssl websocketpp curl' + vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg' vcpkgGitCommitId: "06b5f4a769d848d1a20fa0acd556019728b56273" - vcpkgTriplet: "x64-windows-static" + vcpkgTriplet: 'x64-windows-static' - name: Create Build Environment run: cmake -E make_directory ${{github.workspace}}/build-windows @@ -54,9 +49,10 @@ jobs: shell: bash run: | cmake --build . --config Debug - + - name: Archive debug artifacts uses: actions/upload-artifact@v2 with: name: BeamMP-Server-debug.exe path: ${{github.workspace}}/build-windows/Debug/BeamMP-Server.exe + From ad860835ca9e7b5b3e95853efb16b7a4ca6c8f08 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Mon, 17 Oct 2022 12:10:52 +0200 Subject: [PATCH 173/184] fix linux tests run not installing openssl properly --- .github/workflows/cmake-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index 36875db..405780d 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -68,7 +68,7 @@ jobs: shell: bash run: | sudo apt-get update - sudo apt-get install -y liblua5.3 openssl + sudo apt-get install -y liblua5.3 libcurl4-openssl libssl - name: Test working-directory: ${{github.workspace}} From 309a9d1fa97d00f0ecc5848b0a15afca0fcff970 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Mon, 17 Oct 2022 12:12:55 +0200 Subject: [PATCH 174/184] fix invalid windows workflows indentation --- .github/workflows/cmake-windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake-windows.yml b/.github/workflows/cmake-windows.yml index 6d0624e..4c2ba3b 100644 --- a/.github/workflows/cmake-windows.yml +++ b/.github/workflows/cmake-windows.yml @@ -20,7 +20,7 @@ jobs: with: vcpkgArguments: 'lua zlib rapidjson openssl websocketpp curl' vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg' - vcpkgGitCommitId: "06b5f4a769d848d1a20fa0acd556019728b56273" + vcpkgGitCommitId: "06b5f4a769d848d1a20fa0acd556019728b56273" vcpkgTriplet: 'x64-windows-static' - name: Create Build Environment From 4256977400ad5c578bb52f0a3bc0ef5fbf0589b3 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Mon, 17 Oct 2022 12:21:27 +0200 Subject: [PATCH 175/184] remove crt's "this function or variable may be unsafe" warnings they are useless, as they are in dependencies or parts of the code we don't care about. Also, the "safe" alternatives straightup dont work on linux. --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 34cac15..95bf609 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -139,6 +139,7 @@ set(BeamMP_Includes set(BeamMP_Definitions SECRET_SENTRY_URL="${BEAMMP_SECRET_SENTRY_URL}" + _CRT_SECURE_NO_WARNINGS ) if (UNIX) From 88c0ed56e49cab5240522a818d1669fd335c4472 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Mon, 17 Oct 2022 12:25:13 +0200 Subject: [PATCH 176/184] add _WIN32_WINNT and move CRT no warnings flag --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 95bf609..31c196e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -139,9 +139,12 @@ set(BeamMP_Includes set(BeamMP_Definitions SECRET_SENTRY_URL="${BEAMMP_SECRET_SENTRY_URL}" - _CRT_SECURE_NO_WARNINGS ) +if (WIN32) + list(APPEND BeamMP_Definitions _WIN32_WINNT=0x0601) + list(APPEND BeamMP_Definitions _CRT_SECURE_NO_WARNINGS) +endif() if (UNIX) set(BeamMP_CompileOptions -Wall From bdf2da758c3247c2ff335ce7efa3061623e9f868 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Mon, 17 Oct 2022 14:04:40 +0200 Subject: [PATCH 177/184] remove pps from heartbeat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "PPS has no meaning anymore and is completely irrelevant. You should ignore it, it is not an indicator of ANYTHING. If it’s high, that means NOTHING. If it’s low, that means NOTHING. If it’s -, that means NOTHING." It's packets per second per player per vehicle, but is only sent every 30 seconds, its not averaged, and on the client-side, it shows a ping icon next to it. A client can open a new connection to the server and send a `P`, and measure the time to the `P` response packet. The connection is then closed. This was added ages ago, please use this instead for ping :) --- src/THeartbeatThread.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/THeartbeatThread.cpp b/src/THeartbeatThread.cpp index 29511a4..873825a 100644 --- a/src/THeartbeatThread.cpp +++ b/src/THeartbeatThread.cpp @@ -40,8 +40,6 @@ void THeartbeatThread::operator()() { Body += "&ip=" + Application::Settings.CustomIP; } - Body += "&pps=" + Application::PPS(); - auto SentryReportError = [&](const std::string& transaction, int status) { auto Lock = Sentry.CreateExclusiveContext(); Sentry.SetContext("heartbeat", From a63359479ed8f2732f26dac248667f7bea81661b Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Sat, 22 Oct 2022 20:44:47 +0200 Subject: [PATCH 178/184] rename dependencies for test run in github actions --- .github/workflows/cmake-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index 405780d..7e4b15d 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -68,7 +68,7 @@ jobs: shell: bash run: | sudo apt-get update - sudo apt-get install -y liblua5.3 libcurl4-openssl libssl + sudo apt-get install -y liblua5.3 openssl libssl - name: Test working-directory: ${{github.workspace}} From 340933bbb330053ebfcb1d148fbcb0f006d2ce24 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Sat, 22 Oct 2022 21:01:44 +0200 Subject: [PATCH 179/184] fix dependencies for test run (add curl, fix ssl) --- .github/workflows/cmake-linux.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index 7e4b15d..83464ec 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -56,7 +56,7 @@ jobs: run-tests: needs: linux-build - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/download-artifact@master @@ -68,7 +68,7 @@ jobs: shell: bash run: | sudo apt-get update - sudo apt-get install -y liblua5.3 openssl libssl + sudo apt-get install -y liblua5.3-0 libssl3 curl - name: Test working-directory: ${{github.workspace}} From 917c501fafe74fce805eda4530d5a4154314d645 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Sat, 22 Oct 2022 21:16:31 +0200 Subject: [PATCH 180/184] fix typo causing beammp forum id not to show in identifiers (fix #137) --- src/TNetwork.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index 6adf958..6fbc16c 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -262,7 +262,7 @@ std::shared_ptr TNetwork::Authentication(TConnection&& RawConnection) { Client->SetName(AuthRes["username"]); Client->SetRoles(AuthRes["roles"]); Client->SetIsGuest(AuthRes["guest"]); - for (const auto& ID : AuthRes["identifier"]) { + for (const auto& ID : AuthRes["identifiers"]) { auto Raw = std::string(ID); auto SepIndex = Raw.find(':'); Client->SetIdentifier(Raw.substr(0, SepIndex), Raw.substr(SepIndex + 1)); From 12a0ab7fdd18ded2dcba1d197849f54e596d2bc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=84=97=E3=84=A0=CB=8B=20=E3=84=91=E3=84=A7=CB=8A?= Date: Mon, 26 Sep 2022 18:18:47 +0800 Subject: [PATCH 181/184] document dependencies for macos (#109) --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index b6a1ecd..64006d8 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,17 @@ cmake g++ ``` +#### macOS + +Dependencies for **macOS** can be installed with homebrew. +``` +brew install lua@5.3 rapidjson websocketpp cmake openssl@1.1 +``` +Some packages are included in **macOS** but you might want to install homebrew versions. +``` +brew install curl zlib git make +``` + ### How to build On Windows, use git-bash for these commands. On Linux, these should work in your shell. From aeb024953aa239e5e4b36a94fddec824723b2ad2 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Sat, 22 Oct 2022 22:46:04 +0200 Subject: [PATCH 182/184] fix sentry line that caused build to fail --- deps/sentry-native | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/sentry-native b/deps/sentry-native index 87e67ad..28be51f 160000 --- a/deps/sentry-native +++ b/deps/sentry-native @@ -1 +1 @@ -Subproject commit 87e67ad783a7ec4476b0eb4742bd40fe5a1e2435 +Subproject commit 28be51f5e3acb01327b1164206d3145464577670 From 093f905fd8abadd93bfe0e4776e78af844b9f09c Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Sat, 22 Oct 2022 23:06:11 +0200 Subject: [PATCH 183/184] remove redundant addition libboost library install in cmake-linux action --- .github/workflows/cmake-linux.yml | 2 +- README.md | 27 ++++++++++++--------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index 83464ec..b831148 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -20,7 +20,7 @@ jobs: run: | echo ${#beammp_sentry_url} sudo apt-get update - sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev cmake g++-10 libboost1.74-all-dev libboost1.74-dev + sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev cmake g++-10 libboost1.74-all-dev - name: Create Build Environment run: cmake -E make_directory ${{github.workspace}}/build-linux diff --git a/README.md b/README.md index 64006d8..d120f5b 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ These values are guesstimated and are subject to change with each release. ## Contributing -TLDR; [Issues](https://github.com/BeamMP/BeamMP-Server/issues) with the "help wanted" label or with nobody assigned. +TLDR; [Issues](https://github.com/BeamMP/BeamMP-Server/issues) with the "help wanted" or "good first issue" label or with nobody assigned. To contribute, look at the active [issues](https://github.com/BeamMP/BeamMP-Server/issues). Any issues that have the "help wanted" label or don't have anyone assigned are good tasks to take on. You can either contribute by programming or by testing and adding more info and ideas. @@ -64,25 +64,22 @@ Please use the prepackaged binaries in [Releases](https://github.com/BeamMP/Beam Dependencies for **Windows** can be installed with `vcpkg`. These are: ``` -lua -zlib -rapidjson -openssl -websocketpp -curl +lua zlib rapidjson openssl websocketpp curl ``` +The triplet we use for releases is `x64-windows-static`. #### Linux -Runtime dependencies - you want to find packages for: -- libz -- rapidjson -- lua5.3 -- ssl / openssl -- websocketpp -- curl (with ssl support) +We recommend Ubuntu 22.04 or Arch Linux. Any Linux distribution will work, but you have to figure out the package names yourself (please feel free to PR in a change to this README with that info). -Build-time dependencies are: +##### Runtime Dependencies +**Ubuntu 22.04** +``` + +``` + +##### Buildtime Dependencies +These are needed for you to *build* the server, in addition to the [runtime dependencies](#runtime-dependencies). ``` git make From fa1944dbef9cd6e19df390bc6238c34317aa5b01 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Sat, 22 Oct 2022 23:22:42 +0200 Subject: [PATCH 184/184] update readme to reflect dependency- and code-changes in 3.1.0 --- README.md | 88 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 77 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index d120f5b..724ca30 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ If you need support with understanding the codebase, please write us in the Disc ## About Building from Source -We only allow building unmodified (original) source code for public use. `master` is considered **unstable** and we will not provide technical support if such a build doesn't work, so always build from a tag. You can checkout a tag with `git checkout tags/TAGNAME`, where `TAGNAME` is the tag, for example `v1.20`. +We only allow building unmodified (original) source code for public use. `master` is considered **unstable** and we will not provide technical support if such a build doesn't work, so always build from a tag. You can checkout a tag with `git checkout tags/TAGNAME`, where `TAGNAME` is the tag, for example `v3.1.0`. ## Supported Operating Systems @@ -51,7 +51,7 @@ You can find precompiled binaries under [Releases](https://github.com/BeamMP/Bea ## Build Instructions -**__Do not compile from `master`. Always build from a release tag, i.e. `tags/v2.3.3`!__** +**__Do not compile from `master`. Always build from a release tag, i.e. `tags/v3.1.0`!__** Currently only Linux and Windows are supported (generally). See [Releases](https://github.com/BeamMP/BeamMP-Server/releases/) for official binary releases. On systems to which we do not provide binaries (so anything but windows), you are allowed to compile the program and use it. Other restrictions, such as not being allowed to distribute those binaries, still apply (see [copyright notice](#copyright)). @@ -59,6 +59,8 @@ Currently only Linux and Windows are supported (generally). See [Releases](https #### Windows +There are **no runtime libraries** needed for Windows. + Please use the prepackaged binaries in [Releases](https://github.com/BeamMP/BeamMP-Server/releases/). Dependencies for **Windows** can be installed with `vcpkg`. @@ -73,19 +75,84 @@ The triplet we use for releases is `x64-windows-static`. We recommend Ubuntu 22.04 or Arch Linux. Any Linux distribution will work, but you have to figure out the package names yourself (please feel free to PR in a change to this README with that info). ##### Runtime Dependencies + +These are needed to *run* the server. + +
+ +Ubuntu 22.04 + + +`apt-get install` the following libraries: +``` +liblua5.3-0 +libssl3 +curl +``` +
+ +
+ +Arch Linux + + +`pacman -Syu` the following libraries: +``` +lua53 +openssl +curl +``` +
+ +##### Build Dependencies +These are needed for you to *build* the server, in addition to the [runtime dependencies](#runtime-dependencies). + **Ubuntu 22.04** ``` ``` -##### Buildtime Dependencies -These are needed for you to *build* the server, in addition to the [runtime dependencies](#runtime-dependencies). +
+ +Ubuntu 22.04 + + +`apt-get install` the following libraries and programs: ``` git -make +libz-dev +rapidjson-dev +liblua5.3 +libssl-dev +libwebsocketpp-dev +libcurl4-openssl-dev +cmake +g++-10 +libboost1.74-all-dev +libssl3 +curl +``` +
+ +
+ +Arch Linux + + +`pacman -Syu` the following libraries and programs: +``` +lua53 +openssl +curl +git cmake g++ +cmake +zlib +boost +websocketpp ``` +
#### macOS @@ -103,13 +170,12 @@ brew install curl zlib git make On Windows, use git-bash for these commands. On Linux, these should work in your shell. 1. Make sure you have all [prerequisites](#prerequisites) installed -2. Clone the repository in a location of your choice with `git clone --recurse-submodules https://github.com/BeamMP/BeamMP-Server`. +2. Clone the repository in a location of your choice with `git clone https://github.com/BeamMP/BeamMP-Server` . 3. Change into the BeamMP-Server directory by running `cd BeamMP-Server`. -4. Checkout the branch of the release you want to compile, for example `git checkout tags/v3.0.2` for version 3.0.2. You can find the latest version [here](https://github.com/BeamMP/BeamMP-Server/tags). -5. Ensure that all submodules are initialized by running `git submodule update --init --recursive` -6. Run `cmake . -DCMAKE_BUILD_TYPE=Release` (with `.`) -7. Run `make` -8. You will now have a `BeamMP-Server` file in your directory, which is executable with `./BeamMP-Server` (`.\BeamMP-Server.exe` for windows). Follow the (Windows or Linux, doesnt matter) instructions on the [wiki](https://wiki.beammp.com/en/home/server-installation) for further setup after installation (which we just did), such as port-forwarding and getting a key to actually run the server. +4. Checkout the branch or tag of the release you want to compile, for example `git checkout tags/v3.0.2` for version 3.0.2. You can find the latest version [here](https://github.com/BeamMP/BeamMP-Server/tags). +6. Run `cmake . -DCMAKE_BUILD_TYPE=Release` (with `.`). This may take some time, and will update all submodules and prepare the build. +7. Run `make -j` . This step will take some time and will use a lot of CPU and RAM. Remove the `-j` if you run out of memory. *If you change something in the source code, you only have to re-run this step.* +8. You now have a `BeamMP-Server` file in your directory, which is executable with `./BeamMP-Server` (`.\BeamMP-Server.exe` for windows). Follow the (Windows or Linux, doesnt matter) instructions on the [wiki](https://wiki.beammp.com/en/home/server-installation) for further setup after installation (which we just did), such as port-forwarding and getting a key to actually run the server. *tip: to run the server in the background, simply (in bash, zsh, etc) run:* `nohup ./BeamMP-Server &`*.*