mirror of
https://github.com/BeamMP/BeamMP-Server.git
synced 2026-07-13 10:13:42 +00:00
Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5afc07b0b3 | |||
| bc5e407d60 | |||
| df308c1dc5 | |||
| a998a7c091 | |||
| 277036fc52 | |||
| e776848a76 | |||
| 63fa65e9a7 | |||
| c07baeed1a | |||
| 33b5384398 | |||
| e94cfd641d | |||
| 6e590ff18a | |||
| 91bc7dea79 | |||
| 8b94b1f0ef | |||
| 5dab48af92 | |||
| f3060f5247 | |||
| c61816dfeb | |||
| 2fcb53530a | |||
| cee039d922 | |||
| 566f0b55f7 | |||
| 58a7e39419 | |||
| bf7f1ef1a5 | |||
| e35bf4fe15 | |||
| 419a951c29 | |||
| 135008a73c | |||
| 29c3fed374 | |||
| 93192fd9b5 | |||
| eea041e8eb | |||
| a3670bff4a | |||
| 1b60e89f26 | |||
| 06e5805428 | |||
| fd2f713485 | |||
| c880460a55 | |||
| 7f54bcfaec | |||
| 785c5343cd | |||
| 40e5496819 | |||
| 5f9726f10f | |||
| fcd408970b | |||
| cf5ebcbd1a | |||
| c9d926f9e3 | |||
| 9f47978f0f | |||
| a0f649288e | |||
| b995a222ff | |||
| c5dff8b913 | |||
| f26b091042 | |||
| a008015d78 |
+9
-1
@@ -35,6 +35,7 @@ set(PRJ_HEADERS
|
|||||||
include/Json.h
|
include/Json.h
|
||||||
include/LuaAPI.h
|
include/LuaAPI.h
|
||||||
include/RWMutex.h
|
include/RWMutex.h
|
||||||
|
include/RateLimiter.h
|
||||||
include/SignalHandling.h
|
include/SignalHandling.h
|
||||||
include/TConfig.h
|
include/TConfig.h
|
||||||
include/TConsole.h
|
include/TConsole.h
|
||||||
@@ -50,6 +51,7 @@ set(PRJ_HEADERS
|
|||||||
include/VehicleData.h
|
include/VehicleData.h
|
||||||
include/Env.h
|
include/Env.h
|
||||||
include/Profiling.h
|
include/Profiling.h
|
||||||
|
include/ChronoWrapper.h
|
||||||
)
|
)
|
||||||
# add all source files (.cpp) to this, except the one with main()
|
# add all source files (.cpp) to this, except the one with main()
|
||||||
set(PRJ_SOURCES
|
set(PRJ_SOURCES
|
||||||
@@ -71,9 +73,11 @@ set(PRJ_SOURCES
|
|||||||
src/TResourceManager.cpp
|
src/TResourceManager.cpp
|
||||||
src/TScopedTimer.cpp
|
src/TScopedTimer.cpp
|
||||||
src/TServer.cpp
|
src/TServer.cpp
|
||||||
|
src/RateLimiter.cpp
|
||||||
src/VehicleData.cpp
|
src/VehicleData.cpp
|
||||||
src/Env.cpp
|
src/Env.cpp
|
||||||
src/Profiling.cpp
|
src/Profiling.cpp
|
||||||
|
src/ChronoWrapper.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(Lua REQUIRED)
|
find_package(Lua REQUIRED)
|
||||||
@@ -173,6 +177,11 @@ add_library(commandline_static
|
|||||||
deps/commandline/src/backends/BufferedBackend.cpp
|
deps/commandline/src/backends/BufferedBackend.cpp
|
||||||
deps/commandline/src/backends/BufferedBackend.h
|
deps/commandline/src/backends/BufferedBackend.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Ensure the commandline library uses C++11
|
||||||
|
set_target_properties(commandline_static PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED YES)
|
||||||
|
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
target_compile_definitions(commandline_static PRIVATE -DPLATFORM_WINDOWS=1)
|
target_compile_definitions(commandline_static PRIVATE -DPLATFORM_WINDOWS=1)
|
||||||
else ()
|
else ()
|
||||||
@@ -216,4 +225,3 @@ if(${PROJECT_NAME}_ENABLE_UNIT_TESTING)
|
|||||||
target_link_options(${PROJECT_NAME}-tests PRIVATE "/SUBSYSTEM:CONSOLE")
|
target_link_options(${PROJECT_NAME}-tests PRIVATE "/SUBSYSTEM:CONSOLE")
|
||||||
endif(MSVC)
|
endif(MSVC)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace ChronoWrapper {
|
||||||
|
std::chrono::high_resolution_clock::duration TimeFromStringWithLiteral(const std::string& time_str);
|
||||||
|
}
|
||||||
+1
-1
@@ -128,7 +128,7 @@ private:
|
|||||||
std::string mRole;
|
std::string mRole;
|
||||||
std::string mDID;
|
std::string mDID;
|
||||||
int mID = -1;
|
int mID = -1;
|
||||||
std::chrono::time_point<std::chrono::high_resolution_clock> mLastPingTime;
|
std::chrono::time_point<std::chrono::high_resolution_clock> mLastPingTime = std::chrono::high_resolution_clock::now();
|
||||||
};
|
};
|
||||||
|
|
||||||
std::optional<std::weak_ptr<TClient>> GetClient(class TServer& Server, int ID);
|
std::optional<std::weak_ptr<TClient>> GetClient(class TServer& Server, int ID);
|
||||||
|
|||||||
+5
-2
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -65,7 +66,7 @@ public:
|
|||||||
std::string Resource { "Resources" };
|
std::string Resource { "Resources" };
|
||||||
std::string MapName { "/levels/gridmap_v2/info.json" };
|
std::string MapName { "/levels/gridmap_v2/info.json" };
|
||||||
std::string Key {};
|
std::string Key {};
|
||||||
std::string Password{};
|
std::string Password {};
|
||||||
std::string SSLKeyPath { "./.ssl/HttpServer/key.pem" };
|
std::string SSLKeyPath { "./.ssl/HttpServer/key.pem" };
|
||||||
std::string SSLCertPath { "./.ssl/HttpServer/cert.pem" };
|
std::string SSLCertPath { "./.ssl/HttpServer/cert.pem" };
|
||||||
bool HTTPServerEnabled { false };
|
bool HTTPServerEnabled { false };
|
||||||
@@ -76,12 +77,14 @@ public:
|
|||||||
int Port { 30814 };
|
int Port { 30814 };
|
||||||
std::string CustomIP {};
|
std::string CustomIP {};
|
||||||
bool LogChat { true };
|
bool LogChat { true };
|
||||||
|
bool AllowGuests { true };
|
||||||
bool SendErrors { true };
|
bool SendErrors { true };
|
||||||
bool SendErrorsMessageEnabled { true };
|
bool SendErrorsMessageEnabled { true };
|
||||||
int HTTPServerPort { 8080 };
|
int HTTPServerPort { 8080 };
|
||||||
std::string HTTPServerIP { "127.0.0.1" };
|
std::string HTTPServerIP { "127.0.0.1" };
|
||||||
bool HTTPServerUseSSL { false };
|
bool HTTPServerUseSSL { false };
|
||||||
bool HideUpdateMessages { false };
|
bool HideUpdateMessages { false };
|
||||||
|
std::string UpdateReminderTime { "30s" };
|
||||||
[[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); }
|
[[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -150,7 +153,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, 4, 0 };
|
static inline Version mVersion { 3, 5, 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
void SplitString(std::string const& str, const char delim, std::vector<std::string>& out);
|
void SplitString(std::string const& str, const char delim, std::vector<std::string>& out);
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#include "Common.h"
|
||||||
|
#include <chrono>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <mutex>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
class RateLimiter {
|
||||||
|
public:
|
||||||
|
RateLimiter();
|
||||||
|
bool isConnectionAllowed(const std::string& client_address);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<std::string, std::vector<std::chrono::time_point<std::chrono::high_resolution_clock>>> m_connection;
|
||||||
|
std::mutex m_connection_mutex;
|
||||||
|
|
||||||
|
void blockIP(const std::string& client_address);
|
||||||
|
bool isIPBlocked(const std::string& client_address);
|
||||||
|
};
|
||||||
@@ -48,7 +48,6 @@ public:
|
|||||||
private:
|
private:
|
||||||
void UDPServerMain();
|
void UDPServerMain();
|
||||||
void TCPServerMain();
|
void TCPServerMain();
|
||||||
|
|
||||||
TServer& mServer;
|
TServer& mServer;
|
||||||
TPPSMonitor& mPPSMonitor;
|
TPPSMonitor& mPPSMonitor;
|
||||||
ip::udp::socket mUDPSock;
|
ip::udp::socket mUDPSock;
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#include "ChronoWrapper.h"
|
||||||
|
#include "Common.h"
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
std::chrono::high_resolution_clock::duration ChronoWrapper::TimeFromStringWithLiteral(const std::string& time_str)
|
||||||
|
{
|
||||||
|
// const std::regex time_regex(R"((\d+\.{0,1}\d*)(min|ms|us|ns|[dhs]))"); //i.e one of: "25ns, 6us, 256ms, 2s, 13min, 69h, 356d" will get matched (only available in newer C++ versions)
|
||||||
|
const std::regex time_regex(R"((\d+\.{0,1}\d*)(min|[dhs]))"); //i.e one of: "2.01s, 13min, 69h, 356.69d" will get matched
|
||||||
|
std::smatch match;
|
||||||
|
float time_value;
|
||||||
|
if (!std::regex_search(time_str, match, time_regex)) return std::chrono::nanoseconds(0);
|
||||||
|
time_value = stof(match.str(1));
|
||||||
|
beammp_debugf("Parsed time was: {}{}", time_value, match.str(2));
|
||||||
|
if (match.str(2) == "d") {
|
||||||
|
return std::chrono::seconds((uint64_t)(time_value * 86400)); //86400 seconds in a day
|
||||||
|
}
|
||||||
|
else if (match.str(2) == "h") {
|
||||||
|
return std::chrono::seconds((uint64_t)(time_value * 3600)); //3600 seconds in an hour
|
||||||
|
}
|
||||||
|
else if (match.str(2) == "min") {
|
||||||
|
return std::chrono::seconds((uint64_t)(time_value * 60));
|
||||||
|
}
|
||||||
|
else if (match.str(2) == "s") {
|
||||||
|
return std::chrono::seconds((uint64_t)time_value);
|
||||||
|
}
|
||||||
|
return std::chrono::nanoseconds(0);
|
||||||
|
}
|
||||||
@@ -28,6 +28,7 @@
|
|||||||
#include <regex>
|
#include <regex>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
#include "Compat.h"
|
#include "Compat.h"
|
||||||
#include "CustomAssert.h"
|
#include "CustomAssert.h"
|
||||||
@@ -384,3 +385,4 @@ void SplitString(const std::string& str, const char delim, std::vector<std::stri
|
|||||||
out.push_back(str.substr(start, end - start));
|
out.push_back(str.substr(start, end - start));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+39
-12
@@ -28,15 +28,42 @@
|
|||||||
#include <random>
|
#include <random>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
// TODO: Add sentry error handling back
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
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];
|
||||||
|
}
|
||||||
|
|
||||||
std::string Http::GET(const std::string& host, int port, const std::string& target, unsigned int* status) {
|
std::string Http::GET(const std::string& host, int port, const std::string& target, unsigned int* status) {
|
||||||
httplib::SSLClient client(host, port);
|
std::shared_ptr<httplib::SSLClient> client = getClient({host, port});
|
||||||
client.enable_server_certificate_verification(false);
|
client->enable_server_certificate_verification(false);
|
||||||
client.set_address_family(AF_INET);
|
client->set_address_family(AF_INET);
|
||||||
auto res = client.Get(target.c_str());
|
auto res = client->Get(target.c_str());
|
||||||
if (res) {
|
if (res) {
|
||||||
if (status) {
|
if (status) {
|
||||||
*status = res->status;
|
*status = res->status;
|
||||||
@@ -48,12 +75,12 @@ std::string Http::GET(const std::string& host, int port, const std::string& targ
|
|||||||
}
|
}
|
||||||
|
|
||||||
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::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) {
|
||||||
httplib::SSLClient client(host, port);
|
std::shared_ptr<httplib::SSLClient> client = getClient({host, port});
|
||||||
client.set_read_timeout(std::chrono::seconds(10));
|
client->set_read_timeout(std::chrono::seconds(10));
|
||||||
beammp_assert(client.is_valid());
|
beammp_assert(client->is_valid());
|
||||||
client.enable_server_certificate_verification(false);
|
client->enable_server_certificate_verification(false);
|
||||||
client.set_address_family(AF_INET);
|
client->set_address_family(AF_INET);
|
||||||
auto res = client.Post(target.c_str(), headers, body.c_str(), body.size(), ContentType.c_str());
|
auto res = client->Post(target.c_str(), headers, body.c_str(), body.size(), ContentType.c_str());
|
||||||
if (res) {
|
if (res) {
|
||||||
if (status) {
|
if (status) {
|
||||||
*status = res->status;
|
*status = res->status;
|
||||||
|
|||||||
+39
-18
@@ -60,7 +60,11 @@ std::string LuaAPI::LuaToString(const sol::object Value, size_t Indent, bool Quo
|
|||||||
}
|
}
|
||||||
case sol::type::number: {
|
case sol::type::number: {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << Value.as<float>();
|
if (Value.is<int>()) {
|
||||||
|
ss << Value.as<int>();
|
||||||
|
} else {
|
||||||
|
ss << Value.as<float>();
|
||||||
|
}
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
case sol::type::lua_nil:
|
case sol::type::lua_nil:
|
||||||
@@ -561,7 +565,11 @@ static void JsonEncodeRecursive(nlohmann::json& json, const sol::object& left, c
|
|||||||
key = left.as<std::string>();
|
key = left.as<std::string>();
|
||||||
break;
|
break;
|
||||||
case sol::type::number:
|
case sol::type::number:
|
||||||
key = std::to_string(left.as<double>());
|
if (left.is<int>()) {
|
||||||
|
key = std::to_string(left.as<int>());
|
||||||
|
} else {
|
||||||
|
key = std::to_string(left.as<double>());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
beammp_assert_not_reachable();
|
beammp_assert_not_reachable();
|
||||||
@@ -589,21 +597,30 @@ static void JsonEncodeRecursive(nlohmann::json& json, const sol::object& left, c
|
|||||||
case sol::type::string:
|
case sol::type::string:
|
||||||
value = right.as<std::string>();
|
value = right.as<std::string>();
|
||||||
break;
|
break;
|
||||||
case sol::type::number:
|
case sol::type::number: {
|
||||||
value = right.as<double>();
|
if (right.is<int>()) {
|
||||||
|
value = right.as<int>();
|
||||||
|
} else {
|
||||||
|
value = right.as<double>();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case sol::type::function:
|
case sol::type::function:
|
||||||
beammp_lua_warn("unsure what to do with function in JsonEncode, ignoring");
|
beammp_lua_warn("unsure what to do with function in JsonEncode, ignoring");
|
||||||
return;
|
return;
|
||||||
case sol::type::table: {
|
case sol::type::table: {
|
||||||
bool local_is_array = true;
|
if (right.as<sol::table>().empty()) {
|
||||||
for (const auto& pair : right.as<sol::table>()) {
|
value = nlohmann::json::object();
|
||||||
if (pair.first.get_type() != sol::type::number) {
|
} else {
|
||||||
local_is_array = false;
|
bool local_is_array = true;
|
||||||
|
for (const auto& pair : right.as<sol::table>()) {
|
||||||
|
if (pair.first.get_type() != sol::type::number) {
|
||||||
|
local_is_array = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto& pair : right.as<sol::table>()) {
|
||||||
|
JsonEncodeRecursive(value, pair.first, pair.second, local_is_array, depth + 1);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
for (const auto& pair : right.as<sol::table>()) {
|
|
||||||
JsonEncodeRecursive(value, pair.first, pair.second, local_is_array, depth + 1);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -620,14 +637,18 @@ static void JsonEncodeRecursive(nlohmann::json& json, const sol::object& left, c
|
|||||||
std::string LuaAPI::MP::JsonEncode(const sol::table& object) {
|
std::string LuaAPI::MP::JsonEncode(const sol::table& object) {
|
||||||
nlohmann::json json;
|
nlohmann::json json;
|
||||||
// table
|
// table
|
||||||
bool is_array = true;
|
if (object.as<sol::table>().empty()) {
|
||||||
for (const auto& pair : object.as<sol::table>()) {
|
json = nlohmann::json::object();
|
||||||
if (pair.first.get_type() != sol::type::number) {
|
} else {
|
||||||
is_array = false;
|
bool is_array = true;
|
||||||
|
for (const auto& pair : object.as<sol::table>()) {
|
||||||
|
if (pair.first.get_type() != sol::type::number) {
|
||||||
|
is_array = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto& entry : object) {
|
||||||
|
JsonEncodeRecursive(json, entry.first, entry.second, is_array);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
for (const auto& entry : object) {
|
|
||||||
JsonEncodeRecursive(json, entry.first, entry.second, is_array);
|
|
||||||
}
|
}
|
||||||
return json.dump();
|
return json.dump();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
#include "RateLimiter.h"
|
||||||
|
|
||||||
|
RateLimiter::RateLimiter() {};
|
||||||
|
|
||||||
|
bool RateLimiter::isConnectionAllowed(const std::string& client_address) {
|
||||||
|
if (RateLimiter::isIPBlocked(client_address)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::lock_guard<std::mutex> lock(m_connection_mutex);
|
||||||
|
auto current_time = std::chrono::high_resolution_clock::now();
|
||||||
|
auto& violations = m_connection[client_address];
|
||||||
|
|
||||||
|
// Deleting old violations (older than 5 seconds)
|
||||||
|
violations.erase(std::remove_if(violations.begin(), violations.end(),
|
||||||
|
[&](const auto& timestamp) {
|
||||||
|
return std::chrono::duration_cast<std::chrono::seconds>(current_time - timestamp).count() > 5;
|
||||||
|
}),
|
||||||
|
violations.end());
|
||||||
|
|
||||||
|
violations.push_back(current_time);
|
||||||
|
|
||||||
|
if (violations.size() >= 4) {
|
||||||
|
RateLimiter::blockIP(client_address);
|
||||||
|
beammp_errorf("[DoS Protection] Client with the IP: {} surpassed the violation treshhold and is now on the blocked list", client_address);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // We allow the connection
|
||||||
|
}
|
||||||
|
|
||||||
|
void RateLimiter::blockIP(const std::string& client_address) {
|
||||||
|
std::ofstream block_file("blocked_ips.txt", std::ios::app);
|
||||||
|
if (block_file.is_open()) {
|
||||||
|
block_file << client_address << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RateLimiter::isIPBlocked(const std::string& client_address) {
|
||||||
|
std::ifstream block_file("blocked_ips.txt");
|
||||||
|
std::unordered_set<std::string> blockedIPs;
|
||||||
|
|
||||||
|
if (block_file.is_open()) {
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(block_file, line)) {
|
||||||
|
blockedIPs.insert(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return blockedIPs.contains(client_address);
|
||||||
|
};
|
||||||
@@ -51,12 +51,15 @@ static constexpr std::string_view StrAuthKey = "AuthKey";
|
|||||||
static constexpr std::string_view EnvStrAuthKey = "BEAMMP_AUTH_KEY";
|
static constexpr std::string_view EnvStrAuthKey = "BEAMMP_AUTH_KEY";
|
||||||
static constexpr std::string_view StrLogChat = "LogChat";
|
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 EnvStrAllowGuests = "BEAMMP_ALLOW_GUESTS";
|
||||||
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 StrSendErrors = "SendErrors";
|
||||||
static constexpr std::string_view StrSendErrorsMessageEnabled = "SendErrorsShowMessage";
|
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";
|
||||||
|
|
||||||
TEST_CASE("TConfig::TConfig") {
|
TEST_CASE("TConfig::TConfig") {
|
||||||
const std::string CfgFile = "beammp_server_testconfig.toml";
|
const std::string CfgFile = "beammp_server_testconfig.toml";
|
||||||
@@ -124,6 +127,8 @@ void TConfig::FlushToFile() {
|
|||||||
SetComment(data["General"][StrAuthKey.data()].comments(), " AuthKey has to be filled out in order to run the server");
|
SetComment(data["General"][StrAuthKey.data()].comments(), " AuthKey has to be filled out in order to run the server");
|
||||||
data["General"][StrLogChat.data()] = Application::Settings.LogChat;
|
data["General"][StrLogChat.data()] = Application::Settings.LogChat;
|
||||||
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"][StrAllowGuests.data()] = Application::Settings.AllowGuests;
|
||||||
|
SetComment(data["General"][StrAllowGuests.data()].comments(), " Whether to allow guests");
|
||||||
data["General"][StrDebug.data()] = Application::Settings.DebugModeEnabled;
|
data["General"][StrDebug.data()] = Application::Settings.DebugModeEnabled;
|
||||||
data["General"][StrPrivate.data()] = Application::Settings.Private;
|
data["General"][StrPrivate.data()] = Application::Settings.Private;
|
||||||
data["General"][StrPort.data()] = Application::Settings.Port;
|
data["General"][StrPort.data()] = Application::Settings.Port;
|
||||||
@@ -140,6 +145,8 @@ void TConfig::FlushToFile() {
|
|||||||
// Misc
|
// Misc
|
||||||
data["Misc"][StrHideUpdateMessages.data()] = Application::Settings.HideUpdateMessages;
|
data["Misc"][StrHideUpdateMessages.data()] = Application::Settings.HideUpdateMessages;
|
||||||
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"][StrUpdateReminderTime.data()] = Application::Settings.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.");
|
||||||
data["Misc"][StrSendErrors.data()] = Application::Settings.SendErrors;
|
data["Misc"][StrSendErrors.data()] = Application::Settings.SendErrors;
|
||||||
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`");
|
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.SendErrorsMessageEnabled;
|
data["Misc"][StrSendErrorsMessageEnabled.data()] = Application::Settings.SendErrorsMessageEnabled;
|
||||||
@@ -248,10 +255,12 @@ void TConfig::ParseFromFile(std::string_view name) {
|
|||||||
TryReadValue(data, "General", StrResourceFolder, EnvStrResourceFolder, Application::Settings.Resource);
|
TryReadValue(data, "General", StrResourceFolder, EnvStrResourceFolder, Application::Settings.Resource);
|
||||||
TryReadValue(data, "General", StrAuthKey, EnvStrAuthKey, Application::Settings.Key);
|
TryReadValue(data, "General", StrAuthKey, EnvStrAuthKey, Application::Settings.Key);
|
||||||
TryReadValue(data, "General", StrLogChat, EnvStrLogChat, Application::Settings.LogChat);
|
TryReadValue(data, "General", StrLogChat, EnvStrLogChat, Application::Settings.LogChat);
|
||||||
|
TryReadValue(data, "General", StrAllowGuests, EnvStrAllowGuests, Application::Settings.AllowGuests);
|
||||||
TryReadValue(data, "General", StrPassword, "", Application::Settings.Password);
|
TryReadValue(data, "General", StrPassword, "", Application::Settings.Password);
|
||||||
// Misc
|
// Misc
|
||||||
TryReadValue(data, "Misc", StrSendErrors, "", Application::Settings.SendErrors);
|
TryReadValue(data, "Misc", StrSendErrors, "", Application::Settings.SendErrors);
|
||||||
TryReadValue(data, "Misc", StrHideUpdateMessages, "", Application::Settings.HideUpdateMessages);
|
TryReadValue(data, "Misc", StrHideUpdateMessages, "", Application::Settings.HideUpdateMessages);
|
||||||
|
TryReadValue(data, "Misc", StrUpdateReminderTime, "", Application::Settings.UpdateReminderTime);
|
||||||
TryReadValue(data, "Misc", StrSendErrorsMessageEnabled, "", Application::Settings.SendErrorsMessageEnabled);
|
TryReadValue(data, "Misc", StrSendErrorsMessageEnabled, "", Application::Settings.SendErrorsMessageEnabled);
|
||||||
} catch (const std::exception& err) {
|
} catch (const std::exception& err) {
|
||||||
beammp_error("Error parsing config file value: " + std::string(err.what()));
|
beammp_error("Error parsing config file value: " + std::string(err.what()));
|
||||||
@@ -295,6 +304,7 @@ void TConfig::PrintDebug() {
|
|||||||
beammp_debug(std::string(StrDescription) + ": \"" + Application::Settings.ServerDesc + "\"");
|
beammp_debug(std::string(StrDescription) + ": \"" + Application::Settings.ServerDesc + "\"");
|
||||||
beammp_debug(std::string(StrTags) + ": " + TagsAsPrettyArray());
|
beammp_debug(std::string(StrTags) + ": " + TagsAsPrettyArray());
|
||||||
beammp_debug(std::string(StrLogChat) + ": \"" + (Application::Settings.LogChat ? "true" : "false") + "\"");
|
beammp_debug(std::string(StrLogChat) + ": \"" + (Application::Settings.LogChat ? "true" : "false") + "\"");
|
||||||
|
beammp_debug(std::string(StrAllowGuests) + ": \"" + (Application::Settings.AllowGuests ? "true" : "false") + "\"");
|
||||||
beammp_debug(std::string(StrResourceFolder) + ": \"" + Application::Settings.Resource + "\"");
|
beammp_debug(std::string(StrResourceFolder) + ": \"" + Application::Settings.Resource + "\"");
|
||||||
// special!
|
// special!
|
||||||
beammp_debug("Key Length: " + std::to_string(Application::Settings.Key.length()) + "");
|
beammp_debug("Key Length: " + std::to_string(Application::Settings.Key.length()) + "");
|
||||||
|
|||||||
+21
-1
@@ -26,7 +26,9 @@
|
|||||||
#include "TLuaEngine.h"
|
#include "TLuaEngine.h"
|
||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <lua.hpp>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <openssl/opensslv.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
static inline bool StringStartsWith(const std::string& What, const std::string& StartsWith) {
|
static inline bool StringStartsWith(const std::string& What, const std::string& StartsWith) {
|
||||||
@@ -273,7 +275,25 @@ void TConsole::Command_Version(const std::string& cmd, const std::vector<std::st
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::Console().WriteRaw("Current version: v" + Application::ServerVersionString());
|
std::string platform;
|
||||||
|
#if defined(BEAMMP_WINDOWS)
|
||||||
|
platform = "Windows";
|
||||||
|
#elif defined(BEAMMP_LINUX)
|
||||||
|
platform = "Linux";
|
||||||
|
#elif defined(BEAMMP_FREEBSD)
|
||||||
|
platform = "FreeBSD";
|
||||||
|
#elif defined(BEAMMP_APPLE)
|
||||||
|
platform = "Apple";
|
||||||
|
#else
|
||||||
|
platform = "Unknown";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Application::Console().WriteRaw("Platform: " + platform);
|
||||||
|
Application::Console().WriteRaw("Server: v" + Application::ServerVersionString());
|
||||||
|
std::string lua_version = fmt::format("Lua: v{}.{}.{}", LUA_VERSION_MAJOR, LUA_VERSION_MINOR, LUA_VERSION_RELEASE);
|
||||||
|
Application::Console().WriteRaw(lua_version);
|
||||||
|
std::string openssl_version = fmt::format("OpenSSL: v{}.{}.{}", OPENSSL_VERSION_MAJOR, OPENSSL_VERSION_MINOR, OPENSSL_VERSION_PATCH);
|
||||||
|
Application::Console().WriteRaw(openssl_version);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TConsole::Command_Kick(const std::string&, const std::vector<std::string>& args) {
|
void TConsole::Command_Kick(const std::string&, const std::vector<std::string>& args) {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include "Client.h"
|
#include "Client.h"
|
||||||
#include "Http.h"
|
#include "Http.h"
|
||||||
|
#include "ChronoWrapper.h"
|
||||||
//#include "SocketIO.h"
|
//#include "SocketIO.h"
|
||||||
#include <rapidjson/document.h>
|
#include <rapidjson/document.h>
|
||||||
#include <rapidjson/rapidjson.h>
|
#include <rapidjson/rapidjson.h>
|
||||||
@@ -36,15 +37,17 @@ void THeartbeatThread::operator()() {
|
|||||||
static std::string Last;
|
static std::string Last;
|
||||||
|
|
||||||
static std::chrono::high_resolution_clock::time_point LastNormalUpdateTime = std::chrono::high_resolution_clock::now();
|
static std::chrono::high_resolution_clock::time_point LastNormalUpdateTime = std::chrono::high_resolution_clock::now();
|
||||||
|
static std::chrono::high_resolution_clock::time_point LastUpdateReminderTime = std::chrono::high_resolution_clock::now();
|
||||||
bool isAuth = false;
|
bool isAuth = false;
|
||||||
size_t UpdateReminderCounter = 0;
|
std::chrono::high_resolution_clock::duration UpdateReminderTimePassed;
|
||||||
|
auto UpdateReminderTimeout = ChronoWrapper::TimeFromStringWithLiteral(Application::Settings.UpdateReminderTime);
|
||||||
while (!Application::IsShuttingDown()) {
|
while (!Application::IsShuttingDown()) {
|
||||||
++UpdateReminderCounter;
|
|
||||||
Body = GenerateCall();
|
Body = GenerateCall();
|
||||||
// a hot-change occurs when a setting has changed, to update the backend of that change.
|
// a hot-change occurs when a setting has changed, to update the backend of that change.
|
||||||
auto Now = std::chrono::high_resolution_clock::now();
|
auto Now = std::chrono::high_resolution_clock::now();
|
||||||
bool Unchanged = Last == Body;
|
bool Unchanged = Last == Body;
|
||||||
auto TimePassed = (Now - LastNormalUpdateTime);
|
auto TimePassed = (Now - LastNormalUpdateTime);
|
||||||
|
UpdateReminderTimePassed = (Now - LastUpdateReminderTime);
|
||||||
auto Threshold = Unchanged ? 30 : 5;
|
auto Threshold = Unchanged ? 30 : 5;
|
||||||
if (TimePassed < std::chrono::seconds(Threshold)) {
|
if (TimePassed < std::chrono::seconds(Threshold)) {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
@@ -129,7 +132,9 @@ void THeartbeatThread::operator()() {
|
|||||||
if (isAuth || Application::Settings.Private) {
|
if (isAuth || Application::Settings.Private) {
|
||||||
Application::SetSubsystemStatus("Heartbeat", Application::Status::Good);
|
Application::SetSubsystemStatus("Heartbeat", Application::Status::Good);
|
||||||
}
|
}
|
||||||
if (!Application::Settings.HideUpdateMessages && UpdateReminderCounter % 5) {
|
// beammp_debugf("Update reminder time passed: {}, Update reminder time: {}", UpdateReminderTimePassed.count(), UpdateReminderTimeout.count());
|
||||||
|
if (!Application::Settings.HideUpdateMessages && UpdateReminderTimePassed.count() > UpdateReminderTimeout.count()) {
|
||||||
|
LastUpdateReminderTime = std::chrono::high_resolution_clock::now();
|
||||||
Application::CheckForUpdates();
|
Application::CheckForUpdates();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,6 +153,7 @@ std::string THeartbeatThread::GenerateCall() {
|
|||||||
<< "&clientversion=" << std::to_string(Application::ClientMajorVersion()) + ".0" // FIXME: Wtf.
|
<< "&clientversion=" << std::to_string(Application::ClientMajorVersion()) + ".0" // FIXME: Wtf.
|
||||||
<< "&name=" << Application::Settings.ServerName
|
<< "&name=" << Application::Settings.ServerName
|
||||||
<< "&tags=" << Application::Settings.ServerTags
|
<< "&tags=" << Application::Settings.ServerTags
|
||||||
|
<< "&allowguests=" << (Application::Settings.AllowGuests ? "true" : "false")
|
||||||
<< "&modlist=" << mResourceManager.TrimmedList()
|
<< "&modlist=" << mResourceManager.TrimmedList()
|
||||||
<< "&modstotalsize=" << mResourceManager.MaxModSize()
|
<< "&modstotalsize=" << mResourceManager.MaxModSize()
|
||||||
<< "&modstotal=" << mResourceManager.ModsLoaded()
|
<< "&modstotal=" << mResourceManager.ModsLoaded()
|
||||||
|
|||||||
+14
-7
@@ -66,6 +66,7 @@ void TLuaEngine::operator()() {
|
|||||||
RegisterThread("LuaEngine");
|
RegisterThread("LuaEngine");
|
||||||
Application::SetSubsystemStatus("LuaEngine", Application::Status::Good);
|
Application::SetSubsystemStatus("LuaEngine", Application::Status::Good);
|
||||||
// lua engine main thread
|
// lua engine main thread
|
||||||
|
beammp_infof("Lua v{}.{}.{}", LUA_VERSION_MAJOR, LUA_VERSION_MINOR, LUA_VERSION_RELEASE);
|
||||||
CollectAndInitPlugins();
|
CollectAndInitPlugins();
|
||||||
// now call all onInit's
|
// now call all onInit's
|
||||||
auto Futures = TriggerEvent("onInit", "");
|
auto Futures = TriggerEvent("onInit", "");
|
||||||
@@ -268,7 +269,7 @@ std::vector<std::string> TLuaEngine::StateThreadData::GetStateTableKeys(const st
|
|||||||
|
|
||||||
for (size_t i = 0; i < keys.size(); ++i) {
|
for (size_t i = 0; i < keys.size(); ++i) {
|
||||||
auto obj = current.get<sol::object>(keys.at(i));
|
auto obj = current.get<sol::object>(keys.at(i));
|
||||||
if (obj.get_type() == sol::type::nil) {
|
if (obj.get_type() == sol::type::lua_nil) {
|
||||||
// error
|
// error
|
||||||
break;
|
break;
|
||||||
} else if (i == keys.size() - 1) {
|
} else if (i == keys.size() - 1) {
|
||||||
@@ -458,7 +459,8 @@ std::vector<sol::object> TLuaEngine::StateThreadData::JsonStringToArray(JsonStri
|
|||||||
|
|
||||||
sol::table TLuaEngine::StateThreadData::Lua_TriggerGlobalEvent(const std::string& EventName, sol::variadic_args EventArgs) {
|
sol::table TLuaEngine::StateThreadData::Lua_TriggerGlobalEvent(const std::string& EventName, sol::variadic_args EventArgs) {
|
||||||
auto Table = mStateView.create_table();
|
auto Table = mStateView.create_table();
|
||||||
for (const sol::stack_proxy& Arg : EventArgs) {
|
int i = 1;
|
||||||
|
for (auto Arg : EventArgs) {
|
||||||
switch (Arg.get_type()) {
|
switch (Arg.get_type()) {
|
||||||
case sol::type::none:
|
case sol::type::none:
|
||||||
case sol::type::userdata:
|
case sol::type::userdata:
|
||||||
@@ -466,19 +468,20 @@ sol::table TLuaEngine::StateThreadData::Lua_TriggerGlobalEvent(const std::string
|
|||||||
case sol::type::thread:
|
case sol::type::thread:
|
||||||
case sol::type::function:
|
case sol::type::function:
|
||||||
case sol::type::poly:
|
case sol::type::poly:
|
||||||
Table.add(BEAMMP_INTERNAL_NIL);
|
Table.set(i, BEAMMP_INTERNAL_NIL);
|
||||||
beammp_warnf("Passed a value of type '{}' to TriggerGlobalEvent(\"{}\", ...). This type can not be serialized, and cannot be passed between states. It will arrive as <nil> in handlers.", sol::type_name(EventArgs.lua_state(), Arg.get_type()), EventName);
|
beammp_warnf("Passed a value of type '{}' to TriggerGlobalEvent(\"{}\", ...). This type can not be serialized, and cannot be passed between states. It will arrive as <nil> in handlers.", sol::type_name(EventArgs.lua_state(), Arg.get_type()), EventName);
|
||||||
break;
|
break;
|
||||||
case sol::type::lua_nil:
|
case sol::type::lua_nil:
|
||||||
Table.add(BEAMMP_INTERNAL_NIL);
|
Table.set(i, BEAMMP_INTERNAL_NIL);
|
||||||
break;
|
break;
|
||||||
case sol::type::string:
|
case sol::type::string:
|
||||||
case sol::type::number:
|
case sol::type::number:
|
||||||
case sol::type::boolean:
|
case sol::type::boolean:
|
||||||
case sol::type::table:
|
case sol::type::table:
|
||||||
Table.add(Arg);
|
Table.set(i, Arg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
++i;
|
||||||
}
|
}
|
||||||
JsonString Str { LuaAPI::MP::JsonEncode(Table) };
|
JsonString Str { LuaAPI::MP::JsonEncode(Table) };
|
||||||
beammp_debugf("json: {}", Str.value);
|
beammp_debugf("json: {}", Str.value);
|
||||||
@@ -520,11 +523,13 @@ sol::table TLuaEngine::StateThreadData::Lua_TriggerGlobalEvent(const std::string
|
|||||||
sol::state_view StateView(mState);
|
sol::state_view StateView(mState);
|
||||||
sol::table Result = StateView.create_table();
|
sol::table Result = StateView.create_table();
|
||||||
auto Vector = Self.get<std::vector<std::shared_ptr<TLuaResult>>>("ReturnValueImpl");
|
auto Vector = Self.get<std::vector<std::shared_ptr<TLuaResult>>>("ReturnValueImpl");
|
||||||
|
int i = 1;
|
||||||
for (const auto& Value : Vector) {
|
for (const auto& Value : Vector) {
|
||||||
if (!Value->Ready) {
|
if (!Value->Ready) {
|
||||||
return sol::lua_nil;
|
return sol::lua_nil;
|
||||||
}
|
}
|
||||||
Result.add(Value->Result);
|
Result.set(i, Value->Result);
|
||||||
|
++i;
|
||||||
}
|
}
|
||||||
return Result;
|
return Result;
|
||||||
});
|
});
|
||||||
@@ -534,12 +539,14 @@ sol::table TLuaEngine::StateThreadData::Lua_TriggerGlobalEvent(const std::string
|
|||||||
sol::table TLuaEngine::StateThreadData::Lua_TriggerLocalEvent(const std::string& EventName, sol::variadic_args EventArgs) {
|
sol::table TLuaEngine::StateThreadData::Lua_TriggerLocalEvent(const std::string& EventName, sol::variadic_args EventArgs) {
|
||||||
// TODO: make asynchronous?
|
// TODO: make asynchronous?
|
||||||
sol::table Result = mStateView.create_table();
|
sol::table Result = mStateView.create_table();
|
||||||
|
int i = 1;
|
||||||
for (const auto& Handler : mEngine->GetEventHandlersForState(EventName, mStateId)) {
|
for (const auto& Handler : mEngine->GetEventHandlersForState(EventName, mStateId)) {
|
||||||
auto Fn = mStateView[Handler];
|
auto Fn = mStateView[Handler];
|
||||||
if (Fn.valid() && Fn.get_type() == sol::type::function) {
|
if (Fn.valid() && Fn.get_type() == sol::type::function) {
|
||||||
auto FnRet = Fn(EventArgs);
|
auto FnRet = Fn(EventArgs);
|
||||||
if (FnRet.valid()) {
|
if (FnRet.valid()) {
|
||||||
Result.add(FnRet);
|
Result.set(i, FnRet);
|
||||||
|
++i;
|
||||||
} else {
|
} else {
|
||||||
sol::error Err = FnRet;
|
sol::error Err = FnRet;
|
||||||
beammp_lua_error(std::string("TriggerLocalEvent: ") + Err.what());
|
beammp_lua_error(std::string("TriggerLocalEvent: ") + Err.what());
|
||||||
|
|||||||
+30
-11
@@ -20,6 +20,7 @@
|
|||||||
#include "Client.h"
|
#include "Client.h"
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "LuaAPI.h"
|
#include "LuaAPI.h"
|
||||||
|
#include "RateLimiter.h"
|
||||||
#include "TLuaEngine.h"
|
#include "TLuaEngine.h"
|
||||||
#include "nlohmann/json.hpp"
|
#include "nlohmann/json.hpp"
|
||||||
#include <CustomAssert.h>
|
#include <CustomAssert.h>
|
||||||
@@ -196,10 +197,18 @@ void TNetwork::Identify(TConnection&& RawConnection) {
|
|||||||
RawConnection.Socket.shutdown(socket_base::shutdown_both, ec);
|
RawConnection.Socket.shutdown(socket_base::shutdown_both, ec);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::shared_ptr<TClient> Client { nullptr };
|
std::string client_address = RawConnection.SockAddr.address().to_string();
|
||||||
|
std::shared_ptr<TClient> client { nullptr };
|
||||||
|
RateLimiter ddos_protection;
|
||||||
try {
|
try {
|
||||||
if (Code == 'C') {
|
if (Code == 'C') {
|
||||||
Client = Authentication(std::move(RawConnection));
|
if (ddos_protection.isConnectionAllowed(client_address)) {
|
||||||
|
beammp_infof("[DoS Protection] Client: [{}] is authorized to connect to the server", client_address);
|
||||||
|
client = Authentication(std::move(RawConnection));
|
||||||
|
} else {
|
||||||
|
beammp_infof("[DoS Protection] Client: [{}] has been denied access to the server", client_address);
|
||||||
|
RawConnection.Socket.shutdown(socket_base::shutdown_both, ec);
|
||||||
|
}
|
||||||
} else if (Code == 'D') {
|
} else if (Code == 'D') {
|
||||||
HandleDownload(std::move(RawConnection));
|
HandleDownload(std::move(RawConnection));
|
||||||
} else if (Code == 'P') {
|
} else if (Code == 'P') {
|
||||||
@@ -209,7 +218,7 @@ void TNetwork::Identify(TConnection&& RawConnection) {
|
|||||||
} 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);
|
||||||
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);
|
||||||
@@ -278,7 +287,7 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TCPSend(*Client, StringToVector("A"))) { //changed to A for Accepted version
|
if (!TCPSend(*Client, StringToVector("A"))) { // changed to A for Accepted version
|
||||||
// TODO: handle
|
// TODO: handle
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,16 +298,21 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string key(reinterpret_cast<const char*>(Data.data()), Data.size());
|
std::string Key(reinterpret_cast<const char*>(Data.data()), Data.size());
|
||||||
|
std::string AuthKey = Application::Settings.Key;
|
||||||
|
std::string ClientIp = Client->GetIdentifiers().at("ip");
|
||||||
|
|
||||||
nlohmann::json AuthReq{};
|
nlohmann::json AuthReq {};
|
||||||
std::string AuthResStr{};
|
std::string AuthResStr {};
|
||||||
try {
|
try {
|
||||||
AuthReq = nlohmann::json {
|
AuthReq = nlohmann::json {
|
||||||
{ "key", key }
|
{ "key", Key },
|
||||||
|
{ "auth_key", AuthKey },
|
||||||
|
{ "client_ip", ClientIp }
|
||||||
};
|
};
|
||||||
|
|
||||||
auto Target = "/pkToUser";
|
auto Target = "/pkToUser";
|
||||||
|
|
||||||
unsigned int ResponseCode = 0;
|
unsigned int ResponseCode = 0;
|
||||||
AuthResStr = Http::POST(Application::GetBackendUrlForAuth(), 443, Target, AuthReq.dump(), "application/json", &ResponseCode);
|
AuthResStr = Http::POST(Application::GetBackendUrlForAuth(), 443, Target, AuthReq.dump(), "application/json", &ResponseCode);
|
||||||
|
|
||||||
@@ -334,14 +348,14 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!Application::Settings.Password.empty()) { // ask password
|
if (!Application::Settings.Password.empty()) { // ask password
|
||||||
if(!TCPSend(*Client, StringToVector("S"))) {
|
if (!TCPSend(*Client, StringToVector("S"))) {
|
||||||
// TODO: handle
|
// TODO: handle
|
||||||
}
|
}
|
||||||
beammp_info("Waiting for password");
|
beammp_info("Waiting for password");
|
||||||
Data = TCPRcv(*Client);
|
Data = TCPRcv(*Client);
|
||||||
std::string Pass = std::string(reinterpret_cast<const char*>(Data.data()), Data.size());
|
std::string Pass = std::string(reinterpret_cast<const char*>(Data.data()), Data.size());
|
||||||
if(Pass != HashPassword(Application::Settings.Password)) {
|
if (Pass != HashPassword(Application::Settings.Password)) {
|
||||||
beammp_debug(Client->GetName() + " attempted to connect with a wrong password");
|
beammp_debug(Client->GetName() + " attempted to connect with a wrong password");
|
||||||
ClientKick(*Client, "Wrong password!");
|
ClientKick(*Client, "Wrong password!");
|
||||||
return {};
|
return {};
|
||||||
@@ -384,6 +398,11 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!NotAllowedWithReason && !Application::Settings.AllowGuests && Client->IsGuest()) { //! NotAllowedWithReason because this message has the lowest priority
|
||||||
|
NotAllowedWithReason = true;
|
||||||
|
Reason = "No guests are allowed on this server! To join, sign up at: forum.beammp.com.";
|
||||||
|
}
|
||||||
|
|
||||||
if (NotAllowed) {
|
if (NotAllowed) {
|
||||||
ClientKick(*Client, "you are not allowed on the server!");
|
ClientKick(*Client, "you are not allowed on the server!");
|
||||||
return {};
|
return {};
|
||||||
|
|||||||
+1
-1
@@ -76,7 +76,7 @@ void TPPSMonitor::operator()() {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
for (auto& ClientToKick : TimedOutClients) {
|
for (auto& ClientToKick : TimedOutClients) {
|
||||||
Network().ClientKick(*ClientToKick, "Timeout (no ping for way too long)");
|
ClientToKick->Disconnect("Timeout");
|
||||||
}
|
}
|
||||||
TimedOutClients.clear();
|
TimedOutClients.clear();
|
||||||
if (C == 0 || mInternalPPS == 0) {
|
if (C == 0 || mInternalPPS == 0) {
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
local function assert_eq(x, y, explain)
|
||||||
|
if x ~= y then
|
||||||
|
print("assertion '"..explain.."' failed:\n\tgot:\t", x, "\n\texpected:", y)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param o1 any|table First object to compare
|
||||||
|
---@param o2 any|table Second object to compare
|
||||||
|
---@param ignore_mt boolean True to ignore metatables (a recursive function to tests tables inside tables)
|
||||||
|
function equals(o1, o2, ignore_mt)
|
||||||
|
if o1 == o2 then return true end
|
||||||
|
local o1Type = type(o1)
|
||||||
|
local o2Type = type(o2)
|
||||||
|
if o1Type ~= o2Type then return false end
|
||||||
|
if o1Type ~= 'table' then return false end
|
||||||
|
|
||||||
|
if not ignore_mt then
|
||||||
|
local mt1 = getmetatable(o1)
|
||||||
|
if mt1 and mt1.__eq then
|
||||||
|
--compare using built in method
|
||||||
|
return o1 == o2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local keySet = {}
|
||||||
|
|
||||||
|
for key1, value1 in pairs(o1) do
|
||||||
|
local value2 = o2[key1]
|
||||||
|
if value2 == nil or equals(value1, value2, ignore_mt) == false then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
keySet[key1] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
for key2, _ in pairs(o2) do
|
||||||
|
if not keySet[key2] then return false end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function assert_table_eq(x, y, explain)
|
||||||
|
if not equals(x, y, true) then
|
||||||
|
print("assertion '"..explain.."' failed:\n\tgot:\t", x, "\n\texpected:", y)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_eq(Util.JsonEncode({1, 2, 3, 4, 5}), "[1,2,3,4,5]", "table to array")
|
||||||
|
assert_eq(Util.JsonEncode({"a", 1, 2, 3, 4, 5}), '["a",1,2,3,4,5]', "table to array")
|
||||||
|
assert_eq(Util.JsonEncode({"a", 1, 2.0, 3, 4, 5}), '["a",1,2.0,3,4,5]', "table to array")
|
||||||
|
assert_eq(Util.JsonEncode({hello="world", john={doe = 1, jane = 2.5, mike = {2, 3, 4}}, dave={}}), '{"dave":{},"hello":"world","john":{"doe":1,"jane":2.5,"mike":[2,3,4]}}', "table to obj")
|
||||||
|
assert_eq(Util.JsonEncode({a = nil}), "{}", "null obj member")
|
||||||
|
assert_eq(Util.JsonEncode({1, nil, 3}), "[1,3]", "null array member")
|
||||||
|
assert_eq(Util.JsonEncode({}), "{}", "empty array/table")
|
||||||
|
assert_eq(Util.JsonEncode({1234}), "[1234]", "int")
|
||||||
|
assert_eq(Util.JsonEncode({1234.0}), "[1234.0]", "double")
|
||||||
|
|
||||||
|
assert_table_eq(Util.JsonDecode("[1,2,3,4,5]"), {1, 2, 3, 4, 5}, "decode table to array")
|
||||||
|
assert_table_eq(Util.JsonDecode('["a",1,2,3,4,5]'), {"a", 1, 2, 3, 4, 5}, "decode table to array")
|
||||||
|
assert_table_eq(Util.JsonDecode('["a",1,2.0,3,4,5]'), {"a", 1, 2.0, 3, 4, 5}, "decode table to array")
|
||||||
|
assert_table_eq(Util.JsonDecode('{"dave":{},"hello":"world","john":{"doe":1,"jane":2.5,"mike":[2,3,4]}}'), {hello="world", john={doe = 1, jane = 2.5, mike = {2, 3, 4}}, dave={}}, "decode table to obj")
|
||||||
|
assert_table_eq(Util.JsonDecode("{}"), {a = nil}, "decode null obj member")
|
||||||
|
assert_table_eq(Util.JsonDecode("[1,3]"), {1, 3}, "decode null array member")
|
||||||
|
assert_table_eq(Util.JsonDecode("{}"), {}, "decode empty array/table")
|
||||||
|
assert_table_eq(Util.JsonDecode("[1234]"), {1234}, "decode int")
|
||||||
|
assert_table_eq(Util.JsonDecode("[1234.0]"), {1234.0}, "decode double")
|
||||||
+1
-1
Submodule vcpkg updated: 326d8b43e3...6978381401
Reference in New Issue
Block a user