Merge remote-tracking branch 'origin/master' into rewrite-lua

This commit is contained in:
Lion Kortlepel 2021-09-17 14:24:12 +02:00
commit 883d69ba27
No known key found for this signature in database
GPG Key ID: 4322FF2B4C71259B
16 changed files with 312 additions and 68 deletions

View File

@ -24,7 +24,7 @@ jobs:
tag_name: ${{ github.ref }} tag_name: ${{ github.ref }}
release_name: ${{ github.ref }} release_name: ${{ github.ref }}
draft: false draft: false
prerelease: false prerelease: true
body: | body: |
Files included in this release: Files included in this release:
- `BeamMP-Server.exe` is the windows build - `BeamMP-Server.exe` is the windows build

View File

@ -40,10 +40,10 @@ if (WIN32)
elseif (UNIX) elseif (UNIX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -static-libstdc++") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -static-libstdc++")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -s -fno-builtin") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -gz -fno-builtin")
if (SANITIZE) if (SANITIZE)
message(STATUS "sanitize is ON") message(STATUS "sanitize is ON")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLanAGS} -fsanitize=undefined,thread") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined,thread")
endif (SANITIZE) endif (SANITIZE)
endif () endif ()
@ -90,6 +90,7 @@ add_executable(BeamMP-Server
include/TPPSMonitor.h src/TPPSMonitor.cpp include/TPPSMonitor.h src/TPPSMonitor.cpp
include/TNetwork.h src/TNetwork.cpp include/TNetwork.h src/TNetwork.cpp
include/LuaAPI.h src/LuaAPI.cpp) include/LuaAPI.h src/LuaAPI.cpp)
include/SignalHandling.h src/SignalHandling.cpp)
target_compile_definitions(BeamMP-Server PRIVATE SECRET_SENTRY_URL="${BEAMMP_SECRET_SENTRY_URL}") target_compile_definitions(BeamMP-Server PRIVATE SECRET_SENTRY_URL="${BEAMMP_SECRET_SENTRY_URL}")
include_directories(BeamMP-Server PUBLIC ${Boost_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/include) include_directories(BeamMP-Server PUBLIC ${Boost_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/include)

View File

@ -1,3 +1,20 @@
# v2.3.3
- CHANGED servers to be private by default
# v2.3.2
- ADDED Ctrl+C causes a graceful shutdown on windows (did already on linux)
- ADDED more meaningful shutdown messages
- ADDED even better backend connection error reporting
- ADDED `SendErrors` config in `ServerConfig.toml` to opt-out of error reporting
- ADDED hard-shutdown if Ctrl+C pressed 3 times
- FIXED issue with shells like bash being unusable after server exit
# v2.3.1
- CHANGED join/sync timeout to 20 minutes, players wont drop if loading takes >5 mins
# v2.3.0 # v2.3.0
- ADDED version check - the server will now let you know when a new release is out - ADDED version check - the server will now let you know when a new release is out

View File

@ -40,10 +40,12 @@ public:
, Resource("Resources") , Resource("Resources")
, MapName("/levels/gridmap_v2/info.json") , MapName("/levels/gridmap_v2/info.json")
, MaxPlayers(10) , MaxPlayers(10)
, Private(false) , Private(true)
, MaxCars(1) , MaxCars(1)
, DebugModeEnabled(false) , DebugModeEnabled(false)
, Port(30814) { } , Port(30814)
, SendErrors(true)
, SendErrorsMessageEnabled(true) { }
std::string ServerName; std::string ServerName;
std::string ServerDesc; std::string ServerDesc;
std::string Resource; std::string Resource;
@ -55,6 +57,8 @@ public:
bool DebugModeEnabled; bool DebugModeEnabled;
int Port; int Port;
std::string CustomIP; std::string CustomIP;
bool SendErrors;
bool SendErrorsMessageEnabled;
[[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); } [[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); }
}; };
@ -100,6 +104,7 @@ void RegisterThread(const std::string& str);
#define KB 1024 #define KB 1024
#define MB (KB * 1024) #define MB (KB * 1024)
#define SSU_UNRAW SECRET_SENTRY_URL
#define _file_basename std::filesystem::path(__FILE__).filename().string() #define _file_basename std::filesystem::path(__FILE__).filename().string()
#define _line std::to_string(__LINE__) #define _line std::to_string(__LINE__)
@ -125,9 +130,11 @@ void RegisterThread(const std::string& str);
#else #else
#define _this_location (ThreadName() + _file_basename + ":" + _line + " ") #define _this_location (ThreadName() + _file_basename + ":" + _line + " ")
#endif #endif
#define SU_RAW SSU_UNRAW
#else // !defined(DEBUG) #else // !defined(DEBUG)
#define SU_RAW RAWIFY(SSU_UNRAW)
#define _this_location (ThreadName()) #define _this_location (ThreadName())
#endif // defined(DEBUG) #endif // defined(DEBUG)
@ -168,5 +175,6 @@ std::string Comp(std::string Data);
std::string DeComp(std::string Compressed); std::string DeComp(std::string Compressed);
std::string GetPlatformAgnosticErrorString(); std::string GetPlatformAgnosticErrorString();
#define S_DSN SU_RAW
void LogChatMessage(const std::string& name, int id, const std::string& msg); void LogChatMessage(const std::string& name, int id, const std::string& msg);

