mirror of
https://github.com/SantaSpeen/BeamMP-Server.git
synced 2026-02-16 13:10:39 +00:00
Compare commits
127 Commits
v2.1.4
...
hotfix-no-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aaefea1d01 | ||
|
|
863e8eb8cf | ||
|
|
d0431c0b9d | ||
|
|
0961f86662 | ||
|
|
7f2ca025f8 | ||
|
|
2355327c21 | ||
|
|
3837e101e2 | ||
|
|
fa19ba08e3 | ||
|
|
57d0eb735e | ||
|
|
15704abf6c | ||
|
|
6883c96d33 | ||
|
|
f632606d76 | ||
|
|
c70ada2926 | ||
|
|
80aebcb9a7 | ||
|
|
3fc397814c | ||
|
|
6542be09ee | ||
|
|
38b934bc0f | ||
|
|
a2f92b5791 | ||
|
|
30624c77a2 | ||
|
|
b1664bb184 | ||
|
|
ffac000cd2 | ||
|
|
3cd94380e2 | ||
|
|
b055fd8bda | ||
|
|
d43ee4b7b6 | ||
|
|
a514591650 | ||
|
|
0f9f81e9fa | ||
|
|
11e94e91a7 | ||
|
|
cacdc004da | ||
|
|
8250d5876f | ||
|
|
a7b02c459e | ||
|
|
0d5ef404f6 | ||
|
|
07cf7d7c21 | ||
|
|
809a851c71 | ||
|
|
fe36191baf | ||
|
|
7d137eb496 | ||
|
|
fd6234bd21 | ||
|
|
9f0b057c14 | ||
|
|
0143748953 | ||
|
|
8ec90d5186 | ||
|
|
d054214b7f | ||
|
|
003a8269aa | ||
|
|
59b1b45625 | ||
|
|
15e5cee166 | ||
|
|
12123582ad | ||
|
|
3fb227e468 | ||
|
|
31e9004011 | ||
|
|
f98c8dabb0 | ||
|
|
e8665bfb72 | ||
|
|
3d13381abd | ||
|
|
5352e4ff03 | ||
|
|
c571e218c7 | ||
|
|
5725717e29 | ||
|
|
7f5447f25e | ||
|
|
b33b396089 | ||
|
|
ff3cbebac0 | ||
|
|
0f9a994c10 | ||
|
|
c4b72be50a | ||
|
|
5a3140c84a | ||
|
|
bea8006a26 | ||
|
|
a2dc42c5f5 | ||
|
|
4b92532203 | ||
|
|
683e13a4a0 | ||
|
|
9d6dbefb9d | ||
|
|
981b56b846 | ||
|
|
9f52ab2e54 | ||
|
|
8fada3ac04 | ||
|
|
5330013dc3 | ||
|
|
c7e0461a86 | ||
|
|
032c1daf30 | ||
|
|
d1efebe068 | ||
|
|
c77e2b3fd8 | ||
|
|
e92847e628 | ||
|
|
afb18ccff7 | ||
|
|
4659a9362d | ||
|
|
fe6e1e6266 | ||
|
|
c0faff5b05 | ||
|
|
f4ffa2cdda | ||
|
|
1409d4ef80 | ||
|
|
51e662fdda | ||
|
|
72950fdab2 | ||
|
|
b9f594896a | ||
|
|
8551e56e42 | ||
|
|
f4fc182d5e | ||
|
|
ee1e948a65 | ||
|
|
e3081a971e | ||
|
|
7c5bf9473e | ||
|
|
5a44a8e9c5 | ||
|
|
d1a0eaffab | ||
|
|
10322bf24e | ||
|
|
969cd93358 | ||
|
|
b3a8b1a682 | ||
|
|
2774a73d83 | ||
|
|
739eaad199 | ||
|
|
b53b72d604 | ||
|
|
85fd9e9ee3 | ||
|
|
57e6e98423 | ||
|
|
e3c6bdb50d | ||
|
|
1f89b202b4 | ||
|
|
0a31107e56 | ||
|
|
9237f0dd43 | ||
|
|
ce834a634c | ||
|
|
f65607cb00 | ||
|
|
b0475f262f | ||
|
|
cee824ad46 | ||
|
|
530e977d9d | ||
|
|
807cb68f0f | ||
|
|
3850740ded | ||
|
|
cfc7b302fe | ||
|
|
a7c28a8d0d | ||
|
|
9fe1a94d42 | ||
|
|
1eee56a666 | ||
|
|
28fe6e9634 | ||
|
|
4512f44ea1 | ||
|
|
3efd31bc84 | ||
|
|
5684134894 | ||
|
|
e6c97de3c4 | ||
|
|
f550d0bba9 | ||
|
|
2b4fec6d11 | ||
|
|
550c658ac5 | ||
|
|
da41862f49 | ||
|
|
d5769ce9be | ||
|
|
4bf9244005 | ||
|
|
89f63024ab | ||
|
|
f258678751 | ||
|
|
4d2f68068d | ||
|
|
9f636345ef | ||
|
|
3d0d5e9e4c |
29
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
29
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: "[Bug] Enter issue title here"
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Fill out general information**
|
||||
OS (windows, linux, ...):
|
||||
BeamMP-Server Version:
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Do x ...
|
||||
2. Do y ...
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Logs**
|
||||
Please attach the `Server.log` from the run in which the issue appeared, preferably with Debug turned on in the `ServerConfig.toml`.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: "[Feature Request]"
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. For example: "I'm always frustrated when ...".
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen. Also supply OS information if relevant, for example "*On Linux*, I would like to be able to...".
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the feature request here.
|
||||
11
.github/workflows/cmake-linux.yml
vendored
11
.github/workflows/cmake-linux.yml
vendored
@@ -12,12 +12,15 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'true'
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: Install Dependencies
|
||||
env:
|
||||
beammp_sentry_url: ${{ secrets.BEAMMP_SECRET_SENTRY_URL }}
|
||||
run: |
|
||||
echo ${#beammp_sentry_url}
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev
|
||||
sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev
|
||||
sudo add-apt-repository ppa:mhier/libboost-latest
|
||||
sudo apt-get install -y libboost1.70-dev libboost1.70
|
||||
|
||||
@@ -27,7 +30,9 @@ jobs:
|
||||
- name: Configure CMake
|
||||
shell: bash
|
||||
working-directory: ${{github.workspace}}/build-linux
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_COMPILER=g++-10
|
||||
env:
|
||||
beammp_sentry_url: ${{ secrets.BEAMMP_SECRET_SENTRY_URL }}
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_COMPILER=g++-10 -DBEAMMP_SECRET_SENTRY_URL="$beammp_sentry_url"
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{github.workspace}}/build-linux
|
||||
|
||||
8
.github/workflows/cmake-windows.yml
vendored
8
.github/workflows/cmake-windows.yml
vendored
@@ -12,13 +12,13 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'true'
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: Restore artifacts, or run vcpkg, build and cache artifacts
|
||||
uses: lukka/run-vcpkg@main
|
||||
id: runvcpkg
|
||||
with:
|
||||
vcpkgArguments: 'lua zlib rapidjson boost-beast boost-asio openssl websocketpp'
|
||||
vcpkgArguments: 'lua zlib rapidjson boost-beast boost-asio openssl websocketpp curl'
|
||||
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
|
||||
vcpkgGitCommitId: '8dddc6c899ce6fdbeab38b525a31e7f23cb2d5bb'
|
||||
vcpkgTriplet: 'x64-windows-static'
|
||||
@@ -29,7 +29,9 @@ jobs:
|
||||
- name: Configure CMake
|
||||
shell: bash
|
||||
working-directory: ${{github.workspace}}/build-windows
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE='${{ runner.workspace }}/b/vcpkg/scripts/buildsystems/vcpkg.cmake' -DVCPKG_TARGET_TRIPLET=x64-windows-static
|
||||
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"
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{github.workspace}}/build-windows
|
||||
|
||||
19
.github/workflows/release-build.yml
vendored
19
.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
|
||||
@@ -37,12 +37,12 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'true'
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev
|
||||
sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev
|
||||
sudo add-apt-repository ppa:mhier/libboost-latest
|
||||
sudo apt-get install -y libboost1.70-dev libboost1.70
|
||||
|
||||
@@ -52,14 +52,15 @@ jobs:
|
||||
- name: Configure CMake
|
||||
shell: bash
|
||||
working-directory: ${{github.workspace}}/build-linux
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_COMPILER=g++-10
|
||||
env:
|
||||
beammp_sentry_url: ${{ secrets.BEAMMP_SECRET_SENTRY_URL }}
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_COMPILER=g++-10 -DBEAMMP_SECRET_SENTRY_URL="$beammp_sentry_url"
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{github.workspace}}/build-linux
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE
|
||||
|
||||
|
||||
- name: Upload Release Asset
|
||||
id: upload-release-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
@@ -78,13 +79,13 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'true'
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: Restore artifacts, or run vcpkg, build and cache artifacts
|
||||
uses: lukka/run-vcpkg@main
|
||||
id: runvcpkg
|
||||
with:
|
||||
vcpkgArguments: 'lua zlib rapidjson boost-beast boost-asio openssl websocketpp'
|
||||
vcpkgArguments: 'lua zlib rapidjson boost-beast boost-asio openssl websocketpp curl'
|
||||
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
|
||||
vcpkgGitCommitId: '8dddc6c899ce6fdbeab38b525a31e7f23cb2d5bb'
|
||||
vcpkgTriplet: 'x64-windows-static'
|
||||
@@ -95,7 +96,9 @@ jobs:
|
||||
- name: Configure CMake
|
||||
shell: bash
|
||||
working-directory: ${{github.workspace}}/build-windows
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE='${{ runner.workspace }}/b/vcpkg/scripts/buildsystems/vcpkg.cmake' -DVCPKG_TARGET_TRIPLET=x64-windows-static
|
||||
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"
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{github.workspace}}/build-windows
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,7 +1,9 @@
|
||||
.idea/
|
||||
*.orig
|
||||
*.toml
|
||||
boost_*
|
||||
Resources
|
||||
run-in-env.sh
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -13,3 +13,6 @@
|
||||
[submodule "include/tomlplusplus"]
|
||||
path = include/tomlplusplus
|
||||
url = https://github.com/marzer/tomlplusplus
|
||||
[submodule "include/sentry-native"]
|
||||
path = include/sentry-native
|
||||
url = https://github.com/getsentry/sentry-native
|
||||
|
||||
@@ -1,10 +1,30 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(Server)
|
||||
|
||||
message(STATUS "You can find build instructions and a list of dependencies in the README at \
|
||||
https://github.com/BeamMP/BeamMP-Server")
|
||||
|
||||
project(BeamMP-Server
|
||||
DESCRIPTION "Server for BeamMP - The Multiplayer Mod for BeamNG.drive"
|
||||
HOMEPAGE_URL https://beammp.com
|
||||
LANGUAGES CXX C)
|
||||
|
||||
if (WIN32)
|
||||
# this has to happen before sentry, so that crashpad on windows links with these settings.
|
||||
message(STATUS "MSVC -> forcing use of statically-linked runtime.")
|
||||
STRING(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE})
|
||||
STRING(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
|
||||
endif()
|
||||
|
||||
include_directories("include/sentry-native/include")
|
||||
set(SENTRY_BUILD_SHARED_LIBS OFF)
|
||||
if (MSVC)
|
||||
set(SENTRY_BUILD_RUNTIMESTATIC ON)
|
||||
endif()
|
||||
set(SENTRY_BACKEND breakpad)
|
||||
add_subdirectory("include/sentry-native")
|
||||
|
||||
message(STATUS "Setting compiler flags")
|
||||
if (WIN32)
|
||||
#-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)
|
||||
@@ -12,13 +32,26 @@ 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_FLAGS} -fsanitize=undefined,thread")
|
||||
endif (SANITIZE)
|
||||
endif ()
|
||||
|
||||
message(STATUS "Checking for Sentry URL")
|
||||
# this is set by the build system.
|
||||
# IMPORTANT: if you're building from source, just leave this empty
|
||||
if (NOT DEFINED BEAMMP_SECRET_SENTRY_URL)
|
||||
message(WARNING "No sentry URL configured. Sentry logging is disabled for this build. \
|
||||
This is not an error, and if you're building the BeamMP-Server yourself, this is expected and can be ignored.")
|
||||
set(BEAMMP_SECRET_SENTRY_URL "")
|
||||
else()
|
||||
string(LENGTH ${BEAMMP_SECRET_SENTRY_URL} URL_LEN)
|
||||
message(STATUS "Sentry URL is length ${URL_LEN}")
|
||||
endif()
|
||||
|
||||
message(STATUS "Adding local source dependencies")
|
||||
# this has to happen before -DDEBUG since it wont compile properly with -DDEBUG
|
||||
include_directories("asio/asio/include")
|
||||
include_directories("rapidjson/include")
|
||||
@@ -27,9 +60,9 @@ add_subdirectory("socket.io-client-cpp")
|
||||
add_subdirectory("include/commandline")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
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
|
||||
@@ -46,23 +79,57 @@ add_executable(BeamMP-Server
|
||||
include/TResourceManager.h src/TResourceManager.cpp
|
||||
include/THeartbeatThread.h src/THeartbeatThread.cpp
|
||||
include/Http.h src/Http.cpp
|
||||
#include/SocketIO.h src/SocketIO.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_include_directories(BeamMP-Server PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/commandline")
|
||||
target_compile_definitions(BeamMP-Server PRIVATE SECRET_SENTRY_URL="${BEAMMP_SECRET_SENTRY_URL}")
|
||||
|
||||
target_include_directories(BeamMP-Server PUBLIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/commandline")
|
||||
|
||||
message(STATUS "Looking for Lua")
|
||||
find_package(Lua REQUIRED)
|
||||
target_include_directories(BeamMP-Server PUBLIC ${Boost_INCLUDE_DIRS} ${LUA_INCLUDE_DIR} "socket.io-client-cpp/src" "include/tomlplusplus")
|
||||
target_include_directories(BeamMP-Server PUBLIC
|
||||
${Boost_INCLUDE_DIRS}
|
||||
${LUA_INCLUDE_DIR}
|
||||
${CURL_INCLUDE_DIRS}
|
||||
"socket.io-client-cpp/src"
|
||||
"include/tomlplusplus"
|
||||
"include/sentry-native/include"
|
||||
"include/curl/include")
|
||||
|
||||
message(STATUS "Looking for SSL")
|
||||
find_package(OpenSSL REQUIRED)
|
||||
|
||||
message(STATUS "CURL IS ${CURL_LIBRARIES}")
|
||||
|
||||
if (UNIX)
|
||||
target_link_libraries(BeamMP-Server z pthread stdc++fs ${LUA_LIBRARIES} crypto ${OPENSSL_LIBRARIES} commandline sioclient_tls)
|
||||
target_link_libraries(BeamMP-Server
|
||||
z
|
||||
pthread
|
||||
stdc++fs
|
||||
${LUA_LIBRARIES}
|
||||
crypto
|
||||
${OPENSSL_LIBRARIES}
|
||||
commandline
|
||||
sioclient_tls
|
||||
sentry)
|
||||
elseif (WIN32)
|
||||
include(FindLua)
|
||||
message(STATUS "Looking for libz")
|
||||
find_package(ZLIB REQUIRED)
|
||||
message(STATUS "Looking for RapidJSON")
|
||||
find_package(RapidJSON CONFIG REQUIRED)
|
||||
target_include_directories(BeamMP-Server PRIVATE ${RAPIDJSON_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
|
||||
target_link_libraries(BeamMP-Server PRIVATE ws2_32 ZLIB::ZLIB ${LUA_LIBRARIES} ${OPENSSL_LIBRARIES} commandline sioclient_tls)
|
||||
target_link_libraries(BeamMP-Server PRIVATE
|
||||
ws2_32
|
||||
ZLIB::ZLIB
|
||||
${LUA_LIBRARIES}
|
||||
${OPENSSL_LIBRARIES}
|
||||
commandline
|
||||
sioclient_tls
|
||||
sentry)
|
||||
endif ()
|
||||
|
||||
82
Changelog.md
Normal file
82
Changelog.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# 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
|
||||
- ADDED logging of various errors, crashes and exceptions to the backend
|
||||
- ADDED chat messages are now logged to the server console as [CHAT]
|
||||
- ADDED debug message telling you when the server heartbeats to the backend
|
||||
- REMOVED various [DEBUG] messages which were confusing (such as "breaking client loop")
|
||||
- FIXED various crashes and issues with handling unexpected backend responses
|
||||
- FIXED minor bugs due to code correctness
|
||||
|
||||
# v2.2.0
|
||||
|
||||
- FIXED major security flaw
|
||||
- FIXED minor bugs
|
||||
|
||||
# v2.1.4
|
||||
|
||||
- ADDED debug heartbeat print
|
||||
- ADDED kicking every player before shutdown
|
||||
- FIXED rare bug which led to violent crash
|
||||
- FIXED minor bugs
|
||||
|
||||
# v2.1.3
|
||||
|
||||
- FIXED Lua events not cancelling properly on Linux
|
||||
|
||||
# v2.1.2
|
||||
|
||||
- CHANGED default map to gridmap v2
|
||||
- FIXED version number display
|
||||
|
||||
# v2.1.1
|
||||
# v2.1.0 (pre-v2.1.1)
|
||||
# v2.0.4 (pre-v2.1.0)
|
||||
|
||||
- REMOVED boost as a runtime dependency
|
||||
- FIXED Lua plugins on Linux
|
||||
- FIXED console history on Windows
|
||||
- CHANGED to new config format TOML
|
||||
|
||||
# v2.0.3
|
||||
|
||||
- WORKAROUND for timeout bug / ghost player bug
|
||||
- FIXED 100% CPU spin when stdin is /dev/null.
|
||||
|
||||
# v2.0.2
|
||||
|
||||
- ADDED fully new commandline
|
||||
- ADDED new backend
|
||||
- ADDED automated build system
|
||||
- ADDED lua GetPlayerIdentifiers
|
||||
- ADDED lots of debug info
|
||||
- ADDED better POSTing and GETing
|
||||
- ADDED a license
|
||||
- FIXED ghost players in player list issue
|
||||
- FIXED ghost vehicle after joining issue
|
||||
- FIXED missing vehicle after joining issue
|
||||
- FIXED a lot of desync issues
|
||||
- FIXED some memory leaks
|
||||
- FIXED various crashes
|
||||
- FIXED various data-races
|
||||
- FIXED some linux-specific crashes
|
||||
- FIXED some linux-specific issues
|
||||
- FIXED bug which caused kicking to be logged as leaving
|
||||
- FIXED various internal developer quality-of-life things
|
||||
17
README.md
17
README.md
@@ -11,7 +11,7 @@ The server is the point throug which all clients communicate. You can write lua
|
||||
These values are guesstimated and are subject to change with each release.
|
||||
|
||||
* RAM: 50+ MiB usable (not counting OS overhead)
|
||||
* CPU: Any Hz, preferably multicore
|
||||
* CPU: >1GHz, preferably multicore
|
||||
* OS: Windows, Linux (theoretically any POSIX)
|
||||
* GPU: None
|
||||
* HDD: 10 MiB + Mods/Plugins
|
||||
@@ -61,6 +61,7 @@ These package names are in the debian / ubuntu style. Feel free to PR your own g
|
||||
- `make`
|
||||
- `cmake`
|
||||
- `g++`
|
||||
- `libcurl4-openssl-dev` or similar (search for `libcurl` and look for one with `-dev`)
|
||||
|
||||
Must support ISO C++17. If your distro's `g++` doesn't support C++17, chances are that it has a `g++-8` or `g++-10` package that does. If this is the case. you just need to run CMake with `-DCMAKE_CXX_COMPILER=g++-10` (replace `g++-10` with your compiler's name).
|
||||
- `liblua5.3-dev`
|
||||
@@ -72,17 +73,19 @@ These package names are in the debian / ubuntu style. Feel free to PR your own g
|
||||
- `libopenssl-dev` or `libssl-dev`
|
||||
|
||||
**If** you're building it from source, you'll need `libboost1.70-all-dev` or `libboost1.71-all-dev` or higher as well.
|
||||
If you can't find this version of boost (only 1.6x, for example), you can either update to a newer version of your distro, build boost yourself, or use an unstable rolling release (like Debian `sid` aka `unstable`).
|
||||
|
||||
### How to build
|
||||
|
||||
On windows. use git-bash for these commands. On linux, these should work in your shell.
|
||||
On windows, use git-bash for these commands. On Linux, these should work in your shell.
|
||||
|
||||
1. Make sure you have all [prerequisites](#prerequisites) installed
|
||||
2. Clone the repository in a location of your choice with `git clone --recursive https://github.com/BeamMP/BeamMP-Server`. Now change into the cloned directory by running `cd BeamMP-Server`.
|
||||
3. Checkout the branch of the release you want to compile (`master` is often unstable), for example `git checkout tags/v1.20` for version 1.20. You can find the latest version [here](https://github.com/BeamMP/BeamMP-Server/tags).
|
||||
4. Run `cmake .` (with `.`)
|
||||
5. Run `make`
|
||||
6. You will now have a `BeamMP-Server` file in your directory, which is executable with `./BeamMP-Server` (`.\BeamMP-Server.exe` for windows). Follow the (windows or linux, doesnt matter) instructions on the [wiki](https://wiki.beammp.com/en/home/Server_Mod) for further setup after installation (which we just did), such as port-forwarding and getting a key to actually run the server.
|
||||
2. Clone the repository in a location of your choice with **`git clone --recurse-submodules https://github.com/BeamMP/BeamMP-Server`**. Now change into the cloned directory by running `cd BeamMP-Server`.
|
||||
3. Ensure that all submodules are initialized by running `git submodule update --init --recursive`. Then change into the cloned directory by running `cd BeamMP-Server`.
|
||||
4. Checkout the branch of the release you want to compile (`master` is often unstable), for example `git checkout tags/v1.20` for version 1.20. You can find the latest version [here](https://github.com/BeamMP/BeamMP-Server/tags).
|
||||
5. Run `cmake .` (with `.`)
|
||||
6. Run `make`
|
||||
7. You will now have a `BeamMP-Server` file in your directory, which is executable with `./BeamMP-Server` (`.\BeamMP-Server.exe` for windows). Follow the (windows or linux, doesnt matter) instructions on the [wiki](https://wiki.beammp.com/en/home/Server_Mod) for further setup after installation (which we just did), such as port-forwarding and getting a key to actually run the server.
|
||||
|
||||
*tip: to run the server in the background, simply (in bash, zsh, etc) run:* `nohup ./BeamMP-Server &`*.*
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "TSentry.h"
|
||||
extern TSentry Sentry;
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
@@ -23,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;
|
||||
@@ -38,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()>;
|
||||
@@ -50,16 +58,21 @@ 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.1.4"; }
|
||||
static std::string ServerVersion() { return "2.3.3"; }
|
||||
static std::string ClientVersion() { return "2.0"; }
|
||||
static std::string PPS() { return mPPS; }
|
||||
static void SetPPS(std::string NewPPS) { mPPS = NewPPS; }
|
||||
static void SetPPS(const std::string& NewPPS) { mPPS = NewPPS; }
|
||||
|
||||
static inline TSettings Settings {};
|
||||
|
||||
static std::string GetBackendUrlForAuth() { return "auth.beammp.com"; }
|
||||
static std::string GetBackendHostname() { return "backend.beammp.com"; }
|
||||
static std::string GetBackup1Hostname() { return "backup1.beammp.com"; }
|
||||
static std::string GetBackup2Hostname() { return "backup2.beammp.com"; }
|
||||
static std::string GetBackendUrlForSocketIO() { return "https://backend.beammp.com"; }
|
||||
static void CheckForUpdates();
|
||||
static std::array<int, 3> VersionStrToInts(const std::string& str);
|
||||
static bool IsOutdated(const std::array<int, 3>& Current, const std::array<int, 3>& Newest);
|
||||
|
||||
private:
|
||||
static inline std::string mPPS;
|
||||
@@ -68,12 +81,13 @@ private:
|
||||
static inline std::deque<TShutdownHandler> mShutdownHandlers {};
|
||||
};
|
||||
|
||||
std::string ThreadName();
|
||||
void RegisterThread(const std::string str);
|
||||
std::string ThreadName(bool DebugModeOverride = false);
|
||||
void RegisterThread(const std::string& str);
|
||||
#define RegisterThreadAuto() RegisterThread(__func__)
|
||||
|
||||
#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__)
|
||||
@@ -99,16 +113,22 @@ 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)
|
||||
|
||||
#define warn(x) Application::Console().Write(_this_location + std::string("[WARN] ") + (x))
|
||||
#define info(x) Application::Console().Write(_this_location + std::string("[INFO] ") + (x))
|
||||
#define error(x) Application::Console().Write(_this_location + std::string("[ERROR] ") + (x))
|
||||
#define error(x) \
|
||||
do { \
|
||||
Application::Console().Write(_this_location + std::string("[ERROR] ") + (x)); \
|
||||
Sentry.AddErrorBreadcrumb((x), _file_basename, _line); \
|
||||
} while (false)
|
||||
#define luaprint(x) Application::Console().Write(_this_location + std::string("[LUA] ") + (x))
|
||||
#define debug(x) \
|
||||
do { \
|
||||
@@ -116,8 +136,22 @@ void RegisterThread(const std::string str);
|
||||
Application::Console().Write(_this_location + std::string("[DEBUG] ") + (x)); \
|
||||
} \
|
||||
} while (false)
|
||||
// trace() is a debug-build debug()
|
||||
#if defined(DEBUG)
|
||||
#define trace(x) \
|
||||
do { \
|
||||
if (Application::Settings.DebugModeEnabled) { \
|
||||
Application::Console().Write(_this_location + std::string("[TRACE] ") + (x)); \
|
||||
} \
|
||||
} while (false)
|
||||
#else
|
||||
#define trace(x)
|
||||
#endif // defined(DEBUG)
|
||||
|
||||
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
|
||||
|
||||
@@ -12,9 +12,9 @@ using DWORD = unsigned long;
|
||||
using PDWORD = unsigned long*;
|
||||
using LPDWORD = unsigned long*;
|
||||
char _getch();
|
||||
inline void CloseSocketProper(int socket) {
|
||||
shutdown(socket, SHUT_RDWR);
|
||||
close(socket);
|
||||
inline void CloseSocketProper(int TheSocket) {
|
||||
shutdown(TheSocket, SHUT_RDWR);
|
||||
close(TheSocket);
|
||||
}
|
||||
#endif // unix
|
||||
|
||||
@@ -23,9 +23,9 @@ inline void CloseSocketProper(int socket) {
|
||||
#ifdef WIN32
|
||||
#include <conio.h>
|
||||
#include <winsock2.h>
|
||||
inline void CloseSocketProper(SOCKET socket) {
|
||||
shutdown(socket, SD_BOTH);
|
||||
closesocket(socket);
|
||||
inline void CloseSocketProper(SOCKET TheSocket) {
|
||||
shutdown(TheSocket, 2); // 2 == SD_BOTH
|
||||
closesocket(TheSocket);
|
||||
}
|
||||
#endif // WIN32
|
||||
|
||||
|
||||
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)
|
||||
|
||||
}
|
||||
@@ -40,7 +40,7 @@ static const char* const ANSI_WHITE_BOLD = "\u001b[37;1m";
|
||||
static const char* const ANSI_BOLD = "\u001b[1m";
|
||||
static const char* const ANSI_UNDERLINE = "\u001b[4m";
|
||||
|
||||
#if DEBUG
|
||||
#ifdef DEBUG
|
||||
#include <iostream>
|
||||
inline void _assert([[maybe_unused]] const char* file, [[maybe_unused]] const char* function, [[maybe_unused]] unsigned line,
|
||||
[[maybe_unused]] const char* condition_string, [[maybe_unused]] bool result) {
|
||||
@@ -59,10 +59,15 @@ inline void _assert([[maybe_unused]] const char* file, [[maybe_unused]] const ch
|
||||
#define AssertNotReachable() _assert(__FILE__, __func__, __LINE__, "reached unreachable code", false)
|
||||
#else
|
||||
// In release build, these macros turn into NOPs. The compiler will optimize these out.
|
||||
#define Assert(x) \
|
||||
do { \
|
||||
#define Assert(cond) \
|
||||
do { \
|
||||
bool result = (cond); \
|
||||
if (!result) { \
|
||||
Sentry.LogAssert(#cond, _file_basename, _line, __func__); \
|
||||
} \
|
||||
} while (false)
|
||||
#define AssertNotReachable() \
|
||||
do { \
|
||||
#define AssertNotReachable() \
|
||||
do { \
|
||||
Sentry.LogAssert("code is unreachable", _file_basename, _line, __func__); \
|
||||
} while (false)
|
||||
#endif // DEBUG
|
||||
|
||||
18
include/Defer.h
Normal file
18
include/Defer.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
template <typename FnT>
|
||||
class Defer final {
|
||||
public:
|
||||
Defer(FnT fn)
|
||||
: mFunction([&fn] { (void)fn(); }) { }
|
||||
~Defer() {
|
||||
if (mFunction) {
|
||||
mFunction();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void()> mFunction;
|
||||
};
|
||||
@@ -4,6 +4,9 @@
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Http {
|
||||
std::string GET(const std::string& host, int port, const std::string& target);
|
||||
std::string POST(const std::string& host, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, bool json);
|
||||
}
|
||||
std::string GET(const std::string& host, int port, const std::string& target, unsigned int* status = nullptr);
|
||||
std::string POST(const std::string& host, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, bool json, int* status = nullptr);
|
||||
namespace Status {
|
||||
std::string ToString(int code);
|
||||
}
|
||||
}
|
||||
|
||||
3
include/SignalHandling.h
Normal file
3
include/SignalHandling.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void SetupSignalHandlers();
|
||||
@@ -8,13 +8,13 @@ class TConfig {
|
||||
public:
|
||||
explicit TConfig();
|
||||
|
||||
bool Failed() const { return mFailed; }
|
||||
[[nodiscard]] bool Failed() const { return mFailed; }
|
||||
|
||||
private:
|
||||
void CreateConfigFile(std::string_view name);
|
||||
void ParseFromFile(std::string_view name);
|
||||
void PrintDebug();
|
||||
|
||||
|
||||
void ParseOldFormat();
|
||||
|
||||
bool mFailed { false };
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "commandline/commandline.h"
|
||||
#include "TLuaFile.h"
|
||||
#include "Cryptography.h"
|
||||
#include <atomic>
|
||||
#include <fstream>
|
||||
|
||||
|
||||
@@ -27,12 +27,13 @@ public:
|
||||
|
||||
private:
|
||||
void FolderList(const std::string& Path, bool HotSwap);
|
||||
void RegisterFiles(const std::string& Path, bool HotSwap);
|
||||
bool NewFile(const std::string& Path);
|
||||
void RegisterFiles(const fs::path& Path, bool HotSwap);
|
||||
bool IsNewFile(const std::string& Path);
|
||||
|
||||
TNetwork& mNetwork;
|
||||
TServer& mServer;
|
||||
std::string mPath;
|
||||
bool mShutdown { false };
|
||||
TSetOfLuaFile mLuaFiles;
|
||||
std::mutex mListMutex;
|
||||
};
|
||||
|
||||
@@ -54,6 +54,7 @@ private:
|
||||
bool mStopThread = false;
|
||||
bool mConsole = false;
|
||||
void Load();
|
||||
std::mutex mInitMutex;
|
||||
};
|
||||
|
||||
std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaFile* Caller, std::shared_ptr<TLuaArg> arg, bool Wait);
|
||||
|
||||
38
include/TSentry.h
Normal file
38
include/TSentry.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef SENTRY_H
|
||||
#define SENTRY_H
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
enum class SentryLevel {
|
||||
Debug = -1,
|
||||
Info = 0,
|
||||
Warning = 1,
|
||||
Error = 2,
|
||||
Fatal = 3,
|
||||
};
|
||||
|
||||
// singleton, dont make this twice
|
||||
class TSentry final {
|
||||
public:
|
||||
TSentry();
|
||||
~TSentry();
|
||||
|
||||
void PrintWelcome();
|
||||
void SetupUser();
|
||||
void Log(SentryLevel level, const std::string& logger, const std::string& text);
|
||||
void LogError(const std::string& text, const std::string& file, const std::string& line);
|
||||
void SetContext(const std::string& context_name, const std::unordered_map<std::string, std::string>& map);
|
||||
void LogException(const std::exception& e, const std::string& file, const std::string& line);
|
||||
void LogAssert(const std::string& condition_string, const std::string& file, const std::string& line, const std::string& function);
|
||||
void AddErrorBreadcrumb(const std::string& msg, const std::string& file, const std::string& line);
|
||||
// cleared when Logged
|
||||
void SetTransaction(const std::string& id);
|
||||
[[nodiscard]] std::unique_lock<std::mutex> CreateExclusiveContext();
|
||||
|
||||
private:
|
||||
bool mValid { true };
|
||||
std::mutex mMutex;
|
||||
};
|
||||
|
||||
#endif // SENTRY_H
|
||||
Submodule include/commandline updated: c34703df11...242225dd41
1
include/sentry-native
Submodule
1
include/sentry-native
Submodule
Submodule include/sentry-native added at 521860373d
@@ -92,7 +92,6 @@ TClient::TClient(TServer& Server)
|
||||
|
||||
void TClient::UpdatePingTime() {
|
||||
mLastPingTime = std::chrono::high_resolution_clock::now();
|
||||
//debug(GetName() + ": " + std::string("ping time updated!: ") + ((SecondsSinceLastPing() == 0) ? "OK" : "ERR"));
|
||||
}
|
||||
int TClient::SecondsSinceLastPing() {
|
||||
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
|
||||
@@ -2,11 +2,17 @@
|
||||
|
||||
#include "TConsole.h"
|
||||
#include <array>
|
||||
#include <charconv>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "CustomAssert.h"
|
||||
#include "Http.h"
|
||||
|
||||
std::unique_ptr<TConsole> Application::mConsole = std::make_unique<TConsole>();
|
||||
|
||||
void Application::RegisterShutdownHandler(const TShutdownHandler& Handler) {
|
||||
@@ -17,10 +23,74 @@ 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]();
|
||||
}
|
||||
}
|
||||
|
||||
std::array<int, 3> Application::VersionStrToInts(const std::string& str) {
|
||||
std::array<int, 3> Version;
|
||||
std::stringstream ss(str);
|
||||
for (int& i : Version) {
|
||||
std::string Part;
|
||||
std::getline(ss, Part, '.');
|
||||
std::from_chars(&*Part.begin(), &*Part.begin() + Part.size(), i);
|
||||
}
|
||||
return Version;
|
||||
}
|
||||
|
||||
bool Application::IsOutdated(const std::array<int, 3>& Current, const std::array<int, 3>& Newest) {
|
||||
if (Newest[0] > Current[0]) {
|
||||
return true;
|
||||
} else if (Newest[0] == Current[0] && Newest[1] > Current[1]) {
|
||||
return true;
|
||||
} else if (Newest[0] == Current[0] && Newest[1] == Current[1] && Newest[2] > Current[2]) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Application::CheckForUpdates() {
|
||||
// checks current version against latest version
|
||||
std::regex VersionRegex { R"(\d+\.\d+\.\d+\n*)" };
|
||||
auto Response = Http::GET(GetBackendHostname(), 443, "/v/s");
|
||||
bool Matches = std::regex_match(Response, VersionRegex);
|
||||
if (Matches) {
|
||||
auto MyVersion = VersionStrToInts(ServerVersion());
|
||||
auto RemoteVersion = VersionStrToInts(Response);
|
||||
if (IsOutdated(MyVersion, RemoteVersion)) {
|
||||
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 more info visit https://wiki.beammp.com/en/home/server-maintenance#updating-the-server." + std::string(ANSI_RESET));
|
||||
} else {
|
||||
info("Server up-to-date!");
|
||||
}
|
||||
} else {
|
||||
warn("Unable to fetch version from backend.");
|
||||
trace("got " + Response);
|
||||
auto Lock = Sentry.CreateExclusiveContext();
|
||||
Sentry.SetContext("get-response", { { "response", Response } });
|
||||
Sentry.LogError("failed to get server version", _file_basename, _line);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,10 +140,12 @@ std::string DeComp(std::string Compressed) {
|
||||
|
||||
// thread name stuff
|
||||
|
||||
std::map<std::thread::id, std::string> threadNameMap;
|
||||
static std::map<std::thread::id, std::string> threadNameMap {};
|
||||
static std::mutex ThreadNameMapMutex {};
|
||||
|
||||
std::string ThreadName() {
|
||||
if (Application::Settings.DebugModeEnabled) {
|
||||
std::string ThreadName(bool DebugModeOverride) {
|
||||
auto Lock = std::unique_lock(ThreadNameMapMutex);
|
||||
if (DebugModeOverride || Application::Settings.DebugModeEnabled) {
|
||||
auto id = std::this_thread::get_id();
|
||||
if (threadNameMap.find(id) != threadNameMap.end()) {
|
||||
// found
|
||||
@@ -83,6 +155,19 @@ std::string ThreadName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
void RegisterThread(const std::string str) {
|
||||
void RegisterThread(const std::string& str) {
|
||||
auto Lock = std::unique_lock(ThreadNameMapMutex);
|
||||
threadNameMap[std::this_thread::get_id()] = str;
|
||||
}
|
||||
|
||||
void LogChatMessage(const std::string& name, int id, const std::string& msg) {
|
||||
std::stringstream ss;
|
||||
ss << "[CHAT] ";
|
||||
if (id != -1) {
|
||||
ss << "(" << id << ") <" << name << ">";
|
||||
} else {
|
||||
ss << name << "";
|
||||
}
|
||||
ss << msg;
|
||||
Application::Console().Write(ss.str());
|
||||
}
|
||||
|
||||
207
src/Http.cpp
207
src/Http.cpp
@@ -7,6 +7,7 @@
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/beast.hpp>
|
||||
#include <boost/beast/ssl.hpp>
|
||||
#include <map>
|
||||
|
||||
namespace beast = boost::beast; // from <boost/beast.hpp>
|
||||
namespace http = beast::http; // from <boost/beast/http.hpp>
|
||||
@@ -14,47 +15,85 @@ namespace net = boost::asio; // from <boost/asio.hpp>
|
||||
namespace ssl = net::ssl; // from <boost/asio/ssl.hpp>
|
||||
using tcp = net::ip::tcp; // from <boost/asio/ip/tcp.hpp>
|
||||
|
||||
std::string Http::GET(const std::string& host, int port, const std::string& target) {
|
||||
// FIXME: doesn't support https
|
||||
// if it causes issues, yell at me and I'll fix it asap. - Lion
|
||||
std::string Http::GET(const std::string& host, int port, const std::string& target, unsigned int* status) {
|
||||
try {
|
||||
net::io_context io;
|
||||
tcp::resolver resolver(io);
|
||||
beast::tcp_stream stream(io);
|
||||
auto const results = resolver.resolve(host, std::to_string(port));
|
||||
stream.connect(results);
|
||||
// Check command line arguments.
|
||||
int version = 11;
|
||||
|
||||
http::request<http::string_body> req { http::verb::get, target, 11 /* http 1.1 */ };
|
||||
// The io_context is required for all I/O
|
||||
net::io_context ioc;
|
||||
|
||||
req.set(http::field::host, host);
|
||||
// tell the server what we are (boost beast)
|
||||
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
|
||||
// The SSL context is required, and holds certificates
|
||||
ssl::context ctx(ssl::context::tlsv12_client);
|
||||
|
||||
http::write(stream, req);
|
||||
// This holds the root certificate used for verification
|
||||
// we don't do / have this
|
||||
// load_root_certificates(ctx);
|
||||
|
||||
// used for reading
|
||||
beast::flat_buffer buffer;
|
||||
http::response<http::string_body> response;
|
||||
// Verify the remote server's certificate
|
||||
ctx.set_verify_mode(ssl::verify_none);
|
||||
|
||||
http::read(stream, buffer, response);
|
||||
// These objects perform our I/O
|
||||
tcp::resolver resolver(ioc);
|
||||
beast::ssl_stream<beast::tcp_stream> stream(ioc, ctx);
|
||||
|
||||
std::string result(response.body());
|
||||
|
||||
beast::error_code ec;
|
||||
stream.socket().shutdown(tcp::socket::shutdown_both, ec);
|
||||
if (ec && ec != beast::errc::not_connected) {
|
||||
throw beast::system_error { ec }; // goes down to `return "-1"` anyways
|
||||
// Set SNI Hostname (many hosts need this to handshake successfully)
|
||||
if (!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str())) {
|
||||
beast::error_code ec { static_cast<int>(::ERR_get_error()), net::error::get_ssl_category() };
|
||||
throw beast::system_error { ec };
|
||||
}
|
||||
|
||||
return result;
|
||||
// Look up the domain name
|
||||
auto const results = resolver.resolve(host.c_str(), std::to_string(port));
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
Application::Console().Write(e.what());
|
||||
// Make the connection on the IP address we get from a lookup
|
||||
beast::get_lowest_layer(stream).connect(results);
|
||||
|
||||
// Perform the SSL handshake
|
||||
stream.handshake(ssl::stream_base::client);
|
||||
|
||||
// Set up an HTTP GET request message
|
||||
http::request<http::string_body> req { http::verb::get, target, version };
|
||||
req.set(http::field::host, host);
|
||||
|
||||
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
|
||||
|
||||
// Send the HTTP request to the remote host
|
||||
http::write(stream, req);
|
||||
|
||||
// This buffer is used for reading and must be persisted
|
||||
beast::flat_buffer buffer;
|
||||
|
||||
// Declare a container to hold the response
|
||||
http::response<http::string_body> res;
|
||||
|
||||
// Receive the HTTP response
|
||||
http::read(stream, buffer, res);
|
||||
|
||||
// Gracefully close the stream
|
||||
beast::error_code ec;
|
||||
stream.shutdown(ec);
|
||||
if (ec == net::error::eof) {
|
||||
// Rationale:
|
||||
// http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
|
||||
ec = {};
|
||||
}
|
||||
|
||||
if (status) {
|
||||
*status = res.base().result_int();
|
||||
}
|
||||
|
||||
// ignore ec
|
||||
|
||||
// If we get here then the connection is closed gracefully
|
||||
return std::string(res.body());
|
||||
} catch (std::exception const& e) {
|
||||
Application::Console().Write(__func__ + std::string(": ") + e.what());
|
||||
return "-1";
|
||||
}
|
||||
}
|
||||
|
||||
std::string Http::POST(const std::string& host, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, bool json) {
|
||||
std::string Http::POST(const std::string& host, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, bool json, int* status) {
|
||||
try {
|
||||
net::io_context io;
|
||||
|
||||
@@ -87,7 +126,8 @@ std::string Http::POST(const std::string& host, const std::string& target, const
|
||||
//debug("IPv6 connect failed, trying IPv4");
|
||||
bool ok = try_connect_with_protocol(tcp::v4());
|
||||
if (!ok) {
|
||||
//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";
|
||||
}
|
||||
//}
|
||||
@@ -97,7 +137,6 @@ std::string Http::POST(const std::string& host, const std::string& target, const
|
||||
req.set(http::field::host, host);
|
||||
if (!body.empty()) {
|
||||
if (json) {
|
||||
// FIXME: json is untested.
|
||||
req.set(http::field::content_type, "application/json");
|
||||
} else {
|
||||
req.set(http::field::content_type, "application/x-www-form-urlencoded");
|
||||
@@ -112,6 +151,16 @@ std::string Http::POST(const std::string& host, const std::string& target, const
|
||||
req.set(pair.first, pair.second);
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::string> request_data;
|
||||
for (const auto& header : req.base()) {
|
||||
// need to do explicit casts to convert string_view to string
|
||||
// since string_view may not be null-terminated (and in fact isn't, here)
|
||||
std::string KeyString(header.name_string());
|
||||
std::string ValueString(header.value());
|
||||
request_data[KeyString] = ValueString;
|
||||
}
|
||||
Sentry.SetContext("https-post-request-data", request_data);
|
||||
|
||||
std::stringstream oss;
|
||||
oss << req;
|
||||
|
||||
@@ -125,6 +174,20 @@ std::string Http::POST(const std::string& host, const std::string& target, const
|
||||
|
||||
http::read(stream, buffer, response);
|
||||
|
||||
std::unordered_map<std::string, std::string> response_data;
|
||||
response_data["reponse-code"] = std::to_string(response.result_int());
|
||||
if (status) {
|
||||
*status = response.result_int();
|
||||
}
|
||||
for (const auto& header : response.base()) {
|
||||
// need to do explicit casts to convert string_view to string
|
||||
// since string_view may not be null-terminated (and in fact isn't, here)
|
||||
std::string KeyString(header.name_string());
|
||||
std::string ValueString(header.value());
|
||||
response_data[KeyString] = ValueString;
|
||||
}
|
||||
Sentry.SetContext("https-post-response-data", response_data);
|
||||
|
||||
std::stringstream result;
|
||||
result << response;
|
||||
|
||||
@@ -140,6 +203,92 @@ 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" },
|
||||
{ 103, "Early Hints" },
|
||||
{ 200, "OK" },
|
||||
{ 201, "Created" },
|
||||
{ 202, "Accepted" },
|
||||
{ 203, "Non-Authoritative Information" },
|
||||
{ 204, "No Content" },
|
||||
{ 205, "Reset Content" },
|
||||
{ 206, "Partial Content" },
|
||||
{ 207, "Multi-Status" },
|
||||
{ 208, "Already Reported" },
|
||||
{ 226, "IM Used" },
|
||||
{ 300, "Multiple Choices" },
|
||||
{ 301, "Moved Permanently" },
|
||||
{ 302, "Found" },
|
||||
{ 303, "See Other" },
|
||||
{ 304, "Not Modified" },
|
||||
{ 305, "Use Proxy" },
|
||||
{ 306, "(Unused)" },
|
||||
{ 307, "Temporary Redirect" },
|
||||
{ 308, "Permanent Redirect" },
|
||||
{ 400, "Bad Request" },
|
||||
{ 401, "Unauthorized" },
|
||||
{ 402, "Payment Required" },
|
||||
{ 403, "Forbidden" },
|
||||
{ 404, "Not Found" },
|
||||
{ 405, "Method Not Allowed" },
|
||||
{ 406, "Not Acceptable" },
|
||||
{ 407, "Proxy Authentication Required" },
|
||||
{ 408, "Request Timeout" },
|
||||
{ 409, "Conflict" },
|
||||
{ 410, "Gone" },
|
||||
{ 411, "Length Required" },
|
||||
{ 412, "Precondition Failed" },
|
||||
{ 413, "Payload Too Large" },
|
||||
{ 414, "URI Too Long" },
|
||||
{ 415, "Unsupported Media Type" },
|
||||
{ 416, "Range Not Satisfiable" },
|
||||
{ 417, "Expectation Failed" },
|
||||
{ 421, "Misdirected Request" },
|
||||
{ 422, "Unprocessable Entity" },
|
||||
{ 423, "Locked" },
|
||||
{ 424, "Failed Dependency" },
|
||||
{ 425, "Too Early" },
|
||||
{ 426, "Upgrade Required" },
|
||||
{ 428, "Precondition Required" },
|
||||
{ 429, "Too Many Requests" },
|
||||
{ 431, "Request Header Fields Too Large" },
|
||||
{ 451, "Unavailable For Legal Reasons" },
|
||||
{ 500, "Internal Server Error" },
|
||||
{ 501, "Not Implemented" },
|
||||
{ 502, "Bad Gateway" },
|
||||
{ 503, "Service Unavailable" },
|
||||
{ 504, "Gateway Timeout" },
|
||||
{ 505, "HTTP Version Not Supported" },
|
||||
{ 506, "Variant Also Negotiates" },
|
||||
{ 507, "Insufficient Storage" },
|
||||
{ 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 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;
|
||||
|
||||
@@ -3,8 +3,12 @@
|
||||
#include "Client.h"
|
||||
#include "Http.h"
|
||||
//#include "SocketIO.h"
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/rapidjson.h>
|
||||
#include <sstream>
|
||||
|
||||
namespace json = rapidjson;
|
||||
|
||||
void THeartbeatThread::operator()() {
|
||||
RegisterThread("Heartbeat");
|
||||
std::string Body;
|
||||
@@ -35,33 +39,92 @@ void THeartbeatThread::operator()() {
|
||||
|
||||
Body += "&pps=" + Application::PPS();
|
||||
|
||||
T = Http::POST(Application::GetBackendHostname(), "/heartbeat", {}, Body, false);
|
||||
auto SentryReportError = [&](const std::string& transaction, int status) {
|
||||
auto Lock = Sentry.CreateExclusiveContext();
|
||||
Sentry.SetContext("heartbeat",
|
||||
{ { "response-body", T },
|
||||
{ "request-body", Body } });
|
||||
Sentry.SetTransaction(transaction);
|
||||
Sentry.Log(SentryLevel::Error, "default", Http::Status::ToString(status) + " (" + std::to_string(status) + ")");
|
||||
};
|
||||
|
||||
if (T.substr(0, 2) != "20") {
|
||||
//Backend system refused server startup!
|
||||
auto Target = "/heartbeat";
|
||||
int ResponseCode = -1;
|
||||
const std::vector<std::string> Urls = {
|
||||
Application::GetBackendHostname(),
|
||||
Application::GetBackup1Hostname(),
|
||||
Application::GetBackup2Hostname(),
|
||||
};
|
||||
|
||||
json::Document Doc;
|
||||
bool Ok = false;
|
||||
for (const auto& Url : Urls) {
|
||||
T = Http::POST(Url, Target, { { "api-v", "2" } }, Body, false, &ResponseCode);
|
||||
trace(T);
|
||||
Doc.Parse(T.data(), T.size());
|
||||
if (Doc.HasParseError() || !Doc.IsObject()) {
|
||||
error("Backend response failed to parse as valid json");
|
||||
debug("Response was: `" + T + "`");
|
||||
Sentry.SetContext("JSON Response", { { "reponse", T } });
|
||||
SentryReportError(Url + Target, ResponseCode);
|
||||
} else if (ResponseCode != 200) {
|
||||
SentryReportError(Url + Target, ResponseCode);
|
||||
} else {
|
||||
// all ok
|
||||
Ok = true;
|
||||
break;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
T = Http::POST(Application::GetBackendHostname(), "/heartbeat", {}, Body, false);
|
||||
// TODO backup2 + HTTP flag (no TSL)
|
||||
if (T.substr(0, 2) != "20") {
|
||||
warn("Backend system refused server! Server might not show in the public list");
|
||||
debug("server returned \"" + T + "\"");
|
||||
isAuth = false;
|
||||
}
|
||||
std::string Status {};
|
||||
std::string Code {};
|
||||
std::string Message {};
|
||||
const auto StatusKey = "status";
|
||||
const auto CodeKey = "code";
|
||||
const auto MessageKey = "msg";
|
||||
|
||||
if (Ok) {
|
||||
if (Doc.HasMember(StatusKey) && Doc[StatusKey].IsString()) {
|
||||
Status = Doc[StatusKey].GetString();
|
||||
} else {
|
||||
Sentry.SetContext("JSON Response", { { StatusKey, "invalid string / missing" } });
|
||||
Ok = false;
|
||||
}
|
||||
if (Doc.HasMember(CodeKey) && Doc[CodeKey].IsString()) {
|
||||
Code = Doc[CodeKey].GetString();
|
||||
} else {
|
||||
Sentry.SetContext("JSON Response", { { CodeKey, "invalid string / missing" } });
|
||||
Ok = false;
|
||||
}
|
||||
if (Doc.HasMember(MessageKey) && Doc[MessageKey].IsString()) {
|
||||
Message = Doc[MessageKey].GetString();
|
||||
} else {
|
||||
Sentry.SetContext("JSON Response", { { MessageKey, "invalid string / missing" } });
|
||||
Ok = false;
|
||||
}
|
||||
if (!Ok) {
|
||||
error("Missing/invalid json members in backend response");
|
||||
Sentry.LogError("Missing/invalid json members in backend response", __FILE__, std::to_string(__LINE__));
|
||||
}
|
||||
}
|
||||
|
||||
if (!isAuth) {
|
||||
if (T == "2000") {
|
||||
if (Ok && !isAuth) {
|
||||
if (Status == "2000") {
|
||||
info(("Authenticated!"));
|
||||
isAuth = true;
|
||||
} else if (T == "200") {
|
||||
} else if (Status == "200") {
|
||||
info(("Resumed authenticated session!"));
|
||||
isAuth = true;
|
||||
} else {
|
||||
if (Message.empty()) {
|
||||
Message = "Backend didn't provide a reason";
|
||||
}
|
||||
error("Backend REFUSED the auth key. " + Message);
|
||||
}
|
||||
}
|
||||
|
||||
//SocketIO::Get().SetAuthenticated(isAuth);
|
||||
}
|
||||
}
|
||||
|
||||
std::string THeartbeatThread::GenerateCall() {
|
||||
std::stringstream Ret;
|
||||
|
||||
@@ -86,10 +149,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();
|
||||
}
|
||||
@@ -66,37 +64,47 @@ std::optional<std::reference_wrapper<TLuaFile>> TLuaEngine::GetScript(lua_State*
|
||||
}
|
||||
|
||||
void TLuaEngine::FolderList(const std::string& Path, bool HotSwap) {
|
||||
auto Lock = std::unique_lock(mListMutex);
|
||||
for (const auto& entry : fs::directory_iterator(Path)) {
|
||||
auto pos = entry.path().filename().string().find('.');
|
||||
if (pos == std::string::npos) {
|
||||
RegisterFiles(entry.path().string(), HotSwap);
|
||||
if (fs::is_directory(entry)) {
|
||||
RegisterFiles(entry.path(), HotSwap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TLuaEngine::RegisterFiles(const std::string& Path, bool HotSwap) {
|
||||
std::string Name = Path.substr(Path.find_last_of('\\') + 1);
|
||||
void TLuaEngine::RegisterFiles(const fs::path& Path, bool HotSwap) {
|
||||
std::string Name = Path.filename().string();
|
||||
if (!HotSwap)
|
||||
info(("Loading plugin : ") + Name);
|
||||
std::vector<fs::path> Entries;
|
||||
for (const auto& entry : fs::directory_iterator(Path)) {
|
||||
auto pos = entry.path().string().find((".lua"));
|
||||
if (pos != std::string::npos && entry.path().string().length() - pos == 4) {
|
||||
if (!HotSwap || NewFile(entry.path().string())) {
|
||||
auto FileName = entry.path().string();
|
||||
std::unique_ptr<TLuaFile> ScriptToInsert(new TLuaFile(*this));
|
||||
auto& Script = *ScriptToInsert;
|
||||
mLuaFiles.insert(std::move(ScriptToInsert));
|
||||
Script.Init(Name, FileName, fs::last_write_time(FileName));
|
||||
if (HotSwap)
|
||||
info(("[HOTSWAP] Added : ") + Script.GetFileName().substr(Script.GetFileName().find('\\')));
|
||||
}
|
||||
if (entry.path().extension() == ".lua") {
|
||||
Entries.push_back(entry);
|
||||
}
|
||||
}
|
||||
std::sort(Entries.begin(), Entries.end(), [](const fs::path& first, const fs::path& second) {
|
||||
auto firstStr = first.string();
|
||||
auto secondStr = second.string();
|
||||
std::transform(firstStr.begin(), firstStr.end(), firstStr.begin(), ::tolower);
|
||||
std::transform(secondStr.begin(), secondStr.end(), secondStr.begin(), ::tolower);
|
||||
return firstStr < secondStr;
|
||||
});
|
||||
for (const fs::path& Entry : Entries) {
|
||||
if (!HotSwap || IsNewFile(Entry.string())) {
|
||||
auto FileName = Entry.string();
|
||||
std::unique_ptr<TLuaFile> ScriptToInsert(new TLuaFile(*this));
|
||||
auto& Script = *ScriptToInsert;
|
||||
mLuaFiles.insert(std::move(ScriptToInsert));
|
||||
Script.Init(Name, FileName, fs::last_write_time(FileName));
|
||||
if (HotSwap)
|
||||
info(("[HOTSWAP] Added : ") + Script.GetFileName().substr(Script.GetFileName().find('\\')));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TLuaEngine::NewFile(const std::string& Path) {
|
||||
bool TLuaEngine::IsNewFile(const std::string& Path) {
|
||||
for (auto& Script : mLuaFiles) {
|
||||
if (Path == Script->GetFileName())
|
||||
if (fs::absolute(Path) == fs::absolute(Script->GetFileName()))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "Client.h"
|
||||
#include "Common.h"
|
||||
#include "CustomAssert.h"
|
||||
#include "Defer.h"
|
||||
#include "TLuaEngine.h"
|
||||
#include "TNetwork.h"
|
||||
#include "TServer.h"
|
||||
@@ -44,7 +45,6 @@ void ClearStack(lua_State* L) {
|
||||
}
|
||||
|
||||
std::any Trigger(TLuaFile* lua, const std::string& R, std::shared_ptr<TLuaArg> arg) {
|
||||
RegisterThread(lua->GetFileName());
|
||||
std::lock_guard<std::mutex> lockGuard(lua->Lock);
|
||||
std::packaged_task<std::any(std::shared_ptr<TLuaArg>)> task([lua, R](std::shared_ptr<TLuaArg> arg) { return CallFunction(lua, R, arg); });
|
||||
std::future<std::any> f1 = task.get_future();
|
||||
@@ -110,7 +110,13 @@ bool ConsoleCheck(lua_State* L, int r) {
|
||||
|
||||
bool CheckLua(lua_State* L, int r) {
|
||||
if (r != LUA_OK) {
|
||||
std::string msg = lua_tostring(L, -1);
|
||||
std::string msg = "Unknown";
|
||||
if (lua_isstring(L, -1)) {
|
||||
auto MsgMaybe = lua_tostring(L, -1);
|
||||
if (MsgMaybe) {
|
||||
msg = MsgMaybe;
|
||||
}
|
||||
}
|
||||
auto MaybeS = Engine().GetScript(L);
|
||||
if (MaybeS.has_value()) {
|
||||
TLuaFile& S = MaybeS.value();
|
||||
@@ -118,7 +124,7 @@ bool CheckLua(lua_State* L, int r) {
|
||||
warn(a + " | " + msg);
|
||||
return false;
|
||||
}
|
||||
// What the fuck, what do we do?!
|
||||
// This should never happen since it's not directly called from "userspace" Lua.
|
||||
AssertNotReachable();
|
||||
}
|
||||
return true;
|
||||
@@ -127,7 +133,10 @@ bool CheckLua(lua_State* L, int r) {
|
||||
int lua_RegisterEvent(lua_State* L) {
|
||||
int Args = lua_gettop(L);
|
||||
auto MaybeScript = Engine().GetScript(L);
|
||||
Assert(MaybeScript.has_value());
|
||||
if (!MaybeScript.has_value()) {
|
||||
error("RegisterEvent: There is no script associated with this lua_State.");
|
||||
return 0;
|
||||
}
|
||||
TLuaFile& Script = MaybeScript.value();
|
||||
if (Args == 2 && lua_isstring(L, 1) && lua_isstring(L, 2)) {
|
||||
Script.RegisterEvent(lua_tostring(L, 1), lua_tostring(L, 2));
|
||||
@@ -139,7 +148,10 @@ int lua_RegisterEvent(lua_State* L) {
|
||||
int lua_TriggerEventL(lua_State* L) {
|
||||
int Args = lua_gettop(L);
|
||||
auto MaybeScript = Engine().GetScript(L);
|
||||
Assert(MaybeScript.has_value());
|
||||
if (!MaybeScript.has_value()) {
|
||||
error("TriggerEvent: There is no script associated with this lua_State.");
|
||||
return 0;
|
||||
}
|
||||
TLuaFile& Script = MaybeScript.value();
|
||||
if (Args > 0) {
|
||||
if (lua_isstring(L, 1)) {
|
||||
@@ -155,7 +167,10 @@ int lua_TriggerEventL(lua_State* L) {
|
||||
int lua_TriggerEventG(lua_State* L) {
|
||||
int Args = lua_gettop(L);
|
||||
auto MaybeScript = Engine().GetScript(L);
|
||||
Assert(MaybeScript.has_value());
|
||||
if (!MaybeScript.has_value()) {
|
||||
error("TriggerGlobalEvent: There is no script associated with this lua_State.");
|
||||
return 0;
|
||||
}
|
||||
TLuaFile& Script = MaybeScript.value();
|
||||
if (Args > 0) {
|
||||
if (lua_isstring(L, 1)) {
|
||||
@@ -183,7 +198,6 @@ void ExecuteAsync(TLuaFile* lua, const std::string& FuncName) {
|
||||
}
|
||||
|
||||
void CallAsync(TLuaFile* lua, const std::string& Func, int U) {
|
||||
RegisterThread(lua->GetFileName());
|
||||
lua->SetStopThread(false);
|
||||
int D = 1000 / U;
|
||||
while (!lua->GetStopThread()) {
|
||||
@@ -194,8 +208,11 @@ void CallAsync(TLuaFile* lua, const std::string& Func, int U) {
|
||||
|
||||
int lua_StopThread(lua_State* L) {
|
||||
auto MaybeScript = Engine().GetScript(L);
|
||||
Assert(MaybeScript.has_value());
|
||||
// ugly, but whatever, this is safe as fuck
|
||||
if (!MaybeScript.has_value()) {
|
||||
error("StopThread: There is no script associated with this lua_State.");
|
||||
return 0;
|
||||
}
|
||||
// ugly, but whatever, this is very safe
|
||||
MaybeScript.value().get().SetStopThread(true);
|
||||
return 0;
|
||||
}
|
||||
@@ -209,7 +226,10 @@ int lua_CreateThread(lua_State* L) {
|
||||
int U = int(lua_tointeger(L, 2));
|
||||
if (U > 0 && U < 501) {
|
||||
auto MaybeScript = Engine().GetScript(L);
|
||||
Assert(MaybeScript.has_value());
|
||||
if (!MaybeScript.has_value()) {
|
||||
error("CreateThread: There is no script associated with this lua_State.");
|
||||
return 0;
|
||||
}
|
||||
TLuaFile& Script = MaybeScript.value();
|
||||
std::thread t1(CallAsync, &Script, STR, U);
|
||||
t1.detach();
|
||||
@@ -397,7 +417,9 @@ int lua_sendChat(lua_State* L) {
|
||||
if (lua_isstring(L, 2)) {
|
||||
int ID = int(lua_tointeger(L, 1));
|
||||
if (ID == -1) {
|
||||
std::string Packet = "C:Server: " + std::string(lua_tostring(L, 2));
|
||||
auto msg = std::string(lua_tostring(L, 2));
|
||||
LogChatMessage("<Server> (to everyone) ", -1, msg);
|
||||
std::string Packet = "C:Server: " + msg;
|
||||
Engine().Network().SendToAll(nullptr, Packet, true, true);
|
||||
} else {
|
||||
auto MaybeClient = GetClient(Engine().Server(), ID);
|
||||
@@ -405,7 +427,9 @@ int lua_sendChat(lua_State* L) {
|
||||
auto c = MaybeClient.value().lock();
|
||||
if (!c->IsSynced())
|
||||
return 0;
|
||||
std::string Packet = "C:Server: " + std::string(lua_tostring(L, 2));
|
||||
auto msg = std::string(lua_tostring(L, 2));
|
||||
LogChatMessage("<Server> (to \"" + c->GetName() + "\")", -1, msg);
|
||||
std::string Packet = "C:Server: " + msg;
|
||||
Engine().Network().Respond(*c, Packet, true);
|
||||
} else
|
||||
SendError(Engine(), L, ("SendChatMessage invalid argument [1] invalid ID"));
|
||||
@@ -604,6 +628,7 @@ int lua_Print(lua_State* L) {
|
||||
int lua_TempFix(lua_State* L);
|
||||
|
||||
void TLuaFile::Init(const std::string& PluginName, const std::string& FileName, fs::file_time_type LastWrote) {
|
||||
auto Lock = std::unique_lock(mInitMutex);
|
||||
// set global engine for lua_* functions
|
||||
if (!TheEngine) {
|
||||
TheEngine = &mEngine;
|
||||
@@ -645,7 +670,6 @@ std::string TLuaFile::GetOrigin() {
|
||||
}
|
||||
|
||||
std::any CallFunction(TLuaFile* lua, const std::string& FuncName, std::shared_ptr<TLuaArg> Arg) {
|
||||
RegisterThread(lua->GetFileName());
|
||||
lua_State* luaState = lua->GetState();
|
||||
lua_getglobal(luaState, FuncName.c_str());
|
||||
if (lua_isfunction(luaState, -1)) {
|
||||
|
||||
@@ -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);
|
||||
@@ -280,8 +276,12 @@ void TNetwork::Authentication(SOCKET TCPSock) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto RequestString = R"({"key":")" + Rc + "\"}";
|
||||
|
||||
auto Target = "/pkToUser";
|
||||
int ResponseCode = -1;
|
||||
if (!Rc.empty()) {
|
||||
Rc = Http::POST(Application::GetBackendUrlForAuth(), "/pkToUser", {}, R"({"key":")" + Rc + "\"}", true);
|
||||
Rc = Http::POST(Application::GetBackendUrlForAuth(), Target, {}, RequestString, true, &ResponseCode);
|
||||
}
|
||||
|
||||
json::Document AuthResponse;
|
||||
@@ -292,8 +292,23 @@ void TNetwork::Authentication(SOCKET TCPSock) {
|
||||
}
|
||||
|
||||
if (!AuthResponse.IsObject()) {
|
||||
ClientKick(*Client, "Backend returned invalid auth response format.");
|
||||
error("Backend returned invalid auth response format. This should never happen.");
|
||||
if (Rc == "0") {
|
||||
auto Lock = Sentry.CreateExclusiveContext();
|
||||
Sentry.SetContext("auth",
|
||||
{ { "response-body", Rc },
|
||||
{ "key", RequestString } });
|
||||
Sentry.SetTransaction(Application::GetBackendUrlForAuth() + Target);
|
||||
Sentry.Log(SentryLevel::Info, "default", "backend returned 0 instead of json (" + std::to_string(ResponseCode) + ")");
|
||||
} else { // Rc != "0"
|
||||
ClientKick(*Client, "Backend returned invalid auth response format.");
|
||||
error("Backend returned invalid auth response format. This should never happen.");
|
||||
auto Lock = Sentry.CreateExclusiveContext();
|
||||
Sentry.SetContext("auth",
|
||||
{ { "response-body", Rc },
|
||||
{ "key", RequestString } });
|
||||
Sentry.SetTransaction(Application::GetBackendUrlForAuth() + Target);
|
||||
Sentry.Log(SentryLevel::Error, "default", "unexpected backend response (" + std::to_string(ResponseCode) + ")");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -312,7 +327,6 @@ void TNetwork::Authentication(SOCKET TCPSock) {
|
||||
}
|
||||
|
||||
debug("Name -> " + Client->GetName() + ", Guest -> " + std::to_string(Client->IsGuest()) + ", Roles -> " + Client->GetRoles());
|
||||
debug("There are " + std::to_string(mServer.ClientCount()) + " known clients");
|
||||
mServer.ForEachClient([&](const std::weak_ptr<TClient>& ClientPtr) -> bool {
|
||||
std::shared_ptr<TClient> Cl;
|
||||
{
|
||||
@@ -322,10 +336,7 @@ void TNetwork::Authentication(SOCKET TCPSock) {
|
||||
} else
|
||||
return true;
|
||||
}
|
||||
info("Client Iteration: Name -> " + Cl->GetName() + ", Guest -> " + std::to_string(Cl->IsGuest()) + ", Roles -> " + Cl->GetRoles());
|
||||
if (Cl->GetName() == Client->GetName() && Cl->IsGuest() == Client->IsGuest()) {
|
||||
info("New client matched with current iteration");
|
||||
info("Old client (" + Cl->GetName() + ") kicked: Reconnecting");
|
||||
CloseSocketProper(Cl->GetTCPSock());
|
||||
Cl->SetStatus(-2);
|
||||
return false;
|
||||
@@ -403,7 +414,7 @@ bool TNetwork::TCPSend(TClient& c, const std::string& Data, bool IsSync) {
|
||||
|
||||
bool TNetwork::CheckBytes(TClient& c, int32_t BytesRcv) {
|
||||
if (BytesRcv == 0) {
|
||||
debug("(TCP) Connection closing...");
|
||||
trace("(TCP) Connection closing...");
|
||||
if (c.GetStatus() > -1)
|
||||
c.SetStatus(-1);
|
||||
return false;
|
||||
@@ -415,7 +426,6 @@ bool TNetwork::CheckBytes(TClient& c, int32_t BytesRcv) {
|
||||
#endif // WIN32
|
||||
if (c.GetStatus() > -1)
|
||||
c.SetStatus(-1);
|
||||
info(("Closing socket in CheckBytes, BytesRcv < 0"));
|
||||
CloseSocketProper(c.GetTCPSock());
|
||||
return false;
|
||||
}
|
||||
@@ -431,22 +441,13 @@ std::string TNetwork::TCPRcv(TClient& c) {
|
||||
do {
|
||||
Temp = recv(c.GetTCPSock(), &Data[BytesRcv], 4 - BytesRcv, 0);
|
||||
if (!CheckBytes(c, Temp)) {
|
||||
#ifdef DEBUG
|
||||
error(std::string(__func__) + (": failed on CheckBytes in while(BytesRcv < 4)"));
|
||||
#endif // DEBUG
|
||||
return "";
|
||||
}
|
||||
BytesRcv += Temp;
|
||||
} while (size_t(BytesRcv) < sizeof(Header));
|
||||
memcpy(&Header, &Data[0], sizeof(Header));
|
||||
|
||||
#ifdef DEBUG
|
||||
//debug(std::string(__func__) + (": expecting ") + std::to_string(Header) + (" bytes."));
|
||||
#endif // DEBUG
|
||||
if (!CheckBytes(c, BytesRcv)) {
|
||||
#ifdef DEBUG
|
||||
error(std::string(__func__) + (": failed on CheckBytes"));
|
||||
#endif // DEBUG
|
||||
return "";
|
||||
}
|
||||
if (Header < 100 * MB) {
|
||||
@@ -460,29 +461,15 @@ std::string TNetwork::TCPRcv(TClient& c) {
|
||||
do {
|
||||
Temp = recv(c.GetTCPSock(), &Data[BytesRcv], Header - BytesRcv, 0);
|
||||
if (!CheckBytes(c, Temp)) {
|
||||
#ifdef DEBUG
|
||||
error(std::string(__func__) + (": failed on CheckBytes in while(BytesRcv < Header)"));
|
||||
#endif // DEBUG
|
||||
|
||||
return "";
|
||||
}
|
||||
#ifdef DEBUG
|
||||
//debug(std::string(__func__) + (": Temp: ") + std::to_string(Temp) + (", BytesRcv: ") + std::to_string(BytesRcv));
|
||||
#endif // DEBUG
|
||||
BytesRcv += Temp;
|
||||
} while (BytesRcv < Header);
|
||||
#ifdef DEBUG
|
||||
//debug(std::string(__func__) + (": finished recv with Temp: ") + std::to_string(Temp) + (", BytesRcv: ") + std::to_string(BytesRcv));
|
||||
#endif // DEBUG
|
||||
std::string Ret(Data.data(), Header);
|
||||
|
||||
if (Ret.substr(0, 4) == "ABG:") {
|
||||
Ret = DeComp(Ret.substr(4));
|
||||
}
|
||||
#ifdef DEBUG
|
||||
//debug("Parsing from " + c->GetName() + " -> " +std::to_string(Ret.size()));
|
||||
#endif
|
||||
|
||||
return Ret;
|
||||
}
|
||||
|
||||
@@ -702,19 +689,29 @@ void TNetwork::Parse(TClient& c, const std::string& Packet) {
|
||||
}
|
||||
}
|
||||
|
||||
void TNetwork::SendFile(TClient& c, const std::string& Name) {
|
||||
info(c.GetName() + " requesting : " + Name.substr(Name.find_last_of('/')));
|
||||
void TNetwork::SendFile(TClient& c, const std::string& UnsafeName) {
|
||||
info(c.GetName() + " requesting : " + UnsafeName.substr(UnsafeName.find_last_of('/')));
|
||||
|
||||
if (!std::filesystem::exists(Name)) {
|
||||
if (!fs::path(UnsafeName).has_filename()) {
|
||||
if (!TCPSend(c, "CO")) {
|
||||
// TODO: handle
|
||||
}
|
||||
warn("File " + Name + " could not be accessed!");
|
||||
warn("File " + UnsafeName + " is not a file!");
|
||||
return;
|
||||
} else {
|
||||
if (!TCPSend(c, "AG")) {
|
||||
}
|
||||
auto FileName = fs::path(UnsafeName).filename().string();
|
||||
FileName = Application::Settings.Resource + "/Client/" + FileName;
|
||||
|
||||
if (!std::filesystem::exists(FileName)) {
|
||||
if (!TCPSend(c, "CO")) {
|
||||
// TODO: handle
|
||||
}
|
||||
warn("File " + UnsafeName + " could not be accessed!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TCPSend(c, "AG")) {
|
||||
// TODO: handle
|
||||
}
|
||||
|
||||
///Wait for connections
|
||||
@@ -731,14 +728,14 @@ void TNetwork::SendFile(TClient& c, const std::string& Name) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t Size = size_t(std::filesystem::file_size(Name)), MSize = Size / 2;
|
||||
size_t Size = size_t(std::filesystem::file_size(FileName)), MSize = Size / 2;
|
||||
|
||||
std::thread SplitThreads[2] {
|
||||
std::thread([&] {
|
||||
SplitLoad(c, 0, MSize, false, Name);
|
||||
SplitLoad(c, 0, MSize, false, FileName);
|
||||
}),
|
||||
std::thread([&] {
|
||||
SplitLoad(c, MSize, Size, true, Name);
|
||||
SplitLoad(c, MSize, Size, true, FileName);
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
@@ -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();
|
||||
@@ -44,7 +42,7 @@ void TPPSMonitor::operator()() {
|
||||
V += c->GetCarCount();
|
||||
}
|
||||
// kick on "no ping"
|
||||
if (c->SecondsSinceLastPing() > (5 * 60)) {
|
||||
if (c->SecondsSinceLastPing() > (20 * 60)) {
|
||||
debug("client " + std::string("(") + std::to_string(c->GetID()) + ")" + c->GetName() + " timing out: " + std::to_string(c->SecondsSinceLastPing()) + ", pps: " + Application::PPS());
|
||||
TimedOutClients.push_back(c);
|
||||
}
|
||||
@@ -52,7 +50,7 @@ void TPPSMonitor::operator()() {
|
||||
return true;
|
||||
});
|
||||
for (auto& ClientToKick : TimedOutClients) {
|
||||
Network().ClientKick(*ClientToKick, "Timeout (no ping for >5 min)");
|
||||
Network().ClientKick(*ClientToKick, "Timeout (no ping for way too long)");
|
||||
}
|
||||
TimedOutClients.clear();
|
||||
if (C == 0 || mInternalPPS == 0) {
|
||||
|
||||
@@ -19,7 +19,7 @@ TResourceManager::TResourceManager() {
|
||||
++i;
|
||||
File = File.substr(i,pos-i);
|
||||
}
|
||||
mTrimmedList += File + ';';
|
||||
mTrimmedList += "/" + fs::path(File).filename().string() + ';';
|
||||
mFileSizes += std::to_string(size_t(fs::file_size(entry.path()))) + ';';
|
||||
mMaxModSize += size_t(fs::file_size(entry.path()));
|
||||
mModsLoaded++;
|
||||
|
||||
138
src/TSentry.cpp
Normal file
138
src/TSentry.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
#include "TSentry.h"
|
||||
#include "Common.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <sentry.h>
|
||||
#include <sstream>
|
||||
|
||||
TSentry::TSentry() {
|
||||
if (std::strlen(S_DSN) == 0) {
|
||||
mValid = false;
|
||||
} else {
|
||||
mValid = true;
|
||||
sentry_options_t* options = sentry_options_new();
|
||||
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();
|
||||
sentry_options_set_release(options, ReleaseString.c_str());
|
||||
sentry_options_set_max_breadcrumbs(options, 10);
|
||||
sentry_init(options);
|
||||
}
|
||||
}
|
||||
|
||||
TSentry::~TSentry() {
|
||||
if (mValid) {
|
||||
sentry_close();
|
||||
}
|
||||
}
|
||||
|
||||
void TSentry::PrintWelcome() {
|
||||
if (mValid) {
|
||||
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 {
|
||||
if (Application::Settings.SendErrorsMessageEnabled) {
|
||||
info("Sentry disabled in unofficial build. Automatic error reporting disabled.");
|
||||
} else {
|
||||
info("Sentry disabled in unofficial build");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TSentry::SetupUser() {
|
||||
if (!mValid) {
|
||||
return;
|
||||
}
|
||||
sentry_value_t user = sentry_value_new_object();
|
||||
if (Application::Settings.Key.size() == 36) {
|
||||
sentry_value_set_by_key(user, "id", sentry_value_new_string(Application::Settings.Key.c_str()));
|
||||
} else {
|
||||
sentry_value_set_by_key(user, "id", sentry_value_new_string("unauthenticated"));
|
||||
}
|
||||
sentry_set_user(user);
|
||||
}
|
||||
|
||||
void TSentry::Log(SentryLevel level, const std::string& logger, const std::string& text) {
|
||||
if (!mValid) {
|
||||
return;
|
||||
}
|
||||
SetContext("threads", { { "thread-name", ThreadName(true) } });
|
||||
auto Msg = sentry_value_new_message_event(sentry_level_t(level), logger.c_str(), text.c_str());
|
||||
sentry_capture_event(Msg);
|
||||
sentry_remove_transaction();
|
||||
}
|
||||
|
||||
void TSentry::LogError(const std::string& text, const std::string& file, const std::string& line) {
|
||||
if (!mValid) {
|
||||
return;
|
||||
}
|
||||
SetTransaction(file + ":" + line);
|
||||
Log(SentryLevel::Error, "default", file + ": " + text);
|
||||
}
|
||||
|
||||
void TSentry::SetContext(const std::string& context_name, const std::unordered_map<std::string, std::string>& map) {
|
||||
if (!mValid) {
|
||||
return;
|
||||
}
|
||||
auto ctx = sentry_value_new_object();
|
||||
for (const auto& pair : map) {
|
||||
std::string key = pair.first;
|
||||
if (key == "type") {
|
||||
// `type` is reserved
|
||||
key = "_type";
|
||||
}
|
||||
sentry_value_set_by_key(ctx, key.c_str(), sentry_value_new_string(pair.second.c_str()));
|
||||
}
|
||||
sentry_set_context(context_name.c_str(), ctx);
|
||||
}
|
||||
|
||||
void TSentry::LogException(const std::exception& e, const std::string& file, const std::string& line) {
|
||||
if (!mValid) {
|
||||
return;
|
||||
}
|
||||
SetTransaction(file + ":" + line);
|
||||
Log(SentryLevel::Fatal, "exceptions", std::string(e.what()) + " @ " + file + ":" + line);
|
||||
}
|
||||
|
||||
void TSentry::LogAssert(const std::string& condition_string, const std::string& file, const std::string& line, const std::string& function) {
|
||||
if (!mValid) {
|
||||
return;
|
||||
}
|
||||
SetTransaction(file + ":" + line + ":" + function);
|
||||
std::stringstream ss;
|
||||
ss << "\"" << condition_string << "\" failed @ " << file << ":" << line;
|
||||
Log(SentryLevel::Fatal, "asserts", ss.str());
|
||||
}
|
||||
|
||||
void TSentry::AddErrorBreadcrumb(const std::string& msg, const std::string& file, const std::string& line) {
|
||||
if (!mValid) {
|
||||
return;
|
||||
}
|
||||
auto crumb = sentry_value_new_breadcrumb("default", (msg + " @ " + file + ":" + line).c_str());
|
||||
sentry_value_set_by_key(crumb, "level", sentry_value_new_string("error"));
|
||||
sentry_add_breadcrumb(crumb);
|
||||
}
|
||||
|
||||
void TSentry::SetTransaction(const std::string& id) {
|
||||
if (!mValid) {
|
||||
return;
|
||||
}
|
||||
sentry_set_transaction(id.c_str());
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> TSentry::CreateExclusiveContext() {
|
||||
return std::unique_lock<std::mutex>(mMutex);
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <any>
|
||||
#include <sstream>
|
||||
|
||||
#undef GetObject //Fixes Windows
|
||||
#undef GetObject // Fixes Windows
|
||||
|
||||
#include "Json.h"
|
||||
|
||||
@@ -65,7 +65,7 @@ size_t TServer::ClientCount() const {
|
||||
|
||||
void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::string Packet, TPPSMonitor& PPSMonitor, TNetwork& Network) {
|
||||
if (Packet.find("Zp") != std::string::npos && Packet.size() > 500) {
|
||||
abort();
|
||||
//abort();
|
||||
}
|
||||
if (Packet.substr(0, 4) == "ABG:") {
|
||||
Packet = DeComp(Packet.substr(4));
|
||||
@@ -90,9 +90,7 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::string Pac
|
||||
}
|
||||
switch (Code) {
|
||||
case 'H': // initial connection
|
||||
#ifdef DEBUG
|
||||
debug(std::string("got 'H' packet: '") + Packet + "' (" + std::to_string(Packet.size()) + ")");
|
||||
#endif
|
||||
trace(std::string("got 'H' packet: '") + Packet + "' (" + std::to_string(Packet.size()) + ")");
|
||||
if (!Network.SyncClient(Client)) {
|
||||
// TODO handle
|
||||
}
|
||||
@@ -114,31 +112,27 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::string Pac
|
||||
ParseVehicle(*LockedClient, Packet, Network);
|
||||
return;
|
||||
case 'J':
|
||||
#ifdef DEBUG
|
||||
debug(std::string(("got 'J' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
#endif
|
||||
trace(std::string(("got 'J' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
Network.SendToAll(LockedClient.get(), Packet, false, true);
|
||||
return;
|
||||
case 'C':
|
||||
#ifdef DEBUG
|
||||
debug(std::string(("got 'C' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
#endif
|
||||
trace(std::string(("got 'C' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
if (Packet.length() < 4 || Packet.find(':', 3) == std::string::npos)
|
||||
break;
|
||||
Res = TriggerLuaEvent("onChatMessage", false, nullptr, std::make_unique<TLuaArg>(TLuaArg { { LockedClient->GetID(), LockedClient->GetName(), Packet.substr(Packet.find(':', 3) + 1) } }), true);
|
||||
LogChatMessage(LockedClient->GetName(), LockedClient->GetID(), Packet.substr(Packet.find(':', 3) + 1)); // FIXME: this needs to be adjusted once lua is merged
|
||||
if (std::any_cast<int>(Res))
|
||||
break;
|
||||
Network.SendToAll(nullptr, Packet, true, true);
|
||||
return;
|
||||
case 'E':
|
||||
#ifdef DEBUG
|
||||
debug(std::string(("got 'E' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
#endif
|
||||
trace(std::string(("got 'E' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
HandleEvent(*LockedClient, Packet);
|
||||
return;
|
||||
case 'N':
|
||||
debug("got 'N' packet (" + std::to_string(Packet.size()) + ")");
|
||||
trace("got 'N' packet (" + std::to_string(Packet.size()) + ")");
|
||||
Network.SendToAll(LockedClient.get(), Packet, false, true);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
@@ -198,9 +192,7 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
|
||||
std::string Data = Packet.substr(3), pid, vid;
|
||||
switch (Code) { //Spawned Destroyed Switched/Moved NotFound Reset
|
||||
case 's':
|
||||
#ifdef DEBUG
|
||||
debug(std::string(("got 'Os' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
#endif
|
||||
trace(std::string(("got 'Os' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
if (Data.at(0) == '0') {
|
||||
int CarID = c.GetOpenCarID();
|
||||
debug(c.GetName() + (" created a car with ID ") + std::to_string(CarID));
|
||||
@@ -225,9 +217,7 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
|
||||
}
|
||||
return;
|
||||
case 'c':
|
||||
#ifdef DEBUG
|
||||
debug(std::string(("got 'Oc' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
#endif
|
||||
trace(std::string(("got 'Oc' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
pid = Data.substr(0, Data.find('-'));
|
||||
vid = Data.substr(Data.find('-') + 1, Data.find(':', 1) - Data.find('-') - 1);
|
||||
if (pid.find_first_not_of("0123456789") == std::string::npos && vid.find_first_not_of("0123456789") == std::string::npos) {
|
||||
@@ -258,9 +248,7 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
|
||||
}
|
||||
return;
|
||||
case 'd':
|
||||
#ifdef DEBUG
|
||||
debug(std::string(("got 'Od' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
#endif
|
||||
trace(std::string(("got 'Od' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
pid = Data.substr(0, Data.find('-'));
|
||||
vid = Data.substr(Data.find('-') + 1);
|
||||
if (pid.find_first_not_of("0123456789") == std::string::npos && vid.find_first_not_of("0123456789") == std::string::npos) {
|
||||
@@ -279,9 +267,7 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
|
||||
}
|
||||
return;
|
||||
case 'r':
|
||||
#ifdef DEBUG
|
||||
debug(std::string(("got 'Or' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
#endif
|
||||
trace(std::string(("got 'Or' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
Pos = int(Data.find('-'));
|
||||
pid = Data.substr(0, Pos++);
|
||||
vid = Data.substr(Pos, Data.find(':') - Pos);
|
||||
@@ -300,15 +286,11 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
|
||||
}
|
||||
return;
|
||||
case 't':
|
||||
#ifdef DEBUG
|
||||
debug(std::string(("got 'Ot' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
#endif
|
||||
trace(std::string(("got 'Ot' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
Network.SendToAll(&c, Packet, false, true);
|
||||
return;
|
||||
default:
|
||||
#ifdef DEBUG
|
||||
warn(std::string(("possibly not implemented: '") + Packet + ("' (") + std::to_string(Packet.size()) + (")")));
|
||||
#endif // DEBUG
|
||||
trace(std::string(("possibly not implemented: '") + Packet + ("' (") + std::to_string(Packet.size()) + (")")));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -323,12 +305,22 @@ void TServer::Apply(TClient& c, int VID, const std::string& pckt) {
|
||||
std::string VD = c.GetCarData(VID);
|
||||
if (VD.empty()) {
|
||||
error("Tried to apply change to vehicle that does not exist");
|
||||
auto Lock = Sentry.CreateExclusiveContext();
|
||||
Sentry.SetContext("vehicle-change",
|
||||
{ { "packet", Packet },
|
||||
{ "vehicle-id", std::to_string(VID) },
|
||||
{ "client-car-count", std::to_string(c.GetCarCount()) } });
|
||||
Sentry.LogError("attempt to apply change to nonexistent vehicle", _file_basename, _line);
|
||||
return;
|
||||
}
|
||||
std::string Header = VD.substr(0, VD.find('{'));
|
||||
|
||||
FoundPos = VD.find('{');
|
||||
if (FoundPos == std::string::npos) {
|
||||
auto Lock = Sentry.CreateExclusiveContext();
|
||||
Sentry.SetContext("vehicle-change-packet",
|
||||
{ { "packet", VD } });
|
||||
Sentry.LogError("malformed packet", _file_basename, _line);
|
||||
error("Malformed packet received, no '{' found");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
#include "VehicleData.h"
|
||||
|
||||
#include <utility>
|
||||
#include "Common.h"
|
||||
#include <utility>
|
||||
|
||||
TVehicleData::TVehicleData(int ID, std::string Data)
|
||||
: mID(ID)
|
||||
, mData(std::move(Data)) {
|
||||
#ifdef DEBUG
|
||||
debug("vehicle " + std::to_string(mID) + " constructed");
|
||||
#endif
|
||||
trace("vehicle " + std::to_string(mID) + " constructed");
|
||||
}
|
||||
|
||||
TVehicleData::~TVehicleData() {
|
||||
#ifdef DEBUG
|
||||
debug("vehicle " + std::to_string(mID) + " destroyed");
|
||||
#endif
|
||||
trace("vehicle " + std::to_string(mID) + " destroyed");
|
||||
}
|
||||
|
||||
63
src/main.cpp
63
src/main.cpp
@@ -1,5 +1,9 @@
|
||||
#include "TSentry.h"
|
||||
|
||||
#include "Common.h"
|
||||
#include "CustomAssert.h"
|
||||
#include "Http.h"
|
||||
#include "SignalHandling.h"
|
||||
#include "TConfig.h"
|
||||
#include "THeartbeatThread.h"
|
||||
#include "TLuaEngine.h"
|
||||
@@ -7,45 +11,19 @@
|
||||
#include "TPPSMonitor.h"
|
||||
#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
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
#ifdef __unix
|
||||
#if DEBUG
|
||||
info("registering handlers for SIGINT, SIGTERM, SIGPIPE");
|
||||
#endif // DEBUG
|
||||
signal(SIGPIPE, UnixSignalHandler);
|
||||
signal(SIGTERM, UnixSignalHandler);
|
||||
#ifndef DEBUG
|
||||
signal(SIGINT, UnixSignalHandler);
|
||||
#endif // DEBUG
|
||||
#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 {};
|
||||
|
||||
int main(int argc, char** argv) try {
|
||||
setlocale(LC_ALL, "C");
|
||||
|
||||
SetupSignalHandlers();
|
||||
|
||||
bool Shutdown = false;
|
||||
Application::RegisterShutdownHandler([&Shutdown] { Shutdown = true; });
|
||||
|
||||
@@ -54,11 +32,19 @@ int main(int argc, char** argv) {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
RegisterThread("Main");
|
||||
|
||||
trace("Running in debug mode on a debug build");
|
||||
|
||||
Sentry.SetupUser();
|
||||
Sentry.PrintWelcome();
|
||||
TResourceManager ResourceManager;
|
||||
TPPSMonitor PPSMonitor(Server);
|
||||
THeartbeatThread Heartbeat(ResourceManager, Server);
|
||||
@@ -66,9 +52,14 @@ int main(int argc, char** argv) {
|
||||
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