Compare commits

..

19 Commits

Author SHA1 Message Date
Lion Kortlepel e3416804e4 bump version 2024-11-01 12:50:11 +01:00
Lion 9ad4f61209 Information packet (#382)
This PR adds an "I" packet which returns the server information. This
can be used by external programs and the launcher to get information
about a server without having to connect it to. It can be toggled in the
config and in lua.
2024-11-01 12:27:31 +01:00
Tixx aed6311146 Run clang-format on THeartbeatThread.cpp 2024-11-01 12:22:56 +01:00
Lion 5e41cefa87 Paint packet (#381)
Adds a packet for live color updating `vid:[{}]`
2024-11-01 12:18:06 +01:00
Tixx b1710ee826 Add explanation for why uuid is added later 2024-10-20 18:33:49 +02:00
Lion 03b0bd4d2c Add 'P' packet on UDP (#379)
This can be used to check if UDP works :)

I've tested this a bit with snep ingame, even spamming this on localhost
with a large number of connections seems to not impact gameplay at all.
2024-10-20 18:29:12 +02:00
Tixx 5179ac1fdc Paint packet 2024-10-20 15:38:07 +02:00
Tixx 54e31ce2ec Move backend heartbeat to json 2024-10-14 00:42:14 +02:00
Tixx 4abe9b8636 Add informationpacket setting to the config 2024-10-14 00:39:12 +02:00
Tixx 956d6f50e1 Add setting for the information packet 2024-10-14 00:39:12 +02:00
Tixx 6aeb2eb736 Add server identification packet 2024-10-14 00:39:12 +02:00
Lion Kortlepel f40d4c1ddd add 'P' packet on UDP
this can be used to check if UDP works :)
2024-10-13 22:13:05 +02:00
Lion 4f052a3e0a Make modlist an empty array by default instead of null (#377)
Related launcher PR: https://github.com/BeamMP/BeamMP-Launcher/pull/136
2024-10-12 21:06:41 +02:00
Tixx 2bf9e7d916 Make modlist [] by default instead of null 2024-10-09 19:42:47 +02:00
Lion 89c906232d Report correct client minimum version to the backend (#376) 2024-10-09 18:06:03 +02:00
Lion c39beb5b72 reuse minclientversion where possible
Co-authored-by: Tixx <83774803+WiserTixx@users.noreply.github.com>
2024-10-09 18:03:01 +02:00
Lion Kortlepel 7dd2d89ad9 clarify auth version reject message 2024-10-09 16:48:40 +02:00
Lion Kortlepel 3403c8acba fix version check on auth 2024-10-09 16:44:38 +02:00
Lion Kortlepel 0a6eecee69 report correct client minimum version to the backend 2024-10-09 16:37:16 +02:00
11 changed files with 92 additions and 35 deletions
+7 -7
View File
@@ -74,7 +74,7 @@ public:
static TConsole& Console() { return mConsole; }
static std::string ServerVersionString();
static const Version& ServerVersion() { return mVersion; }
static uint8_t ClientMajorVersion() { return 2; }
static Version ClientMinimumVersion() { return Version { 2, 2, 0 }; }
static std::string PPS() { return mPPS; }
static void SetPPS(const std::string& NewPPS) { mPPS = NewPPS; }
@@ -127,7 +127,7 @@ private:
static inline std::mutex mShutdownHandlersMutex {};
static inline std::deque<TShutdownHandler> mShutdownHandlers {};
static inline Version mVersion { 3, 6, 0 };
static inline Version mVersion { 3, 7, 0 };
};
void SplitString(std::string const& str, const char delim, std::vector<std::string>& out);
@@ -162,13 +162,13 @@ void RegisterThread(const std::string& str);
#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)
@@ -178,7 +178,7 @@ void RegisterThread(const std::string& str);
#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) \
@@ -221,7 +221,7 @@ void RegisterThread(const std::string& str);
#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_debugf(...) beammp_debug(fmt::format(__VA_ARGS__))
+2 -1
View File
@@ -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;
+1
View File
@@ -29,6 +29,7 @@ public:
//~THeartbeatThread();
void operator()() override;
static inline std::string lastCall = "";
private:
std::string GenerateCall();
std::string GetPlayers();
+1 -1
View File
@@ -43,5 +43,5 @@ private:
int mModsLoaded = 0;
std::mutex mModsMutex;
nlohmann::json mMods;
nlohmann::json mMods = nlohmann::json::array();
};
+10
View File
@@ -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;
+2
View File
@@ -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 } },
+6
View File
@@ -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)));
+26 -19
View File
@@ -20,8 +20,10 @@
#include "ChronoWrapper.h"
#include "Client.h"
#include "Common.h"
#include "Http.h"
// #include "SocketIO.h"
#include <nlohmann/json.hpp>
#include <rapidjson/document.h>
#include <rapidjson/rapidjson.h>
#include <sstream>
@@ -64,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)) {
@@ -137,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=" << std::to_string(Application::ClientMajorVersion()) + ".0" // FIXME: Wtf.
<< "&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)
+2 -1
View File
@@ -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,
+21 -6
View File
@@ -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"
@@ -112,9 +113,19 @@ void TNetwork::UDPServerMain() {
try {
ip::udp::endpoint remote_client_ep {};
std::vector<uint8_t> Data = UDPRcvFromClient(remote_client_ep);
auto Pos = std::find(Data.begin(), Data.end(), ':');
if (Data.empty() || Pos > Data.begin() + 2)
if (Data.empty()) {
continue;
}
if (Data.size() == 1 && Data.at(0) == 'P') {
mUDPSock.send_to(const_buffer("P", 1), remote_client_ep, {}, ec);
// ignore errors
(void)ec;
continue;
}
auto Pos = std::find(Data.begin(), Data.end(), ':');
if (Pos > Data.begin() + 2) {
continue;
}
uint8_t ID = uint8_t(Data.at(0)) - 1;
mServer.ForEachClient([&](std::weak_ptr<TClient> ClientPtr) -> bool {
std::shared_ptr<TClient> Client;
@@ -246,6 +257,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);
}
@@ -293,10 +307,11 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
if (Data.size() > 3 && std::equal(Data.begin(), Data.begin() + VC.size(), VC.begin(), VC.end())) {
std::string ClientVersionStr(reinterpret_cast<const char*>(Data.data() + 2), Data.size() - 2);
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());
ClientKick(*Client, "Outdated Version!");
Version MinClientVersion = Application::ClientMinimumVersion();
if (Application::IsOutdated(ClientVersion, MinClientVersion)) {
beammp_errorf("Client tried to connect with version '{}', but only versions >= {} are allowed",
ClientVersion.AsString(), MinClientVersion.AsString());
ClientKick(*Client, fmt::format("Outdated version, launcher version >={} required to join!", MinClientVersion.AsString()));
return nullptr;
}
} else {
+14
View File
@@ -436,6 +436,20 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
Network.SendToAll(&c, StringToVector(Packet), false, true);
return;
}
case 'p': {
beammp_trace(std::string(("got 'Op' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
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()) {
Data = Data.substr(Data.find('['));
LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onVehiclePaintChanged", "", c.GetID(), VID, Data));
Network.SendToAll(&c, StringToVector(Packet), false, true);
}
return;
}
default:
beammp_trace(std::string(("possibly not implemented: '") + Packet + ("' (") + std::to_string(Packet.size()) + (")")));
return;