114
include/Cryptography.h Normal file
View File

@ -0,0 +1,114 @@
// Copyright Anonymous275 8/11/2020
#pragma once
#include <array>
#include <cstdarg>
#include <string>
namespace Crypto {
constexpr auto time = __TIME__;
constexpr auto seed = static_cast<int>(time[7]) + static_cast<int>(time[6]) * 10 + static_cast<int>(time[4]) * 60 + static_cast<int>(time[3]) * 600 + static_cast<int>(time[1]) * 3600 + static_cast<int>(time[0]) * 36000;
// 1988, Stephen Park and Keith Miller
// "Random Number Generators: Good Ones Are Hard To Find", considered as "minimal standard"
// Park-Miller 31 bit pseudo-random number generator, implemented with G. Carta's optimisation:
// with 32-bit math and without division
template <int N>
struct RandomGenerator {
private:
static constexpr unsigned a = 16807; // 7^5
static constexpr unsigned m = 2147483647; // 2^31 - 1
static constexpr unsigned s = RandomGenerator<N - 1>::value;
static constexpr unsigned lo = a * (s & 0xFFFFu); // Multiply lower 16 bits by 16807
static constexpr unsigned hi = a * (s >> 16u); // Multiply higher 16 bits by 16807
static constexpr unsigned lo2 = lo + ((hi & 0x7FFFu) << 16u); // Combine lower 15 bits of hi with lo's upper bits
static constexpr unsigned hi2 = hi >> 15u; // Discard lower 15 bits of hi
static constexpr unsigned lo3 = lo2 + hi;
public:
static constexpr unsigned max = m;
static constexpr unsigned value = lo3 > m ? lo3 - m : lo3;
};
template <>
struct RandomGenerator<0> {
static constexpr unsigned value = seed;
};
template <int N, int M>
struct RandomInt {
static constexpr auto value = RandomGenerator<N + 1>::value % M;
};
template <int N>
struct RandomChar {
static const char value = static_cast<char>(1 + RandomInt<N, 0x7F - 1>::value);
};
template <size_t N, int K, typename Char>
struct MangleString {
private:
const char _key;
std::array<Char, N + 1> _encrypted;
constexpr Char enc(Char c) const {
return c ^ _key;
}
Char dec(Char c) const {
return c ^ _key;
}
public:
template <size_t... Is>
constexpr MangleString(const Char* str, std::index_sequence<Is...>)
: _key(RandomChar<K>::value)
, _encrypted { enc(str[Is])... } { }
decltype(auto) decrypt() {
for (size_t i = 0; i < N; ++i) {
_encrypted[i] = dec(_encrypted[i]);
}
_encrypted[N] = '\0';
return _encrypted.data();
}
};
static auto w_printf = [](const char* fmt, ...) {
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
};
static auto w_printf_s = [](const char* fmt, ...) {
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
};
static auto w_sprintf_s = [](char* buf, size_t buf_size, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
vsprintf(buf, fmt, args);
va_end(args);
};
static auto w_sprintf_s_ret = [](char* buf, size_t buf_size, const char* fmt, ...) {
int ret;
va_list args;
va_start(args, fmt);
ret = vsprintf(buf, fmt, args);
va_end(args);
return ret;
};
#define XOR_C(s) [] { constexpr Crypto::MangleString< sizeof(s)/sizeof(char) - 1, __COUNTER__, char > expr( s, std::make_index_sequence< sizeof(s)/sizeof(char) - 1>() ); return expr; }().decrypt()
#define XOR_W(s) [] { constexpr Crypto::MangleString< sizeof(s)/sizeof(wchar_t) - 1, __COUNTER__, wchar_t > expr( s, std::make_index_sequence< sizeof(s)/sizeof(wchar_t) - 1>() ); return expr; }().decrypt()
#define RAWIFY(s) XOR_C(s)
}

