Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ set(PRJ_HEADERS
include/ChronoWrapper.h
include/TConnectionLimiter.h
include/TLuaResult.h
include/RegionHandler.h
)
# add all source files (.cpp) to this, except the one with main()
set(PRJ_SOURCES
Expand Down Expand Up @@ -84,6 +85,7 @@ set(PRJ_SOURCES
src/ChronoWrapper.cpp
src/TConnectionLimiter.cpp
src/TLuaResult.cpp
src/RegionHandler.cpp
)

find_package(Lua REQUIRED)
Expand Down
12 changes: 4 additions & 8 deletions include/Common.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,12 @@ class Application final {

static inline struct Settings Settings { };

static std::vector<std::string> GetBackendUrlsInOrder() {
return {
"https://backend.beammp.com",
};
}
static std::vector<std::string> GetBackendUrlsInOrder();

static std::string GetServerCheckUrl() { return "https://check.beammp.com"; }
static std::string GetServerCheckUrl();

static std::string GetBackendUrlForAuth() { return "https://auth.beammp.com"; }
static std::string GetBackendUrlForSocketIO() { return "https://backend.beammp.com"; }
static std::string GetBackendUrlForAuth();
static std::string GetBackendUrlForSocketIO();
static void CheckForUpdates();
static std::array<uint8_t, 3> VersionStrToInts(const std::string& str);
static bool IsOutdated(const Version& Current, const Version& Newest);
Expand Down
4 changes: 2 additions & 2 deletions include/Http.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@
namespace fs = std::filesystem;

namespace Http {
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 = {});
std::string GET(std::string url, unsigned int* status = nullptr, const bool& redirect = true);
std::string POST(std::string url, const std::string& body, const std::string& ContentType, unsigned int* status = nullptr, const std::map<std::string, std::string>& headers = {}, const bool& redirect = true);
namespace Status {
std::string ToString(int code);
}
Expand Down
34 changes: 34 additions & 0 deletions include/RegionHandler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// BeamMP, the BeamNG.drive multiplayer mod.
// Copyright (C) 2024 BeamMP Ltd., BeamMP team and contributors.
//
// BeamMP Ltd. can be contacted by electronic mail via contact@beammp.com.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

#pragma once

#include <vector>
#include <string>
#include <array>

class RegionHandler final {
public:
RegionHandler() = delete;
static void TopLevelDomainFailed();
static std::string RegionToTopLevelDomain();
static std::string RedirectURL(const std::string &URL);
private:
static inline unsigned int mRegionIndex { 0 };
const static inline std::array<std::string, 2> mValidTLDs {"beammp.com", "beammp.net"};
};
1 change: 1 addition & 0 deletions include/Settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ struct Settings {
General_Debug,
General_AllowGuests,
General_InformationPacket,
General_Region,
};

Sync<std::unordered_map<Key, SettingsTypeVariant>> SettingsMap;
Expand Down
22 changes: 22 additions & 0 deletions src/Common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "Compat.h"
#include "CustomAssert.h"
#include "Http.h"
#include "RegionHandler.h"

void Application::RegisterShutdownHandler(const TShutdownHandler& Handler) {
std::unique_lock Lock(mShutdownHandlersMutex);
Expand Down Expand Up @@ -73,6 +74,27 @@
return mVersion.AsString();
}

std::vector<std::string> Application::GetBackendUrlsInOrder() {
return {
"https://backend.beammp.com",
};
}

std::string Application::GetServerCheckUrl()
{
return "https://check.beammp.com";
}

std::string Application::GetBackendUrlForAuth()
{
return "https://auth.beammp.com";
}

std::string Application::GetBackendUrlForSocketIO()
{
return "https://backend.beammp.com";
}

std::array<uint8_t, 3> Application::VersionStrToInts(const std::string& str) {
std::array<uint8_t, 3> Version;
std::stringstream ss(str);
Expand Down Expand Up @@ -403,7 +425,7 @@
// is pretty optimistic.
std::vector<uint8_t> output_buffer(std::min<size_t>(input.size() * 5, STARTING_MAX_DECOMPRESSION_BUFFER_SIZE));

uLongf output_size = output_buffer.size();

Check warning on line 428 in src/Common.cpp

View workflow job for this annotation

GitHub Actions / windows-build

'initializing': conversion from 'size_t' to 'uLongf', possible loss of data [D:\a\BeamMP-Server\BeamMP-Server\bin\BeamMP-Server.vcxproj]

while (true) {
int res = uncompress(
Expand All @@ -423,11 +445,11 @@
// if decompression fails, we double the buffer size (up to the allowed limit) and try again
output_buffer.resize(std::max<size_t>(output_buffer.size() * 2, MAX_DECOMPRESSION_BUFFER_SIZE));
beammp_warnf("zlib uncompress() failed, trying with a larger buffer size of {}", output_buffer.size());
output_size = output_buffer.size();

Check warning on line 448 in src/Common.cpp

View workflow job for this annotation

GitHub Actions / windows-build

'=': conversion from 'size_t' to 'uLongf', possible loss of data [D:\a\BeamMP-Server\BeamMP-Server\bin\BeamMP-Server.vcxproj]
} else if (res != Z_OK) {
beammp_error("zlib uncompress() failed: " + std::to_string(res));
if (res == Z_DATA_ERROR) {
throw InvalidDataError {};

Check warning on line 452 in src/Common.cpp

View workflow job for this annotation

GitHub Actions / windows-build

'runtime_error': this base class is inaccessible [D:\a\BeamMP-Server\BeamMP-Server\bin\BeamMP-Server.vcxproj]

Check warning on line 452 in src/Common.cpp

View workflow job for this annotation

GitHub Actions / windows-build

throwing 'InvalidDataError' the following types will not be considered at the catch site [D:\a\BeamMP-Server\BeamMP-Server\bin\BeamMP-Server.vcxproj]
} else {
throw std::runtime_error("zlib uncompress() failed");
}
Expand All @@ -440,9 +462,9 @@
}

std::vector<uint8_t> Comp(std::span<const uint8_t> input) {
auto max_size = compressBound(input.size());

Check warning on line 465 in src/Common.cpp

View workflow job for this annotation

GitHub Actions / windows-build

'argument': conversion from 'size_t' to 'uLong', possible loss of data [D:\a\BeamMP-Server\BeamMP-Server\bin\BeamMP-Server.vcxproj]
std::vector<uint8_t> output(max_size);
uLongf output_size = output.size();

Check warning on line 467 in src/Common.cpp

View workflow job for this annotation

GitHub Actions / windows-build

'initializing': conversion from 'size_t' to 'uLongf', possible loss of data [D:\a\BeamMP-Server\BeamMP-Server\bin\BeamMP-Server.vcxproj]
int res = compress(
reinterpret_cast<Bytef*>(output.data()),
&output_size,
Expand Down
35 changes: 33 additions & 2 deletions src/Http.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include "Http.h"

#include "RegionHandler.h"
#include "Common.h"
#include "CustomAssert.h"

Expand Down Expand Up @@ -115,10 +116,13 @@ class CurlLease {
}
};

std::string Http::GET(const std::string& url, unsigned int* status) {
std::string Http::GET(std::string url, unsigned int* status, const bool& redirect) {
std::string Ret;
CurlLease Lease{};
CURL* curl = Lease.GetHandle();
if (redirect) {
url = RegionHandler::RedirectURL(url);
}
if (curl) {
CURLcode res;
char errbuf[CURL_ERROR_SIZE];
Expand All @@ -142,6 +146,18 @@ std::string Http::GET(const std::string& url, unsigned int* status) {
if (res != CURLE_OK) {
beammp_error("GET to " + url + " failed: " + std::string(curl_easy_strerror(res)));
beammp_error("Curl error: " + std::string(errbuf));
if (!redirect) {
return Http::ErrorString;
}
RegionHandler::TopLevelDomainFailed();
url = RegionHandler::RedirectURL(url);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
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 Http::ErrorString;
}

Expand All @@ -156,10 +172,13 @@ std::string Http::GET(const std::string& url, unsigned int* status) {
return Ret;
}

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 Http::POST(std::string url, const std::string& body, const std::string& ContentType, unsigned int* status, const std::map<std::string, std::string>& headers, const bool& redirect) {
std::string Ret;
CurlLease Lease{};
CURL* curl = Lease.GetHandle();
if (redirect) {
url = RegionHandler::RedirectURL(url);
}
if (curl) {
CURLcode res;
char errbuf[CURL_ERROR_SIZE];
Expand Down Expand Up @@ -195,6 +214,18 @@ std::string Http::POST(const std::string& url, const std::string& body, const st
if (res != CURLE_OK) {
beammp_error("POST to " + url + " failed: " + std::string(curl_easy_strerror(res)));
beammp_error("Curl error: " + std::string(errbuf));
if (!redirect) {
return Http::ErrorString;
}
RegionHandler::TopLevelDomainFailed();
url = RegionHandler::RedirectURL(url);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
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 Http::ErrorString;
}

Expand Down
53 changes: 53 additions & 0 deletions src/RegionHandler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// BeamMP, the BeamNG.drive multiplayer mod.
// Copyright (C) 2024 BeamMP Ltd., BeamMP team and contributors.
//
// BeamMP Ltd. can be contacted by electronic mail via contact@beammp.com.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

#include "RegionHandler.h"
#include "Common.h"

#include <regex>

void RegionHandler::TopLevelDomainFailed()
{
static bool isDeveloperRegion = Application::Settings.getAsString(Settings::Key::General_Region) == "Developer";
if (!isDeveloperRegion) {
beammp_info("Top level domain of " + mValidTLDs[mRegionIndex % mValidTLDs.size()] + " didn't respond correctly , changing domain to " + mValidTLDs[(mRegionIndex + 1) % mValidTLDs.size()]);
}
mRegionIndex++;
}

std::string RegionHandler::RegionToTopLevelDomain()
{
static bool isDeveloperRegion = Application::Settings.getAsString(Settings::Key::General_Region) == "Developer";
if (isDeveloperRegion) {
return "beammp.dev";
}
return mValidTLDs[mRegionIndex % mValidTLDs.size()]; // Global
}

std::string RegionHandler::RedirectURL(const std::string &URL)
{
std::regex link_pattern(R"(^(https:\/\/.*)beammp\.com(\/.*)?$)");
std::smatch link_match;
if (std::regex_search(URL, link_match, link_pattern) && link_match.position() == 0) {
//TLD matched beammp.com
std::string before = link_match[1].str(); // "https://..." up to beammp
std::string after = link_match[2].matched ? link_match[2].str() : ""; // "/path" or ""
return before + RegionToTopLevelDomain() + after;
}
return URL; //if it didn't match, just return the unmodified URL
}
2 changes: 2 additions & 0 deletions src/Settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Settings::Settings() {
{ General_Debug, false },
{ General_AllowGuests, true },
{ General_InformationPacket, true },
{ General_Region, std::string("")},
{ Misc_ImScaredOfUpdates, true },
{ Misc_UpdateReminderTime, "30s" }
};
Expand All @@ -56,6 +57,7 @@ Settings::Settings() {
{ { "General", "Debug" }, { General_Debug, READ_WRITE } },
{ { "General", "AllowGuests" }, { General_AllowGuests, READ_WRITE } },
{ { "General", "InformationPacket" }, { General_InformationPacket, READ_WRITE } },
{ { "General", "Region" }, { General_Region, READ_WRITE } },
{ { "Misc", "ImScaredOfUpdates" }, { Misc_ImScaredOfUpdates, READ_WRITE } },
{ { "Misc", "UpdateReminderTime" }, { Misc_UpdateReminderTime, READ_WRITE } }
};
Expand Down
13 changes: 13 additions & 0 deletions src/TConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ static constexpr std::string_view StrTags = "Tags";
static constexpr std::string_view EnvStrTags = "BEAMMP_TAGS";
static constexpr std::string_view StrResourceFolder = "ResourceFolder";
static constexpr std::string_view EnvStrResourceFolder = "BEAMMP_RESOURCE_FOLDER";
static constexpr std::string_view StrRegion = "BackendRegion";
static constexpr std::string_view EnvStrRegion = "BEAMMP_BACKEND_REGION";
static constexpr std::string_view StrAuthKey = "AuthKey";
static constexpr std::string_view EnvStrAuthKey = "BEAMMP_AUTH_KEY";
static constexpr std::string_view StrLogChat = "LogChat";
Expand Down Expand Up @@ -151,6 +153,9 @@ void TConfig::FlushToFile() {
data["General"][StrMap.data()] = Application::Settings.getAsString(Settings::Key::General_Map);
data["General"][StrDescription.data()] = Application::Settings.getAsString(Settings::Key::General_Description);
data["General"][StrResourceFolder.data()] = Application::Settings.getAsString(Settings::Key::General_ResourceFolder);
if (Application::Settings.getAsString(Settings::Key::General_Region) != "") {
data["General"][StrRegion.data()] = Application::Settings.getAsString(Settings::Key::General_Region);
}
// data["General"][StrPassword.data()] = Application::Settings.Password;
// SetComment(data["General"][StrPassword.data()].comments(), " Sets a password on this server, which restricts people from joining. To join, a player must enter this exact password. Leave empty ("") to disable the password.");
// Misc
Expand Down Expand Up @@ -221,16 +226,22 @@ void TConfig::TryReadValue(toml::value& Table, const std::string& Category, cons
if constexpr (std::is_same_v<T, std::string>) {
if (Table[Category.c_str()][Key.data()].is_string())
Application::Settings.set(key, Table[Category.c_str()][Key.data()].as_string());
else if (Table[Category.c_str()][Key.data()].is_empty())
beammp_debugf("Value '{}.{}' is empty", Category, Key);
else
beammp_warnf("Value '{}.{}' has unexpected type, expected type 'string'", Category, Key);
} else if constexpr (std::is_same_v<T, int>) {
if (Table[Category.c_str()][Key.data()].is_integer())
Application::Settings.set(key, int(Table[Category.c_str()][Key.data()].as_integer()));
else if (Table[Category.c_str()][Key.data()].is_empty())
beammp_debugf("Value '{}.{}' is empty", Category, Key);
else
beammp_warnf("Value '{}.{}' has unexpected type, expected type 'integer'", Category, Key);
} else if constexpr (std::is_same_v<T, bool>) {
if (Table[Category.c_str()][Key.data()].is_boolean())
Application::Settings.set(key, Table[Category.c_str()][Key.data()].as_boolean());
else if (Table[Category.c_str()][Key.data()].is_empty())
beammp_debugf("Value '{}.{}' is empty", Category, Key);
else
beammp_warnf("Value '{}.{}' has unexpected type, expected type 'boolean'", Category, Key);
} else {
Expand Down Expand Up @@ -270,6 +281,7 @@ void TConfig::ParseFromFile(std::string_view name) {
TryReadValue(data, "General", StrDescription, EnvStrDescription, Settings::Key::General_Description);
TryReadValue(data, "General", StrTags, EnvStrTags, Settings::Key::General_Tags);
TryReadValue(data, "General", StrResourceFolder, EnvStrResourceFolder, Settings::Key::General_ResourceFolder);
TryReadValue(data, "General", StrRegion, EnvStrRegion, Settings::Key::General_Region);
TryReadValue(data, "General", StrAuthKey, EnvStrAuthKey, Settings::Key::General_AuthKey);
TryReadValue(data, "General", StrLogChat, EnvStrLogChat, Settings::Key::General_LogChat);
TryReadValue(data, "General", StrAllowGuests, EnvStrAllowGuests, Settings::Key::General_AllowGuests);
Expand Down Expand Up @@ -322,6 +334,7 @@ void TConfig::PrintDebug() {
beammp_debug(std::string(StrTags) + ": " + TagsAsPrettyArray());
beammp_debug(std::string(StrLogChat) + ": \"" + (Application::Settings.getAsBool(Settings::Key::General_LogChat) ? "true" : "false") + "\"");
beammp_debug(std::string(StrResourceFolder) + ": \"" + Application::Settings.getAsString(Settings::Key::General_ResourceFolder) + "\"");
beammp_debug(std::string(StrRegion) + ": \"" + Application::Settings.getAsString(Settings::Key::General_Region) + "\"");
beammp_debug(std::string(StrAllowGuests) + ": \"" + (Application::Settings.getAsBool(Settings::Key::General_AllowGuests) ? "true" : "false") + "\"");
// special!
beammp_debug("Key Length: " + std::to_string(Application::Settings.getAsString(Settings::Key::General_AuthKey).length()) + "");
Expand Down
1 change: 1 addition & 0 deletions src/TConsole.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "Http.h"
#include "LuaAPI.h"
#include "TLuaEngine.h"
#include "RegionHandler.h"

#include <ctime>
#include <lua.hpp>
Expand Down
2 changes: 1 addition & 1 deletion src/THeartbeatThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
#include "Client.h"
#include "Common.h"
#include "Http.h"
#include "RegionHandler.h"
// #include "SocketIO.h"
#include <nlohmann/json.hpp>
#include <sstream>

void THeartbeatThread::operator()() {
RegisterThread("Heartbeat");
Expand Down
1 change: 1 addition & 0 deletions src/TNetwork.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "Common.h"
#include "Env.h"
#include "LuaAPI.h"
#include "RegionHandler.h"
#include "TConnectionLimiter.h"
#include "THeartbeatThread.h"
#include "TLuaEngine.h"
Expand Down
Loading