Compare commits

...

29 Commits

Author SHA1 Message Date
Tixx
976ab68ca3 Bump version 2024-11-05 23:13:46 +01:00
Tixx
58a76e1df2 Add some information about tags in the build instructions (#386) 2024-11-05 10:30:05 +01:00
O1LER
c696276fc3 Update README.md
Co-authored-by: Tixx <83774803+WiserTixx@users.noreply.github.com>
2024-11-02 23:30:22 +01:00
O1LER
6ce0608bb3 Third times the charm 2024-11-02 14:35:17 +01:00
O1LER
52ad237419 Fix bracket skill issue 2024-11-02 14:33:52 +01:00
O1LER
71038dc617 Hide links 2024-11-02 14:32:47 +01:00
O1LER
dc3bb517a3 Update README.md
Co-authored-by: Tixx <83774803+WiserTixx@users.noreply.github.com>
2024-11-02 14:29:48 +01:00
O1LER
7b2c48c8d4 Add some information about tags in the build instructions 2024-11-02 14:04:09 +01:00
Lion
ef557ebfc4 Fix backend heartbeat (#385) 2024-11-01 17:40:33 +01:00
Tixx
b2e953b92a Fix JSON heartbeat request 2024-11-01 17:30:17 +01:00
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
12 changed files with 98 additions and 40 deletions

View File

@@ -38,7 +38,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 `v3.1.0`.
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.4.1`. See [the tags](https://github.com/BeamMP/BeamMP-Server/tags) for possible versions/tags, as well as [the releases](https://github.com/BeamMP/BeamMP-Server/releases) to check which version is marked as a release/prerelease. We recommend using the [latest release](https://github.com/BeamMP/BeamMP-Server/releases/latest).
## Supported Operating Systems
@@ -65,10 +65,11 @@ If you are building for ARM (like aarch64), you need to run `export VCPKG_FORCE_
You can build on **Windows, Linux** or other platforms by following these steps:
1. Check out the repository with git: `git clone --recursive https://github.com/BeamMP/BeamMP-Server`.
2. Go into the directory `cd BeamMP-Server`.
3. Run CMake `cmake -S . -B bin -DCMAKE_BUILD_TYPE=Release` - this can take a few minutes and may take a lot of disk space and bandwidth.
4. Build via `cmake --build bin --parallel --config Release -t BeamMP-Server`.
5. Your executable can be found in `bin/`.
2. Specify the server version to build by checking out a tag: `git checkout tags/v3.4.1` - [Possible versions/tags](https://github.com/BeamMP/BeamMP-Server/tags)
3. Go into the directory `cd BeamMP-Server`.
4. Run CMake `cmake -S . -B bin -DCMAKE_BUILD_TYPE=Release` - this can take a few minutes and may take a lot of disk space and bandwidth.
5. Build via `cmake --build bin --parallel --config Release -t BeamMP-Server`.
6. Your executable can be found in `bin/`.
When you make changes to the code, you only have to run step 4 again.
### Building for FreeBSD

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, 1 };
};
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__))

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;

View File

@@ -29,6 +29,7 @@ public:
//~THeartbeatThread();
void operator()() override;
static inline std::string lastCall = "";
private:
std::string GenerateCall();
std::string GetPlayers();

View File

@@ -43,5 +43,5 @@ private:
int mModsLoaded = 0;
std::mutex mModsMutex;
nlohmann::json mMods;
nlohmann::json mMods = nlohmann::json::array();
};

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;

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 } },

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)));

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", std::to_string(mServer.ClientCount()) },
{ "maxplayers", std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxPlayers)) },
{ "port", std::to_string(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", std::to_string(mResourceManager.MaxModSize()) },
{ "modstotal", std::to_string(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)

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,

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 {

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;