3
include/SignalHandling.h Normal file
View File

@ -0,0 +1,3 @@
#pragma once
void SetupSignalHandlers();

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "commandline.h" #include "commandline.h"
#include "Cryptography.h"
#include <atomic> #include <atomic>
#include <fstream> #include <fstream>

1
include/commandline Submodule

@ -0,0 +1 @@
Subproject commit 4931aa89c1b400732394d8b8523974ed09f427dc

View File

@ -23,10 +23,26 @@ void Application::RegisterShutdownHandler(const TShutdownHandler& Handler) {
} }
void Application::GracefullyShutdown() { void Application::GracefullyShutdown() {
info("please wait while all subsystems are shutting down..."); static bool AlreadyShuttingDown = false;
static uint8_t ShutdownAttempts = 0;
if (AlreadyShuttingDown) {
++ShutdownAttempts;
// hard shutdown at 2 additional tries
if (ShutdownAttempts == 2) {
info("hard shutdown forced by multiple shutdown requests");
std::exit(0);
}
info("already shutting down!");
return;
} else {
AlreadyShuttingDown = true;
}
trace("waiting for lock release");
std::unique_lock Lock(mShutdownHandlersMutex); std::unique_lock Lock(mShutdownHandlersMutex);
for (auto& Handler : mShutdownHandlers) { info("please wait while all subsystems are shutting down...");
Handler(); for (size_t i = 0; i < mShutdownHandlers.size(); ++i) {
info("Subsystem " + std::to_string(i + 1) + "/" + std::to_string(mShutdownHandlers.size()) + " shutting down");
mShutdownHandlers[i]();
} }
} }
@ -68,7 +84,7 @@ void Application::CheckForUpdates() {
auto RemoteVersion = Version(VersionStrToInts(Response)); auto RemoteVersion = Version(VersionStrToInts(Response));
if (IsOutdated(MyVersion, RemoteVersion)) { if (IsOutdated(MyVersion, RemoteVersion)) {
std::string RealVersionString = RemoteVersion.AsString(); std::string RealVersionString = RemoteVersion.AsString();
warn(std::string(ANSI_YELLOW_BOLD) + "NEW VERSION OUT! There's a new version (v" + RealVersionString + ") of the BeamMP-Server available! For info on how to update your server, visit https://wiki.beammp.com/en/home/server-maintenance#updating-the-server." + std::string(ANSI_RESET)); warn(std::string(ANSI_YELLOW_BOLD) + "NEW VERSION OUT! There's a new version (v" + RealVersionString + ") of the BeamMP-Server available! For more info visit https://wiki.beammp.com/en/home/server-maintenance#updating-the-server." + std::string(ANSI_RESET));
} else { } else {
info("Server up-to-date!"); info("Server up-to-date!");
} }

View File

@ -49,6 +49,7 @@ std::string GenericRequest(http::verb verb, const std::string& host, int port, c
bool ok = try_connect_with_protocol(tcp::v4()); bool ok = try_connect_with_protocol(tcp::v4());
if (!ok) { if (!ok) {
Application::Console().Write("[ERROR] failed to resolve or connect in POST " + host + target); Application::Console().Write("[ERROR] failed to resolve or connect in POST " + host + target);
Sentry.AddErrorBreadcrumb("failed to resolve or connect to " + host + target, __FILE__, std::to_string(__LINE__)); // FIXME: this is ugly.
return "-1"; return "-1";
} }
//} //}
@ -127,6 +128,7 @@ std::string GenericRequest(http::verb verb, const std::string& host, int port, c
} catch (const std::exception& e) { } catch (const std::exception& e) {
Application::Console().Write(__func__ + std::string(": ") + e.what()); Application::Console().Write(__func__ + std::string(": ") + e.what());
Sentry.AddErrorBreadcrumb(e.what(), __FILE__, std::to_string(__LINE__)); // FIXME: this is ugly.
return Http::ErrorString; return Http::ErrorString;
} }
} }
@ -141,6 +143,7 @@ std::string Http::POST(const std::string& host, int port, const std::string& tar
// RFC 2616, RFC 7231 // RFC 2616, RFC 7231
static std::map<size_t, const char*> Map = { static std::map<size_t, const char*> Map = {
{ -1, "Invalid Response Code"},
{ 100, "Continue" }, { 100, "Continue" },
{ 101, "Switching Protocols" }, { 101, "Switching Protocols" },
{ 102, "Processing" }, { 102, "Processing" },
@ -203,12 +206,22 @@ static std::map<size_t, const char*> Map = {
{ 508, "Loop Detected" }, { 508, "Loop Detected" },
{ 510, "Not Extended" }, { 510, "Not Extended" },
{ 511, "Network Authentication Required" }, { 511, "Network Authentication Required" },
// cloudflare status codes
{ 520, "(CDN) Web Server Returns An Unknown Error" },
{ 521, "(CDN) Web Server Is Down" },
{ 522, "(CDN) Connection Timed Out" },
{ 523, "(CDN) Origin Is Unreachable" },
{ 524, "(CDN) A Timeout Occurred" },
{ 525, "(CDN) SSL Handshake Failed" },
{ 526, "(CDN) Invalid SSL Certificate" },
{ 527, "(CDN) Railgun Listener To Origin Error" },
{ 530, "(CDN) 1XXX Internal Error" },
}; };
std::string Http::Status::ToString(int code) { std::string Http::Status::ToString(int code) {
if (Map.find(code) != Map.end()) { if (Map.find(code) != Map.end()) {
return Map.at(code); return Map.at(code);
} else { } else {
return "Unassigned"; return std::to_string(code);
} }
} }

65
src/SignalHandling.cpp Normal file
View File

@ -0,0 +1,65 @@
#include "SignalHandling.h"
#include "Common.h"
#ifdef __unix
#include <csignal>
static void UnixSignalHandler(int sig) {
switch (sig) {
case SIGPIPE:
warn("ignoring SIGPIPE");
break;
case SIGTERM:
info("gracefully shutting down via SIGTERM");
Application::GracefullyShutdown();
break;
case SIGINT:
info("gracefully shutting down via SIGINT");
Application::GracefullyShutdown();
break;
default:
debug("unhandled signal: " + std::to_string(sig));
break;
}
}
#endif // __unix
#ifdef WIN32
#include <windows.h>
// return TRUE if handled, FALSE if not
BOOL WINAPI Win32CtrlC_Handler(DWORD CtrlType) {
switch (CtrlType) {
case CTRL_C_EVENT:
info("gracefully shutting down via CTRL+C");
Application::GracefullyShutdown();
return TRUE;
case CTRL_BREAK_EVENT:
info("gracefully shutting down via CTRL+BREAK");
Application::GracefullyShutdown();
return TRUE;
case CTRL_CLOSE_EVENT:
info("gracefully shutting down via close");
Application::GracefullyShutdown();
return TRUE;
}
// we dont care for any others like CTRL_LOGOFF_EVENT and CTRL_SHUTDOWN_EVENT
return FALSE;
}
#endif // WIN32
void SetupSignalHandlers() {
// signal handlers for unix#include <windows.h>
#ifdef __unix
trace("registering handlers for SIGINT, SIGTERM, SIGPIPE");
signal(SIGPIPE, UnixSignalHandler);
signal(SIGTERM, UnixSignalHandler);
#ifndef DEBUG
signal(SIGINT, UnixSignalHandler);
#endif // DEBUG
#endif // __unix
// signal handlers for win32
#ifdef WIN32
trace("registering handlers for CTRL_*_EVENTs");
SetConsoleCtrlHandler(Win32CtrlC_Handler, TRUE);
#endif // WIN32
}

View File

@ -20,6 +20,8 @@ static constexpr std::string_view StrName = "Name";
static constexpr std::string_view StrDescription = "Description"; static constexpr std::string_view StrDescription = "Description";
static constexpr std::string_view StrResourceFolder = "ResourceFolder"; static constexpr std::string_view StrResourceFolder = "ResourceFolder";
static constexpr std::string_view StrAuthKey = "AuthKey"; static constexpr std::string_view StrAuthKey = "AuthKey";
static constexpr std::string_view StrSendErrors = "SendErrors";
static constexpr std::string_view StrSendErrorsMessageEnabled = "SendErrorsShowMessage";
TConfig::TConfig() { TConfig::TConfig() {
if (!fs::exists(ConfigFileName) || !fs::is_regular_file(ConfigFileName)) { if (!fs::exists(ConfigFileName) || !fs::is_regular_file(ConfigFileName)) {
@ -34,6 +36,15 @@ TConfig::TConfig() {
} }
} }
void WriteSendErrors(const std::string& name) {
std::ofstream CfgFile { name, std::ios::out | std::ios::app };
CfgFile << "# You can turn on/off the SendErrors message you get on startup here" << std::endl
<< StrSendErrorsMessageEnabled << " = true" << std::endl
<< "# 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`."
<< std::endl
<< StrSendErrors << " = true" << std::endl;
}
void TConfig::FlushToFile() { void TConfig::FlushToFile() {
auto data = toml::parse(ConfigFileName); auto data = toml::parse(ConfigFileName);
data["General"] = toml::table(); data["General"] = toml::table();
@ -68,6 +79,7 @@ void TConfig::CreateConfigFile(std::string_view name) {
} }
auto data = toml::parse<toml::preserve_comments>(name.data()); auto data = toml::parse<toml::preserve_comments>(name.data());
//{ StrSendErrors, Application::Settings.SendErrors },
data["General"] = toml::table(); data["General"] = toml::table();
data["General"][StrAuthKey.data()] = Application::Settings.Key; data["General"][StrAuthKey.data()] = Application::Settings.Key;
@ -90,6 +102,8 @@ void TConfig::CreateConfigFile(std::string_view name) {
ofs << data << '\n'; ofs << data << '\n';
beammp_error("There was no \"" + std::string(ConfigFileName) + "\" file (this is normal for the first time running the server), so one was generated for you. It was automatically filled with the settings from your Server.cfg, if you have one. Please open ServerConfig.toml and ensure your AuthKey and other settings are filled in and correct, then restart the server. The old Server.cfg file will no longer be used and causes a warning if it exists from now on."); beammp_error("There was no \"" + std::string(ConfigFileName) + "\" file (this is normal for the first time running the server), so one was generated for you. It was automatically filled with the settings from your Server.cfg, if you have one. Please open ServerConfig.toml and ensure your AuthKey and other settings are filled in and correct, then restart the server. The old Server.cfg file will no longer be used and causes a warning if it exists from now on.");
mFailed = true; mFailed = true;
ofs.close();
WriteSendErrors(std::string(name));
} else { } else {
beammp_error("Couldn't create " + std::string(name) + ". Check permissions, try again, and contact support if it continues not to work."); beammp_error("Couldn't create " + std::string(name) + ". Check permissions, try again, and contact support if it continues not to work.");
mFailed = true; mFailed = true;
@ -109,6 +123,19 @@ void TConfig::ParseFromFile(std::string_view name) {
Application::Settings.ServerDesc = data["General"][StrDescription.data()].as_string(); Application::Settings.ServerDesc = data["General"][StrDescription.data()].as_string();
Application::Settings.Resource = data["General"][StrResourceFolder.data()].as_string(); Application::Settings.Resource = data["General"][StrResourceFolder.data()].as_string();
Application::Settings.Key = data["General"][StrAuthKey.data()].as_string(); Application::Settings.Key = data["General"][StrAuthKey.data()].as_string();
}
// added later, so behaves differently
if (auto val = GeneralTable[StrSendErrors].value<bool>(); val.has_value()) {
Application::Settings.SendErrors = val.value();
} else {
// dont throw, instead write it into the file and use default
WriteSendErrors(std::string(name));
}
if (auto val = GeneralTable[StrSendErrorsMessageEnabled].value<bool>(); val.has_value()) {
Application::Settings.SendErrorsMessageEnabled = val.value();
} else {
// no idea what to do here, ignore...?
// this entire toml parser sucks and is replaced in the upcoming lua.
} 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()));
mFailed = true; mFailed = true;

View File

@ -36,9 +36,6 @@ void THeartbeatThread::operator()() {
Body += "&pps=" + Application::PPS(); Body += "&pps=" + Application::PPS();
auto SentryReportError = [&](const std::string& transaction, int status) { auto SentryReportError = [&](const std::string& transaction, int status) {
if (status < 0) {
status = 0;
}
auto Lock = Sentry.CreateExclusiveContext(); auto Lock = Sentry.CreateExclusiveContext();
Sentry.SetContext("heartbeat", Sentry.SetContext("heartbeat",
@ -53,16 +50,16 @@ void THeartbeatThread::operator()() {
unsigned int ResponseCode = 0; unsigned int ResponseCode = 0;
T = Http::POST(Application::GetBackendHostname(), 443, Target, {}, Body, "application/x-www-form-urlencoded", &ResponseCode); T = Http::POST(Application::GetBackendHostname(), 443, Target, {}, Body, "application/x-www-form-urlencoded", &ResponseCode);
if (T.substr(0, 2) != "20" || ResponseCode != 200) { if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) {
trace("got " + T + " from backend"); trace("got " + T + " from backend");
SentryReportError(Application::GetBackendHostname() + Target, ResponseCode); SentryReportError(Application::GetBackendHostname() + Target, ResponseCode);
std::this_thread::sleep_for(std::chrono::milliseconds(500)); std::this_thread::sleep_for(std::chrono::milliseconds(500));
T = Http::POST(Application::GetBackup1Hostname(), 443, Target, {}, Body, "application/x-www-form-urlencoded", &ResponseCode); T = Http::POST(Application::GetBackup1Hostname(), 443, Target, {}, Body, "application/x-www-form-urlencoded", &ResponseCode);
if (T.substr(0, 2) != "20" || ResponseCode != 200) { if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) {
SentryReportError(Application::GetBackup1Hostname() + Target, ResponseCode); SentryReportError(Application::GetBackup1Hostname() + Target, ResponseCode);
std::this_thread::sleep_for(std::chrono::milliseconds(500)); std::this_thread::sleep_for(std::chrono::milliseconds(500));
T = Http::POST(Application::GetBackup2Hostname(), 443, Target, {}, Body, "application/x-www-form-urlencoded", &ResponseCode); T = Http::POST(Application::GetBackup2Hostname(), 443, Target, {}, Body, "application/x-www-form-urlencoded", &ResponseCode);
if (T.substr(0, 2) != "20" || ResponseCode != 200) { if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) {
warn("Backend system refused server! Server will not show in the public server list."); warn("Backend system refused server! Server will not show in the public server list.");
isAuth = false; isAuth = false;
@ -109,10 +106,8 @@ THeartbeatThread::THeartbeatThread(TResourceManager& ResourceManager, TServer& S
, mServer(Server) { , mServer(Server) {
Application::RegisterShutdownHandler([&] { Application::RegisterShutdownHandler([&] {
if (mThread.joinable()) { if (mThread.joinable()) {
debug("shutting down Heartbeat");
mShutdown = true; mShutdown = true;
mThread.join(); mThread.join();
debug("shut down Heartbeat");
} }
}); });
Start(); Start();

View File

@ -20,18 +20,14 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R
}); });
Application::RegisterShutdownHandler([&] { Application::RegisterShutdownHandler([&] {
if (mUDPThread.joinable()) { if (mUDPThread.joinable()) {
debug("shutting down TCPServer");
mShutdown = true; mShutdown = true;
mUDPThread.detach(); mUDPThread.detach();
debug("shut down TCPServer");
} }
}); });
Application::RegisterShutdownHandler([&] { Application::RegisterShutdownHandler([&] {
if (mTCPThread.joinable()) { if (mTCPThread.joinable()) {
debug("shutting down TCPServer");
mShutdown = true; mShutdown = true;
mTCPThread.detach(); mTCPThread.detach();
debug("shut down TCPServer");
} }
}); });
mTCPThread = std::thread(&TNetwork::TCPServerMain, this); mTCPThread = std::thread(&TNetwork::TCPServerMain, this);

View File

@ -1,22 +1,17 @@
#include "TSentry.h" #include "TSentry.h"
#include "Common.h" #include "Common.h"
#include <cstring>
#include <sentry.h> #include <sentry.h>
#include <sstream> #include <sstream>
// compile-time length of a string/array
template <size_t N>
constexpr size_t ConstexprLength(char const (&)[N]) {
return N - 1;
}
TSentry::TSentry() { TSentry::TSentry() {
if constexpr (ConstexprLength(SECRET_SENTRY_URL) == 0) { if (std::strlen(S_DSN) == 0) {
mValid = false; mValid = false;
} else { } else {
mValid = true; mValid = true;
sentry_options_t* options = sentry_options_new(); sentry_options_t* options = sentry_options_new();
sentry_options_set_dsn(options, SECRET_SENTRY_URL); sentry_options_set_dsn(options, S_DSN);
auto ReleaseString = "BeamMP-Server@" + Application::ServerVersionString(); auto ReleaseString = "BeamMP-Server@" + Application::ServerVersionString();
sentry_options_set_symbolize_stacktraces(options, true); sentry_options_set_symbolize_stacktraces(options, true);
sentry_options_set_release(options, ReleaseString.c_str()); sentry_options_set_release(options, ReleaseString.c_str());
@ -33,9 +28,26 @@ TSentry::~TSentry() {
void TSentry::PrintWelcome() { void TSentry::PrintWelcome() {
if (mValid) { if (mValid) {
info("Sentry started"); if (!Application::Settings.SendErrors) {
mValid = false;
if (Application::Settings.SendErrorsMessageEnabled) {
info("Opted out of error reporting (SendErrors), Sentry disabled.");
} else {
info("Sentry disabled");
}
} else {
if (Application::Settings.SendErrorsMessageEnabled) {
info("Sentry started! Reporting errors automatically. This sends data to the developers in case of errors and crashes. You can learn more, turn this message off or opt-out of this in the ServerConfig.toml.");
} else {
info("Sentry started");
}
}
} else { } else {
info("Sentry disabled in unofficial build"); if (Application::Settings.SendErrorsMessageEnabled) {
info("Sentry disabled in unofficial build. Automatic error reporting disabled.");
} else {
info("Sentry disabled in unofficial build");
}
} }
} }

View File

@ -4,6 +4,7 @@
#include "CustomAssert.h" #include "CustomAssert.h"
#include "Http.h" #include "Http.h"
#include "LuaAPI.h" #include "LuaAPI.h"
#include "SignalHandling.h"
#include "TConfig.h" #include "TConfig.h"
#include "THeartbeatThread.h" #include "THeartbeatThread.h"
#include "TLuaEngine.h" #include "TLuaEngine.h"
@ -12,48 +13,18 @@
#include "TResourceManager.h" #include "TResourceManager.h"
#include "TServer.h" #include "TServer.h"
#include <iostream>
#include <thread> #include <thread>
#ifdef __unix
#include <csignal>
void UnixSignalHandler(int sig) {
switch (sig) {
case SIGPIPE:
beammp_warn("ignoring SIGPIPE");
break;
case SIGTERM:
beammp_info("gracefully shutting down via SIGTERM");
Application::GracefullyShutdown();
break;
case SIGINT:
beammp_info("gracefully shutting down via SIGINT");
Application::GracefullyShutdown();
break;
default:
beammp_debug("unhandled signal: " + std::to_string(sig));
break;
}
}
#endif // __unix
// this is provided by the build system, leave empty for source builds // this is provided by the build system, leave empty for source builds
// global, yes, this is ugly, no, it cant be done another way // global, yes, this is ugly, no, it cant be done another way
TSentry Sentry {}; TSentry Sentry {};
#include <iostream>
int main(int argc, char** argv) try { int main(int argc, char** argv) try {
#ifdef __unix
trace("registering handlers for SIGINT, SIGTERM, SIGPIPE");
signal(SIGPIPE, UnixSignalHandler);
signal(SIGTERM, UnixSignalHandler);
#ifndef DEBUG
signal(SIGINT, UnixSignalHandler);
#endif // DEBUG
#endif // __unix
setlocale(LC_ALL, "C"); setlocale(LC_ALL, "C");
SetupSignalHandlers();
bool Shutdown = false; bool Shutdown = false;
Application::RegisterShutdownHandler([&Shutdown] { Shutdown = true; }); Application::RegisterShutdownHandler([&Shutdown] { Shutdown = true; });
Application::RegisterShutdownHandler([] { Application::RegisterShutdownHandler([] {
@ -68,7 +39,10 @@ int main(int argc, char** argv) try {
if (Config.Failed()) { if (Config.Failed()) {
beammp_info("Closing in 10 seconds"); beammp_info("Closing in 10 seconds");
std::this_thread::sleep_for(std::chrono::seconds(10)); // loop to make it possible to ctrl+c instead
for (size_t i = 0; i < 20; ++i) {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
return 1; return 1;
} }
@ -78,7 +52,6 @@ int main(int argc, char** argv) try {
Sentry.SetupUser(); Sentry.SetupUser();
Sentry.PrintWelcome(); Sentry.PrintWelcome();
Application::CheckForUpdates();
TResourceManager ResourceManager; TResourceManager ResourceManager;
TPPSMonitor PPSMonitor(Server); TPPSMonitor PPSMonitor(Server);
THeartbeatThread Heartbeat(ResourceManager, Server); THeartbeatThread Heartbeat(ResourceManager, Server);
@ -86,11 +59,13 @@ int main(int argc, char** argv) try {
LuaEngine.SetNetwork(&Network); LuaEngine.SetNetwork(&Network);
PPSMonitor.SetNetwork(Network); PPSMonitor.SetNetwork(Network);
Application::Console().InitializeLuaConsole(LuaEngine); Application::Console().InitializeLuaConsole(LuaEngine);
Application::CheckForUpdates();
// TODO: replace // TODO: replace
while (!Shutdown) { while (!Shutdown) {
std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::this_thread::sleep_for(std::chrono::milliseconds(50));
} }
info("Shutdown.");
} catch (const std::exception& e) { } catch (const std::exception& e) {
error(e.what()); error(e.what());
Sentry.LogException(e, _file_basename, _line); Sentry.LogException(e, _file_basename, _line);