diff --git a/include/Settings.h b/include/Settings.h index 1ef8ea12..3034c1ce 100644 --- a/include/Settings.h +++ b/include/Settings.h @@ -86,7 +86,8 @@ struct Settings { General_LogChat, General_ResourceFolder, General_Debug, - General_AllowGuests + General_AllowGuests, + General_InformationPacket, }; Sync<std::unordered_map<Key, SettingsTypeVariant>> SettingsMap; diff --git a/include/THeartbeatThread.h b/include/THeartbeatThread.h index 5a0c1eec..f81534c1 100644 --- a/include/THeartbeatThread.h +++ b/include/THeartbeatThread.h @@ -29,6 +29,7 @@ class THeartbeatThread : public IThreaded { //~THeartbeatThread(); void operator()() override; + static inline std::string lastCall = ""; private: std::string GenerateCall(); std::string GetPlayers(); diff --git a/src/LuaAPI.cpp b/src/LuaAPI.cpp index 582da467..4da07b3d 100644 --- a/src/LuaAPI.cpp +++ b/src/LuaAPI.cpp @@ -317,6 +317,14 @@ void LuaAPI::MP::Set(int ConfigID, sol::object NewValue) { beammp_lua_error("set invalid argument [2] expected string"); } break; + case 7: // Information packet + if (NewValue.is<bool>()) { + Application::Settings.set(Settings::Key::General_InformationPacket, NewValue.as<bool>()); + beammp_info(std::string("Set `InformationPacket` to ") + (Application::Settings.getAsBool(Settings::Key::General_InformationPacket) ? "true" : "false")); + } else { + beammp_lua_error("set invalid argument [2] expected boolean"); + } + break; default: beammp_warn("Invalid config ID \"" + std::to_string(ConfigID) + "\". Use `MP.Settings.*` enum for this."); break; @@ -339,6 +347,8 @@ TLuaValue LuaAPI::MP::Get(int ConfigID) { return Application::Settings.getAsString(Settings::Key::General_Name); case 6: // Desc return Application::Settings.getAsString(Settings::Key::General_Description); + case 7: // Information packet + return Application::Settings.getAsBool(Settings::Key::General_InformationPacket); default: beammp_warn("Invalid config ID \"" + std::to_string(ConfigID) + "\". Use `MP.Settings.*` enum for this."); return 0; diff --git a/src/Settings.cpp b/src/Settings.cpp index a82a66b8..d2393cd3 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -34,6 +34,7 @@ Settings::Settings() { { General_ResourceFolder, std::string("Resources") }, { General_Debug, false }, { General_AllowGuests, true }, + { General_InformationPacket, true }, { Misc_SendErrorsShowMessage, true }, { Misc_SendErrors, true }, { Misc_ImScaredOfUpdates, true }, @@ -54,6 +55,7 @@ Settings::Settings() { { { "General", "ResourceFolder" }, { General_ResourceFolder, READ_ONLY } }, { { "General", "Debug" }, { General_Debug, READ_WRITE } }, { { "General", "AllowGuests" }, { General_AllowGuests, READ_WRITE } }, + { { "General", "InformationPacket" }, { General_InformationPacket, READ_WRITE } }, { { "Misc", "SendErrorsShowMessage" }, { Misc_SendErrorsShowMessage, READ_WRITE } }, { { "Misc", "SendErrors" }, { Misc_SendErrors, READ_WRITE } }, { { "Misc", "ImScaredOfUpdates" }, { Misc_ImScaredOfUpdates, READ_WRITE } }, diff --git a/src/TConfig.cpp b/src/TConfig.cpp index c556b042..66c0eff9 100644 --- a/src/TConfig.cpp +++ b/src/TConfig.cpp @@ -56,6 +56,8 @@ static constexpr std::string_view StrLogChat = "LogChat"; static constexpr std::string_view EnvStrLogChat = "BEAMMP_LOG_CHAT"; static constexpr std::string_view StrAllowGuests = "AllowGuests"; static constexpr std::string_view EnvStrAllowGuests = "BEAMMP_ALLOW_GUESTS"; +static constexpr std::string_view StrInformationPacket = "InformationPacket"; +static constexpr std::string_view EnvStrInformationPacket = "BEAMMP_INFORMATION_PACKET"; static constexpr std::string_view StrPassword = "Password"; // Misc @@ -132,6 +134,8 @@ void TConfig::FlushToFile() { SetComment(data["General"][StrLogChat.data()].comments(), " Whether to log chat messages in the console / log"); data["General"][StrDebug.data()] = Application::Settings.getAsBool(Settings::Key::General_Debug); data["General"][StrPrivate.data()] = Application::Settings.getAsBool(Settings::Key::General_Private); + SetComment(data["General"][StrInformationPacket.data()].comments(), " Whether to allow unconnected clients to get the public server information without joining"); + data["General"][StrInformationPacket.data()] = Application::Settings.getAsBool(Settings::Key::General_InformationPacket); data["General"][StrAllowGuests.data()] = Application::Settings.getAsBool(Settings::Key::General_AllowGuests); SetComment(data["General"][StrAllowGuests.data()].comments(), " Whether to allow guests"); data["General"][StrPort.data()] = Application::Settings.getAsInt(Settings::Key::General_Port); @@ -248,6 +252,7 @@ void TConfig::ParseFromFile(std::string_view name) { // Read into new Settings Singleton TryReadValue(data, "General", StrDebug, EnvStrDebug, Settings::Key::General_Debug); TryReadValue(data, "General", StrPrivate, EnvStrPrivate, Settings::Key::General_Private); + TryReadValue(data, "General", StrInformationPacket, EnvStrInformationPacket, Settings::Key::General_InformationPacket); TryReadValue(data, "General", StrPort, EnvStrPort, Settings::Key::General_Port); TryReadValue(data, "General", StrMaxCars, EnvStrMaxCars, Settings::Key::General_MaxCars); TryReadValue(data, "General", StrMaxPlayers, EnvStrMaxPlayers, Settings::Key::General_MaxPlayers); @@ -299,6 +304,7 @@ void TConfig::PrintDebug() { } beammp_debug(std::string(StrDebug) + ": " + std::string(Application::Settings.getAsBool(Settings::Key::General_Debug) ? "true" : "false")); beammp_debug(std::string(StrPrivate) + ": " + std::string(Application::Settings.getAsBool(Settings::Key::General_Private) ? "true" : "false")); + beammp_debug(std::string(StrInformationPacket) + ": " + std::string(Application::Settings.getAsBool(Settings::Key::General_InformationPacket) ? "true" : "false")); beammp_debug(std::string(StrPort) + ": " + std::to_string(Application::Settings.getAsInt(Settings::Key::General_Port))); beammp_debug(std::string(StrMaxCars) + ": " + std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxCars))); beammp_debug(std::string(StrMaxPlayers) + ": " + std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxPlayers))); diff --git a/src/THeartbeatThread.cpp b/src/THeartbeatThread.cpp index 303cc9b1..057d70da 100644 --- a/src/THeartbeatThread.cpp +++ b/src/THeartbeatThread.cpp @@ -23,6 +23,7 @@ #include "Common.h" #include "Http.h" // #include "SocketIO.h" +#include <nlohmann/json.hpp> #include <rapidjson/document.h> #include <rapidjson/rapidjson.h> #include <sstream> @@ -65,7 +66,7 @@ void THeartbeatThread::operator()() { json::Document Doc; 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" } }); + T = Http::POST(Url, 443, Target, Body, "application/json", &ResponseCode, { { "api-v", "2" } }); Doc.Parse(T.data(), T.size()); if (Doc.HasParseError() || !Doc.IsObject()) { if (!Application::Settings.getAsBool(Settings::Key::General_Private)) { @@ -138,25 +139,30 @@ void THeartbeatThread::operator()() { } std::string THeartbeatThread::GenerateCall() { - std::stringstream Ret; + nlohmann::json Ret = { + { "players", mServer.ClientCount() }, + { "maxplayers", Application::Settings.getAsInt(Settings::Key::General_MaxPlayers) }, + { "port", Application::Settings.getAsInt(Settings::Key::General_Port) }, + { "map", Application::Settings.getAsString(Settings::Key::General_Map) }, + { "private", Application::Settings.getAsBool(Settings::Key::General_Private) }, + { "version", Application::ServerVersionString() }, + { "clientversion", Application::ClientMinimumVersion().AsString() }, + { "name", Application::Settings.getAsString(Settings::Key::General_Name) }, + { "tags", Application::Settings.getAsString(Settings::Key::General_Tags) }, + { "guests", Application::Settings.getAsBool(Settings::Key::General_AllowGuests) }, + { "modlist", mResourceManager.TrimmedList() }, + { "modstotalsize", mResourceManager.MaxModSize() }, + { "modstotal", mResourceManager.ModsLoaded() }, + { "playerslist", GetPlayers() }, + { "desc", Application::Settings.getAsString(Settings::Key::General_Description) } + }; - Ret << "uuid=" << Application::Settings.getAsString(Settings::Key::General_AuthKey) - << "&players=" << mServer.ClientCount() - << "&maxplayers=" << Application::Settings.getAsInt(Settings::Key::General_MaxPlayers) - << "&port=" << Application::Settings.getAsInt(Settings::Key::General_Port) - << "&map=" << Application::Settings.getAsString(Settings::Key::General_Map) - << "&private=" << (Application::Settings.getAsBool(Settings::Key::General_Private) ? "true" : "false") - << "&version=" << Application::ServerVersionString() - << "&clientversion=" << Application::ClientMinimumVersion().AsString() - << "&name=" << Application::Settings.getAsString(Settings::Key::General_Name) - << "&tags=" << Application::Settings.getAsString(Settings::Key::General_Tags) - << "&guests=" << (Application::Settings.getAsBool(Settings::Key::General_AllowGuests) ? "true" : "false") - << "&modlist=" << mResourceManager.TrimmedList() - << "&modstotalsize=" << mResourceManager.MaxModSize() - << "&modstotal=" << mResourceManager.ModsLoaded() - << "&playerslist=" << GetPlayers() - << "&desc=" << Application::Settings.getAsString(Settings::Key::General_Description); - return Ret.str(); + lastCall = Ret.dump(); + + // Add sensitive information here because value of lastCall is used for the information packet. + Ret["uuid"] = Application::Settings.getAsString(Settings::Key::General_AuthKey); + + return Ret.dump(); } THeartbeatThread::THeartbeatThread(TResourceManager& ResourceManager, TServer& Server) : mResourceManager(ResourceManager) diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 42440b11..fb3f4c69 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -1024,7 +1024,8 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, TLuaStateI "MaxPlayers", 3, "Map", 4, "Name", 5, - "Description", 6); + "Description", 6, + "InformationPacket", 7); MPTable.create_named("CallStrategy", "BestEffort", CallStrategy::BestEffort, diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index f89035a4..6fc0b875 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -20,6 +20,7 @@ #include "Client.h" #include "Common.h" #include "LuaAPI.h" +#include "THeartbeatThread.h" #include "TLuaEngine.h" #include "TScopedTimer.h" #include "nlohmann/json.hpp" @@ -246,6 +247,9 @@ void TNetwork::Identify(TConnection&& RawConnection) { boost::system::error_code ec; write(RawConnection.Socket, buffer("P"), ec); return; + } else if (Code == 'I') { + boost::system::error_code ec; + write(RawConnection.Socket, buffer(Application::Settings.getAsBool(Settings::Key::General_InformationPacket) ? THeartbeatThread::lastCall : ""), ec); } else { beammp_errorf("Invalid code got in Identify: '{}'", Code); }