mirror of
https://github.com/SantaSpeen/BeamMP-Server.git
synced 2026-04-24 11:16:37 +00:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f757ba3b49 | ||
|
|
fea331bfac | ||
|
|
5ee380ac8c | ||
|
|
2190593c78 | ||
|
|
0dc1f6a846 | ||
|
|
1820ed8671 | ||
|
|
8b79a10df9 | ||
|
|
36699676b5 | ||
|
|
2355327c21 | ||
|
|
3837e101e2 | ||
|
|
fa19ba08e3 | ||
|
|
57d0eb735e | ||
|
|
15704abf6c | ||
|
|
6883c96d33 | ||
|
|
f632606d76 | ||
|
|
c70ada2926 | ||
|
|
80aebcb9a7 | ||
|
|
3fc397814c | ||
|
|
6542be09ee | ||
|
|
38b934bc0f | ||
|
|
a2f92b5791 | ||
|
|
30624c77a2 | ||
|
|
b1664bb184 | ||
|
|
ffac000cd2 | ||
|
|
3cd94380e2 | ||
|
|
b055fd8bda | ||
|
|
d43ee4b7b6 | ||
|
|
a514591650 | ||
|
|
0f9f81e9fa | ||
|
|
11e94e91a7 |
12
.github/workflows/cmake-windows.yml
vendored
12
.github/workflows/cmake-windows.yml
vendored
@@ -3,7 +3,7 @@ name: CMake Windows Build
|
||||
on: [push]
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
BUILD_TYPE: RelWithDebInfo
|
||||
|
||||
jobs:
|
||||
windows-build:
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
working-directory: ${{github.workspace}}/build-windows
|
||||
env:
|
||||
beammp_sentry_url: ${{ secrets.BEAMMP_SECRET_SENTRY_URL }}
|
||||
run: cmake $GITHUB_WORKSPACE -DSENTRY_BACKEND=breakpad -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE='${{ runner.workspace }}/b/vcpkg/scripts/buildsystems/vcpkg.cmake' -DVCPKG_TARGET_TRIPLET=x64-windows-static -DBEAMMP_SECRET_SENTRY_URL="$beammp_sentry_url"
|
||||
run: cmake $GITHUB_WORKSPACE -DSENTRY_BACKEND=breakpad -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE='${{ runner.workspace }}/b/vcpkg/scripts/buildsystems/vcpkg.cmake' -DVCPKG_TARGET_TRIPLET=x64-windows-static -DBEAMMP_SECRET_SENTRY_URL="$beammp_sentry_url"
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{github.workspace}}/build-windows
|
||||
@@ -42,6 +42,12 @@ jobs:
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: BeamMP-Server.exe
|
||||
path: ${{github.workspace}}/build-windows/Release/BeamMP-Server.exe
|
||||
path: ${{github.workspace}}/build-windows/RelWithDebInfo/BeamMP-Server.exe
|
||||
|
||||
- name: Archive artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: BeamMP-Server.pdb
|
||||
path: ${{github.workspace}}/build-windows/RelWithDebInfo/BeamMP-Server.pdb
|
||||
|
||||
|
||||
|
||||
2
.github/workflows/release-build.yml
vendored
2
.github/workflows/release-build.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: ${{ github.ref }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
prerelease: true
|
||||
body: |
|
||||
Files included in this release:
|
||||
- `BeamMP-Server.exe` is the windows build
|
||||
|
||||
@@ -16,6 +16,7 @@ if (WIN32)
|
||||
endif()
|
||||
|
||||
include_directories("include/sentry-native/include")
|
||||
|
||||
set(SENTRY_BUILD_SHARED_LIBS OFF)
|
||||
if (MSVC)
|
||||
set(SENTRY_BUILD_RUNTIMESTATIC ON)
|
||||
@@ -25,6 +26,7 @@ add_subdirectory("include/sentry-native")
|
||||
|
||||
message(STATUS "Setting compiler flags")
|
||||
if (WIN32)
|
||||
add_subdirectory("include/watchdog")
|
||||
#-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static
|
||||
set(VcpkgRoot ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET})
|
||||
include_directories(${VcpkgRoot}/include)
|
||||
@@ -32,10 +34,10 @@ if (WIN32)
|
||||
elseif (UNIX)
|
||||
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_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -s -fno-builtin")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -gz -fno-builtin")
|
||||
if (SANITIZE)
|
||||
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 ()
|
||||
|
||||
@@ -65,8 +67,7 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
|
||||
message(STATUS "Looking for Boost")
|
||||
find_package(Boost REQUIRED COMPONENTS system thread)
|
||||
|
||||
add_executable(BeamMP-Server
|
||||
src/main.cpp
|
||||
file(GLOB source_files src/main.cpp
|
||||
include/TConsole.h src/TConsole.cpp
|
||||
include/TServer.h src/TServer.cpp
|
||||
include/Compat.h src/Compat.cpp
|
||||
@@ -81,7 +82,10 @@ 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)
|
||||
|
||||
add_executable(BeamMP-Server ${source_files})
|
||||
|
||||
target_compile_definitions(BeamMP-Server PRIVATE SECRET_SENTRY_URL="${BEAMMP_SECRET_SENTRY_URL}")
|
||||
|
||||
@@ -117,6 +121,11 @@ if (UNIX)
|
||||
sioclient_tls
|
||||
sentry)
|
||||
elseif (WIN32)
|
||||
set_source_files_properties(${source_files} PROPERTIES COMPILE_FLAGS "/Gh /GH")
|
||||
add_compile_options("$<$<NOT:$<CONFIG:Debug>>:/Zi>")
|
||||
add_link_options("$<$<NOT:$<CONFIG:Debug>>:/DEBUG>")
|
||||
add_link_options("$<$<NOT:$<CONFIG:Debug>>:/OPT:REF>")
|
||||
add_link_options("$<$<NOT:$<CONFIG:Debug>>:/OPT:ICF>")
|
||||
include(FindLua)
|
||||
message(STATUS "Looking for libz")
|
||||
find_package(ZLIB REQUIRED)
|
||||
@@ -130,5 +139,7 @@ elseif (WIN32)
|
||||
${OPENSSL_LIBRARIES}
|
||||
commandline
|
||||
sioclient_tls
|
||||
sentry)
|
||||
sentry
|
||||
watchdog
|
||||
Dbghelp)
|
||||
endif ()
|
||||
|
||||
17
Changelog.md
17
Changelog.md
@@ -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
|
||||
|
||||
- ADDED version check - the server will now let you know when a new release is out
|
||||
|
||||
@@ -27,10 +27,12 @@ public:
|
||||
, Resource("Resources")
|
||||
, MapName("/levels/gridmap_v2/info.json")
|
||||
, MaxPlayers(10)
|
||||
, Private(false)
|
||||
, Private(true)
|
||||
, 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; }
|
||||
@@ -83,6 +87,7 @@ void RegisterThread(const std::string& str);
|
||||
|
||||
#define KB 1024
|
||||
#define MB (KB * 1024)
|
||||
#define SSU_UNRAW SECRET_SENTRY_URL
|
||||
|
||||
#define _file_basename std::filesystem::path(__FILE__).filename().string()
|
||||
#define _line std::to_string(__LINE__)
|
||||
@@ -108,9 +113,11 @@ void RegisterThread(const std::string& str);
|
||||
#else
|
||||
#define _this_location (ThreadName() + _file_basename + ":" + _line + " ")
|
||||
#endif
|
||||
#define SU_RAW SSU_UNRAW
|
||||
|
||||
#else // !defined(DEBUG)
|
||||
|
||||
#define SU_RAW RAWIFY(SSU_UNRAW)
|
||||
#define _this_location (ThreadName())
|
||||
|
||||
#endif // defined(DEBUG)
|
||||
@@ -146,3 +153,5 @@ void LogChatMessage(const std::string& name, int id, const std::string& msg);
|
||||
#define Biggest 30000
|
||||
std::string Comp(std::string Data);
|
||||
std::string DeComp(std::string Compressed);
|
||||
|
||||
#define S_DSN SU_RAW
|
||||
|
||||
114
include/Cryptography.h
Normal file
114
include/Cryptography.h
Normal 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
3
include/SignalHandling.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void SetupSignalHandlers();
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "commandline/commandline.h"
|
||||
#include "TLuaFile.h"
|
||||
#include "Cryptography.h"
|
||||
#include <atomic>
|
||||
#include <fstream>
|
||||
|
||||
|
||||
Submodule include/commandline updated: c34703df11...4931aa89c1
8
include/watchdog/CMakeLists.txt
Normal file
8
include/watchdog/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(watchdog CXX ASM_MASM)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
add_library(watchdog STATIC watchdog.cpp watchdog.h x64Def.asm)
|
||||
set_source_files_properties(watchdog.cpp PROPERTIES COMPILE_FLAGS "/O2 /Ob2 /DNDEBUG")
|
||||
STRING(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
|
||||
target_link_libraries(watchdog Dbghelp)
|
||||
80
include/watchdog/stack_string.h
Normal file
80
include/watchdog/stack_string.h
Normal file
@@ -0,0 +1,80 @@
|
||||
//
|
||||
// Created by Anonymous275 on 9/5/2021.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
namespace fst {
|
||||
template<size_t Cap>
|
||||
class stack_string {
|
||||
public:
|
||||
stack_string() noexcept {
|
||||
memset(Data, 0, Cap);
|
||||
}
|
||||
explicit stack_string(const char* Ptr) {
|
||||
size_t len = strlen(Ptr);
|
||||
Copy(Ptr, len);
|
||||
memset(Data + len, 0, Cap - len);
|
||||
}
|
||||
stack_string(const char* Ptr, size_t PSize) {
|
||||
Copy(Ptr, PSize);
|
||||
memset(Data + PSize, 0, Cap - PSize);
|
||||
}
|
||||
inline size_t capacity() noexcept {
|
||||
return Cap;
|
||||
}
|
||||
inline size_t size() noexcept {
|
||||
return Size;
|
||||
}
|
||||
inline size_t length() noexcept {
|
||||
return Size;
|
||||
}
|
||||
inline char* get() {
|
||||
return Data;
|
||||
}
|
||||
[[nodiscard]] inline const char* c_str() const noexcept {
|
||||
return Data;
|
||||
}
|
||||
char& operator[](size_t idx) {
|
||||
if (idx >= Size) {
|
||||
throw std::exception("stack_string out of boundaries operator[]");
|
||||
}
|
||||
return Data[idx];
|
||||
}
|
||||
inline void resize(size_t newSize) noexcept {
|
||||
Size = newSize;
|
||||
}
|
||||
inline void push_back(const char* Ptr) {
|
||||
Copy(Ptr, strlen(Ptr));
|
||||
}
|
||||
inline void push_back(const char* Ptr, size_t Count) {
|
||||
Copy(Ptr, Count);
|
||||
}
|
||||
inline void push_back(char Ptr) {
|
||||
Copy(&Ptr, 1);
|
||||
}
|
||||
friend std::ostream& operator<<(std::ostream& os, const stack_string& obj) {
|
||||
os << obj.Data;
|
||||
return os;
|
||||
}
|
||||
inline stack_string& operator+=(const char* Ptr) {
|
||||
push_back(Ptr);
|
||||
return *this;
|
||||
}
|
||||
inline stack_string& operator+=(char Ptr) {
|
||||
push_back(Ptr);
|
||||
return *this;
|
||||
}
|
||||
private:
|
||||
inline void Copy(const char* Ptr, size_t PSize) {
|
||||
if((PSize + Size) <= Cap) {
|
||||
memcpy(&Data[Size], Ptr, PSize);
|
||||
Size += PSize;
|
||||
} else throw std::exception("stack_string out of boundaries copy");
|
||||
}
|
||||
char Data[Cap]{};
|
||||
size_t Size{0};
|
||||
};
|
||||
}
|
||||
281
include/watchdog/watchdog.cpp
Normal file
281
include/watchdog/watchdog.cpp
Normal file
@@ -0,0 +1,281 @@
|
||||
//
|
||||
// Created by Anonymous275 on 9/9/2021.
|
||||
//
|
||||
|
||||
#include <windows.h>
|
||||
#include <imagehlp.h>
|
||||
#include <strsafe.h>
|
||||
#include <cstdint>
|
||||
#include "stack_string.h"
|
||||
|
||||
struct function_info {
|
||||
void* func_address;
|
||||
uint32_t thread_id;
|
||||
bool enter;
|
||||
};
|
||||
|
||||
fst::stack_string<1024> crash_file;
|
||||
|
||||
template <typename I>
|
||||
fst::stack_string<(sizeof(I)<<1)+1> HexString(I w) {
|
||||
static const char* digits = "0123456789ABCDEF";
|
||||
const size_t hex_len = sizeof(I)<<1;
|
||||
fst::stack_string<hex_len+1> rc;
|
||||
rc.resize(hex_len+1);
|
||||
memset(rc.get(), '0', hex_len);
|
||||
memset(rc.get() + hex_len, 0, 1);
|
||||
for (size_t i=0, j=(hex_len-1)*4 ; i<hex_len; ++i,j-=4)
|
||||
rc[i] = digits[(w>>j) & 0x0f];
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<class T_>
|
||||
class heap_array {
|
||||
public:
|
||||
heap_array() noexcept {
|
||||
Data = (T_*)(GlobalAlloc(GPTR, Cap * sizeof(T_)));
|
||||
init = true;
|
||||
}
|
||||
explicit heap_array(size_t Cap_) noexcept {
|
||||
Cap = Cap_;
|
||||
Data = (T_*)(GlobalAlloc(GPTR, Cap * sizeof(T_)));
|
||||
init = true;
|
||||
}
|
||||
~heap_array() {
|
||||
free(Data);
|
||||
}
|
||||
inline T_* get() noexcept {
|
||||
return Data;
|
||||
}
|
||||
inline const T_* cget() noexcept {
|
||||
return Data;
|
||||
}
|
||||
inline void insert(const T_& T) {
|
||||
if(!init)return;
|
||||
if(Size >= Cap) {
|
||||
Grow();
|
||||
}
|
||||
Data[Size++] = T;
|
||||
}
|
||||
inline void string_insert(const T_* T, size_t len = 0) {
|
||||
if(len == 0)len = strlen(T);
|
||||
if(Size+len >= Cap) {
|
||||
Grow(len);
|
||||
}
|
||||
memcpy(&Data[Size], T, len);
|
||||
Size += len;
|
||||
}
|
||||
inline T_ at(size_t idx) {
|
||||
return Data[idx];
|
||||
}
|
||||
inline size_t size() const noexcept {
|
||||
return Size;
|
||||
}
|
||||
const T_& operator[](size_t idx) {
|
||||
if (idx >= Size) {
|
||||
throw std::exception("out of boundaries operator[]");
|
||||
}
|
||||
return Data[idx];
|
||||
}
|
||||
private:
|
||||
inline void Grow(size_t add = 0) {
|
||||
Cap = (Cap*2) + add;
|
||||
auto* NewData = (T_*)(GlobalAlloc(GPTR, Cap * sizeof(T_)));
|
||||
for(size_t C = 0; C < Size; C++) {
|
||||
NewData[C] = Data[C];
|
||||
}
|
||||
GlobalFree(Data);
|
||||
Data = NewData;
|
||||
}
|
||||
size_t Size{0}, Cap{5};
|
||||
bool init{false};
|
||||
T_* Data;
|
||||
};
|
||||
|
||||
heap_array<function_info>* watch_data;
|
||||
|
||||
struct watchdog_mutex {
|
||||
static void Create() noexcept {
|
||||
hMutex = CreateMutex(nullptr, FALSE, nullptr);
|
||||
}
|
||||
static void Lock() {
|
||||
WaitForSingleObject(hMutex, INFINITE);
|
||||
}
|
||||
static void Unlock() {
|
||||
ReleaseMutex(hMutex);
|
||||
}
|
||||
struct [[nodiscard]] ScopedLock {
|
||||
ScopedLock() {
|
||||
if(hMutex)
|
||||
watchdog_mutex::Lock();
|
||||
}
|
||||
~ScopedLock() {
|
||||
if(hMutex)
|
||||
watchdog_mutex::Unlock();
|
||||
}
|
||||
};
|
||||
private:
|
||||
static HANDLE hMutex;
|
||||
};
|
||||
HANDLE watchdog_mutex::hMutex{nullptr};
|
||||
std::atomic<bool> Init{false}, Sym;
|
||||
std::atomic<int64_t> Offset{0};
|
||||
|
||||
void watchdog_setOffset(int64_t Off) {
|
||||
Offset.store(Off);
|
||||
}
|
||||
|
||||
void notify(const char* msg) {
|
||||
HANDLE stdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (stdOut != nullptr && stdOut != INVALID_HANDLE_VALUE) {
|
||||
DWORD written = 0;
|
||||
WriteConsoleA(stdOut, "[WATCHDOG] ", 11, &written, nullptr);
|
||||
WriteConsoleA(stdOut, msg, DWORD(strlen(msg)), &written, nullptr);
|
||||
WriteConsoleA(stdOut, "\n", 1, &written, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
fst::stack_string<MAX_SYM_NAME> FindFunction(void* Address) {
|
||||
if(!Sym.load()) {
|
||||
fst::stack_string<MAX_SYM_NAME> undName;
|
||||
return undName;
|
||||
}
|
||||
static HANDLE process = GetCurrentProcess();
|
||||
DWORD64 symDisplacement = 0;
|
||||
fst::stack_string<MAX_SYM_NAME> undName;
|
||||
TCHAR buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
|
||||
memset(&buffer,0, sizeof(buffer));
|
||||
auto pSymbolInfo = (PSYMBOL_INFO)buffer;
|
||||
pSymbolInfo->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
pSymbolInfo->MaxNameLen = MAX_SYM_NAME;
|
||||
if (SymFromAddr(process, DWORD64(Address) + Offset, &symDisplacement, pSymbolInfo)) {
|
||||
undName.push_back(pSymbolInfo->Name);
|
||||
}
|
||||
return undName;
|
||||
}
|
||||
|
||||
|
||||
fst::stack_string<512> getCrashInfo(void* Address){
|
||||
if(!Sym.load()){
|
||||
fst::stack_string<512> Value;
|
||||
Value.push_back("unknown", 7);
|
||||
return Value;
|
||||
}
|
||||
DWORD pdwDisplacement = 0;
|
||||
IMAGEHLP_LINE64 line{sizeof(IMAGEHLP_LINE64)};
|
||||
SymGetLineFromAddr64(GetCurrentProcess(), DWORD64(Address) + Offset, &pdwDisplacement, &line);
|
||||
char* Name = nullptr;
|
||||
if(line.FileName) {
|
||||
Name = strrchr(line.FileName, '\\');
|
||||
}
|
||||
fst::stack_string<512> Value;
|
||||
if(Name)Value.push_back(Name+1);
|
||||
else Value.push_back("unknown", 7);
|
||||
char buffer[20];
|
||||
auto n = sprintf(buffer, ":%lu", line.LineNumber);
|
||||
Value.push_back(buffer, n);
|
||||
return Value;
|
||||
}
|
||||
const char* getFunctionDetails(void* Address) {
|
||||
return FindFunction(Address).c_str();
|
||||
}
|
||||
const char* getCrashLocation(void* Address) {
|
||||
return getCrashInfo(Address).c_str();
|
||||
}
|
||||
|
||||
void InitSym(const char* PDBLocation) {
|
||||
SymInitialize(GetCurrentProcess(), PDBLocation, TRUE);
|
||||
Sym.store(true);
|
||||
}
|
||||
|
||||
void write_report(const char* report, size_t size) {
|
||||
HANDLE hFile = CreateFile(crash_file.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (hFile == INVALID_HANDLE_VALUE) {
|
||||
notify("Failed to open crash file for writing!");
|
||||
return;
|
||||
}
|
||||
DWORD dwBytesWritten = 0;
|
||||
auto Flag = WriteFile(hFile, report, DWORD(size), &dwBytesWritten, nullptr);
|
||||
if (Flag == FALSE) {
|
||||
notify("Failed to write to crash file!");
|
||||
}
|
||||
CloseHandle(hFile);
|
||||
}
|
||||
|
||||
void generate_crash_report(uint32_t Code, void* Address) {
|
||||
watchdog_mutex::ScopedLock guard;
|
||||
notify("generating crash report, please wait");
|
||||
Init.store(false);
|
||||
heap_array<char> Report(watch_data->size() * sizeof(function_info));
|
||||
Report.string_insert("crash code ");
|
||||
Report.string_insert(HexString(Code).c_str());
|
||||
Report.string_insert(" at ");
|
||||
Report.string_insert(HexString(size_t(Address) + Offset).c_str());
|
||||
Report.string_insert("\n");
|
||||
if(Address) {
|
||||
Report.string_insert("origin and line number -> ");
|
||||
Report.string_insert(getCrashInfo(Address).c_str());
|
||||
Report.string_insert("\n");
|
||||
}
|
||||
Report.string_insert("Call history: \n");
|
||||
char buff[20];
|
||||
for(size_t C = 0; C < watch_data->size(); C++){
|
||||
auto entry = watch_data->at(C);
|
||||
auto Name = FindFunction(entry.func_address);
|
||||
if(entry.enter){
|
||||
Report.string_insert("[Entry] ");
|
||||
}
|
||||
else {
|
||||
Report.string_insert("[Exit ] ");
|
||||
}
|
||||
auto n = sprintf(buff, "(%d) ", entry.thread_id);
|
||||
Report.string_insert(buff, n);
|
||||
if(Name.size() > 0){
|
||||
Report.string_insert(Name.c_str(), Name.size());
|
||||
Report.string_insert(" | ");
|
||||
auto location = getCrashInfo(entry.func_address);
|
||||
Report.string_insert(location.c_str(), location.size());
|
||||
}
|
||||
else {
|
||||
Report.string_insert(HexString(size_t(entry.func_address) + Offset).c_str());
|
||||
}
|
||||
Report.string_insert("\n");
|
||||
}
|
||||
write_report(Report.cget(), Report.size());
|
||||
notify("crash report generated");
|
||||
Init.store(true);
|
||||
}
|
||||
|
||||
LONG WINAPI CrashHandler(EXCEPTION_POINTERS* p) {
|
||||
Init.store(false);
|
||||
notify("CAUGHT EXCEPTION!");
|
||||
generate_crash_report(p->ExceptionRecord->ExceptionCode, p->ExceptionRecord->ExceptionAddress);
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
void watchdog_init(const char* crashFile, const char* SpecificPDBLocation, bool Symbols) {
|
||||
if(Symbols)SymInitialize(GetCurrentProcess(), SpecificPDBLocation, TRUE);
|
||||
Sym.store(Symbols);
|
||||
SetUnhandledExceptionFilter(CrashHandler);
|
||||
watch_data = new heap_array<function_info>();
|
||||
watchdog_mutex::Create();
|
||||
crash_file.push_back(crashFile);
|
||||
notify("initialized!");
|
||||
Init.store(true);
|
||||
}
|
||||
|
||||
inline void AddEntry(void* func_address, uint32_t thread_id, bool entry) {
|
||||
watchdog_mutex::ScopedLock guard;
|
||||
if(Init.load()) {
|
||||
watch_data->insert({func_address, thread_id, entry});
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void FuncEntry(void* func) {
|
||||
AddEntry(func, GetCurrentThreadId(), true);
|
||||
}
|
||||
void FuncExit(void* func) {
|
||||
AddEntry(func, GetCurrentThreadId(), false);
|
||||
}
|
||||
}
|
||||
12
include/watchdog/watchdog.h
Normal file
12
include/watchdog/watchdog.h
Normal file
@@ -0,0 +1,12 @@
|
||||
//
|
||||
// Created by Anonymous275 on 9/9/2021.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
extern void watchdog_init(const char* crashFile, const char* SpecificPDBLocation, bool Symbols = true);
|
||||
extern void generate_crash_report(uint32_t Code, void* Address);
|
||||
const char* getFunctionDetails(void* Address);
|
||||
extern void watchdog_setOffset(int64_t Off);
|
||||
const char* getCrashLocation(void* Address);
|
||||
void InitSym(const char* PDBLocation);
|
||||
85
include/watchdog/x64Def.asm
Normal file
85
include/watchdog/x64Def.asm
Normal file
@@ -0,0 +1,85 @@
|
||||
;//
|
||||
;// Created by Anonymous275 on 9/9/2021.
|
||||
;//
|
||||
;External C functions used by _penter and _pexit
|
||||
extern FuncEntry:Proc
|
||||
extern FuncExit:Proc
|
||||
|
||||
.code
|
||||
|
||||
_penter proc
|
||||
|
||||
; Store the volatile registers
|
||||
push r11
|
||||
push r10
|
||||
push r9
|
||||
push r8
|
||||
push rax
|
||||
push rdx
|
||||
push rcx
|
||||
|
||||
; reserve space for 4 registers [ rcx,rdx,r8 and r9 ] 32 bytes
|
||||
sub rsp,20h
|
||||
|
||||
; Get the return address of the function
|
||||
mov rcx,rsp
|
||||
mov rcx,qword ptr[rcx+58h]
|
||||
sub rcx,5
|
||||
|
||||
;call the function to get the name of the callee and caller
|
||||
call FuncEntry
|
||||
|
||||
;Release the space reserved for the registersk by adding 32 bytes
|
||||
add rsp,20h
|
||||
|
||||
;Restore the registers back by poping out
|
||||
pop rcx
|
||||
pop rdx
|
||||
pop rax
|
||||
pop r8
|
||||
pop r9
|
||||
pop r10
|
||||
pop r11
|
||||
|
||||
;return
|
||||
ret
|
||||
|
||||
_penter endp
|
||||
|
||||
_pexit proc
|
||||
|
||||
; Store the volatile registers
|
||||
push r11
|
||||
push r10
|
||||
push r9
|
||||
push r8
|
||||
push rax
|
||||
push rdx
|
||||
push rcx
|
||||
|
||||
; reserve space for 4 registers [ rcx,rdx,r8 and r9 ] 32 bytes
|
||||
sub rsp,20h
|
||||
|
||||
; Get the return address of the function
|
||||
mov rcx,rsp
|
||||
mov rcx,qword ptr[rcx+58h]
|
||||
|
||||
call FuncExit
|
||||
|
||||
;Release the space reserved for the registersk by adding 32 bytes
|
||||
add rsp,20h
|
||||
|
||||
;Restore the registers back by poping out
|
||||
pop rcx
|
||||
pop rdx
|
||||
pop rax
|
||||
pop r8
|
||||
pop r9
|
||||
pop r10
|
||||
pop r11
|
||||
|
||||
;return
|
||||
ret
|
||||
|
||||
_pexit endp
|
||||
end
|
||||
@@ -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,9 +81,11 @@ 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!");
|
||||
char* crasher = nullptr;
|
||||
crasher[4555] = 'c';
|
||||
}
|
||||
} else {
|
||||
warn("Unable to fetch version from backend.");
|
||||
|
||||
17
src/Http.cpp
17
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
|
||||
@@ -127,6 +127,7 @@ std::string Http::POST(const std::string& host, const std::string& target, const
|
||||
bool ok = try_connect_with_protocol(tcp::v4());
|
||||
if (!ok) {
|
||||
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";
|
||||
}
|
||||
//}
|
||||
@@ -202,12 +203,14 @@ std::string Http::POST(const std::string& host, const std::string& target, const
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
Application::Console().Write(e.what());
|
||||
Sentry.AddErrorBreadcrumb(e.what(), __FILE__, std::to_string(__LINE__)); // FIXME: this is ugly.
|
||||
return "-1";
|
||||
}
|
||||
}
|
||||
|
||||
// RFC 2616, RFC 7231
|
||||
static std::map<size_t, const char*> Map = {
|
||||
{ -1, "Invalid Response Code"},
|
||||
{ 100, "Continue" },
|
||||
{ 101, "Switching Protocols" },
|
||||
{ 102, "Processing" },
|
||||
@@ -270,12 +273,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;
|
||||
|
||||
@@ -36,9 +36,6 @@ void THeartbeatThread::operator()() {
|
||||
Body += "&pps=" + Application::PPS();
|
||||
|
||||
auto SentryReportError = [&](const std::string& transaction, int status) {
|
||||
if (status < 0) {
|
||||
status = 0;
|
||||
}
|
||||
auto Lock = Sentry.CreateExclusiveContext();
|
||||
Sentry.SetContext("heartbeat",
|
||||
{ { "response-body", T },
|
||||
@@ -52,16 +49,16 @@ void THeartbeatThread::operator()() {
|
||||
int ResponseCode = -1;
|
||||
T = Http::POST(Application::GetBackendHostname(), Target, {}, Body, false, &ResponseCode);
|
||||
|
||||
if (T.substr(0, 2) != "20" || ResponseCode != 200) {
|
||||
if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) {
|
||||
trace("got " + T + " from backend");
|
||||
SentryReportError(Application::GetBackendHostname() + Target, ResponseCode);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
T = Http::POST(Application::GetBackup1Hostname(), Target, {}, Body, false, &ResponseCode);
|
||||
if (T.substr(0, 2) != "20" || ResponseCode != 200) {
|
||||
if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) {
|
||||
SentryReportError(Application::GetBackup1Hostname() + Target, ResponseCode);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
T = Http::POST(Application::GetBackup2Hostname(), Target, {}, Body, false, &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.");
|
||||
isAuth = false;
|
||||
SentryReportError(Application::GetBackup2Hostname() + Target, ResponseCode);
|
||||
@@ -107,10 +104,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();
|
||||
|
||||
@@ -1,22 +1,17 @@
|
||||
#include "TSentry.h"
|
||||
#include "Common.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <sentry.h>
|
||||
#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() {
|
||||
if constexpr (ConstexprLength(SECRET_SENTRY_URL) == 0) {
|
||||
if (std::strlen(S_DSN) == 0) {
|
||||
mValid = false;
|
||||
} else {
|
||||
mValid = true;
|
||||
sentry_options_t* options = sentry_options_new();
|
||||
sentry_options_set_dsn(options, SECRET_SENTRY_URL);
|
||||
sentry_options_set_dsn(options, S_DSN);
|
||||
sentry_options_set_debug(options, false); // needs to always be false
|
||||
sentry_options_set_symbolize_stacktraces(options, true);
|
||||
auto ReleaseString = "BeamMP-Server@" + Application::ServerVersion();
|
||||
@@ -34,9 +29,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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
113
src/main.cpp
113
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"
|
||||
@@ -10,81 +11,61 @@
|
||||
#include "TPPSMonitor.h"
|
||||
#include "TResourceManager.h"
|
||||
#include "TServer.h"
|
||||
|
||||
#include "../include/watchdog/watchdog.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) {
|
||||
#ifdef WIN32
|
||||
watchdog_init("watchdog_crash.log", nullptr);
|
||||
#endif
|
||||
try {
|
||||
setlocale(LC_ALL, "C");
|
||||
|
||||
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; });
|
||||
bool Shutdown = false;
|
||||
Application::RegisterShutdownHandler([&Shutdown] { Shutdown = true; });
|
||||
|
||||
TServer Server(argc, argv);
|
||||
TConfig Config;
|
||||
TServer Server(argc, argv);
|
||||
TConfig Config;
|
||||
|
||||
if (Config.Failed()) {
|
||||
info("Closing in 10 seconds");
|
||||
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||
return 1;
|
||||
if (Config.Failed()) {
|
||||
info("Closing in 10 seconds");
|
||||
// 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;
|
||||
}
|
||||
|
||||
RegisterThread("Main");
|
||||
|
||||
trace("Running in debug mode on a debug build");
|
||||
|
||||
Sentry.SetupUser();
|
||||
Sentry.PrintWelcome();
|
||||
TResourceManager ResourceManager;
|
||||
TPPSMonitor PPSMonitor(Server);
|
||||
THeartbeatThread Heartbeat(ResourceManager, Server);
|
||||
TNetwork Network(Server, PPSMonitor, ResourceManager);
|
||||
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(50));
|
||||
}
|
||||
info("Shutdown.");
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
error(e.what());
|
||||
Sentry.LogException(e, _file_basename, _line);
|
||||
}
|
||||
|
||||
RegisterThread("Main");
|
||||
|
||||
trace("Running in debug mode on a debug build");
|
||||
|
||||
Sentry.SetupUser();
|
||||
Sentry.PrintWelcome();
|
||||
Application::CheckForUpdates();
|
||||
TResourceManager ResourceManager;
|
||||
TPPSMonitor PPSMonitor(Server);
|
||||
THeartbeatThread Heartbeat(ResourceManager, Server);
|
||||
TNetwork Network(Server, PPSMonitor, ResourceManager);
|
||||
TLuaEngine LuaEngine(Server, Network);
|
||||
PPSMonitor.SetNetwork(Network);
|
||||
Application::Console().InitializeLuaConsole(LuaEngine);
|
||||
|
||||
// TODO: replace
|
||||
while (!Shutdown) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
error(e.what());
|
||||
Sentry.LogException(e, _file_basename, _line);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user