mirror of
https://github.com/BeamMP/BeamMP-Server.git
synced 2026-07-13 18:24:16 +00:00
Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f738950934 | |||
| fbce8a946e | |||
| bd9b6212e2 | |||
| b112ee20d8 | |||
| 8593aeb21d | |||
| 840f9b9f9d | |||
| ec21cbbe86 | |||
| e90f1af109 | |||
| c78775bfd8 | |||
| 9c3042280d | |||
| 4f2ef3c3a7 | |||
| 6a2ee052ba | |||
| 2658d0f785 | |||
| a7eeda0569 | |||
| cd29f25435 | |||
| 7c864d94b3 | |||
| 26f1be0a51 | |||
| d7e75ae0c7 | |||
| 1d90f53527 | |||
| 7dd6b41642 | |||
| d7f3bc8b9f | |||
| 687a988701 | |||
| 9046b5a4d3 | |||
| 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 |
@@ -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/
|
||||
.sentry-native/
|
||||
*.orig
|
||||
*.toml
|
||||
boost_*
|
||||
|
||||
@@ -104,6 +104,7 @@ set(PRJ_LIBRARIES
|
||||
httplib::httplib
|
||||
libzip::zip
|
||||
OpenSSL::SSL OpenSSL::Crypto
|
||||
CURL::libcurl
|
||||
${LUA_LIBRARIES}
|
||||
)
|
||||
|
||||
@@ -116,6 +117,7 @@ find_package(httplib CONFIG REQUIRED)
|
||||
find_package(libzip CONFIG REQUIRED)
|
||||
find_package(RapidJSON CONFIG REQUIRED)
|
||||
find_package(sol2 CONFIG REQUIRED)
|
||||
find_package(CURL CONFIG REQUIRED)
|
||||
add_subdirectory("deps/toml11")
|
||||
|
||||
include_directories(include)
|
||||
|
||||
+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`.
|
||||
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`).
|
||||
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
|
||||
`cmake . -DCMAKE_BUILD_TYPE=Debug -DSENTRY_BACKEND=none` .
|
||||
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` .
|
||||
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.
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
@@ -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`.
|
||||
2. Go into the directory `cd BeamMP-Server`.
|
||||
3. Run CMake `cmake -S . -B bin -DCMAKE_BUILD_TYPE=Release` - this can take a few minutes and may take a lot of disk space and bandwidth.
|
||||
4. Build via `cmake --build bin --parallel --config Release -t BeamMP-Server`.
|
||||
5. Your executable can be found in `bin/`.
|
||||
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. Run CMake `cmake -S . -B bin -DCMAKE_BUILD_TYPE=Release` - this can take a few minutes and may take a lot of disk space and bandwidth.
|
||||
5. Build via `cmake --build bin --parallel --config Release -t BeamMP-Server`.
|
||||
6. Your executable can be found in `bin/`.
|
||||
|
||||
When you make changes to the code, you only have to run step 4 again.
|
||||
### Building for FreeBSD
|
||||
|
||||
+3
-4
@@ -82,11 +82,11 @@ public:
|
||||
|
||||
static std::vector<std::string> GetBackendUrlsInOrder() {
|
||||
return {
|
||||
"backend.beammp.com",
|
||||
"https://backend.beammp.com",
|
||||
};
|
||||
}
|
||||
|
||||
static std::string GetBackendUrlForAuth() { return "auth.beammp.com"; }
|
||||
static std::string GetBackendUrlForAuth() { return "https://auth.beammp.com"; }
|
||||
static std::string GetBackendUrlForSocketIO() { return "https://backend.beammp.com"; }
|
||||
static void CheckForUpdates();
|
||||
static std::array<uint8_t, 3> VersionStrToInts(const std::string& str);
|
||||
@@ -127,7 +127,7 @@ private:
|
||||
static inline std::mutex mShutdownHandlersMutex {};
|
||||
static inline std::deque<TShutdownHandler> mShutdownHandlers {};
|
||||
|
||||
static inline Version mVersion { 3, 7, 0 };
|
||||
static inline Version mVersion { 3, 8, 1 };
|
||||
};
|
||||
|
||||
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 MB (KB * 1024llu)
|
||||
#define GB (MB * 1024llu)
|
||||
#define SSU_UNRAW SECRET_SENTRY_URL
|
||||
|
||||
#define _file_basename std::filesystem::path(__FILE__).filename().string()
|
||||
#define _line std::to_string(__LINE__)
|
||||
|
||||
+3
-2
@@ -23,6 +23,7 @@
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <curl/curl.h>
|
||||
|
||||
#if defined(BEAMMP_LINUX)
|
||||
#pragma GCC diagnostic push
|
||||
@@ -38,8 +39,8 @@
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace Http {
|
||||
std::string GET(const std::string& host, int port, const std::string& target, unsigned int* status = nullptr);
|
||||
std::string POST(const std::string& host, int port, const std::string& target, const std::string& body, const std::string& ContentType, unsigned int* status = nullptr, const httplib::Headers& headers = {});
|
||||
std::string GET(const std::string& url, unsigned int* status = nullptr);
|
||||
std::string POST(const std::string& url, const std::string& body, const std::string& ContentType, unsigned int* status = nullptr, const std::map<std::string, std::string>& headers = {});
|
||||
namespace Status {
|
||||
std::string ToString(int code);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
#include <limits>
|
||||
#include <unordered_map>
|
||||
|
||||
#undef max
|
||||
#undef min
|
||||
|
||||
namespace prof {
|
||||
|
||||
using Duration = std::chrono::duration<double, std::milli>;
|
||||
|
||||
@@ -68,8 +68,6 @@ struct Settings {
|
||||
// Keys have their TOML section name as prefix
|
||||
|
||||
// [Misc]
|
||||
Misc_SendErrorsShowMessage,
|
||||
Misc_SendErrors,
|
||||
Misc_ImScaredOfUpdates,
|
||||
Misc_UpdateReminderTime,
|
||||
|
||||
|
||||
+12
-7
@@ -79,13 +79,18 @@ std::string TClient::GetCarPositionRaw(int Ident) {
|
||||
void TClient::Disconnect(std::string_view Reason) {
|
||||
beammp_debugf("Disconnecting client {} for reason: {}", GetID(), Reason);
|
||||
boost::system::error_code ec;
|
||||
mSocket.shutdown(socket_base::shutdown_both, ec);
|
||||
if (ec) {
|
||||
beammp_debugf("Failed to shutdown client socket: {}", ec.message());
|
||||
}
|
||||
mSocket.close(ec);
|
||||
if (ec) {
|
||||
beammp_debugf("Failed to close client socket: {}", ec.message());
|
||||
if (mSocket.is_open()) {
|
||||
mSocket.shutdown(socket_base::shutdown_both, ec);
|
||||
if (ec) {
|
||||
beammp_debugf("Failed to shutdown client socket: {}", ec.message());
|
||||
}
|
||||
mSocket.cancel();
|
||||
mSocket.close(ec);
|
||||
if (ec) {
|
||||
beammp_debugf("Failed to close client socket: {}", ec.message());
|
||||
}
|
||||
} else {
|
||||
beammp_debug("Socket is already closed.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -215,7 +215,7 @@ void Application::CheckForUpdates() {
|
||||
// checks current version against latest version
|
||||
std::regex VersionRegex { R"(\d+\.\d+\.\d+\n*)" };
|
||||
for (const auto& url : GetBackendUrlsInOrder()) {
|
||||
auto Response = Http::GET(url, 443, "/v/s");
|
||||
auto Response = Http::GET(url + "/v/s");
|
||||
bool Matches = std::regex_match(Response, VersionRegex);
|
||||
if (Matches) {
|
||||
auto MyVersion = ServerVersion();
|
||||
|
||||
+69
-48
@@ -29,67 +29,88 @@
|
||||
#include <stdexcept>
|
||||
|
||||
using json = nlohmann::json;
|
||||
struct Connection {
|
||||
std::string host {};
|
||||
int port {};
|
||||
Connection() = default;
|
||||
Connection(std::string host, int port)
|
||||
: host(host)
|
||||
, port(port) {};
|
||||
};
|
||||
constexpr uint8_t CONNECTION_AMOUNT = 10;
|
||||
static thread_local uint8_t write_index = 0;
|
||||
static thread_local std::array<Connection, CONNECTION_AMOUNT> connections;
|
||||
static thread_local std::array<std::shared_ptr<httplib::SSLClient>, CONNECTION_AMOUNT> clients;
|
||||
|
||||
[[nodiscard]] static std::shared_ptr<httplib::SSLClient> getClient(Connection connectionInfo) {
|
||||
for (uint8_t i = 0; i < CONNECTION_AMOUNT; i++) {
|
||||
if (connectionInfo.host == connections[i].host
|
||||
&& connectionInfo.port == connections[i].port) {
|
||||
beammp_tracef("Old client reconnected, with ip {} and port {}", connectionInfo.host, connectionInfo.port);
|
||||
return clients[i];
|
||||
}
|
||||
}
|
||||
uint8_t i = write_index;
|
||||
write_index++;
|
||||
write_index %= CONNECTION_AMOUNT;
|
||||
clients[i] = std::make_shared<httplib::SSLClient>(connectionInfo.host, connectionInfo.port);
|
||||
connections[i] = { connectionInfo.host, connectionInfo.port };
|
||||
beammp_tracef("New client connected, with ip {} and port {}", connectionInfo.host, connectionInfo.port);
|
||||
return clients[i];
|
||||
static size_t CurlWriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {
|
||||
std::string* Result = reinterpret_cast<std::string*>(userp);
|
||||
std::string NewContents(reinterpret_cast<char*>(contents), size * nmemb);
|
||||
*Result += NewContents;
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
std::string Http::GET(const std::string& host, int port, const std::string& target, unsigned int* status) {
|
||||
std::shared_ptr<httplib::SSLClient> client = getClient({ host, port });
|
||||
client->enable_server_certificate_verification(false);
|
||||
client->set_address_family(AF_INET);
|
||||
auto res = client->Get(target.c_str());
|
||||
if (res) {
|
||||
if (status) {
|
||||
*status = res->status;
|
||||
std::string Http::GET(const std::string& url, unsigned int* status) {
|
||||
std::string Ret;
|
||||
static thread_local CURL* curl = curl_easy_init();
|
||||
if (curl) {
|
||||
CURLcode res;
|
||||
char errbuf[CURL_ERROR_SIZE];
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&Ret);
|
||||
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10); // seconds
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
|
||||
errbuf[0] = 0;
|
||||
res = curl_easy_perform(curl);
|
||||
if (res != CURLE_OK) {
|
||||
beammp_error("GET to " + url + " failed: " + std::string(curl_easy_strerror(res)));
|
||||
beammp_error("Curl error: " + std::string(errbuf));
|
||||
return Http::ErrorString;
|
||||
}
|
||||
return res->body;
|
||||
|
||||
if (status) {
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, status);
|
||||
}
|
||||
|
||||
} else {
|
||||
beammp_error("Curl easy init failed");
|
||||
return Http::ErrorString;
|
||||
}
|
||||
return Ret;
|
||||
}
|
||||
|
||||
std::string Http::POST(const std::string& host, int port, const std::string& target, const std::string& body, const std::string& ContentType, unsigned int* status, const httplib::Headers& headers) {
|
||||
std::shared_ptr<httplib::SSLClient> client = getClient({ host, port });
|
||||
client->set_read_timeout(std::chrono::seconds(10));
|
||||
beammp_assert(client->is_valid());
|
||||
client->enable_server_certificate_verification(false);
|
||||
client->set_address_family(AF_INET);
|
||||
auto res = client->Post(target.c_str(), headers, body.c_str(), body.size(), ContentType.c_str());
|
||||
if (res) {
|
||||
if (status) {
|
||||
*status = res->status;
|
||||
std::string Http::POST(const std::string& url, const std::string& body, const std::string& ContentType, unsigned int* status, const std::map<std::string, std::string>& headers) {
|
||||
std::string Ret;
|
||||
static thread_local CURL* curl = curl_easy_init();
|
||||
if (curl) {
|
||||
CURLcode res;
|
||||
char errbuf[CURL_ERROR_SIZE];
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&Ret);
|
||||
curl_easy_setopt(curl, CURLOPT_POST, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, body.size());
|
||||
struct curl_slist* list = nullptr;
|
||||
list = curl_slist_append(list, ("Content-Type: " + ContentType).c_str());
|
||||
|
||||
for (auto [header, value] : headers) {
|
||||
list = curl_slist_append(list, (header + ": " + value).c_str());
|
||||
}
|
||||
return res->body;
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
|
||||
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10); // seconds
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
|
||||
errbuf[0] = 0;
|
||||
res = curl_easy_perform(curl);
|
||||
curl_slist_free_all(list);
|
||||
if (res != CURLE_OK) {
|
||||
beammp_error("POST to " + url + " failed: " + std::string(curl_easy_strerror(res)));
|
||||
beammp_error("Curl error: " + std::string(errbuf));
|
||||
return Http::ErrorString;
|
||||
}
|
||||
|
||||
if (status) {
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, status);
|
||||
}
|
||||
|
||||
} else {
|
||||
beammp_debug("POST failed: " + httplib::to_string(res.error()));
|
||||
beammp_error("Curl easy init failed");
|
||||
return Http::ErrorString;
|
||||
}
|
||||
return Ret;
|
||||
}
|
||||
|
||||
// RFC 2616, RFC 7231
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@ std::pair<bool, std::string> LuaAPI::MP::SendChatMessage(int ID, const std::stri
|
||||
|
||||
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;
|
||||
std::string Packet = "n" + Category + ":" + Icon + ":" + Message;
|
||||
if (ID == -1) {
|
||||
Engine->Network().SendToAll(nullptr, StringToVector(Packet), true, true);
|
||||
Result.first = true;
|
||||
|
||||
@@ -35,8 +35,6 @@ Settings::Settings() {
|
||||
{ General_Debug, false },
|
||||
{ General_AllowGuests, true },
|
||||
{ General_InformationPacket, true },
|
||||
{ Misc_SendErrorsShowMessage, true },
|
||||
{ Misc_SendErrors, true },
|
||||
{ Misc_ImScaredOfUpdates, true },
|
||||
{ Misc_UpdateReminderTime, "30s" }
|
||||
};
|
||||
@@ -56,8 +54,6 @@ Settings::Settings() {
|
||||
{ { "General", "Debug" }, { General_Debug, READ_WRITE } },
|
||||
{ { "General", "AllowGuests" }, { General_AllowGuests, READ_WRITE } },
|
||||
{ { "General", "InformationPacket" }, { General_InformationPacket, READ_WRITE } },
|
||||
{ { "Misc", "SendErrorsShowMessage" }, { Misc_SendErrorsShowMessage, READ_WRITE } },
|
||||
{ { "Misc", "SendErrors" }, { Misc_SendErrors, READ_WRITE } },
|
||||
{ { "Misc", "ImScaredOfUpdates" }, { Misc_ImScaredOfUpdates, READ_WRITE } },
|
||||
{ { "Misc", "UpdateReminderTime" }, { Misc_UpdateReminderTime, READ_WRITE } }
|
||||
};
|
||||
|
||||
+9
-11
@@ -61,10 +61,10 @@ static constexpr std::string_view EnvStrInformationPacket = "BEAMMP_INFORMATION_
|
||||
static constexpr std::string_view StrPassword = "Password";
|
||||
|
||||
// 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 EnvStrHideUpdateMessages = "BEAMMP_IM_SCARED_OF_UPDATES";
|
||||
static constexpr std::string_view StrUpdateReminderTime = "UpdateReminderTime";
|
||||
static constexpr std::string_view EnvStrUpdateReminderTime = "BEAMMP_UPDATE_REMINDER_TIME";
|
||||
|
||||
TEST_CASE("TConfig::TConfig") {
|
||||
const std::string CfgFile = "beammp_server_testconfig.toml";
|
||||
@@ -152,12 +152,8 @@ void TConfig::FlushToFile() {
|
||||
// Misc
|
||||
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.");
|
||||
data["Misc"][StrSendErrors.data()] = Application::Settings.getAsBool(Settings::Key::Misc_SendErrors);
|
||||
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"][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;
|
||||
Ss << "# This is the BeamMP-Server config file.\n"
|
||||
"# Help & Documentation: `https://docs.beammp.com/server/server-maintenance/`\n"
|
||||
@@ -253,7 +249,11 @@ void TConfig::ParseFromFile(std::string_view name) {
|
||||
TryReadValue(data, "General", StrDebug, EnvStrDebug, Settings::Key::General_Debug);
|
||||
TryReadValue(data, "General", StrPrivate, EnvStrPrivate, Settings::Key::General_Private);
|
||||
TryReadValue(data, "General", StrInformationPacket, EnvStrInformationPacket, Settings::Key::General_InformationPacket);
|
||||
TryReadValue(data, "General", StrPort, EnvStrPort, Settings::Key::General_Port);
|
||||
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", StrMaxPlayers, EnvStrMaxPlayers, Settings::Key::General_MaxPlayers);
|
||||
TryReadValue(data, "General", StrMap, EnvStrMap, Settings::Key::General_Map);
|
||||
@@ -265,10 +265,8 @@ void TConfig::ParseFromFile(std::string_view name) {
|
||||
TryReadValue(data, "General", StrLogChat, EnvStrLogChat, Settings::Key::General_LogChat);
|
||||
TryReadValue(data, "General", StrAllowGuests, EnvStrAllowGuests, Settings::Key::General_AllowGuests);
|
||||
// Misc
|
||||
TryReadValue(data, "Misc", StrSendErrors, "", Settings::Key::Misc_SendErrors);
|
||||
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", StrHideUpdateMessages, EnvStrHideUpdateMessages, Settings::Key::Misc_ImScaredOfUpdates);
|
||||
TryReadValue(data, "Misc", StrUpdateReminderTime, EnvStrUpdateReminderTime, Settings::Key::Misc_UpdateReminderTime);
|
||||
|
||||
} catch (const std::exception& err) {
|
||||
beammp_error("Error parsing config file value: " + std::string(err.what()));
|
||||
|
||||
+22
-22
@@ -24,12 +24,8 @@
|
||||
#include "Http.h"
|
||||
// #include "SocketIO.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/rapidjson.h>
|
||||
#include <sstream>
|
||||
|
||||
namespace json = rapidjson;
|
||||
|
||||
void THeartbeatThread::operator()() {
|
||||
RegisterThread("Heartbeat");
|
||||
std::string Body;
|
||||
@@ -63,15 +59,19 @@ void THeartbeatThread::operator()() {
|
||||
auto Target = "/heartbeat";
|
||||
unsigned int ResponseCode = 0;
|
||||
|
||||
json::Document Doc;
|
||||
nlohmann::json Doc;
|
||||
bool Ok = false;
|
||||
for (const auto& Url : Application::GetBackendUrlsInOrder()) {
|
||||
T = Http::POST(Url, 443, Target, Body, "application/json", &ResponseCode, { { "api-v", "2" } });
|
||||
Doc.Parse(T.data(), T.size());
|
||||
if (Doc.HasParseError() || !Doc.IsObject()) {
|
||||
T = Http::POST(Url + Target, Body, "application/json", &ResponseCode, { { "api-v", "2" } });
|
||||
|
||||
if (!Application::Settings.getAsBool(Settings::Key::General_Private)) {
|
||||
beammp_debug("Backend response was: `" + T + "`");
|
||||
}
|
||||
|
||||
Doc = nlohmann::json::parse(T, nullptr, false);
|
||||
if (Doc.is_discarded() || !Doc.is_object()) {
|
||||
if (!Application::Settings.getAsBool(Settings::Key::General_Private)) {
|
||||
beammp_trace("Backend response failed to parse as valid json");
|
||||
beammp_trace("Response was: `" + T + "`");
|
||||
}
|
||||
} else if (ResponseCode != 200) {
|
||||
beammp_errorf("Response code from the heartbeat: {}", ResponseCode);
|
||||
@@ -90,18 +90,18 @@ void THeartbeatThread::operator()() {
|
||||
const auto MessageKey = "msg";
|
||||
|
||||
if (Ok) {
|
||||
if (Doc.HasMember(StatusKey) && Doc[StatusKey].IsString()) {
|
||||
Status = Doc[StatusKey].GetString();
|
||||
if (Doc.contains(StatusKey) && Doc[StatusKey].is_string()) {
|
||||
Status = Doc[StatusKey];
|
||||
} else {
|
||||
Ok = false;
|
||||
}
|
||||
if (Doc.HasMember(CodeKey) && Doc[CodeKey].IsString()) {
|
||||
Code = Doc[CodeKey].GetString();
|
||||
if (Doc.contains(CodeKey) && Doc[CodeKey].is_string()) {
|
||||
Code = Doc[CodeKey];
|
||||
} else {
|
||||
Ok = false;
|
||||
}
|
||||
if (Doc.HasMember(MessageKey) && Doc[MessageKey].IsString()) {
|
||||
Message = Doc[MessageKey].GetString();
|
||||
if (Doc.contains(MessageKey) && Doc[MessageKey].is_string()) {
|
||||
Message = Doc[MessageKey];
|
||||
} else {
|
||||
Ok = false;
|
||||
}
|
||||
@@ -140,19 +140,19 @@ void THeartbeatThread::operator()() {
|
||||
|
||||
std::string THeartbeatThread::GenerateCall() {
|
||||
nlohmann::json Ret = {
|
||||
{ "players", mServer.ClientCount() },
|
||||
{ "maxplayers", Application::Settings.getAsInt(Settings::Key::General_MaxPlayers) },
|
||||
{ "port", Application::Settings.getAsInt(Settings::Key::General_Port) },
|
||||
{ "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) },
|
||||
{ "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) },
|
||||
{ "guests", Application::Settings.getAsBool(Settings::Key::General_AllowGuests) ? "true" : "false" },
|
||||
{ "modlist", mResourceManager.TrimmedList() },
|
||||
{ "modstotalsize", mResourceManager.MaxModSize() },
|
||||
{ "modstotal", mResourceManager.ModsLoaded() },
|
||||
{ "modstotalsize", std::to_string(mResourceManager.MaxModSize()) },
|
||||
{ "modstotal", std::to_string(mResourceManager.ModsLoaded()) },
|
||||
{ "playerslist", GetPlayers() },
|
||||
{ "desc", Application::Settings.getAsString(Settings::Key::General_Description) }
|
||||
};
|
||||
|
||||
+6
-2
@@ -43,6 +43,8 @@ static sol::protected_function AddTraceback(sol::state_view StateView, sol::prot
|
||||
static std::optional<sol::function> GetLuaHandler(sol::state_view StateView, const std::string Handler, const std::string EventName);
|
||||
|
||||
static std::optional<sol::function> GetLuaHandler(sol::state_view StateView, const std::string Handler, const std::string EventName) {
|
||||
if (!StateView)
|
||||
return std::nullopt;
|
||||
auto Res = StateView.safe_script("return " + Handler, sol::script_pass_on_error);
|
||||
if (!Res.valid()) {
|
||||
beammp_errorf("invalid handler for event \"{}\". handler: \"{}\"", EventName, Handler);
|
||||
@@ -880,12 +882,14 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, TLuaStateI
|
||||
});
|
||||
MPTable.set_function("SendChatMessage", &LuaAPI::MP::SendChatMessage);
|
||||
MPTable.set_function("SendNotification", [&](sol::variadic_args Args) {
|
||||
if (Args.size() == 3) {
|
||||
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 or 3 arguments.");
|
||||
beammp_lua_error("SendNotification expects 2, 3 or 4 arguments.");
|
||||
}
|
||||
});
|
||||
MPTable.set_function("GetPlayers", [&]() -> sol::table {
|
||||
|
||||
+24
-22
@@ -86,7 +86,7 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R
|
||||
void TNetwork::UDPServerMain() {
|
||||
RegisterThread("UDPServer");
|
||||
// listen on all ipv6 addresses
|
||||
ip::udp::endpoint UdpListenEndpoint(ip::address::from_string("::"), Application::Settings.getAsInt(Settings::Key::General_Port));
|
||||
ip::udp::endpoint UdpListenEndpoint(ip::make_address("::"), Application::Settings.getAsInt(Settings::Key::General_Port));
|
||||
boost::system::error_code ec;
|
||||
mUDPSock.open(UdpListenEndpoint.protocol(), ec);
|
||||
if (ec) {
|
||||
@@ -172,7 +172,7 @@ void TNetwork::TCPServerMain() {
|
||||
|
||||
// listen on all ipv6 addresses
|
||||
auto port = uint16_t(Application::Settings.getAsInt(Settings::Key::General_Port));
|
||||
ip::tcp::endpoint ListenEp(ip::address::from_string("::"), port);
|
||||
ip::tcp::endpoint ListenEp(ip::make_address("::"), port);
|
||||
beammp_infof("Listening on 0.0.0.0:{0} and [::]:{0}", port);
|
||||
ip::tcp::socket Listener(mServer.IoCtx());
|
||||
boost::system::error_code ec;
|
||||
@@ -258,8 +258,16 @@ void TNetwork::Identify(TConnection&& RawConnection) {
|
||||
write(RawConnection.Socket, buffer("P"), ec);
|
||||
return;
|
||||
} else if (Code == 'I') {
|
||||
const std::string Data = Application::Settings.getAsBool(Settings::Key::General_InformationPacket) ? THeartbeatThread::lastCall : "";
|
||||
|
||||
const auto Size = static_cast<int32_t>(Data.size());
|
||||
std::vector<uint8_t> ToSend;
|
||||
ToSend.resize(Data.size() + sizeof(Size));
|
||||
std::memcpy(ToSend.data(), &Size, sizeof(Size));
|
||||
std::memcpy(ToSend.data() + sizeof(Size), Data.data(), Data.size());
|
||||
|
||||
boost::system::error_code ec;
|
||||
write(RawConnection.Socket, buffer(Application::Settings.getAsBool(Settings::Key::General_InformationPacket) ? THeartbeatThread::lastCall : ""), ec);
|
||||
write(RawConnection.Socket, buffer(ToSend), ec);
|
||||
} else {
|
||||
beammp_errorf("Invalid code got in Identify: '{}'", Code);
|
||||
}
|
||||
@@ -346,7 +354,7 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
|
||||
auto Target = "/pkToUser";
|
||||
|
||||
unsigned int ResponseCode = 0;
|
||||
AuthResStr = Http::POST(Application::GetBackendUrlForAuth(), 443, Target, AuthReq.dump(), "application/json", &ResponseCode);
|
||||
AuthResStr = Http::POST(Application::GetBackendUrlForAuth() + Target, AuthReq.dump(), "application/json", &ResponseCode);
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
beammp_debugf("Invalid json sent by client, kicking: {}", e.what());
|
||||
@@ -354,6 +362,8 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
beammp_debug("Response from authentication backend: " + AuthResStr);
|
||||
|
||||
try {
|
||||
nlohmann::json AuthRes = nlohmann::json::parse(AuthResStr);
|
||||
|
||||
@@ -430,35 +440,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.";
|
||||
}
|
||||
|
||||
bool Allowed = true;
|
||||
if (NotAllowed) {
|
||||
Allowed = false;
|
||||
if (!NotAllowed && !NotAllowedWithReason && mServer.ClientCount() >= size_t(Application::Settings.getAsInt(Settings::Key::General_MaxPlayers)) && !BypassLimit) {
|
||||
NotAllowedWithReason = true;
|
||||
Reason = "Server full!";
|
||||
}
|
||||
|
||||
if (NotAllowedWithReason) {
|
||||
Allowed = false;
|
||||
}
|
||||
|
||||
if (NotAllowed) {
|
||||
ClientKick(*Client, "you are not allowed on the server!");
|
||||
} else if (NotAllowedWithReason) {
|
||||
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) {
|
||||
return {};
|
||||
} else if (mServer.ClientCount() < size_t(Application::Settings.getAsInt(Settings::Key::General_MaxPlayers)) || BypassLimit) {
|
||||
if (!NotAllowed && !NotAllowedWithReason) {
|
||||
beammp_info("Identification success");
|
||||
mServer.InsertClient(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;
|
||||
}
|
||||
|
||||
|
||||
@@ -121,6 +121,8 @@ void TResourceManager::RefreshFiles() {
|
||||
}
|
||||
EVP_MD_CTX_free(mdctx);
|
||||
|
||||
stream.close();
|
||||
|
||||
std::string result;
|
||||
for (size_t i = 0; i < sha256_len; i++) {
|
||||
result += fmt::format("{:02x}", sha256_value[i]);
|
||||
|
||||
@@ -253,7 +253,6 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::vector<uin
|
||||
HandleEvent(*LockedClient, StringPacket);
|
||||
return;
|
||||
case 'N':
|
||||
beammp_trace("got 'N' packet (" + std::to_string(Packet.size()) + ")");
|
||||
Network.SendToAll(LockedClient.get(), Packet, false, true);
|
||||
return;
|
||||
case 'Z': // position packet
|
||||
|
||||
+2
-1
@@ -13,6 +13,7 @@
|
||||
"nlohmann-json",
|
||||
"openssl",
|
||||
"rapidjson",
|
||||
"sol2"
|
||||
"sol2",
|
||||
"curl"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user