diff --git a/.github/workflows/cmake-windows.yml b/.github/workflows/cmake-windows.yml index aa9b43b..b882766 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: '8dddc6c899ce6fdbeab38b525a31e7f23cb2d5bb' + vcpkgGitCommitId: 'a106de33bbee694e3be6243718aa2a549a692832' vcpkgTriplet: 'x64-windows-static' - name: Create Build Environment diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index 8d2063a..2f8e3f3 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: '8dddc6c899ce6fdbeab38b525a31e7f23cb2d5bb' + vcpkgGitCommitId: 'a106de33bbee694e3be6243718aa2a549a692832' vcpkgTriplet: 'x64-windows-static' - name: Create Build Environment diff --git a/CMakeLists.txt b/CMakeLists.txt index 3fb1abf..cb1aeab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,8 +88,12 @@ add_subdirectory("deps/sentry-native") set(CMAKE_CXX_STANDARD 17) # ------------------------ DEPENDENCIES ------------------------------ +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 diff --git a/Changelog.md b/Changelog.md index 7da09ba..da9eb18 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,8 +9,10 @@ # v3.0.2 - ADDED Periodic update message if a new server is released +- ADDED Config setting for the IP the http server listens on - CHANGED Default MaxPlayers to 8 -- FIXED `MP.CreateEventTimer` filling up the queue (see https://wiki.beammp.com/en/Scripting/new-lua-scripting#mpcreateeventtimerevent_name-string-interval_ms-number-strategy-number-since-v302) +- CHANGED Default http server listen IP to localhost +- FIXED `MP.CreateEventTimer` filling up the queue (see ) - FIXED `MP.TriggerClientEvent` not kicking the client if it failed - FIXED Lua result queue handling not checking all results diff --git a/deps/commandline b/deps/commandline index 01434c1..d6b1c32 160000 --- a/deps/commandline +++ b/deps/commandline @@ -1 +1 @@ -Subproject commit 01434c11aaf82d37a126dc70f5aa02cc523dbbb4 +Subproject commit d6b1c32c8af6ad5306f9f001305b3be9928ae4bb diff --git a/include/Client.h b/include/Client.h index 93882f2..d513659 100644 --- a/include/Client.h +++ b/include/Client.h @@ -92,7 +92,7 @@ private: std::queue mPacketsSync; std::unordered_map mIdentifiers; bool mIsGuest = false; - std::mutex mVehicleDataMutex; + mutable std::mutex mVehicleDataMutex; TSetOfVehicleData mVehicleData; std::string mName = "Unknown Client"; SOCKET mSocket[2] { SOCKET(0), SOCKET(0) }; diff --git a/include/Common.h b/include/Common.h index 5823b5a..8b806d5 100644 --- a/include/Common.h +++ b/include/Common.h @@ -14,7 +14,6 @@ extern TSentry Sentry; #include #include #include -#include #include "Compat.h" @@ -56,7 +55,8 @@ public: bool SendErrors { true }; bool SendErrorsMessageEnabled { true }; int HTTPServerPort { 8080 }; - bool HTTPServerUseSSL { true }; + std::string HTTPServerIP { "127.0.0.1" }; + bool HTTPServerUseSSL { false }; bool HideUpdateMessages { false }; [[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); } }; diff --git a/include/TConsole.h b/include/TConsole.h index aa2fcbd..1f990b8 100644 --- a/include/TConsole.h +++ b/include/TConsole.h @@ -20,6 +20,7 @@ public: void WriteRaw(const std::string& str); void InitializeLuaConsole(TLuaEngine& Engine); void BackupOldLog(); + void StartLoggingToFile(); Commandline& Internal() { return mCommandline; } private: @@ -60,4 +61,6 @@ private: bool mFirstTime { true }; std::string mStateId; const std::string mDefaultStateId = "BEAMMP_SERVER_CONSOLE"; + std::ofstream mLogFileStream; + std::mutex mLogFileStreamMtx; }; diff --git a/src/Client.cpp b/src/Client.cpp index fe90463..410d966 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -25,6 +25,7 @@ void TClient::ClearCars() { int TClient::GetOpenCarID() const { int OpenID = 0; bool found; + std::unique_lock lock(mVehicleDataMutex); do { found = true; for (auto& v : mVehicleData) { diff --git a/src/Common.cpp b/src/Common.cpp index ab87144..23c483d 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -97,6 +97,7 @@ void Application::SetSubsystemStatus(const std::string& Subsystem, Status status void Application::CheckForUpdates() { Application::SetSubsystemStatus("UpdateCheck", Application::Status::Starting); + static bool FirstTime = true; // checks current version against latest version std::regex VersionRegex { R"(\d+\.\d+\.\d+\n*)" }; for (const auto& url : GetBackendUrlsInOrder()) { @@ -109,22 +110,29 @@ void Application::CheckForUpdates() { std::string RealVersionString = RemoteVersion.AsString(); beammp_warn(std::string(ANSI_YELLOW_BOLD) + "NEW VERSION IS OUT! Please update to the new version (v" + RealVersionString + ") of the BeamMP-Server! Download it here: https://beammp.com/! For a guide on how to update, visit: https://wiki.beammp.com/en/home/server-maintenance#updating-the-server" + std::string(ANSI_RESET)); } else { - beammp_info("Server up-to-date!"); + if (FirstTime) { + beammp_info("Server up-to-date!"); + } } Application::SetSubsystemStatus("UpdateCheck", Application::Status::Good); break; } else { - 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); + if (FirstTime) { + 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); + } } } if (Application::GetSubsystemStatuses().at("UpdateCheck") == Application::Status::Bad) { - beammp_warn("Unable to fetch version info from backend."); + if (FirstTime) { + beammp_warn("Unable to fetch version info from backend."); + } } + FirstTime = false; } // thread name stuff diff --git a/src/Http.cpp b/src/Http.cpp index 467fd3c..aeb66d5 100644 --- a/src/Http.cpp +++ b/src/Http.cpp @@ -293,7 +293,7 @@ Http::Server::THttpServerInstance::THttpServerInstance() { mThread.detach(); } -void Http::Server::THttpServerInstance::operator()() { +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) { @@ -370,6 +370,14 @@ void Http::Server::THttpServerInstance::operator()() { HttpLibServerInstance->Get({ 0x2f, 0x6b, 0x69, 0x74, 0x74, 0x79 }, [](const httplib::Request&, httplib::Response& res) { res.set_content(std::string(Magic), "text/plain"); }); + HttpLibServerInstance->set_logger([](const httplib::Request& Req, const httplib::Response& Res) { + beammp_debug("Http Server: " + Req.method + " " + Req.target + " -> " + std::to_string(Res.status)); + }); Application::SetSubsystemStatus("HTTPServer", Application::Status::Good); - HttpLibServerInstance->listen("0.0.0.0", Application::Settings.HTTPServerPort); + auto ret = HttpLibServerInstance->listen(Application::Settings.HTTPServerIP.c_str(), Application::Settings.HTTPServerPort); + if (!ret) { + beammp_error("Failed to start http server (failed to listen). Please ensure the http server is configured properly in the ServerConfig.toml, or turn it off if you don't need it."); + } +} catch (const std::exception& e) { + beammp_error("Failed to start http server. Please ensure the http server is configured properly in the ServerConfig.toml, or turn it off if you don't need it. Error: " + std::string(e.what())); } diff --git a/src/TConfig.cpp b/src/TConfig.cpp index cc30a1b..0ffcbe8 100644 --- a/src/TConfig.cpp +++ b/src/TConfig.cpp @@ -30,6 +30,7 @@ static constexpr std::string_view StrHTTPServerUseSSL = "UseSSL"; static constexpr std::string_view StrSSLKeyPath = "SSLKeyPath"; static constexpr std::string_view StrSSLCertPath = "SSLCertPath"; static constexpr std::string_view StrHTTPServerPort = "HTTPServerPort"; +static constexpr std::string_view StrHTTPServerIP = "HTTPServerIP"; TConfig::TConfig(const std::string& ConfigFileName) : mConfigFileName(ConfigFileName) { @@ -87,8 +88,10 @@ void TConfig::FlushToFile() { data["HTTP"][StrSSLKeyPath.data()] = Application::Settings.SSLKeyPath; data["HTTP"][StrSSLCertPath.data()] = Application::Settings.SSLCertPath; data["HTTP"][StrHTTPServerPort.data()] = Application::Settings.HTTPServerPort; + SetComment(data["HTTP"][StrHTTPServerIP.data()].comments(), " Which IP to listen on. Pick 0.0.0.0 for a public-facing server with no specific IP, and 127.0.0.1 or 'localhost' for a local server."); + data["HTTP"][StrHTTPServerIP.data()] = Application::Settings.HTTPServerIP; data["HTTP"][StrHTTPServerUseSSL.data()] = Application::Settings.HTTPServerUseSSL; - SetComment(data["HTTP"][StrHTTPServerUseSSL.data()].comments(), " Recommended to keep enabled. With SSL the server will serve https and requires valid key and cert files"); + SetComment(data["HTTP"][StrHTTPServerUseSSL.data()].comments(), " Recommended to have enabled for servers which face the internet. With SSL the server will serve https and requires valid key and cert files"); data["HTTP"][StrHTTPServerEnabled.data()] = Application::Settings.HTTPServerEnabled; SetComment(data["HTTP"][StrHTTPServerEnabled.data()].comments(), " Enables the internal HTTP server"); std::ofstream Stream(mConfigFileName, std::ios::trunc | std::ios::out); @@ -176,6 +179,7 @@ void TConfig::ParseFromFile(std::string_view name) { TryReadValue(data, "HTTP", StrSSLKeyPath, Application::Settings.SSLKeyPath); TryReadValue(data, "HTTP", StrSSLCertPath, Application::Settings.SSLCertPath); TryReadValue(data, "HTTP", StrHTTPServerPort, Application::Settings.HTTPServerPort); + TryReadValue(data, "HTTP", StrHTTPServerIP, Application::Settings.HTTPServerIP); TryReadValue(data, "HTTP", StrHTTPServerEnabled, Application::Settings.HTTPServerEnabled); TryReadValue(data, "HTTP", StrHTTPServerUseSSL, Application::Settings.HTTPServerUseSSL); } catch (const std::exception& err) { @@ -215,6 +219,7 @@ void TConfig::PrintDebug() { beammp_debug(std::string(StrSSLKeyPath) + ": \"" + Application::Settings.SSLKeyPath + "\""); beammp_debug(std::string(StrSSLCertPath) + ": \"" + Application::Settings.SSLCertPath + "\""); beammp_debug(std::string(StrHTTPServerPort) + ": \"" + std::to_string(Application::Settings.HTTPServerPort) + "\""); + beammp_debug(std::string(StrHTTPServerIP) + ": \"" + Application::Settings.HTTPServerIP + "\""); // special! beammp_debug("Key Length: " + std::to_string(Application::Settings.Key.length()) + ""); } diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 0688fe3..eefedac 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -97,6 +97,25 @@ 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) { + // TODO: Sanitize by removing all ansi escape codes (vt100) + std::unique_lock Lock(mLogFileStreamMtx); + mLogFileStream.write(ToWrite.c_str(), ToWrite.size()); + mLogFileStream.write("\n", 1); + mLogFileStream.flush(); + }; +} + void TConsole::ChangeToLuaConsole(const std::string& LuaStateId) { if (!mIsLuaConsole) { if (!mLuaEngine) { diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index 356d07c..dfa8776 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -165,7 +165,7 @@ void TNetwork::TCPServerMain() { // set timeout size_t SendTimeoutMS = 30 * 1000; #if defined(BEAMMP_WINDOWS) - ret = ::setsockopt(client.Socket, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast(&ms), sizeof(ms)); + 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); diff --git a/src/main.cpp b/src/main.cpp index 3a1eae9..99d194f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -119,15 +119,14 @@ int BeamMPServerMain(MainArguments Arguments) { } Application::SetSubsystemStatus("Main", Application::Status::Starting); - bool Success = Application::Console().Internal().enable_write_to_file("Server.log"); - if (!Success) { - beammp_error("unable to open file for writing: \"Server.log\""); - } + + Application::Console().StartLoggingToFile(); SetupSignalHandlers(); bool Shutdown = false; Application::RegisterShutdownHandler([&Shutdown] { + beammp_info("If this takes too long, you can press Ctrl+C repeatedly to force a shutdown."); Application::SetSubsystemStatus("Main", Application::Status::ShuttingDown); Shutdown = true; }); @@ -172,6 +171,10 @@ int BeamMPServerMain(MainArguments Arguments) { Application::SetSubsystemStatus("Main", Application::Status::Good); RegisterThread("Main(Waiting)"); + std::set IgnoreSubsystems { + "UpdateCheck" // Ignore as not to confuse users (non-vital system) + }; + bool FullyStarted = false; while (!Shutdown) { if (!FullyStarted) { @@ -180,6 +183,9 @@ int BeamMPServerMain(MainArguments Arguments) { std::string SystemsBadList {}; auto Statuses = Application::GetSubsystemStatuses(); for (const auto& NameStatusPair : Statuses) { + if (IgnoreSubsystems.count(NameStatusPair.first) > 0) { + continue; // ignore + } if (NameStatusPair.second == Application::Status::Starting) { FullyStarted = false; } else if (NameStatusPair.second == Application::Status::Bad) {