mirror of
https://github.com/BeamMP/BeamMP-Server.git
synced 2026-07-18 12:44:11 +00:00
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e3416804e4 | |||
| 9ad4f61209 | |||
| aed6311146 | |||
| 5e41cefa87 | |||
| b1710ee826 | |||
| 03b0bd4d2c | |||
| 5179ac1fdc | |||
| 54e31ce2ec | |||
| 4abe9b8636 | |||
| 956d6f50e1 | |||
| 6aeb2eb736 | |||
| f40d4c1ddd | |||
| 4f052a3e0a | |||
| 2bf9e7d916 | |||
| 89c906232d | |||
| c39beb5b72 | |||
| 7dd2d89ad9 | |||
| 3403c8acba | |||
| 0a6eecee69 | |||
| cf3985ce00 | |||
| b04c5068ea | |||
| 077bb6b1cd | |||
| 0850cde1fb | |||
| 611e53b484 | |||
| f039f57f11 | |||
| 5d34090952 | |||
| 88ca17236a | |||
| a4b62d013c | |||
| 9a0270cb09 | |||
| 55f1a3c734 | |||
| bb3c762d68 | |||
| 3ade7f5743 | |||
| 9d44c2063c | |||
| 17185da53b | |||
| 623dfa17d5 | |||
| 7f69e336a9 | |||
| f08dfc0585 | |||
| a9dee2bec5 | |||
| 5319c2878a | |||
| 73f494041a | |||
| caafb216c9 |
@@ -66,7 +66,6 @@ public:
|
|||||||
std::string GetCarData(int Ident);
|
std::string GetCarData(int Ident);
|
||||||
std::string GetCarPositionRaw(int Ident);
|
std::string GetCarPositionRaw(int Ident);
|
||||||
void SetUDPAddr(const ip::udp::endpoint& Addr) { mUDPAddress = Addr; }
|
void SetUDPAddr(const ip::udp::endpoint& Addr) { mUDPAddress = Addr; }
|
||||||
void SetDownSock(ip::tcp::socket&& CSock) { mDownSocket = std::move(CSock); }
|
|
||||||
void SetTCPSock(ip::tcp::socket&& CSock) { mSocket = std::move(CSock); }
|
void SetTCPSock(ip::tcp::socket&& CSock) { mSocket = std::move(CSock); }
|
||||||
void Disconnect(std::string_view Reason);
|
void Disconnect(std::string_view Reason);
|
||||||
bool IsDisconnected() const { return !mSocket.is_open(); }
|
bool IsDisconnected() const { return !mSocket.is_open(); }
|
||||||
@@ -75,8 +74,6 @@ public:
|
|||||||
[[nodiscard]] const std::unordered_map<std::string, std::string>& GetIdentifiers() const { return mIdentifiers; }
|
[[nodiscard]] const std::unordered_map<std::string, std::string>& GetIdentifiers() const { return mIdentifiers; }
|
||||||
[[nodiscard]] const ip::udp::endpoint& GetUDPAddr() const { return mUDPAddress; }
|
[[nodiscard]] const ip::udp::endpoint& GetUDPAddr() const { return mUDPAddress; }
|
||||||
[[nodiscard]] ip::udp::endpoint& GetUDPAddr() { return mUDPAddress; }
|
[[nodiscard]] ip::udp::endpoint& GetUDPAddr() { return mUDPAddress; }
|
||||||
[[nodiscard]] ip::tcp::socket& GetDownSock() { return mDownSocket; }
|
|
||||||
[[nodiscard]] const ip::tcp::socket& GetDownSock() const { return mDownSocket; }
|
|
||||||
[[nodiscard]] ip::tcp::socket& GetTCPSock() { return mSocket; }
|
[[nodiscard]] ip::tcp::socket& GetTCPSock() { return mSocket; }
|
||||||
[[nodiscard]] const ip::tcp::socket& GetTCPSock() const { return mSocket; }
|
[[nodiscard]] const ip::tcp::socket& GetTCPSock() const { return mSocket; }
|
||||||
[[nodiscard]] std::string GetRoles() const { return mRole; }
|
[[nodiscard]] std::string GetRoles() const { return mRole; }
|
||||||
@@ -122,7 +119,6 @@ private:
|
|||||||
SparseArray<std::string> mVehiclePosition;
|
SparseArray<std::string> mVehiclePosition;
|
||||||
std::string mName = "Unknown Client";
|
std::string mName = "Unknown Client";
|
||||||
ip::tcp::socket mSocket;
|
ip::tcp::socket mSocket;
|
||||||
ip::tcp::socket mDownSocket;
|
|
||||||
ip::udp::endpoint mUDPAddress {};
|
ip::udp::endpoint mUDPAddress {};
|
||||||
int mUnicycleID = -1;
|
int mUnicycleID = -1;
|
||||||
std::string mRole;
|
std::string mRole;
|
||||||
|
|||||||
+13
-7
@@ -74,7 +74,7 @@ public:
|
|||||||
static TConsole& Console() { return mConsole; }
|
static TConsole& Console() { return mConsole; }
|
||||||
static std::string ServerVersionString();
|
static std::string ServerVersionString();
|
||||||
static const Version& ServerVersion() { return mVersion; }
|
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 std::string PPS() { return mPPS; }
|
||||||
static void SetPPS(const std::string& NewPPS) { mPPS = NewPPS; }
|
static void SetPPS(const std::string& NewPPS) { mPPS = NewPPS; }
|
||||||
|
|
||||||
@@ -127,7 +127,7 @@ private:
|
|||||||
static inline std::mutex mShutdownHandlersMutex {};
|
static inline std::mutex mShutdownHandlersMutex {};
|
||||||
static inline std::deque<TShutdownHandler> mShutdownHandlers {};
|
static inline std::deque<TShutdownHandler> mShutdownHandlers {};
|
||||||
|
|
||||||
static inline Version mVersion { 3, 5, 0 };
|
static inline Version mVersion { 3, 7, 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
void SplitString(std::string const& str, const char delim, std::vector<std::string>& out);
|
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
|
#else
|
||||||
#define _function_name std::string(__func__)
|
#define _function_name std::string(__func__)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
#define DEBUG
|
#define DEBUG
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(DEBUG)
|
#if defined(DEBUG)
|
||||||
|
|
||||||
// if this is defined, we will show the full function signature infront of
|
// if this is defined, we will show the full function signature infront of
|
||||||
// each info/debug/warn... call instead of the 'filename:line' format.
|
// each info/debug/warn... call instead of the 'filename:line' format.
|
||||||
#if defined(BMP_FULL_FUNCTION_NAMES)
|
#if defined(BMP_FULL_FUNCTION_NAMES)
|
||||||
@@ -178,7 +178,7 @@ void RegisterThread(const std::string& str);
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // defined(DEBUG)
|
#endif // defined(DEBUG)
|
||||||
|
|
||||||
#define beammp_warn(x) Application::Console().Write(_this_location + std::string("[WARN] ") + (x))
|
#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_info(x) Application::Console().Write(_this_location + std::string("[INFO] ") + (x))
|
||||||
#define beammp_error(x) \
|
#define beammp_error(x) \
|
||||||
@@ -221,7 +221,7 @@ void RegisterThread(const std::string& str);
|
|||||||
#else
|
#else
|
||||||
#define beammp_trace(x)
|
#define beammp_trace(x)
|
||||||
#endif // defined(DEBUG)
|
#endif // defined(DEBUG)
|
||||||
|
|
||||||
#define beammp_errorf(...) beammp_error(fmt::format(__VA_ARGS__))
|
#define beammp_errorf(...) beammp_error(fmt::format(__VA_ARGS__))
|
||||||
#define beammp_infof(...) beammp_info(fmt::format(__VA_ARGS__))
|
#define beammp_infof(...) beammp_info(fmt::format(__VA_ARGS__))
|
||||||
#define beammp_debugf(...) beammp_debug(fmt::format(__VA_ARGS__))
|
#define beammp_debugf(...) beammp_debug(fmt::format(__VA_ARGS__))
|
||||||
@@ -268,3 +268,9 @@ std::vector<uint8_t> DeComp(std::span<const uint8_t> input);
|
|||||||
|
|
||||||
std::string GetPlatformAgnosticErrorString();
|
std::string GetPlatformAgnosticErrorString();
|
||||||
#define S_DSN SU_RAW
|
#define S_DSN SU_RAW
|
||||||
|
|
||||||
|
class InvalidDataError : std::runtime_error {
|
||||||
|
public:
|
||||||
|
InvalidDataError() : std::runtime_error("Invalid data") {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -35,8 +35,10 @@ namespace MP {
|
|||||||
inline size_t GetPlayerCount() { return Engine->Server().ClientCount(); }
|
inline size_t GetPlayerCount() { return Engine->Server().ClientCount(); }
|
||||||
std::pair<bool, std::string> DropPlayer(int ID, std::optional<std::string> MaybeReason);
|
std::pair<bool, std::string> DropPlayer(int ID, std::optional<std::string> MaybeReason);
|
||||||
std::pair<bool, std::string> SendChatMessage(int ID, const std::string& Message);
|
std::pair<bool, std::string> SendChatMessage(int ID, const std::string& Message);
|
||||||
|
std::pair<bool, std::string> SendNotification(int ID, const std::string& Message, const std::string& Icon, const std::string& Category);
|
||||||
std::pair<bool, std::string> RemoveVehicle(int PlayerID, int VehicleID);
|
std::pair<bool, std::string> RemoveVehicle(int PlayerID, int VehicleID);
|
||||||
void Set(int ConfigID, sol::object NewValue);
|
void Set(int ConfigID, sol::object NewValue);
|
||||||
|
TLuaValue Get(int ConfigID);
|
||||||
bool IsPlayerGuest(int ID);
|
bool IsPlayerGuest(int ID);
|
||||||
bool IsPlayerConnected(int ID);
|
bool IsPlayerConnected(int ID);
|
||||||
void Sleep(size_t Ms);
|
void Sleep(size_t Ms);
|
||||||
|
|||||||
+2
-1
@@ -86,7 +86,8 @@ struct Settings {
|
|||||||
General_LogChat,
|
General_LogChat,
|
||||||
General_ResourceFolder,
|
General_ResourceFolder,
|
||||||
General_Debug,
|
General_Debug,
|
||||||
General_AllowGuests
|
General_AllowGuests,
|
||||||
|
General_InformationPacket,
|
||||||
};
|
};
|
||||||
|
|
||||||
Sync<std::unordered_map<Key, SettingsTypeVariant>> SettingsMap;
|
Sync<std::unordered_map<Key, SettingsTypeVariant>> SettingsMap;
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ public:
|
|||||||
//~THeartbeatThread();
|
//~THeartbeatThread();
|
||||||
void operator()() override;
|
void operator()() override;
|
||||||
|
|
||||||
|
static inline std::string lastCall = "";
|
||||||
private:
|
private:
|
||||||
std::string GenerateCall();
|
std::string GenerateCall();
|
||||||
std::string GetPlayers();
|
std::string GetPlayers();
|
||||||
|
|||||||
@@ -263,6 +263,7 @@ private:
|
|||||||
sol::table Lua_TriggerGlobalEvent(const std::string& EventName, sol::variadic_args EventArgs);
|
sol::table Lua_TriggerGlobalEvent(const std::string& EventName, sol::variadic_args EventArgs);
|
||||||
sol::table Lua_TriggerLocalEvent(const std::string& EventName, sol::variadic_args EventArgs);
|
sol::table Lua_TriggerLocalEvent(const std::string& EventName, sol::variadic_args EventArgs);
|
||||||
sol::table Lua_GetPlayerIdentifiers(int ID);
|
sol::table Lua_GetPlayerIdentifiers(int ID);
|
||||||
|
std::variant<std::string, sol::nil_t> Lua_GetPlayerRole(int ID);
|
||||||
sol::table Lua_GetPlayers();
|
sol::table Lua_GetPlayers();
|
||||||
std::string Lua_GetPlayerName(int ID);
|
std::string Lua_GetPlayerName(int ID);
|
||||||
sol::table Lua_GetPlayerVehicles(int ID);
|
sol::table Lua_GetPlayerVehicles(int ID);
|
||||||
|
|||||||
+1
-2
@@ -58,7 +58,6 @@ private:
|
|||||||
std::mutex mOpenIDMutex;
|
std::mutex mOpenIDMutex;
|
||||||
|
|
||||||
std::vector<uint8_t> UDPRcvFromClient(ip::udp::endpoint& ClientEndpoint);
|
std::vector<uint8_t> UDPRcvFromClient(ip::udp::endpoint& ClientEndpoint);
|
||||||
void HandleDownload(TConnection&& TCPSock);
|
|
||||||
void OnConnect(const std::weak_ptr<TClient>& c);
|
void OnConnect(const std::weak_ptr<TClient>& c);
|
||||||
void TCPClient(const std::weak_ptr<TClient>& c);
|
void TCPClient(const std::weak_ptr<TClient>& c);
|
||||||
void Looper(const std::weak_ptr<TClient>& c);
|
void Looper(const std::weak_ptr<TClient>& c);
|
||||||
@@ -67,7 +66,7 @@ private:
|
|||||||
void Parse(TClient& c, const std::vector<uint8_t>& Packet);
|
void Parse(TClient& c, const std::vector<uint8_t>& Packet);
|
||||||
void SendFile(TClient& c, const std::string& Name);
|
void SendFile(TClient& c, const std::string& Name);
|
||||||
static bool TCPSendRaw(TClient& C, ip::tcp::socket& socket, const uint8_t* Data, size_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 void SendFileToClient(TClient& c, size_t Size, const std::string& Name);
|
||||||
static const uint8_t* SendSplit(TClient& c, ip::tcp::socket& Socket, const uint8_t* DataPtr, size_t Size);
|
static const uint8_t* SendSplit(TClient& c, ip::tcp::socket& Socket, const uint8_t* DataPtr, size_t Size);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
class TResourceManager {
|
class TResourceManager {
|
||||||
public:
|
public:
|
||||||
@@ -30,10 +31,17 @@ public:
|
|||||||
[[nodiscard]] std::string FileSizes() const { return mFileSizes; }
|
[[nodiscard]] std::string FileSizes() const { return mFileSizes; }
|
||||||
[[nodiscard]] int ModsLoaded() const { return mModsLoaded; }
|
[[nodiscard]] int ModsLoaded() const { return mModsLoaded; }
|
||||||
|
|
||||||
|
[[nodiscard]] std::string NewFileList() const;
|
||||||
|
|
||||||
|
void RefreshFiles();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t mMaxModSize = 0;
|
size_t mMaxModSize = 0;
|
||||||
std::string mFileSizes;
|
std::string mFileSizes;
|
||||||
std::string mFileList;
|
std::string mFileList;
|
||||||
std::string mTrimmedList;
|
std::string mTrimmedList;
|
||||||
int mModsLoaded = 0;
|
int mModsLoaded = 0;
|
||||||
|
|
||||||
|
std::mutex mModsMutex;
|
||||||
|
nlohmann::json mMods = nlohmann::json::array();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -144,7 +144,6 @@ void TClient::EnqueuePacket(const std::vector<uint8_t>& Packet) {
|
|||||||
TClient::TClient(TServer& Server, ip::tcp::socket&& Socket)
|
TClient::TClient(TServer& Server, ip::tcp::socket&& Socket)
|
||||||
: mServer(Server)
|
: mServer(Server)
|
||||||
, mSocket(std::move(Socket))
|
, mSocket(std::move(Socket))
|
||||||
, mDownSocket(ip::tcp::socket(Server.IoCtx()))
|
|
||||||
, mLastPingTime(std::chrono::high_resolution_clock::now()) {
|
, mLastPingTime(std::chrono::high_resolution_clock::now()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -418,7 +418,11 @@ std::vector<uint8_t> DeComp(std::span<const uint8_t> input) {
|
|||||||
output_size = output_buffer.size();
|
output_size = output_buffer.size();
|
||||||
} else if (res != Z_OK) {
|
} else if (res != Z_OK) {
|
||||||
beammp_error("zlib uncompress() failed: " + std::to_string(res));
|
beammp_error("zlib uncompress() failed: " + std::to_string(res));
|
||||||
throw std::runtime_error("zlib uncompress() failed");
|
if (res == Z_DATA_ERROR) {
|
||||||
|
throw InvalidDataError {};
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("zlib uncompress() failed");
|
||||||
|
}
|
||||||
} else if (res == Z_OK) {
|
} else if (res == Z_OK) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -205,6 +205,37 @@ std::pair<bool, std::string> LuaAPI::MP::SendChatMessage(int ID, const std::stri
|
|||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<bool, std::string> LuaAPI::MP::SendNotification(int ID, const std::string& Message, const std::string& Icon, const std::string& Category) {
|
||||||
|
std::pair<bool, std::string> Result;
|
||||||
|
std::string Packet = "N" + Category + ":" + Icon + ":" + Message;
|
||||||
|
if (ID == -1) {
|
||||||
|
Engine->Network().SendToAll(nullptr, StringToVector(Packet), true, true);
|
||||||
|
Result.first = true;
|
||||||
|
} else {
|
||||||
|
auto MaybeClient = GetClient(Engine->Server(), ID);
|
||||||
|
if (MaybeClient) {
|
||||||
|
auto c = MaybeClient.value().lock();
|
||||||
|
if (!c->IsSynced()) {
|
||||||
|
Result.first = false;
|
||||||
|
Result.second = "Player is not synced yet";
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
if (!Engine->Network().Respond(*c, StringToVector(Packet), true)) {
|
||||||
|
beammp_errorf("Failed to send notification to player (id {}) - did the player disconnect?", ID);
|
||||||
|
Result.first = false;
|
||||||
|
Result.second = "Failed to send packet";
|
||||||
|
}
|
||||||
|
Result.first = true;
|
||||||
|
} else {
|
||||||
|
beammp_lua_error("SendNotification invalid argument [1] invalid ID");
|
||||||
|
Result.first = false;
|
||||||
|
Result.second = "Invalid Player ID";
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
std::pair<bool, std::string> LuaAPI::MP::RemoveVehicle(int PID, int VID) {
|
std::pair<bool, std::string> LuaAPI::MP::RemoveVehicle(int PID, int VID) {
|
||||||
std::pair<bool, std::string> Result;
|
std::pair<bool, std::string> Result;
|
||||||
auto MaybeClient = GetClient(Engine->Server(), PID);
|
auto MaybeClient = GetClient(Engine->Server(), PID);
|
||||||
@@ -286,12 +317,44 @@ void LuaAPI::MP::Set(int ConfigID, sol::object NewValue) {
|
|||||||
beammp_lua_error("set invalid argument [2] expected string");
|
beammp_lua_error("set invalid argument [2] expected string");
|
||||||
}
|
}
|
||||||
break;
|
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:
|
default:
|
||||||
beammp_warn("Invalid config ID \"" + std::to_string(ConfigID) + "\". Use `MP.Settings.*` enum for this.");
|
beammp_warn("Invalid config ID \"" + std::to_string(ConfigID) + "\". Use `MP.Settings.*` enum for this.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TLuaValue LuaAPI::MP::Get(int ConfigID) {
|
||||||
|
switch (ConfigID) {
|
||||||
|
case 0: // debug
|
||||||
|
return Application::Settings.getAsBool(Settings::Key::General_Debug);
|
||||||
|
case 1: // private
|
||||||
|
return Application::Settings.getAsBool(Settings::Key::General_Private);
|
||||||
|
case 2: // max cars
|
||||||
|
return Application::Settings.getAsInt(Settings::Key::General_MaxCars);
|
||||||
|
case 3: // max players
|
||||||
|
return Application::Settings.getAsInt(Settings::Key::General_MaxPlayers);
|
||||||
|
case 4: // Map
|
||||||
|
return Application::Settings.getAsString(Settings::Key::General_Map);
|
||||||
|
case 5: // Name
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void LuaAPI::MP::Sleep(size_t Ms) {
|
void LuaAPI::MP::Sleep(size_t Ms) {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(Ms));
|
std::this_thread::sleep_for(std::chrono::milliseconds(Ms));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ Settings::Settings() {
|
|||||||
{ General_ResourceFolder, std::string("Resources") },
|
{ General_ResourceFolder, std::string("Resources") },
|
||||||
{ General_Debug, false },
|
{ General_Debug, false },
|
||||||
{ General_AllowGuests, true },
|
{ General_AllowGuests, true },
|
||||||
|
{ General_InformationPacket, true },
|
||||||
{ Misc_SendErrorsShowMessage, true },
|
{ Misc_SendErrorsShowMessage, true },
|
||||||
{ Misc_SendErrors, true },
|
{ Misc_SendErrors, true },
|
||||||
{ Misc_ImScaredOfUpdates, true },
|
{ Misc_ImScaredOfUpdates, true },
|
||||||
@@ -54,6 +55,7 @@ Settings::Settings() {
|
|||||||
{ { "General", "ResourceFolder" }, { General_ResourceFolder, READ_ONLY } },
|
{ { "General", "ResourceFolder" }, { General_ResourceFolder, READ_ONLY } },
|
||||||
{ { "General", "Debug" }, { General_Debug, READ_WRITE } },
|
{ { "General", "Debug" }, { General_Debug, READ_WRITE } },
|
||||||
{ { "General", "AllowGuests" }, { General_AllowGuests, READ_WRITE } },
|
{ { "General", "AllowGuests" }, { General_AllowGuests, READ_WRITE } },
|
||||||
|
{ { "General", "InformationPacket" }, { General_InformationPacket, READ_WRITE } },
|
||||||
{ { "Misc", "SendErrorsShowMessage" }, { Misc_SendErrorsShowMessage, READ_WRITE } },
|
{ { "Misc", "SendErrorsShowMessage" }, { Misc_SendErrorsShowMessage, READ_WRITE } },
|
||||||
{ { "Misc", "SendErrors" }, { Misc_SendErrors, READ_WRITE } },
|
{ { "Misc", "SendErrors" }, { Misc_SendErrors, READ_WRITE } },
|
||||||
{ { "Misc", "ImScaredOfUpdates" }, { Misc_ImScaredOfUpdates, READ_WRITE } },
|
{ { "Misc", "ImScaredOfUpdates" }, { Misc_ImScaredOfUpdates, READ_WRITE } },
|
||||||
|
|||||||
+7
-1
@@ -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 EnvStrLogChat = "BEAMMP_LOG_CHAT";
|
||||||
static constexpr std::string_view StrAllowGuests = "AllowGuests";
|
static constexpr std::string_view StrAllowGuests = "AllowGuests";
|
||||||
static constexpr std::string_view EnvStrAllowGuests = "BEAMMP_ALLOW_GUESTS";
|
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";
|
static constexpr std::string_view StrPassword = "Password";
|
||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
@@ -132,6 +134,8 @@ void TConfig::FlushToFile() {
|
|||||||
SetComment(data["General"][StrLogChat.data()].comments(), " Whether to log chat messages in the console / log");
|
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"][StrDebug.data()] = Application::Settings.getAsBool(Settings::Key::General_Debug);
|
||||||
data["General"][StrPrivate.data()] = Application::Settings.getAsBool(Settings::Key::General_Private);
|
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);
|
data["General"][StrAllowGuests.data()] = Application::Settings.getAsBool(Settings::Key::General_AllowGuests);
|
||||||
SetComment(data["General"][StrAllowGuests.data()].comments(), " Whether to allow guests");
|
SetComment(data["General"][StrAllowGuests.data()].comments(), " Whether to allow guests");
|
||||||
data["General"][StrPort.data()] = Application::Settings.getAsInt(Settings::Key::General_Port);
|
data["General"][StrPort.data()] = Application::Settings.getAsInt(Settings::Key::General_Port);
|
||||||
@@ -139,7 +143,7 @@ void TConfig::FlushToFile() {
|
|||||||
SetComment(data["General"][StrTags.data()].comments(), " Add custom identifying tags to your server to make it easier to find. Format should be TagA,TagB,TagC. Note the comma seperation.");
|
SetComment(data["General"][StrTags.data()].comments(), " Add custom identifying tags to your server to make it easier to find. Format should be TagA,TagB,TagC. Note the comma seperation.");
|
||||||
data["General"][StrTags.data()] = Application::Settings.getAsString(Settings::Key::General_Tags);
|
data["General"][StrTags.data()] = Application::Settings.getAsString(Settings::Key::General_Tags);
|
||||||
data["General"][StrMaxCars.data()] = Application::Settings.getAsInt(Settings::Key::General_MaxCars);
|
data["General"][StrMaxCars.data()] = Application::Settings.getAsInt(Settings::Key::General_MaxCars);
|
||||||
data["General"][StrMaxPlayers.data()] = Application::Settings.getAsInt(Settings::Key::General_MaxCars);
|
data["General"][StrMaxPlayers.data()] = Application::Settings.getAsInt(Settings::Key::General_MaxPlayers);
|
||||||
data["General"][StrMap.data()] = Application::Settings.getAsString(Settings::Key::General_Map);
|
data["General"][StrMap.data()] = Application::Settings.getAsString(Settings::Key::General_Map);
|
||||||
data["General"][StrDescription.data()] = Application::Settings.getAsString(Settings::Key::General_Description);
|
data["General"][StrDescription.data()] = Application::Settings.getAsString(Settings::Key::General_Description);
|
||||||
data["General"][StrResourceFolder.data()] = Application::Settings.getAsString(Settings::Key::General_ResourceFolder);
|
data["General"][StrResourceFolder.data()] = Application::Settings.getAsString(Settings::Key::General_ResourceFolder);
|
||||||
@@ -248,6 +252,7 @@ void TConfig::ParseFromFile(std::string_view name) {
|
|||||||
// Read into new Settings Singleton
|
// Read into new Settings Singleton
|
||||||
TryReadValue(data, "General", StrDebug, EnvStrDebug, Settings::Key::General_Debug);
|
TryReadValue(data, "General", StrDebug, EnvStrDebug, Settings::Key::General_Debug);
|
||||||
TryReadValue(data, "General", StrPrivate, EnvStrPrivate, Settings::Key::General_Private);
|
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", StrPort, EnvStrPort, Settings::Key::General_Port);
|
||||||
TryReadValue(data, "General", StrMaxCars, EnvStrMaxCars, Settings::Key::General_MaxCars);
|
TryReadValue(data, "General", StrMaxCars, EnvStrMaxCars, Settings::Key::General_MaxCars);
|
||||||
TryReadValue(data, "General", StrMaxPlayers, EnvStrMaxPlayers, Settings::Key::General_MaxPlayers);
|
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(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(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(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(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)));
|
beammp_debug(std::string(StrMaxPlayers) + ": " + std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxPlayers)));
|
||||||
|
|||||||
+26
-19
@@ -20,8 +20,10 @@
|
|||||||
|
|
||||||
#include "ChronoWrapper.h"
|
#include "ChronoWrapper.h"
|
||||||
#include "Client.h"
|
#include "Client.h"
|
||||||
|
#include "Common.h"
|
||||||
#include "Http.h"
|
#include "Http.h"
|
||||||
// #include "SocketIO.h"
|
// #include "SocketIO.h"
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
#include <rapidjson/document.h>
|
#include <rapidjson/document.h>
|
||||||
#include <rapidjson/rapidjson.h>
|
#include <rapidjson/rapidjson.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -64,7 +66,7 @@ void THeartbeatThread::operator()() {
|
|||||||
json::Document Doc;
|
json::Document Doc;
|
||||||
bool Ok = false;
|
bool Ok = false;
|
||||||
for (const auto& Url : Application::GetBackendUrlsInOrder()) {
|
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());
|
Doc.Parse(T.data(), T.size());
|
||||||
if (Doc.HasParseError() || !Doc.IsObject()) {
|
if (Doc.HasParseError() || !Doc.IsObject()) {
|
||||||
if (!Application::Settings.getAsBool(Settings::Key::General_Private)) {
|
if (!Application::Settings.getAsBool(Settings::Key::General_Private)) {
|
||||||
@@ -137,25 +139,30 @@ void THeartbeatThread::operator()() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string THeartbeatThread::GenerateCall() {
|
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)
|
lastCall = Ret.dump();
|
||||||
<< "&players=" << mServer.ClientCount()
|
|
||||||
<< "&maxplayers=" << Application::Settings.getAsInt(Settings::Key::General_MaxPlayers)
|
// Add sensitive information here because value of lastCall is used for the information packet.
|
||||||
<< "&port=" << Application::Settings.getAsInt(Settings::Key::General_Port)
|
Ret["uuid"] = Application::Settings.getAsString(Settings::Key::General_AuthKey);
|
||||||
<< "&map=" << Application::Settings.getAsString(Settings::Key::General_Map)
|
|
||||||
<< "&private=" << (Application::Settings.getAsBool(Settings::Key::General_Private) ? "true" : "false")
|
return Ret.dump();
|
||||||
<< "&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();
|
|
||||||
}
|
}
|
||||||
THeartbeatThread::THeartbeatThread(TResourceManager& ResourceManager, TServer& Server)
|
THeartbeatThread::THeartbeatThread(TResourceManager& ResourceManager, TServer& Server)
|
||||||
: mResourceManager(ResourceManager)
|
: mResourceManager(ResourceManager)
|
||||||
|
|||||||
+25
-1
@@ -595,6 +595,16 @@ sol::table TLuaEngine::StateThreadData::Lua_GetPlayerIdentifiers(int ID) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::variant<std::string, sol::nil_t> TLuaEngine::StateThreadData::Lua_GetPlayerRole(int ID) {
|
||||||
|
auto MaybeClient = GetClient(mEngine->Server(), ID);
|
||||||
|
if (MaybeClient) {
|
||||||
|
return MaybeClient.value().lock()->GetRoles();
|
||||||
|
} else {
|
||||||
|
return sol::nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
sol::table TLuaEngine::StateThreadData::Lua_GetPlayers() {
|
sol::table TLuaEngine::StateThreadData::Lua_GetPlayers() {
|
||||||
sol::table Result = mStateView.create_table();
|
sol::table Result = mStateView.create_table();
|
||||||
mEngine->Server().ForEachClient([&](std::weak_ptr<TClient> Client) -> bool {
|
mEngine->Server().ForEachClient([&](std::weak_ptr<TClient> Client) -> bool {
|
||||||
@@ -869,6 +879,15 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, TLuaStateI
|
|||||||
return Lua_GetPositionRaw(PID, VID);
|
return Lua_GetPositionRaw(PID, VID);
|
||||||
});
|
});
|
||||||
MPTable.set_function("SendChatMessage", &LuaAPI::MP::SendChatMessage);
|
MPTable.set_function("SendChatMessage", &LuaAPI::MP::SendChatMessage);
|
||||||
|
MPTable.set_function("SendNotification", [&](sol::variadic_args Args) {
|
||||||
|
if (Args.size() == 3) {
|
||||||
|
LuaAPI::MP::SendNotification(Args.get<int>(0), Args.get<std::string>(1), Args.get<std::string>(2), Args.get<std::string>(1));
|
||||||
|
} else if (Args.size() == 4) {
|
||||||
|
LuaAPI::MP::SendNotification(Args.get<int>(0), Args.get<std::string>(1), Args.get<std::string>(2), Args.get<std::string>(3));
|
||||||
|
} else {
|
||||||
|
beammp_lua_error("SendNotification expects 2 or 3 arguments.");
|
||||||
|
}
|
||||||
|
});
|
||||||
MPTable.set_function("GetPlayers", [&]() -> sol::table {
|
MPTable.set_function("GetPlayers", [&]() -> sol::table {
|
||||||
return Lua_GetPlayers();
|
return Lua_GetPlayers();
|
||||||
});
|
});
|
||||||
@@ -883,6 +902,9 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, TLuaStateI
|
|||||||
MPTable.set_function("GetPlayerIdentifiers", [&](int ID) -> sol::table {
|
MPTable.set_function("GetPlayerIdentifiers", [&](int ID) -> sol::table {
|
||||||
return Lua_GetPlayerIdentifiers(ID);
|
return Lua_GetPlayerIdentifiers(ID);
|
||||||
});
|
});
|
||||||
|
MPTable.set_function("GetPlayerRole", [&](int ID) -> std::variant<std::string, sol::nil_t> {
|
||||||
|
return Lua_GetPlayerRole(ID);
|
||||||
|
});
|
||||||
MPTable.set_function("Sleep", &LuaAPI::MP::Sleep);
|
MPTable.set_function("Sleep", &LuaAPI::MP::Sleep);
|
||||||
// const std::string& EventName, size_t IntervalMS, int strategy
|
// const std::string& EventName, size_t IntervalMS, int strategy
|
||||||
MPTable.set_function("CreateEventTimer", [&](sol::variadic_args Args) {
|
MPTable.set_function("CreateEventTimer", [&](sol::variadic_args Args) {
|
||||||
@@ -910,6 +932,7 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, TLuaStateI
|
|||||||
mEngine->CancelEventTimers(EventName, mStateId);
|
mEngine->CancelEventTimers(EventName, mStateId);
|
||||||
});
|
});
|
||||||
MPTable.set_function("Set", &LuaAPI::MP::Set);
|
MPTable.set_function("Set", &LuaAPI::MP::Set);
|
||||||
|
MPTable.set_function("Get", &LuaAPI::MP::Get);
|
||||||
|
|
||||||
auto UtilTable = StateView.create_named_table("Util");
|
auto UtilTable = StateView.create_named_table("Util");
|
||||||
UtilTable.set_function("LogDebug", [this](sol::variadic_args args) {
|
UtilTable.set_function("LogDebug", [this](sol::variadic_args args) {
|
||||||
@@ -1001,7 +1024,8 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, TLuaStateI
|
|||||||
"MaxPlayers", 3,
|
"MaxPlayers", 3,
|
||||||
"Map", 4,
|
"Map", 4,
|
||||||
"Name", 5,
|
"Name", 5,
|
||||||
"Description", 6);
|
"Description", 6,
|
||||||
|
"InformationPacket", 7);
|
||||||
|
|
||||||
MPTable.create_named("CallStrategy",
|
MPTable.create_named("CallStrategy",
|
||||||
"BestEffort", CallStrategy::BestEffort,
|
"BestEffort", CallStrategy::BestEffort,
|
||||||
|
|||||||
+72
-138
@@ -20,6 +20,7 @@
|
|||||||
#include "Client.h"
|
#include "Client.h"
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "LuaAPI.h"
|
#include "LuaAPI.h"
|
||||||
|
#include "THeartbeatThread.h"
|
||||||
#include "TLuaEngine.h"
|
#include "TLuaEngine.h"
|
||||||
#include "TScopedTimer.h"
|
#include "TScopedTimer.h"
|
||||||
#include "nlohmann/json.hpp"
|
#include "nlohmann/json.hpp"
|
||||||
@@ -112,9 +113,19 @@ void TNetwork::UDPServerMain() {
|
|||||||
try {
|
try {
|
||||||
ip::udp::endpoint remote_client_ep {};
|
ip::udp::endpoint remote_client_ep {};
|
||||||
std::vector<uint8_t> Data = UDPRcvFromClient(remote_client_ep);
|
std::vector<uint8_t> Data = UDPRcvFromClient(remote_client_ep);
|
||||||
auto Pos = std::find(Data.begin(), Data.end(), ':');
|
if (Data.empty()) {
|
||||||
if (Data.empty() || Pos > Data.begin() + 2)
|
|
||||||
continue;
|
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;
|
uint8_t ID = uint8_t(Data.at(0)) - 1;
|
||||||
mServer.ForEachClient([&](std::weak_ptr<TClient> ClientPtr) -> bool {
|
mServer.ForEachClient([&](std::weak_ptr<TClient> ClientPtr) -> bool {
|
||||||
std::shared_ptr<TClient> Client;
|
std::shared_ptr<TClient> Client;
|
||||||
@@ -240,16 +251,20 @@ void TNetwork::Identify(TConnection&& RawConnection) {
|
|||||||
if (Code == 'C') {
|
if (Code == 'C') {
|
||||||
Client = Authentication(std::move(RawConnection));
|
Client = Authentication(std::move(RawConnection));
|
||||||
} else if (Code == 'D') {
|
} else if (Code == 'D') {
|
||||||
HandleDownload(std::move(RawConnection));
|
beammp_errorf("Old download packet detected - the client is wildly out of date, this will be ignored");
|
||||||
|
return;
|
||||||
} else if (Code == 'P') {
|
} else if (Code == 'P') {
|
||||||
boost::system::error_code ec;
|
boost::system::error_code ec;
|
||||||
write(RawConnection.Socket, buffer("P"), ec);
|
write(RawConnection.Socket, buffer("P"), ec);
|
||||||
return;
|
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 {
|
} else {
|
||||||
beammp_errorf("Invalid code got in Identify: '{}'", Code);
|
beammp_errorf("Invalid code got in Identify: '{}'", Code);
|
||||||
}
|
}
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
beammp_errorf("Error during handling of code {} - client left in invalid state, closing socket", Code);
|
beammp_errorf("Error during handling of code {} - client left in invalid state, closing socket: {}", Code, e.what());
|
||||||
boost::system::error_code ec;
|
boost::system::error_code ec;
|
||||||
RawConnection.Socket.shutdown(socket_base::shutdown_both, ec);
|
RawConnection.Socket.shutdown(socket_base::shutdown_both, ec);
|
||||||
if (ec) {
|
if (ec) {
|
||||||
@@ -262,27 +277,7 @@ void TNetwork::Identify(TConnection&& RawConnection) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TNetwork::HandleDownload(TConnection&& Conn) {
|
|
||||||
char D;
|
|
||||||
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);
|
|
||||||
mServer.ForEachClient([&](const std::weak_ptr<TClient>& ClientPtr) -> bool {
|
|
||||||
ReadLock Lock(mServer.GetClientMutex());
|
|
||||||
if (!ClientPtr.expired()) {
|
|
||||||
auto c = ClientPtr.lock();
|
|
||||||
if (c->GetID() == ID) {
|
|
||||||
c->SetDownSock(std::move(Conn.Socket));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string HashPassword(const std::string& str) {
|
std::string HashPassword(const std::string& str) {
|
||||||
std::stringstream ret;
|
std::stringstream ret;
|
||||||
@@ -312,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())) {
|
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);
|
std::string ClientVersionStr(reinterpret_cast<const char*>(Data.data() + 2), Data.size() - 2);
|
||||||
Version ClientVersion = Application::VersionStrToInts(ClientVersionStr + ".0");
|
Version ClientVersion = Application::VersionStrToInts(ClientVersionStr + ".0");
|
||||||
if (ClientVersion.major != Application::ClientMajorVersion()) {
|
Version MinClientVersion = Application::ClientMinimumVersion();
|
||||||
beammp_errorf("Client tried to connect with version '{}', but only versions '{}.x.x' is allowed",
|
if (Application::IsOutdated(ClientVersion, MinClientVersion)) {
|
||||||
ClientVersion.AsString(), Application::ClientMajorVersion());
|
beammp_errorf("Client tried to connect with version '{}', but only versions >= {} are allowed",
|
||||||
ClientKick(*Client, "Outdated Version!");
|
ClientVersion.AsString(), MinClientVersion.AsString());
|
||||||
|
ClientKick(*Client, fmt::format("Outdated version, launcher version >={} required to join!", MinClientVersion.AsString()));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -404,10 +400,21 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
|
|||||||
|
|
||||||
auto Futures = LuaAPI::MP::Engine->TriggerEvent("onPlayerAuth", "", Client->GetName(), Client->GetRoles(), Client->IsGuest(), Client->GetIdentifiers());
|
auto Futures = LuaAPI::MP::Engine->TriggerEvent("onPlayerAuth", "", Client->GetName(), Client->GetRoles(), Client->IsGuest(), Client->GetIdentifiers());
|
||||||
TLuaEngine::WaitForAll(Futures);
|
TLuaEngine::WaitForAll(Futures);
|
||||||
bool NotAllowed = std::any_of(Futures.begin(), Futures.end(),
|
bool NotAllowed = false;
|
||||||
[](const std::shared_ptr<TLuaResult>& Result) {
|
bool BypassLimit = false;
|
||||||
return !Result->Error && Result->Result.is<int>() && bool(Result->Result.as<int>());
|
|
||||||
});
|
for (const auto& Result : Futures) {
|
||||||
|
if (!Result->Error && Result->Result.is<int>()) {
|
||||||
|
auto Res = Result->Result.as<int>();
|
||||||
|
|
||||||
|
if (Res == 1) {
|
||||||
|
NotAllowed = true;
|
||||||
|
break;
|
||||||
|
} else if (Res == 2) {
|
||||||
|
BypassLimit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
std::string Reason;
|
std::string Reason;
|
||||||
bool NotAllowedWithReason = std::any_of(Futures.begin(), Futures.end(),
|
bool NotAllowedWithReason = std::any_of(Futures.begin(), Futures.end(),
|
||||||
[&Reason](const std::shared_ptr<TLuaResult>& Result) -> bool {
|
[&Reason](const std::shared_ptr<TLuaResult>& Result) -> bool {
|
||||||
@@ -440,7 +447,7 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
|
|||||||
|
|
||||||
if (!Allowed) {
|
if (!Allowed) {
|
||||||
return {};
|
return {};
|
||||||
} else if (mServer.ClientCount() < size_t(Application::Settings.getAsInt(Settings::Key::General_MaxPlayers))) {
|
} else if (mServer.ClientCount() < size_t(Application::Settings.getAsInt(Settings::Key::General_MaxPlayers)) || BypassLimit) {
|
||||||
beammp_info("Identification success");
|
beammp_info("Identification success");
|
||||||
mServer.InsertClient(Client);
|
mServer.InsertClient(Client);
|
||||||
TCPClient(Client);
|
TCPClient(Client);
|
||||||
@@ -546,7 +553,17 @@ std::vector<uint8_t> TNetwork::TCPRcv(TClient& c) {
|
|||||||
constexpr std::string_view ABG = "ABG:";
|
constexpr std::string_view ABG = "ABG:";
|
||||||
if (Data.size() >= ABG.size() && std::equal(Data.begin(), Data.begin() + ABG.size(), ABG.begin(), ABG.end())) {
|
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());
|
Data.erase(Data.begin(), Data.begin() + ABG.size());
|
||||||
return DeComp(Data);
|
try {
|
||||||
|
return DeComp(Data);
|
||||||
|
} catch (const InvalidDataError& ) {
|
||||||
|
beammp_errorf("Failed to decompress packet from a client. The receive failed and the client may be disconnected as a result");
|
||||||
|
// return empty -> error
|
||||||
|
return std::vector<uint8_t>();
|
||||||
|
} catch (const std::runtime_error& e) {
|
||||||
|
beammp_errorf("Failed to decompress packet from a client: {}. The server may be out of RAM! The receive failed and the client may be disconnected as a result", e.what());
|
||||||
|
// return empty -> error
|
||||||
|
return std::vector<uint8_t>();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return Data;
|
return Data;
|
||||||
}
|
}
|
||||||
@@ -748,11 +765,11 @@ void TNetwork::Parse(TClient& c, const std::vector<uint8_t>& Packet) {
|
|||||||
case 'S':
|
case 'S':
|
||||||
if (SubCode == 'R') {
|
if (SubCode == 'R') {
|
||||||
beammp_debug("Sending Mod Info");
|
beammp_debug("Sending Mod Info");
|
||||||
std::string ToSend = mResourceManager.FileList() + mResourceManager.FileSizes();
|
std::string ToSend = mResourceManager.NewFileList();
|
||||||
if (ToSend.empty())
|
beammp_debugf("Mod Info: {}", ToSend);
|
||||||
ToSend = "-";
|
|
||||||
if (!TCPSend(c, StringToVector(ToSend))) {
|
if (!TCPSend(c, StringToVector(ToSend))) {
|
||||||
// TODO: error
|
ClientKick(c, "TCP Send 'SY' failed");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -762,8 +779,6 @@ void TNetwork::Parse(TClient& c, const std::vector<uint8_t>& Packet) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TNetwork::SendFile(TClient& c, const std::string& UnsafeName) {
|
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 (!fs::path(UnsafeName).has_filename()) {
|
||||||
if (!TCPSend(c, StringToVector("CO"))) {
|
if (!TCPSend(c, StringToVector("CO"))) {
|
||||||
// TODO: handle
|
// TODO: handle
|
||||||
@@ -786,87 +801,9 @@ void TNetwork::SendFile(TClient& c, const std::string& UnsafeName) {
|
|||||||
// TODO: handle
|
// TODO: handle
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wait for connections
|
|
||||||
int T = 0;
|
|
||||||
while (!c.GetDownSock().is_open() && T < 50) {
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
||||||
T++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!c.GetDownSock().is_open()) {
|
|
||||||
beammp_error("Client doesn't have a download socket!");
|
|
||||||
if (!c.IsDisconnected())
|
|
||||||
c.Disconnect("Missing download socket");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Size = size_t(std::filesystem::file_size(FileName));
|
size_t Size = size_t(std::filesystem::file_size(FileName));
|
||||||
size_t MSize = Size / 2;
|
|
||||||
|
|
||||||
std::thread SplitThreads[2] {
|
SendFileToClient(c, Size, FileName);
|
||||||
std::thread([&] {
|
|
||||||
RegisterThread("SplitLoad_0");
|
|
||||||
SplitLoad(c, 0, MSize, false, FileName);
|
|
||||||
}),
|
|
||||||
std::thread([&] {
|
|
||||||
RegisterThread("SplitLoad_1");
|
|
||||||
SplitLoad(c, MSize, Size, true, FileName);
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
for (auto& SplitThread : SplitThreads) {
|
|
||||||
if (SplitThread.joinable()) {
|
|
||||||
SplitThread.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::pair<size_t /* count */, size_t /* last chunk */> 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(BEAMMP_LINUX)
|
#if defined(BEAMMP_LINUX)
|
||||||
@@ -876,8 +813,8 @@ const uint8_t* /* end ptr */ TNetwork::SendSplit(TClient& c, ip::tcp::socket& So
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#endif
|
#endif
|
||||||
void TNetwork::SplitLoad(TClient& c, size_t Offset, size_t End, bool D, const std::string& Name) {
|
void TNetwork::SendFileToClient(TClient& c, size_t Size, const std::string& Name) {
|
||||||
TScopedTimer timer(fmt::format("Download of {}-{} for '{}'", Offset, End, Name));
|
TScopedTimer timer(fmt::format("Download of '{}' for client {}", Name, c.GetID()));
|
||||||
#if defined(BEAMMP_LINUX)
|
#if defined(BEAMMP_LINUX)
|
||||||
signal(SIGPIPE, SIG_IGN);
|
signal(SIGPIPE, SIG_IGN);
|
||||||
// on linux, we can use sendfile(2)!
|
// on linux, we can use sendfile(2)!
|
||||||
@@ -887,11 +824,11 @@ void TNetwork::SplitLoad(TClient& c, size_t Offset, size_t End, bool D, const st
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// native handle, needed in order to make native syscalls with it
|
// native handle, needed in order to make native syscalls with it
|
||||||
int socket = D ? c.GetDownSock().native_handle() : c.GetTCPSock().native_handle();
|
int socket = c.GetTCPSock().native_handle();
|
||||||
|
|
||||||
ssize_t ret = 0;
|
ssize_t ret = 0;
|
||||||
auto ToSendTotal = End - Offset;
|
auto ToSendTotal = Size;
|
||||||
auto Start = Offset;
|
auto Start = 0;
|
||||||
while (ret < ssize_t(ToSendTotal)) {
|
while (ret < ssize_t(ToSendTotal)) {
|
||||||
auto SysOffset = off_t(Start + size_t(ret));
|
auto SysOffset = off_t(Start + size_t(ret));
|
||||||
ret = sendfile(socket, fd, &SysOffset, ToSendTotal - size_t(ret));
|
ret = sendfile(socket, fd, &SysOffset, ToSendTotal - size_t(ret));
|
||||||
@@ -905,35 +842,32 @@ void TNetwork::SplitLoad(TClient& c, size_t Offset, size_t End, bool D, const st
|
|||||||
std::ifstream f(Name.c_str(), std::ios::binary);
|
std::ifstream f(Name.c_str(), std::ios::binary);
|
||||||
uint32_t Split = 125 * MB;
|
uint32_t Split = 125 * MB;
|
||||||
std::vector<uint8_t> Data;
|
std::vector<uint8_t> Data;
|
||||||
if (End > Split)
|
if (Size > Split)
|
||||||
Data.resize(Split);
|
Data.resize(Split);
|
||||||
else
|
else
|
||||||
Data.resize(End);
|
Data.resize(Size);
|
||||||
ip::tcp::socket* TCPSock { nullptr };
|
ip::tcp::socket* TCPSock = &c.GetTCPSock();
|
||||||
if (D)
|
std::streamsize Sent = 0;
|
||||||
TCPSock = &c.GetDownSock();
|
while (!c.IsDisconnected() && Sent < Size) {
|
||||||
else
|
size_t Diff = Size - Sent;
|
||||||
TCPSock = &c.GetTCPSock();
|
|
||||||
while (!c.IsDisconnected() && Offset < End) {
|
|
||||||
size_t Diff = End - Offset;
|
|
||||||
if (Diff > Split) {
|
if (Diff > Split) {
|
||||||
f.seekg(Offset, std::ios_base::beg);
|
f.seekg(Sent, std::ios_base::beg);
|
||||||
f.read(reinterpret_cast<char*>(Data.data()), Split);
|
f.read(reinterpret_cast<char*>(Data.data()), Split);
|
||||||
if (!TCPSendRaw(c, *TCPSock, Data.data(), Split)) {
|
if (!TCPSendRaw(c, *TCPSock, Data.data(), Split)) {
|
||||||
if (!c.IsDisconnected())
|
if (!c.IsDisconnected())
|
||||||
c.Disconnect("TCPSendRaw failed in mod download (1)");
|
c.Disconnect("TCPSendRaw failed in mod download (1)");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Offset += Split;
|
Sent += Split;
|
||||||
} else {
|
} else {
|
||||||
f.seekg(Offset, std::ios_base::beg);
|
f.seekg(Sent, std::ios_base::beg);
|
||||||
f.read(reinterpret_cast<char*>(Data.data()), Diff);
|
f.read(reinterpret_cast<char*>(Data.data()), Diff);
|
||||||
if (!TCPSendRaw(c, *TCPSock, Data.data(), int32_t(Diff))) {
|
if (!TCPSendRaw(c, *TCPSock, Data.data(), int32_t(Diff))) {
|
||||||
if (!c.IsDisconnected())
|
if (!c.IsDisconnected())
|
||||||
c.Disconnect("TCPSendRaw failed in mod download (2)");
|
c.Disconnect("TCPSendRaw failed in mod download (2)");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Offset += Diff;
|
Sent += Diff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -17,9 +17,14 @@
|
|||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#include "TResourceManager.h"
|
#include "TResourceManager.h"
|
||||||
|
#include "Common.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <fmt/core.h>
|
||||||
|
#include <ios>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
@@ -52,3 +57,83 @@ TResourceManager::TResourceManager() {
|
|||||||
|
|
||||||
Application::SetSubsystemStatus("ResourceManager", Application::Status::Good);
|
Application::SetSubsystemStatus("ResourceManager", Application::Status::Good);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string TResourceManager::NewFileList() const {
|
||||||
|
return mMods.dump();
|
||||||
|
}
|
||||||
|
void TResourceManager::RefreshFiles() {
|
||||||
|
mMods.clear();
|
||||||
|
std::unique_lock Lock(mModsMutex);
|
||||||
|
|
||||||
|
std::string Path = Application::Settings.getAsString(Settings::Key::General_ResourceFolder) + "/Client";
|
||||||
|
for (const auto& entry : fs::directory_iterator(Path)) {
|
||||||
|
std::string File(entry.path().string());
|
||||||
|
|
||||||
|
if (entry.path().extension() != ".zip" || std::filesystem::is_directory(entry.path())) {
|
||||||
|
beammp_warnf("'{}' is not a ZIP file and will be ignored", File);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
EVP_MD_CTX* mdctx;
|
||||||
|
const EVP_MD* md;
|
||||||
|
uint8_t sha256_value[EVP_MAX_MD_SIZE];
|
||||||
|
md = EVP_sha256();
|
||||||
|
if (md == nullptr) {
|
||||||
|
throw std::runtime_error("EVP_sha256() failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
mdctx = EVP_MD_CTX_new();
|
||||||
|
if (mdctx == nullptr) {
|
||||||
|
throw std::runtime_error("EVP_MD_CTX_new() failed");
|
||||||
|
}
|
||||||
|
if (!EVP_DigestInit_ex2(mdctx, md, NULL)) {
|
||||||
|
EVP_MD_CTX_free(mdctx);
|
||||||
|
throw std::runtime_error("EVP_DigestInit_ex2() failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ifstream stream(File, std::ios::binary);
|
||||||
|
|
||||||
|
const size_t FileSize = std::filesystem::file_size(File);
|
||||||
|
size_t Read = 0;
|
||||||
|
std::vector<char> Data;
|
||||||
|
while (Read < FileSize) {
|
||||||
|
Data.resize(size_t(std::min<size_t>(FileSize - Read, 4096)));
|
||||||
|
size_t RealDataSize = Data.size();
|
||||||
|
stream.read(Data.data(), std::streamsize(Data.size()));
|
||||||
|
if (stream.eof() || stream.fail()) {
|
||||||
|
RealDataSize = size_t(stream.gcount());
|
||||||
|
}
|
||||||
|
Data.resize(RealDataSize);
|
||||||
|
if (RealDataSize == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (RealDataSize > 0 && !EVP_DigestUpdate(mdctx, Data.data(), Data.size())) {
|
||||||
|
EVP_MD_CTX_free(mdctx);
|
||||||
|
throw std::runtime_error("EVP_DigestUpdate() failed");
|
||||||
|
}
|
||||||
|
Read += RealDataSize;
|
||||||
|
}
|
||||||
|
unsigned int sha256_len = 0;
|
||||||
|
if (!EVP_DigestFinal_ex(mdctx, sha256_value, &sha256_len)) {
|
||||||
|
EVP_MD_CTX_free(mdctx);
|
||||||
|
throw std::runtime_error("EVP_DigestFinal_ex() failed");
|
||||||
|
}
|
||||||
|
EVP_MD_CTX_free(mdctx);
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
for (size_t i = 0; i < sha256_len; i++) {
|
||||||
|
result += fmt::format("{:02x}", sha256_value[i]);
|
||||||
|
}
|
||||||
|
beammp_debugf("sha256('{}'): {}", File, result);
|
||||||
|
mMods.push_back(nlohmann::json {
|
||||||
|
{ "file_name", std::filesystem::path(File).filename() },
|
||||||
|
{ "file_size", std::filesystem::file_size(File) },
|
||||||
|
{ "hash_algorithm", "sha256" },
|
||||||
|
{ "hash", result },
|
||||||
|
});
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
beammp_errorf("Sha256 hashing of '{}' failed: {}", File, e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+27
-1
@@ -165,7 +165,19 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::vector<uin
|
|||||||
constexpr std::string_view ABG = "ABG:";
|
constexpr std::string_view ABG = "ABG:";
|
||||||
if (Packet.size() >= ABG.size() && std::equal(Packet.begin(), Packet.begin() + ABG.size(), ABG.begin(), ABG.end())) {
|
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.erase(Packet.begin(), Packet.begin() + ABG.size());
|
||||||
Packet = DeComp(Packet);
|
try {
|
||||||
|
Packet = DeComp(Packet);
|
||||||
|
} catch (const InvalidDataError& ) {
|
||||||
|
auto LockedClient = Client.lock();
|
||||||
|
beammp_errorf("Failed to decompress packet from client {}. The client sent invalid data and will now be disconnected.", LockedClient->GetID());
|
||||||
|
Network.ClientKick(*LockedClient, "Sent invalid compressed packet (this is likely a bug on your end)");
|
||||||
|
return;
|
||||||
|
} catch (const std::runtime_error& e) {
|
||||||
|
auto LockedClient = Client.lock();
|
||||||
|
beammp_errorf("Failed to decompress packet from client {}: {}. The server might be out of RAM! The client will now be disconnected.", LockedClient->GetID(), e.what());
|
||||||
|
Network.ClientKick(*LockedClient, "Decompression failed (likely a server-side problem)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (Packet.empty()) {
|
if (Packet.empty()) {
|
||||||
return;
|
return;
|
||||||
@@ -424,6 +436,20 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
|
|||||||
Network.SendToAll(&c, StringToVector(Packet), false, true);
|
Network.SendToAll(&c, StringToVector(Packet), false, true);
|
||||||
return;
|
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:
|
default:
|
||||||
beammp_trace(std::string(("possibly not implemented: '") + Packet + ("' (") + std::to_string(Packet.size()) + (")")));
|
beammp_trace(std::string(("possibly not implemented: '") + Packet + ("' (") + std::to_string(Packet.size()) + (")")));
|
||||||
return;
|
return;
|
||||||
|
|||||||
+7
-6
@@ -36,19 +36,19 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
static const std::string sCommandlineArguments = R"(
|
static const std::string sCommandlineArguments = R"(
|
||||||
USAGE:
|
USAGE:
|
||||||
BeamMP-Server [arguments]
|
BeamMP-Server [arguments]
|
||||||
|
|
||||||
ARGUMENTS:
|
ARGUMENTS:
|
||||||
--help
|
--help
|
||||||
Displays this help and exits.
|
Displays this help and exits.
|
||||||
--port=1234
|
--port=1234
|
||||||
Sets the server's listening TCP and
|
Sets the server's listening TCP and
|
||||||
UDP port. Overrides ENV and ServerConfig.
|
UDP port. Overrides ENV and ServerConfig.
|
||||||
--config=/path/to/ServerConfig.toml
|
--config=/path/to/ServerConfig.toml
|
||||||
Absolute or relative path to the
|
Absolute or relative path to the
|
||||||
Server Config file, including the
|
Server Config file, including the
|
||||||
filename. For paths and filenames with
|
filename. For paths and filenames with
|
||||||
spaces, put quotes around the path.
|
spaces, put quotes around the path.
|
||||||
--working-directory=/path/to/folder
|
--working-directory=/path/to/folder
|
||||||
Sets the working directory of the Server.
|
Sets the working directory of the Server.
|
||||||
@@ -59,7 +59,7 @@ ARGUMENTS:
|
|||||||
|
|
||||||
EXAMPLES:
|
EXAMPLES:
|
||||||
BeamMP-Server --config=../MyWestCoastServerConfig.toml
|
BeamMP-Server --config=../MyWestCoastServerConfig.toml
|
||||||
Runs the BeamMP-Server and uses the server config file
|
Runs the BeamMP-Server and uses the server config file
|
||||||
which is one directory above it and is named
|
which is one directory above it and is named
|
||||||
'MyWestCoastServerConfig.toml'.
|
'MyWestCoastServerConfig.toml'.
|
||||||
)";
|
)";
|
||||||
@@ -190,6 +190,7 @@ int BeamMPServerMain(MainArguments Arguments) {
|
|||||||
|
|
||||||
beammp_trace("Running in debug mode on a debug build");
|
beammp_trace("Running in debug mode on a debug build");
|
||||||
TResourceManager ResourceManager;
|
TResourceManager ResourceManager;
|
||||||
|
ResourceManager.RefreshFiles();
|
||||||
TPPSMonitor PPSMonitor(Server);
|
TPPSMonitor PPSMonitor(Server);
|
||||||
THeartbeatThread Heartbeat(ResourceManager, Server);
|
THeartbeatThread Heartbeat(ResourceManager, Server);
|
||||||
TNetwork Network(Server, PPSMonitor, ResourceManager);
|
TNetwork Network(Server, PPSMonitor, ResourceManager);
|
||||||
|
|||||||
Reference in New Issue
Block a user