mirror of
https://github.com/BeamMP/BeamMP-Server.git
synced 2026-07-17 12:13:57 +00:00
Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b4d4967529 | |||
| 51c24b82fe | |||
| 5e13f9dd2d | |||
| f8d66c4336 | |||
| 9875defe86 | |||
| fc208770dd | |||
| 99a51808a0 | |||
| 1c07cf83b2 | |||
| 99136f133a | |||
| 6c9d58582b | |||
| 4edae00998 | |||
| 17e9c05f46 | |||
| 71e3cb83ae | |||
| fb2e26bd28 | |||
| 976ab68ca3 | |||
| 58a76e1df2 | |||
| c696276fc3 | |||
| 6ce0608bb3 | |||
| 52ad237419 | |||
| 71038dc617 | |||
| dc3bb517a3 | |||
| 7b2c48c8d4 | |||
| ef557ebfc4 | |||
| b2e953b92a | |||
| 576d765557 | |||
| 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 | |||
| caafb216c9 |
@@ -0,0 +1,6 @@
|
|||||||
|
Please replace this text <-> with your PR description and leave the below declarations intact.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
By creating this pull request, I understand that code that is AI generated or otherwise automatically generated may be rejected without further discussion.
|
||||||
|
I declare that I fully understand all code I pushed into this PR, and wrote all this code myself and own the rights to this code.
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
.idea/
|
.idea/
|
||||||
.sentry-native/
|
|
||||||
*.orig
|
*.orig
|
||||||
*.toml
|
*.toml
|
||||||
boost_*
|
boost_*
|
||||||
|
|||||||
+2
-2
@@ -108,8 +108,8 @@ A BeamMP-Developer must review your code in detail, and leave a review. If this
|
|||||||
5. Run `git submodule update --init --recursive`.
|
5. Run `git submodule update --init --recursive`.
|
||||||
6. Make a new branch for your feature or fix from the branch you are on. You can do this via `git checkout -b <branch name>`. See [this section on branches](#branches) for more info on branch naming.
|
6. Make a new branch for your feature or fix from the branch you are on. You can do this via `git checkout -b <branch name>`. See [this section on branches](#branches) for more info on branch naming.
|
||||||
7. Install all dependencies. Those are usually listed in the `README.md` in the branch you're in, or, more reliably, in one of the files in `.github/workflows` (if you can read `yaml`).
|
7. Install all dependencies. Those are usually listed in the `README.md` in the branch you're in, or, more reliably, in one of the files in `.github/workflows` (if you can read `yaml`).
|
||||||
8. Run CMake to configure the project. You can find tutorials on this online. You will want to tell CMake to build with `CMAKE_BUILD_TYPE=Debug`, for example by passing it to CMake via the commandline switch `-DCMAKE_BUILD_TYPE=Debug`. You may also want to turn off sentry by setting `SENTRY_BACKEND=none` (for example via commandline switch `-DSENTRY_BACKEND=none`). An example invocation on Linux with GNU make would be
|
8. Run CMake to configure the project. You can find tutorials on this online. You will want to tell CMake to build with `CMAKE_BUILD_TYPE=Debug`, for example by passing it to CMake via the commandline switch `-DCMAKE_BUILD_TYPE=Debug`. An example invocation on Linux with GNU make would be
|
||||||
`cmake . -DCMAKE_BUILD_TYPE=Debug -DSENTRY_BACKEND=none` .
|
`cmake . -DCMAKE_BUILD_TYPE=Debug` .
|
||||||
9. Build the `BeamMP-Server` target to build the BeamMP-Server, or the `BeamMP-Server-tests` target to build the unit-tests (does not include a working server). In the example from 8. (on Linux), you could build with `make BeamMP-Server`, `make -j BeamMP-Server` or `cmake --build . --parallel --target BeamMP-Server` . Or, on Windows, (in Visual Studio), you would just press some big green "run" or "debug" button.
|
9. Build the `BeamMP-Server` target to build the BeamMP-Server, or the `BeamMP-Server-tests` target to build the unit-tests (does not include a working server). In the example from 8. (on Linux), you could build with `make BeamMP-Server`, `make -j BeamMP-Server` or `cmake --build . --parallel --target BeamMP-Server` . Or, on Windows, (in Visual Studio), you would just press some big green "run" or "debug" button.
|
||||||
10. When making changes, refer to [this section on how to commit properly](#how-to-commit). Not following those guidelines will result in your changes being rejected, so please take a look.
|
10. When making changes, refer to [this section on how to commit properly](#how-to-commit). Not following those guidelines will result in your changes being rejected, so please take a look.
|
||||||
11. Make sure to add Unit-tests with `doctest` if you build new stuff. You can find examples all over the latest version of the codebase (search for `TEST_CASE`).
|
11. Make sure to add Unit-tests with `doctest` if you build new stuff. You can find examples all over the latest version of the codebase (search for `TEST_CASE`).
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ If you need support with understanding the codebase, please write us in the Disc
|
|||||||
|
|
||||||
## About Building from Source
|
## 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
|
## Supported Operating Systems
|
||||||
|
|
||||||
@@ -66,9 +66,10 @@ 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`.
|
1. Check out the repository with git: `git clone --recursive https://github.com/BeamMP/BeamMP-Server`.
|
||||||
2. Go into the directory `cd 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.
|
3. 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)
|
||||||
4. Build via `cmake --build bin --parallel --config Release -t 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. Your executable can be found in `bin/`.
|
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.
|
When you make changes to the code, you only have to run step 4 again.
|
||||||
### Building for FreeBSD
|
### Building for FreeBSD
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
+2
-3
@@ -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, 1 };
|
static inline Version mVersion { 3, 7, 2 };
|
||||||
};
|
};
|
||||||
|
|
||||||
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);
|
||||||
@@ -139,7 +139,6 @@ void RegisterThread(const std::string& str);
|
|||||||
#define KB 1024llu
|
#define KB 1024llu
|
||||||
#define MB (KB * 1024llu)
|
#define MB (KB * 1024llu)
|
||||||
#define GB (MB * 1024llu)
|
#define GB (MB * 1024llu)
|
||||||
#define SSU_UNRAW SECRET_SENTRY_URL
|
|
||||||
|
|
||||||
#define _file_basename std::filesystem::path(__FILE__).filename().string()
|
#define _file_basename std::filesystem::path(__FILE__).filename().string()
|
||||||
#define _line std::to_string(__LINE__)
|
#define _line std::to_string(__LINE__)
|
||||||
|
|||||||
@@ -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
-3
@@ -68,8 +68,6 @@ struct Settings {
|
|||||||
// Keys have their TOML section name as prefix
|
// Keys have their TOML section name as prefix
|
||||||
|
|
||||||
// [Misc]
|
// [Misc]
|
||||||
Misc_SendErrorsShowMessage,
|
|
||||||
Misc_SendErrors,
|
|
||||||
Misc_ImScaredOfUpdates,
|
Misc_ImScaredOfUpdates,
|
||||||
Misc_UpdateReminderTime,
|
Misc_UpdateReminderTime,
|
||||||
|
|
||||||
@@ -86,7 +84,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();
|
||||||
};
|
};
|
||||||
|
|||||||
+11
-8
@@ -79,13 +79,17 @@ std::string TClient::GetCarPositionRaw(int Ident) {
|
|||||||
void TClient::Disconnect(std::string_view Reason) {
|
void TClient::Disconnect(std::string_view Reason) {
|
||||||
beammp_debugf("Disconnecting client {} for reason: {}", GetID(), Reason);
|
beammp_debugf("Disconnecting client {} for reason: {}", GetID(), Reason);
|
||||||
boost::system::error_code ec;
|
boost::system::error_code ec;
|
||||||
mSocket.shutdown(socket_base::shutdown_both, ec);
|
if (mSocket.is_open()) {
|
||||||
if (ec) {
|
mSocket.shutdown(socket_base::shutdown_both, ec);
|
||||||
beammp_debugf("Failed to shutdown client socket: {}", ec.message());
|
if (ec) {
|
||||||
}
|
beammp_debugf("Failed to shutdown client socket: {}", ec.message());
|
||||||
mSocket.close(ec);
|
}
|
||||||
if (ec) {
|
mSocket.close(ec);
|
||||||
beammp_debugf("Failed to close client socket: {}", ec.message());
|
if (ec) {
|
||||||
|
beammp_debugf("Failed to close client socket: {}", ec.message());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
beammp_debug("Socket is already closed.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +148,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()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-4
@@ -34,8 +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 },
|
||||||
{ Misc_SendErrorsShowMessage, true },
|
{ General_InformationPacket, true },
|
||||||
{ Misc_SendErrors, true },
|
|
||||||
{ Misc_ImScaredOfUpdates, true },
|
{ Misc_ImScaredOfUpdates, true },
|
||||||
{ Misc_UpdateReminderTime, "30s" }
|
{ Misc_UpdateReminderTime, "30s" }
|
||||||
};
|
};
|
||||||
@@ -54,8 +53,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 } },
|
||||||
{ { "Misc", "SendErrorsShowMessage" }, { Misc_SendErrorsShowMessage, READ_WRITE } },
|
{ { "General", "InformationPacket" }, { General_InformationPacket, READ_WRITE } },
|
||||||
{ { "Misc", "SendErrors" }, { Misc_SendErrors, READ_WRITE } },
|
|
||||||
{ { "Misc", "ImScaredOfUpdates" }, { Misc_ImScaredOfUpdates, READ_WRITE } },
|
{ { "Misc", "ImScaredOfUpdates" }, { Misc_ImScaredOfUpdates, READ_WRITE } },
|
||||||
{ { "Misc", "UpdateReminderTime" }, { Misc_UpdateReminderTime, READ_WRITE } }
|
{ { "Misc", "UpdateReminderTime" }, { Misc_UpdateReminderTime, READ_WRITE } }
|
||||||
};
|
};
|
||||||
|
|||||||
+11
-9
@@ -56,11 +56,11 @@ 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
|
||||||
static constexpr std::string_view StrSendErrors = "SendErrors";
|
|
||||||
static constexpr std::string_view StrSendErrorsMessageEnabled = "SendErrorsShowMessage";
|
|
||||||
static constexpr std::string_view StrHideUpdateMessages = "ImScaredOfUpdates";
|
static constexpr std::string_view StrHideUpdateMessages = "ImScaredOfUpdates";
|
||||||
static constexpr std::string_view StrUpdateReminderTime = "UpdateReminderTime";
|
static constexpr std::string_view StrUpdateReminderTime = "UpdateReminderTime";
|
||||||
|
|
||||||
@@ -132,6 +132,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);
|
||||||
@@ -148,12 +150,8 @@ void TConfig::FlushToFile() {
|
|||||||
// Misc
|
// Misc
|
||||||
data["Misc"][StrHideUpdateMessages.data()] = Application::Settings.getAsBool(Settings::Key::Misc_ImScaredOfUpdates);
|
data["Misc"][StrHideUpdateMessages.data()] = Application::Settings.getAsBool(Settings::Key::Misc_ImScaredOfUpdates);
|
||||||
SetComment(data["Misc"][StrHideUpdateMessages.data()].comments(), " Hides the periodic update message which notifies you of a new server version. You should really keep this on and always update as soon as possible. For more information visit https://wiki.beammp.com/en/home/server-maintenance#updating-the-server. An update message will always appear at startup regardless.");
|
SetComment(data["Misc"][StrHideUpdateMessages.data()].comments(), " Hides the periodic update message which notifies you of a new server version. You should really keep this on and always update as soon as possible. For more information visit https://wiki.beammp.com/en/home/server-maintenance#updating-the-server. An update message will always appear at startup regardless.");
|
||||||
data["Misc"][StrSendErrors.data()] = Application::Settings.getAsBool(Settings::Key::Misc_SendErrors);
|
|
||||||
data["Misc"][StrUpdateReminderTime.data()] = Application::Settings.getAsString(Settings::Key::Misc_UpdateReminderTime);
|
data["Misc"][StrUpdateReminderTime.data()] = Application::Settings.getAsString(Settings::Key::Misc_UpdateReminderTime);
|
||||||
SetComment(data["Misc"][StrUpdateReminderTime.data()].comments(), " Specifies the time between update reminders. You can use any of \"s, min, h, d\" at the end to specify the units seconds, minutes, hours or days. So 30d or 0.5min will print the update message every 30 days or half a minute.");
|
SetComment(data["Misc"][StrUpdateReminderTime.data()].comments(), " Specifies the time between update reminders. You can use any of \"s, min, h, d\" at the end to specify the units seconds, minutes, hours or days. So 30d or 0.5min will print the update message every 30 days or half a minute.");
|
||||||
SetComment(data["Misc"][StrSendErrors.data()].comments(), " If SendErrors is `true`, the server will send helpful info about crashes and other issues back to the BeamMP developers. This info may include your config, who is on your server at the time of the error, and similar general information. This kind of data is vital in helping us diagnose and fix issues faster. This has no impact on server performance. You can opt-out of this system by setting this to `false`");
|
|
||||||
data["Misc"][StrSendErrorsMessageEnabled.data()] = Application::Settings.getAsBool(Settings::Key::Misc_SendErrorsShowMessage);
|
|
||||||
SetComment(data["Misc"][StrSendErrorsMessageEnabled.data()].comments(), " You can turn on/off the SendErrors message you get on startup here");
|
|
||||||
std::stringstream Ss;
|
std::stringstream Ss;
|
||||||
Ss << "# This is the BeamMP-Server config file.\n"
|
Ss << "# This is the BeamMP-Server config file.\n"
|
||||||
"# Help & Documentation: `https://docs.beammp.com/server/server-maintenance/`\n"
|
"# Help & Documentation: `https://docs.beammp.com/server/server-maintenance/`\n"
|
||||||
@@ -248,7 +246,12 @@ 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", StrPort, EnvStrPort, Settings::Key::General_Port);
|
TryReadValue(data, "General", StrInformationPacket, EnvStrInformationPacket, Settings::Key::General_InformationPacket);
|
||||||
|
if (Env::Get(Env::Key::PROVIDER_PORT_ENV).has_value()) {
|
||||||
|
TryReadValue(data, "General", StrPort, Env::Get(Env::Key::PROVIDER_PORT_ENV).value(), Settings::Key::General_Port);
|
||||||
|
} else {
|
||||||
|
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);
|
||||||
TryReadValue(data, "General", StrMap, EnvStrMap, Settings::Key::General_Map);
|
TryReadValue(data, "General", StrMap, EnvStrMap, Settings::Key::General_Map);
|
||||||
@@ -260,9 +263,7 @@ void TConfig::ParseFromFile(std::string_view name) {
|
|||||||
TryReadValue(data, "General", StrLogChat, EnvStrLogChat, Settings::Key::General_LogChat);
|
TryReadValue(data, "General", StrLogChat, EnvStrLogChat, Settings::Key::General_LogChat);
|
||||||
TryReadValue(data, "General", StrAllowGuests, EnvStrAllowGuests, Settings::Key::General_AllowGuests);
|
TryReadValue(data, "General", StrAllowGuests, EnvStrAllowGuests, Settings::Key::General_AllowGuests);
|
||||||
// Misc
|
// Misc
|
||||||
TryReadValue(data, "Misc", StrSendErrors, "", Settings::Key::Misc_SendErrors);
|
|
||||||
TryReadValue(data, "Misc", StrHideUpdateMessages, "", Settings::Key::Misc_ImScaredOfUpdates);
|
TryReadValue(data, "Misc", StrHideUpdateMessages, "", Settings::Key::Misc_ImScaredOfUpdates);
|
||||||
TryReadValue(data, "Misc", StrSendErrorsMessageEnabled, "", Settings::Key::Misc_SendErrorsShowMessage);
|
|
||||||
TryReadValue(data, "Misc", StrUpdateReminderTime, "", Settings::Key::Misc_UpdateReminderTime);
|
TryReadValue(data, "Misc", StrUpdateReminderTime, "", Settings::Key::Misc_UpdateReminderTime);
|
||||||
|
|
||||||
} catch (const std::exception& err) {
|
} catch (const std::exception& err) {
|
||||||
@@ -299,6 +300,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", 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)
|
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)
|
||||||
|
|||||||
+27
-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,17 @@ 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() == 2) {
|
||||||
|
LuaAPI::MP::SendNotification(Args.get<int>(0), Args.get<std::string>(1), "", Args.get<std::string>(1));
|
||||||
|
} else 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, 3 or 4 arguments.");
|
||||||
|
}
|
||||||
|
});
|
||||||
MPTable.set_function("GetPlayers", [&]() -> sol::table {
|
MPTable.set_function("GetPlayers", [&]() -> sol::table {
|
||||||
return Lua_GetPlayers();
|
return Lua_GetPlayers();
|
||||||
});
|
});
|
||||||
@@ -883,6 +904,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 +934,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 +1026,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,
|
||||||
|
|||||||
+70
-154
@@ -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 {
|
||||||
@@ -423,35 +430,27 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
|
|||||||
Reason = "No guests are allowed on this server! To join, sign up at: forum.beammp.com.";
|
Reason = "No guests are allowed on this server! To join, sign up at: forum.beammp.com.";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Allowed = true;
|
if (!NotAllowed && !NotAllowedWithReason && mServer.ClientCount() >= size_t(Application::Settings.getAsInt(Settings::Key::General_MaxPlayers)) && !BypassLimit) {
|
||||||
if (NotAllowed) {
|
NotAllowedWithReason = true;
|
||||||
Allowed = false;
|
Reason = "Server full!";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NotAllowedWithReason) {
|
if (NotAllowedWithReason) {
|
||||||
Allowed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NotAllowed) {
|
|
||||||
ClientKick(*Client, "you are not allowed on the server!");
|
|
||||||
} else if (NotAllowedWithReason) {
|
|
||||||
ClientKick(*Client, Reason);
|
ClientKick(*Client, Reason);
|
||||||
|
} else if (NotAllowed) {
|
||||||
|
ClientKick(*Client, "you are not allowed on the server!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto PostFutures = LuaAPI::MP::Engine->TriggerEvent("postPlayerAuth", "", NotAllowed || NotAllowedWithReason, Reason, Client->GetName(), Client->GetRoles(), Client->IsGuest(), Client->GetIdentifiers());
|
||||||
|
// the post event is not cancellable so we dont wait for it
|
||||||
|
LuaAPI::MP::Engine->ReportErrors(PostFutures);
|
||||||
|
|
||||||
if (!Allowed) {
|
if (!NotAllowed && !NotAllowedWithReason) {
|
||||||
return {};
|
|
||||||
} else if (mServer.ClientCount() < size_t(Application::Settings.getAsInt(Settings::Key::General_MaxPlayers))) {
|
|
||||||
beammp_info("Identification success");
|
beammp_info("Identification success");
|
||||||
mServer.InsertClient(Client);
|
mServer.InsertClient(Client);
|
||||||
TCPClient(Client);
|
TCPClient(Client);
|
||||||
} else {
|
|
||||||
ClientKick(*Client, "Server full!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PostFutures = LuaAPI::MP::Engine->TriggerEvent("postPlayerAuth", "", Allowed, Client->GetName(), Client->GetRoles(), Client->IsGuest(), Client->GetIdentifiers());
|
|
||||||
// the post event is not cancellable so we dont wait for it
|
|
||||||
LuaAPI::MP::Engine->ReportErrors(PostFutures);
|
|
||||||
|
|
||||||
return Client;
|
return Client;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -758,11 +757,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;
|
||||||
@@ -772,8 +771,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
|
||||||
@@ -796,87 +793,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)
|
||||||
@@ -886,8 +805,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)!
|
||||||
@@ -897,11 +816,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));
|
||||||
@@ -915,35 +834,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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+14
-1
@@ -253,7 +253,6 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::vector<uin
|
|||||||
HandleEvent(*LockedClient, StringPacket);
|
HandleEvent(*LockedClient, StringPacket);
|
||||||
return;
|
return;
|
||||||
case 'N':
|
case 'N':
|
||||||
beammp_trace("got 'N' packet (" + std::to_string(Packet.size()) + ")");
|
|
||||||
Network.SendToAll(LockedClient.get(), Packet, false, true);
|
Network.SendToAll(LockedClient.get(), Packet, false, true);
|
||||||
return;
|
return;
|
||||||
case 'Z': // position packet
|
case 'Z': // position packet
|
||||||
@@ -436,6 +435,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;
|
||||||
|
|||||||
@@ -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