mirror of
https://github.com/SantaSpeen/BeamMP-Server.git
synced 2026-04-24 12:16:36 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3fc397814c | ||
|
|
6542be09ee | ||
|
|
38b934bc0f | ||
|
|
a2f92b5791 | ||
|
|
30624c77a2 | ||
|
|
b1664bb184 | ||
|
|
ffac000cd2 | ||
|
|
3cd94380e2 | ||
|
|
b055fd8bda | ||
|
|
d43ee4b7b6 | ||
|
|
a514591650 | ||
|
|
0f9f81e9fa | ||
|
|
11e94e91a7 |
@@ -81,7 +81,8 @@ add_executable(BeamMP-Server
|
||||
include/Http.h src/Http.cpp
|
||||
include/TSentry.h src/TSentry.cpp
|
||||
include/TPPSMonitor.h src/TPPSMonitor.cpp
|
||||
include/TNetwork.h src/TNetwork.cpp)
|
||||
include/TNetwork.h src/TNetwork.cpp
|
||||
include/SignalHandling.h src/SignalHandling.cpp)
|
||||
|
||||
target_compile_definitions(BeamMP-Server PRIVATE SECRET_SENTRY_URL="${BEAMMP_SECRET_SENTRY_URL}")
|
||||
|
||||
|
||||
13
Changelog.md
13
Changelog.md
@@ -1,3 +1,16 @@
|
||||
# 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
|
||||
|
||||
- ADDED version check - the server will now let you know when a new release is out
|
||||
|
||||
@@ -30,7 +30,9 @@ public:
|
||||
, Private(false)
|
||||
, MaxCars(1)
|
||||
, DebugModeEnabled(false)
|
||||
, Port(30814) { }
|
||||
, Port(30814)
|
||||
, SendErrors(true)
|
||||
, SendErrorsMessageEnabled(true) { }
|
||||
std::string ServerName;
|
||||
std::string ServerDesc;
|
||||
std::string Resource;
|
||||
@@ -42,6 +44,8 @@ public:
|
||||
bool DebugModeEnabled;
|
||||
int Port;
|
||||
std::string CustomIP;
|
||||
bool SendErrors;
|
||||
bool SendErrorsMessageEnabled;
|
||||
[[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); }
|
||||
};
|
||||
using TShutdownHandler = std::function<void()>;
|
||||
@@ -54,7 +58,7 @@ public:
|
||||
// Causes all threads to finish up and exit gracefull gracefully
|
||||
static void GracefullyShutdown();
|
||||
static TConsole& Console() { return *mConsole; }
|
||||
static std::string ServerVersion() { return "2.3.1"; }
|
||||
static std::string ServerVersion() { return "2.3.2"; }
|
||||
static std::string ClientVersion() { return "2.0"; }
|
||||
static std::string PPS() { return mPPS; }
|
||||
static void SetPPS(const std::string& NewPPS) { mPPS = NewPPS; }
|
||||
|
||||
3
include/SignalHandling.h
Normal file
3
include/SignalHandling.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void SetupSignalHandlers();
|
||||
Submodule include/commandline updated: c34703df11...4931aa89c1
@@ -23,10 +23,26 @@ void Application::RegisterShutdownHandler(const TShutdownHandler& Handler) {
|
||||
}
|
||||
|
||||
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);
|
||||
for (auto& Handler : mShutdownHandlers) {
|
||||
Handler();
|
||||
info("please wait while all subsystems are shutting down...");
|
||||
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]();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +81,7 @@ void Application::CheckForUpdates() {
|
||||
std::string RealVersionString = std::to_string(RemoteVersion[0]) + ".";
|
||||
RealVersionString += std::to_string(RemoteVersion[1]) + ".";
|
||||
RealVersionString += std::to_string(RemoteVersion[2]);
|
||||
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 {
|
||||
info("Server up-to-date!");
|
||||
}
|
||||
|
||||
14
src/Http.cpp
14
src/Http.cpp
@@ -82,7 +82,7 @@ std::string Http::GET(const std::string& host, int port, const std::string& targ
|
||||
if (status) {
|
||||
*status = res.base().result_int();
|
||||
}
|
||||
|
||||
|
||||
// ignore ec
|
||||
|
||||
// If we get here then the connection is closed gracefully
|
||||
@@ -270,12 +270,22 @@ static std::map<size_t, const char*> Map = {
|
||||
{ 508, "Loop Detected" },
|
||||
{ 510, "Not Extended" },
|
||||
{ 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) {
|
||||
if (Map.find(code) != Map.end()) {
|
||||
return Map.at(code);
|
||||
} else {
|
||||
return "Unassigned";
|
||||
return std::to_string(code);
|
||||
}
|
||||
}
|
||||
|
||||
65
src/SignalHandling.cpp
Normal file
65
src/SignalHandling.cpp
Normal 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
|
||||
}
|
||||
@@ -18,6 +18,8 @@ static constexpr std::string_view StrName = "Name";
|
||||
static constexpr std::string_view StrDescription = "Description";
|
||||
static constexpr std::string_view StrResourceFolder = "ResourceFolder";
|
||||
static constexpr std::string_view StrAuthKey = "AuthKey";
|
||||
static constexpr std::string_view StrSendErrors = "SendErrors";
|
||||
static constexpr std::string_view StrSendErrorsMessageEnabled = "SendErrorsShowMessage";
|
||||
|
||||
TConfig::TConfig() {
|
||||
if (!fs::exists(ConfigFileName) || !fs::is_regular_file(ConfigFileName)) {
|
||||
@@ -32,6 +34,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::CreateConfigFile(std::string_view name) {
|
||||
// build from old config Server.cfg
|
||||
|
||||
@@ -59,6 +70,7 @@ void TConfig::CreateConfigFile(std::string_view name) {
|
||||
{ StrDescription, Application::Settings.ServerDesc },
|
||||
{ StrResourceFolder, Application::Settings.Resource },
|
||||
{ StrAuthKey, Application::Settings.Key },
|
||||
//{ StrSendErrors, Application::Settings.SendErrors },
|
||||
|
||||
} } },
|
||||
|
||||
@@ -72,6 +84,8 @@ void TConfig::CreateConfigFile(std::string_view name) {
|
||||
ofs << tbl << '\n';
|
||||
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;
|
||||
ofs.close();
|
||||
WriteSendErrors(std::string(name));
|
||||
} else {
|
||||
error("Couldn't create " + std::string(name) + ". Check permissions, try again, and contact support if it continues not to work.");
|
||||
mFailed = true;
|
||||
@@ -132,6 +146,19 @@ void TConfig::ParseFromFile(std::string_view name) {
|
||||
} else {
|
||||
throw std::runtime_error(std::string(StrAuthKey));
|
||||
}
|
||||
// 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) {
|
||||
error("Error parsing config file value: " + std::string(err.what()));
|
||||
mFailed = true;
|
||||
|
||||
@@ -107,10 +107,8 @@ THeartbeatThread::THeartbeatThread(TResourceManager& ResourceManager, TServer& S
|
||||
, mServer(Server) {
|
||||
Application::RegisterShutdownHandler([&] {
|
||||
if (mThread.joinable()) {
|
||||
debug("shutting down Heartbeat");
|
||||
mShutdown = true;
|
||||
mThread.join();
|
||||
debug("shut down Heartbeat");
|
||||
}
|
||||
});
|
||||
Start();
|
||||
|
||||
@@ -23,10 +23,8 @@ TLuaEngine::TLuaEngine(TServer& Server, TNetwork& Network)
|
||||
FolderList(Path, false);
|
||||
mPath = Path;
|
||||
Application::RegisterShutdownHandler([&] {if (mThread.joinable()) {
|
||||
debug("shutting down LuaEngine");
|
||||
mShutdown = true;
|
||||
mThread.join();
|
||||
debug("shut down LuaEngine");
|
||||
} });
|
||||
Start();
|
||||
}
|
||||
|
||||
@@ -20,18 +20,14 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R
|
||||
});
|
||||
Application::RegisterShutdownHandler([&] {
|
||||
if (mUDPThread.joinable()) {
|
||||
debug("shutting down TCPServer");
|
||||
mShutdown = true;
|
||||
mUDPThread.detach();
|
||||
debug("shut down TCPServer");
|
||||
}
|
||||
});
|
||||
Application::RegisterShutdownHandler([&] {
|
||||
if (mTCPThread.joinable()) {
|
||||
debug("shutting down TCPServer");
|
||||
mShutdown = true;
|
||||
mTCPThread.detach();
|
||||
debug("shut down TCPServer");
|
||||
}
|
||||
});
|
||||
mTCPThread = std::thread(&TNetwork::TCPServerMain, this);
|
||||
|
||||
@@ -7,10 +7,8 @@ TPPSMonitor::TPPSMonitor(TServer& Server)
|
||||
Application::SetPPS("-");
|
||||
Application::RegisterShutdownHandler([&] {
|
||||
if (mThread.joinable()) {
|
||||
debug("shutting down PPSMonitor");
|
||||
mShutdown = true;
|
||||
mThread.join();
|
||||
debug("shut down PPSMonitor");
|
||||
}
|
||||
});
|
||||
Start();
|
||||
|
||||
@@ -34,9 +34,26 @@ TSentry::~TSentry() {
|
||||
|
||||
void TSentry::PrintWelcome() {
|
||||
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 {
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
47
src/main.cpp
47
src/main.cpp
@@ -3,6 +3,7 @@
|
||||
#include "Common.h"
|
||||
#include "CustomAssert.h"
|
||||
#include "Http.h"
|
||||
#include "SignalHandling.h"
|
||||
#include "TConfig.h"
|
||||
#include "THeartbeatThread.h"
|
||||
#include "TLuaEngine.h"
|
||||
@@ -11,48 +12,18 @@
|
||||
#include "TResourceManager.h"
|
||||
#include "TServer.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#ifdef __unix
|
||||
#include <csignal>
|
||||
|
||||
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
|
||||
|
||||
// this is provided by the build system, leave empty for source builds
|
||||
// global, yes, this is ugly, no, it cant be done another way
|
||||
TSentry Sentry {};
|
||||
|
||||
#include <iostream>
|
||||
|
||||
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");
|
||||
|
||||
SetupSignalHandlers();
|
||||
|
||||
bool Shutdown = false;
|
||||
Application::RegisterShutdownHandler([&Shutdown] { Shutdown = true; });
|
||||
|
||||
@@ -61,7 +32,10 @@ int main(int argc, char** argv) try {
|
||||
|
||||
if (Config.Failed()) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -71,7 +45,6 @@ int main(int argc, char** argv) try {
|
||||
|
||||
Sentry.SetupUser();
|
||||
Sentry.PrintWelcome();
|
||||
Application::CheckForUpdates();
|
||||
TResourceManager ResourceManager;
|
||||
TPPSMonitor PPSMonitor(Server);
|
||||
THeartbeatThread Heartbeat(ResourceManager, Server);
|
||||
@@ -79,11 +52,13 @@ int main(int argc, char** argv) try {
|
||||
TLuaEngine LuaEngine(Server, Network);
|
||||
PPSMonitor.SetNetwork(Network);
|
||||
Application::Console().InitializeLuaConsole(LuaEngine);
|
||||
Application::CheckForUpdates();
|
||||
|
||||
// TODO: replace
|
||||
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) {
|
||||
error(e.what());
|
||||
Sentry.LogException(e, _file_basename, _line);
|
||||
|
||||
Reference in New Issue
Block a user