mirror of
https://github.com/BeamMP/BeamMP-Server.git
synced 2026-07-13 18:24:16 +00:00
Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 588242822c | |||
| 58da200901 | |||
| f4ccf6c177 | |||
| 27103a73a9 | |||
| 2727f90430 | |||
| 2a96546c8c | |||
| 6462636b29 | |||
| 5742ab0dad | |||
| 0087205d55 | |||
| d16843e45d | |||
| 24516dbfd7 | |||
| a311d58e11 | |||
| 1444d91e7e | |||
| 1e2f060107 | |||
| fdb5da2ed6 | |||
| de57613326 | |||
| b49782e8a3 | |||
| ff80b4cf63 | |||
| 4c23b78f84 | |||
| aca3c52c20 | |||
| 51d6c4fb0a | |||
| 2af9491fd6 | |||
| 95c036836e | |||
| a7f2f85e45 | |||
| 42c5aaad5a | |||
| 88684bd9af | |||
| c6457f7df4 | |||
| ba3fd0e144 | |||
| e3b6fd7998 | |||
| 943159cd40 | |||
| 9423831937 | |||
| 95188042c5 | |||
| a0a7b8ecce | |||
| 53617abae4 | |||
| 853b078124 | |||
| 549517c518 | |||
| 2be4b8fd91 | |||
| 2cfb27820a | |||
| 1ff12cb2bf | |||
| 518cb0664e | |||
| 80432eb718 | |||
| b1caf5c29a | |||
| 950cee9fd0 | |||
| 046097579e |
@@ -3,7 +3,7 @@ name: CMake Windows Build
|
|||||||
on: [push]
|
on: [push]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
BUILD_TYPE: RelWithDebInfo
|
BUILD_TYPE: Release
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
windows-build:
|
windows-build:
|
||||||
@@ -31,7 +31,7 @@ jobs:
|
|||||||
working-directory: ${{github.workspace}}/build-windows
|
working-directory: ${{github.workspace}}/build-windows
|
||||||
env:
|
env:
|
||||||
beammp_sentry_url: ${{ secrets.BEAMMP_SECRET_SENTRY_URL }}
|
beammp_sentry_url: ${{ secrets.BEAMMP_SECRET_SENTRY_URL }}
|
||||||
run: cmake $GITHUB_WORKSPACE -DSENTRY_BACKEND=breakpad -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE='${{ runner.workspace }}/b/vcpkg/scripts/buildsystems/vcpkg.cmake' -DVCPKG_TARGET_TRIPLET=x64-windows-static -DBEAMMP_SECRET_SENTRY_URL="$beammp_sentry_url"
|
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
|
- name: Build
|
||||||
working-directory: ${{github.workspace}}/build-windows
|
working-directory: ${{github.workspace}}/build-windows
|
||||||
@@ -42,12 +42,6 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: BeamMP-Server.exe
|
name: BeamMP-Server.exe
|
||||||
path: ${{github.workspace}}/build-windows/RelWithDebInfo/BeamMP-Server.exe
|
path: ${{github.workspace}}/build-windows/Release/BeamMP-Server.exe
|
||||||
|
|
||||||
- name: Archive artifacts
|
|
||||||
uses: actions/upload-artifact@v2
|
|
||||||
with:
|
|
||||||
name: BeamMP-Server.pdb
|
|
||||||
path: ${{github.workspace}}/build-windows/RelWithDebInfo/BeamMP-Server.pdb
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
tag_name: ${{ github.ref }}
|
tag_name: ${{ github.ref }}
|
||||||
release_name: ${{ github.ref }}
|
release_name: ${{ github.ref }}
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: true
|
prerelease: false
|
||||||
body: |
|
body: |
|
||||||
Files included in this release:
|
Files included in this release:
|
||||||
- `BeamMP-Server.exe` is the windows build
|
- `BeamMP-Server.exe` is the windows build
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
.idea/
|
.idea/
|
||||||
|
.sentry-native/
|
||||||
*.orig
|
*.orig
|
||||||
*.toml
|
*.toml
|
||||||
boost_*
|
boost_*
|
||||||
|
|||||||
+4
-7
@@ -1,18 +1,15 @@
|
|||||||
[submodule "include/commandline"]
|
[submodule "include/commandline"]
|
||||||
path = include/commandline
|
path = include/commandline
|
||||||
url = https://github.com/lionkor/commandline
|
url = https://github.com/lionkor/commandline
|
||||||
[submodule "socket.io-client-cpp"]
|
|
||||||
path = socket.io-client-cpp
|
|
||||||
url = https://github.com/socketio/socket.io-client-cpp
|
|
||||||
[submodule "asio"]
|
[submodule "asio"]
|
||||||
path = asio
|
path = asio
|
||||||
url = https://github.com/chriskohlhoff/asio
|
url = https://github.com/chriskohlhoff/asio
|
||||||
[submodule "rapidjson"]
|
[submodule "rapidjson"]
|
||||||
path = rapidjson
|
path = rapidjson
|
||||||
url = https://github.com/Tencent/rapidjson
|
url = https://github.com/Tencent/rapidjson
|
||||||
[submodule "include/tomlplusplus"]
|
[submodule "include/toml11"]
|
||||||
path = include/tomlplusplus
|
path = include/toml11
|
||||||
url = https://github.com/marzer/tomlplusplus
|
url = https://github.com/ToruNiina/toml11
|
||||||
[submodule "include/sentry-native"]
|
[submodule "include/sentry-native"]
|
||||||
path = include/sentry-native
|
path = include/sentry-native
|
||||||
url = https://github.com/getsentry/sentry-native
|
url = https://github.com/getsentry/sentry-native
|
||||||
+17
-21
@@ -1,5 +1,10 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
message(STATUS "You can find build instructions and a list of dependencies in the README at \
|
message(STATUS "You can find build instructions and a list of dependencies in the README at \
|
||||||
https://github.com/BeamMP/BeamMP-Server")
|
https://github.com/BeamMP/BeamMP-Server")
|
||||||
|
|
||||||
@@ -16,7 +21,6 @@ if (WIN32)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
include_directories("include/sentry-native/include")
|
include_directories("include/sentry-native/include")
|
||||||
|
|
||||||
set(SENTRY_BUILD_SHARED_LIBS OFF)
|
set(SENTRY_BUILD_SHARED_LIBS OFF)
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
set(SENTRY_BUILD_RUNTIMESTATIC ON)
|
set(SENTRY_BUILD_RUNTIMESTATIC ON)
|
||||||
@@ -26,7 +30,7 @@ add_subdirectory("include/sentry-native")
|
|||||||
|
|
||||||
message(STATUS "Setting compiler flags")
|
message(STATUS "Setting compiler flags")
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
add_subdirectory("include/watchdog")
|
|
||||||
#-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static
|
#-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static
|
||||||
set(VcpkgRoot ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET})
|
set(VcpkgRoot ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET})
|
||||||
include_directories(${VcpkgRoot}/include)
|
include_directories(${VcpkgRoot}/include)
|
||||||
@@ -34,10 +38,10 @@ if (WIN32)
|
|||||||
elseif (UNIX)
|
elseif (UNIX)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -static-libstdc++")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -static-libstdc++")
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g")
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g")
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -gz -fno-builtin")
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -s -fno-builtin")
|
||||||
if (SANITIZE)
|
if (SANITIZE)
|
||||||
message(STATUS "sanitize is ON")
|
message(STATUS "sanitize is ON")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined,thread")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLanAGS} -fsanitize=undefined,thread")
|
||||||
endif (SANITIZE)
|
endif (SANITIZE)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
@@ -58,16 +62,19 @@ message(STATUS "Adding local source dependencies")
|
|||||||
include_directories("asio/asio/include")
|
include_directories("asio/asio/include")
|
||||||
include_directories("rapidjson/include")
|
include_directories("rapidjson/include")
|
||||||
include_directories("websocketpp")
|
include_directories("websocketpp")
|
||||||
add_subdirectory("socket.io-client-cpp")
|
|
||||||
add_subdirectory("include/commandline")
|
add_subdirectory("include/commandline")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
|
||||||
|
|
||||||
message(STATUS "Looking for Boost")
|
message(STATUS "Looking for Boost")
|
||||||
find_package(Boost REQUIRED COMPONENTS system thread)
|
find_package(Boost REQUIRED COMPONENTS system thread)
|
||||||
|
|
||||||
file(GLOB source_files src/main.cpp
|
add_executable(BeamMP-Server
|
||||||
|
src/main.cpp
|
||||||
include/TConsole.h src/TConsole.cpp
|
include/TConsole.h src/TConsole.cpp
|
||||||
include/TServer.h src/TServer.cpp
|
include/TServer.h src/TServer.cpp
|
||||||
include/Compat.h src/Compat.cpp
|
include/Compat.h src/Compat.cpp
|
||||||
@@ -82,10 +89,7 @@ file(GLOB source_files src/main.cpp
|
|||||||
include/Http.h src/Http.cpp
|
include/Http.h src/Http.cpp
|
||||||
include/TSentry.h src/TSentry.cpp
|
include/TSentry.h src/TSentry.cpp
|
||||||
include/TPPSMonitor.h src/TPPSMonitor.cpp
|
include/TPPSMonitor.h src/TPPSMonitor.cpp
|
||||||
include/TNetwork.h src/TNetwork.cpp
|
include/TNetwork.h src/TNetwork.cpp)
|
||||||
include/SignalHandling.h src/SignalHandling.cpp)
|
|
||||||
|
|
||||||
add_executable(BeamMP-Server ${source_files})
|
|
||||||
|
|
||||||
target_compile_definitions(BeamMP-Server PRIVATE SECRET_SENTRY_URL="${BEAMMP_SECRET_SENTRY_URL}")
|
target_compile_definitions(BeamMP-Server PRIVATE SECRET_SENTRY_URL="${BEAMMP_SECRET_SENTRY_URL}")
|
||||||
|
|
||||||
@@ -99,12 +103,13 @@ target_include_directories(BeamMP-Server PUBLIC
|
|||||||
${Boost_INCLUDE_DIRS}
|
${Boost_INCLUDE_DIRS}
|
||||||
${LUA_INCLUDE_DIR}
|
${LUA_INCLUDE_DIR}
|
||||||
${CURL_INCLUDE_DIRS}
|
${CURL_INCLUDE_DIRS}
|
||||||
"socket.io-client-cpp/src"
|
|
||||||
"include/tomlplusplus"
|
"include/tomlplusplus"
|
||||||
"include/sentry-native/include"
|
"include/sentry-native/include"
|
||||||
"include/curl/include")
|
"include/curl/include")
|
||||||
|
|
||||||
message(STATUS "Looking for SSL")
|
message(STATUS "Looking for SSL")
|
||||||
|
|
||||||
|
|
||||||
find_package(OpenSSL REQUIRED)
|
find_package(OpenSSL REQUIRED)
|
||||||
|
|
||||||
message(STATUS "CURL IS ${CURL_LIBRARIES}")
|
message(STATUS "CURL IS ${CURL_LIBRARIES}")
|
||||||
@@ -118,14 +123,8 @@ if (UNIX)
|
|||||||
crypto
|
crypto
|
||||||
${OPENSSL_LIBRARIES}
|
${OPENSSL_LIBRARIES}
|
||||||
commandline
|
commandline
|
||||||
sioclient_tls
|
|
||||||
sentry)
|
sentry)
|
||||||
elseif (WIN32)
|
elseif (WIN32)
|
||||||
set_source_files_properties(${source_files} PROPERTIES COMPILE_FLAGS "/Gh /GH")
|
|
||||||
add_compile_options("$<$<NOT:$<CONFIG:Debug>>:/Zi>")
|
|
||||||
add_link_options("$<$<NOT:$<CONFIG:Debug>>:/DEBUG>")
|
|
||||||
add_link_options("$<$<NOT:$<CONFIG:Debug>>:/OPT:REF>")
|
|
||||||
add_link_options("$<$<NOT:$<CONFIG:Debug>>:/OPT:ICF>")
|
|
||||||
include(FindLua)
|
include(FindLua)
|
||||||
message(STATUS "Looking for libz")
|
message(STATUS "Looking for libz")
|
||||||
find_package(ZLIB REQUIRED)
|
find_package(ZLIB REQUIRED)
|
||||||
@@ -138,8 +137,5 @@ elseif (WIN32)
|
|||||||
${LUA_LIBRARIES}
|
${LUA_LIBRARIES}
|
||||||
${OPENSSL_LIBRARIES}
|
${OPENSSL_LIBRARIES}
|
||||||
commandline
|
commandline
|
||||||
sioclient_tls
|
sentry)
|
||||||
sentry
|
|
||||||
watchdog
|
|
||||||
Dbghelp)
|
|
||||||
endif ()
|
endif ()
|
||||||
|
|||||||
@@ -1,20 +1,3 @@
|
|||||||
# v2.3.3
|
|
||||||
|
|
||||||
- CHANGED servers to be private by default
|
|
||||||
|
|
||||||
# v2.3.2
|
|
||||||
|
|
||||||
- ADDED Ctrl+C causes a graceful shutdown on windows (did already on linux)
|
|
||||||
- ADDED more meaningful shutdown messages
|
|
||||||
- ADDED even better backend connection error reporting
|
|
||||||
- ADDED `SendErrors` config in `ServerConfig.toml` to opt-out of error reporting
|
|
||||||
- ADDED hard-shutdown if Ctrl+C pressed 3 times
|
|
||||||
- FIXED issue with shells like bash being unusable after server exit
|
|
||||||
|
|
||||||
# v2.3.1
|
|
||||||
|
|
||||||
- CHANGED join/sync timeout to 20 minutes, players wont drop if loading takes >5 mins
|
|
||||||
|
|
||||||
# v2.3.0
|
# v2.3.0
|
||||||
|
|
||||||
- ADDED version check - the server will now let you know when a new release is out
|
- ADDED version check - the server will now let you know when a new release is out
|
||||||
|
|||||||
@@ -75,12 +75,18 @@ These package names are in the debian / ubuntu style. Feel free to PR your own g
|
|||||||
**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'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`).
|
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`).
|
||||||
|
|
||||||
|
In the end you should end up with a command something like this:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo apt install git make cmake g++-10 liblua5.3 libz-dev rapidjson-dev libopenssl-dev libboost1.71-all-dev
|
||||||
|
```
|
||||||
|
|
||||||
### How to build
|
### 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
|
1. Make sure you have all [prerequisites](#prerequisites) installed
|
||||||
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`.
|
2. Clone the repository in a location of your choice with `git clone --recurse-submodules https://github.com/BeamMP/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`.
|
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).
|
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 `.`)
|
5. Run `cmake .` (with `.`)
|
||||||
|
|||||||
+14
-3
@@ -12,6 +12,17 @@
|
|||||||
|
|
||||||
class TServer;
|
class TServer;
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
// for socklen_t
|
||||||
|
#include <WS2tcpip.h>
|
||||||
|
#endif // WIN32
|
||||||
|
|
||||||
|
struct TConnection final {
|
||||||
|
SOCKET Socket;
|
||||||
|
struct sockaddr SockAddr;
|
||||||
|
socklen_t SockAddrLen;
|
||||||
|
};
|
||||||
|
|
||||||
class TClient final {
|
class TClient final {
|
||||||
public:
|
public:
|
||||||
using TSetOfVehicleData = std::vector<TVehicleData>;
|
using TSetOfVehicleData = std::vector<TVehicleData>;
|
||||||
@@ -30,7 +41,7 @@ public:
|
|||||||
TVehicleDataLockPair GetAllCars();
|
TVehicleDataLockPair GetAllCars();
|
||||||
void SetName(const std::string& Name) { mName = Name; }
|
void SetName(const std::string& Name) { mName = Name; }
|
||||||
void SetRoles(const std::string& Role) { mRole = Role; }
|
void SetRoles(const std::string& Role) { mRole = Role; }
|
||||||
void AddIdentifier(const std::string& ID) { mIdentifiers.insert(ID); };
|
void SetIdentifier(const std::string& key, const std::string& value) { mIdentifiers[key] = value; }
|
||||||
std::string GetCarData(int Ident);
|
std::string GetCarData(int Ident);
|
||||||
void SetUDPAddr(sockaddr_in Addr) { mUDPAddress = Addr; }
|
void SetUDPAddr(sockaddr_in Addr) { mUDPAddress = Addr; }
|
||||||
void SetDownSock(SOCKET CSock) { mSocket[1] = CSock; }
|
void SetDownSock(SOCKET CSock) { mSocket[1] = CSock; }
|
||||||
@@ -38,7 +49,7 @@ public:
|
|||||||
void SetStatus(int Status) { mStatus = Status; }
|
void SetStatus(int Status) { mStatus = Status; }
|
||||||
// locks
|
// locks
|
||||||
void DeleteCar(int Ident);
|
void DeleteCar(int Ident);
|
||||||
[[nodiscard]] std::set<std::string> GetIdentifiers() const { return mIdentifiers; }
|
[[nodiscard]] const std::unordered_map<std::string, std::string>& GetIdentifiers() const { return mIdentifiers; }
|
||||||
[[nodiscard]] sockaddr_in GetUDPAddr() const { return mUDPAddress; }
|
[[nodiscard]] sockaddr_in GetUDPAddr() const { return mUDPAddress; }
|
||||||
[[nodiscard]] SOCKET GetDownSock() const { return mSocket[1]; }
|
[[nodiscard]] SOCKET GetDownSock() const { return mSocket[1]; }
|
||||||
[[nodiscard]] SOCKET GetTCPSock() const { return mSocket[0]; }
|
[[nodiscard]] SOCKET GetTCPSock() const { return mSocket[0]; }
|
||||||
@@ -78,7 +89,7 @@ private:
|
|||||||
bool mIsSyncing = false;
|
bool mIsSyncing = false;
|
||||||
mutable std::mutex mMissedPacketsMutex;
|
mutable std::mutex mMissedPacketsMutex;
|
||||||
std::queue<std::string> mPacketsSync;
|
std::queue<std::string> mPacketsSync;
|
||||||
std::set<std::string> mIdentifiers;
|
std::unordered_map<std::string, std::string> mIdentifiers;
|
||||||
bool mIsGuest = false;
|
bool mIsGuest = false;
|
||||||
std::mutex mVehicleDataMutex;
|
std::mutex mVehicleDataMutex;
|
||||||
TSetOfVehicleData mVehicleData;
|
TSetOfVehicleData mVehicleData;
|
||||||
|
|||||||
+24
-14
@@ -5,13 +5,26 @@ extern TSentry Sentry;
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <cstring>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "Compat.h"
|
||||||
|
|
||||||
#include "TConsole.h"
|
#include "TConsole.h"
|
||||||
|
|
||||||
|
struct Version {
|
||||||
|
uint8_t major;
|
||||||
|
uint8_t minor;
|
||||||
|
uint8_t patch;
|
||||||
|
Version(uint8_t major, uint8_t minor, uint8_t patch);
|
||||||
|
Version(const std::array<uint8_t, 3>& v);
|
||||||
|
std::string AsString();
|
||||||
|
};
|
||||||
|
|
||||||
// static class handling application start, shutdown, etc.
|
// static class handling application start, shutdown, etc.
|
||||||
// yes, static classes, singletons, globals are all pretty
|
// yes, static classes, singletons, globals are all pretty
|
||||||
// bad idioms. In this case we need a central way to access
|
// bad idioms. In this case we need a central way to access
|
||||||
@@ -27,12 +40,10 @@ public:
|
|||||||
, Resource("Resources")
|
, Resource("Resources")
|
||||||
, MapName("/levels/gridmap_v2/info.json")
|
, MapName("/levels/gridmap_v2/info.json")
|
||||||
, MaxPlayers(10)
|
, MaxPlayers(10)
|
||||||
, Private(true)
|
, Private(false)
|
||||||
, MaxCars(1)
|
, MaxCars(1)
|
||||||
, DebugModeEnabled(false)
|
, DebugModeEnabled(false)
|
||||||
, Port(30814)
|
, Port(30814) { }
|
||||||
, SendErrors(true)
|
|
||||||
, SendErrorsMessageEnabled(true) { }
|
|
||||||
std::string ServerName;
|
std::string ServerName;
|
||||||
std::string ServerDesc;
|
std::string ServerDesc;
|
||||||
std::string Resource;
|
std::string Resource;
|
||||||
@@ -44,10 +55,9 @@ public:
|
|||||||
bool DebugModeEnabled;
|
bool DebugModeEnabled;
|
||||||
int Port;
|
int Port;
|
||||||
std::string CustomIP;
|
std::string CustomIP;
|
||||||
bool SendErrors;
|
|
||||||
bool SendErrorsMessageEnabled;
|
|
||||||
[[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); }
|
[[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
using TShutdownHandler = std::function<void()>;
|
using TShutdownHandler = std::function<void()>;
|
||||||
|
|
||||||
// methods
|
// methods
|
||||||
@@ -58,8 +68,9 @@ public:
|
|||||||
// Causes all threads to finish up and exit gracefull gracefully
|
// Causes all threads to finish up and exit gracefull gracefully
|
||||||
static void GracefullyShutdown();
|
static void GracefullyShutdown();
|
||||||
static TConsole& Console() { return *mConsole; }
|
static TConsole& Console() { return *mConsole; }
|
||||||
static std::string ServerVersion() { return "2.3.2"; }
|
static std::string ServerVersionString();
|
||||||
static std::string ClientVersion() { return "2.0"; }
|
static const Version& ServerVersion() { return mVersion; }
|
||||||
|
static std::string ClientVersionString() { return "2.0"; }
|
||||||
static std::string PPS() { return mPPS; }
|
static std::string PPS() { return mPPS; }
|
||||||
static void SetPPS(const std::string& NewPPS) { mPPS = NewPPS; }
|
static void SetPPS(const std::string& NewPPS) { mPPS = NewPPS; }
|
||||||
|
|
||||||
@@ -71,14 +82,16 @@ public:
|
|||||||
static std::string GetBackup2Hostname() { return "backup2.beammp.com"; }
|
static std::string GetBackup2Hostname() { return "backup2.beammp.com"; }
|
||||||
static std::string GetBackendUrlForSocketIO() { return "https://backend.beammp.com"; }
|
static std::string GetBackendUrlForSocketIO() { return "https://backend.beammp.com"; }
|
||||||
static void CheckForUpdates();
|
static void CheckForUpdates();
|
||||||
static std::array<int, 3> VersionStrToInts(const std::string& str);
|
static std::array<uint8_t, 3> VersionStrToInts(const std::string& str);
|
||||||
static bool IsOutdated(const std::array<int, 3>& Current, const std::array<int, 3>& Newest);
|
static bool IsOutdated(const Version& Current, const Version& Newest);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static inline std::string mPPS;
|
static inline std::string mPPS;
|
||||||
static std::unique_ptr<TConsole> mConsole;
|
static std::unique_ptr<TConsole> mConsole;
|
||||||
static inline std::mutex mShutdownHandlersMutex {};
|
static inline std::mutex mShutdownHandlersMutex {};
|
||||||
static inline std::deque<TShutdownHandler> mShutdownHandlers {};
|
static inline std::deque<TShutdownHandler> mShutdownHandlers {};
|
||||||
|
|
||||||
|
static inline Version mVersion { 2, 4, 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string ThreadName(bool DebugModeOverride = false);
|
std::string ThreadName(bool DebugModeOverride = false);
|
||||||
@@ -87,7 +100,6 @@ void RegisterThread(const std::string& str);
|
|||||||
|
|
||||||
#define KB 1024
|
#define KB 1024
|
||||||
#define MB (KB * 1024)
|
#define MB (KB * 1024)
|
||||||
#define SSU_UNRAW SECRET_SENTRY_URL
|
|
||||||
|
|
||||||
#define _file_basename std::filesystem::path(__FILE__).filename().string()
|
#define _file_basename std::filesystem::path(__FILE__).filename().string()
|
||||||
#define _line std::to_string(__LINE__)
|
#define _line std::to_string(__LINE__)
|
||||||
@@ -113,11 +125,9 @@ void RegisterThread(const std::string& str);
|
|||||||
#else
|
#else
|
||||||
#define _this_location (ThreadName() + _file_basename + ":" + _line + " ")
|
#define _this_location (ThreadName() + _file_basename + ":" + _line + " ")
|
||||||
#endif
|
#endif
|
||||||
#define SU_RAW SSU_UNRAW
|
|
||||||
|
|
||||||
#else // !defined(DEBUG)
|
#else // !defined(DEBUG)
|
||||||
|
|
||||||
#define SU_RAW RAWIFY(SSU_UNRAW)
|
|
||||||
#define _this_location (ThreadName())
|
#define _this_location (ThreadName())
|
||||||
|
|
||||||
#endif // defined(DEBUG)
|
#endif // defined(DEBUG)
|
||||||
@@ -154,4 +164,4 @@ void LogChatMessage(const std::string& name, int id, const std::string& msg);
|
|||||||
std::string Comp(std::string Data);
|
std::string Comp(std::string Data);
|
||||||
std::string DeComp(std::string Compressed);
|
std::string DeComp(std::string Compressed);
|
||||||
|
|
||||||
#define S_DSN SU_RAW
|
std::string GetPlatformAgnosticErrorString();
|
||||||
|
|||||||
+3
-1
@@ -7,6 +7,7 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
using SOCKET = int;
|
using SOCKET = int;
|
||||||
using DWORD = unsigned long;
|
using DWORD = unsigned long;
|
||||||
using PDWORD = unsigned long*;
|
using PDWORD = unsigned long*;
|
||||||
@@ -26,6 +27,7 @@ inline void CloseSocketProper(int TheSocket) {
|
|||||||
inline void CloseSocketProper(SOCKET TheSocket) {
|
inline void CloseSocketProper(SOCKET TheSocket) {
|
||||||
shutdown(TheSocket, 2); // 2 == SD_BOTH
|
shutdown(TheSocket, 2); // 2 == SD_BOTH
|
||||||
closesocket(TheSocket);
|
closesocket(TheSocket);
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif // WIN32
|
#endif // WIN32
|
||||||
|
|
||||||
@@ -33,4 +35,4 @@ inline void CloseSocketProper(SOCKET TheSocket) {
|
|||||||
|
|
||||||
#if !defined(WIN32) && !defined(__unix)
|
#if !defined(WIN32) && !defined(__unix)
|
||||||
#error "OS not supported"
|
#error "OS not supported"
|
||||||
#endif
|
#endif
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
// 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)
|
|
||||||
|
|
||||||
}
|
|
||||||
+2
-1
@@ -5,8 +5,9 @@
|
|||||||
|
|
||||||
namespace Http {
|
namespace Http {
|
||||||
std::string GET(const std::string& host, int port, const std::string& target, unsigned int* status = nullptr);
|
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);
|
std::string POST(const std::string& host, int port, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, const std::string& ContentType, unsigned int* status = nullptr);
|
||||||
namespace Status {
|
namespace Status {
|
||||||
std::string ToString(int code);
|
std::string ToString(int code);
|
||||||
}
|
}
|
||||||
|
const std::string ErrorString = "-1";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
void SetupSignalHandlers();
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <deque>
|
|
||||||
#include <mutex>
|
|
||||||
#include <sio_client.h>
|
|
||||||
#include <thread>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We send relevant server events over socket.io to the backend.
|
|
||||||
*
|
|
||||||
* We send all events to `backend.beammp.com`, to the room `/key`
|
|
||||||
* where `key` is the currently active auth-key.
|
|
||||||
*/
|
|
||||||
|
|
||||||
enum class SocketIOEvent {
|
|
||||||
ConsoleOut,
|
|
||||||
CPUUsage,
|
|
||||||
MemoryUsage,
|
|
||||||
NetworkUsage,
|
|
||||||
PlayerList,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class SocketIORoom {
|
|
||||||
None,
|
|
||||||
Stats,
|
|
||||||
Player,
|
|
||||||
Info,
|
|
||||||
Console,
|
|
||||||
};
|
|
||||||
|
|
||||||
class SocketIO final {
|
|
||||||
private:
|
|
||||||
struct Event;
|
|
||||||
|
|
||||||
public:
|
|
||||||
enum class EventType {
|
|
||||||
};
|
|
||||||
|
|
||||||
// Singleton pattern
|
|
||||||
static SocketIO& Get();
|
|
||||||
|
|
||||||
void Emit(SocketIOEvent Event, const std::string& Data);
|
|
||||||
|
|
||||||
~SocketIO();
|
|
||||||
|
|
||||||
void SetAuthenticated(bool auth) { mAuthenticated = auth; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
SocketIO() noexcept;
|
|
||||||
|
|
||||||
void ThreadMain();
|
|
||||||
|
|
||||||
struct Event {
|
|
||||||
std::string Name;
|
|
||||||
std::string Data;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool mAuthenticated { false };
|
|
||||||
sio::client mClient;
|
|
||||||
std::thread mThread;
|
|
||||||
std::atomic_bool mCloseThread { false };
|
|
||||||
std::mutex mQueueMutex;
|
|
||||||
std::deque<Event> mQueue;
|
|
||||||
|
|
||||||
friend std::unique_ptr<SocketIO> std::make_unique<SocketIO>();
|
|
||||||
};
|
|
||||||
|
|
||||||
@@ -10,6 +10,8 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] bool Failed() const { return mFailed; }
|
[[nodiscard]] bool Failed() const { return mFailed; }
|
||||||
|
|
||||||
|
void FlushToFile();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void CreateConfigFile(std::string_view name);
|
void CreateConfigFile(std::string_view name);
|
||||||
void ParseFromFile(std::string_view name);
|
void ParseFromFile(std::string_view name);
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include "commandline/commandline.h"
|
#include "commandline/commandline.h"
|
||||||
#include "TLuaFile.h"
|
#include "TLuaFile.h"
|
||||||
#include "Cryptography.h"
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
|
|||||||
@@ -18,4 +18,4 @@ private:
|
|||||||
bool mShutdown = false;
|
bool mShutdown = false;
|
||||||
TResourceManager& mResourceManager;
|
TResourceManager& mResourceManager;
|
||||||
TServer& mServer;
|
TServer& mServer;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,10 +4,11 @@
|
|||||||
#include "IThreaded.h"
|
#include "IThreaded.h"
|
||||||
#include "TLuaFile.h"
|
#include "TLuaFile.h"
|
||||||
#include "TServer.h"
|
#include "TServer.h"
|
||||||
|
#include <optional>
|
||||||
#include <lua.hpp>
|
#include <lua.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
class TLuaEngine : public IThreaded {
|
class TLuaEngine : public IThreaded {
|
||||||
public:
|
public:
|
||||||
@@ -25,6 +26,7 @@ public:
|
|||||||
|
|
||||||
std::optional<std::reference_wrapper<TLuaFile>> GetScript(lua_State* L);
|
std::optional<std::reference_wrapper<TLuaFile>> GetScript(lua_State* L);
|
||||||
|
|
||||||
|
static std::unordered_map<std::string, lua_State*> mGlobals;
|
||||||
private:
|
private:
|
||||||
void FolderList(const std::string& Path, bool HotSwap);
|
void FolderList(const std::string& Path, bool HotSwap);
|
||||||
void RegisterFiles(const fs::path& Path, bool HotSwap);
|
void RegisterFiles(const fs::path& Path, bool HotSwap);
|
||||||
|
|||||||
+7
-6
@@ -37,12 +37,13 @@ public:
|
|||||||
~TLuaFile();
|
~TLuaFile();
|
||||||
void SetStopThread(bool StopThread) { mStopThread = StopThread; }
|
void SetStopThread(bool StopThread) { mStopThread = StopThread; }
|
||||||
TLuaEngine& Engine() { return mEngine; }
|
TLuaEngine& Engine() { return mEngine; }
|
||||||
[[nodiscard]] std::string GetPluginName() const;
|
std::string GetPluginPath() const;
|
||||||
[[nodiscard]] std::string GetFileName() const;
|
std::string GetPluginName() const;
|
||||||
[[nodiscard]] const lua_State* GetState() const;
|
std::string GetFileName() const;
|
||||||
[[nodiscard]] bool GetStopThread() const { return mStopThread; }
|
const lua_State* GetState() const;
|
||||||
[[nodiscard]] const TLuaEngine& Engine() const { return mEngine; }
|
bool GetStopThread() const { return mStopThread; }
|
||||||
[[nodiscard]] std::string GetRegistered(const std::string& Event) const;
|
const TLuaEngine& Engine() const { return mEngine; }
|
||||||
|
std::string GetRegistered(const std::string& Event) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TLuaEngine& mEngine;
|
TLuaEngine& mEngine;
|
||||||
|
|||||||
+4
-2
@@ -4,6 +4,8 @@
|
|||||||
#include "TResourceManager.h"
|
#include "TResourceManager.h"
|
||||||
#include "TServer.h"
|
#include "TServer.h"
|
||||||
|
|
||||||
|
struct TConnection;
|
||||||
|
|
||||||
class TNetwork {
|
class TNetwork {
|
||||||
public:
|
public:
|
||||||
TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& ResourceManager);
|
TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& ResourceManager);
|
||||||
@@ -15,8 +17,8 @@ public:
|
|||||||
std::string TCPRcv(TClient& c);
|
std::string TCPRcv(TClient& c);
|
||||||
void ClientKick(TClient& c, const std::string& R);
|
void ClientKick(TClient& c, const std::string& R);
|
||||||
[[nodiscard]] bool SyncClient(const std::weak_ptr<TClient>& c);
|
[[nodiscard]] bool SyncClient(const std::weak_ptr<TClient>& c);
|
||||||
void Identify(SOCKET TCPSock);
|
void Identify(const TConnection& client);
|
||||||
void Authentication(SOCKET TCPSock);
|
void Authentication(const TConnection& ClientConnection);
|
||||||
[[nodiscard]] bool CheckBytes(TClient& c, int32_t BytesRcv);
|
[[nodiscard]] bool CheckBytes(TClient& c, int32_t BytesRcv);
|
||||||
void SyncResources(TClient& c);
|
void SyncResources(TClient& c);
|
||||||
[[nodiscard]] bool UDPSend(TClient& Client, std::string Data) const;
|
[[nodiscard]] bool UDPSend(TClient& Client, std::string Data) const;
|
||||||
|
|||||||
+1
-1
Submodule include/commandline updated: 4931aa89c1...c34703df11
Submodule
+1
Submodule include/toml11 added at 647381020e
Submodule include/tomlplusplus deleted from bc6891e1fb
@@ -1,8 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
|
||||||
project(watchdog CXX ASM_MASM)
|
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
|
||||||
add_library(watchdog STATIC watchdog.cpp watchdog.h x64Def.asm)
|
|
||||||
set_source_files_properties(watchdog.cpp PROPERTIES COMPILE_FLAGS "/O2 /Ob2 /DNDEBUG")
|
|
||||||
STRING(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
|
|
||||||
target_link_libraries(watchdog Dbghelp)
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by Anonymous275 on 9/5/2021.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <cstring>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace fst {
|
|
||||||
template<size_t Cap>
|
|
||||||
class stack_string {
|
|
||||||
public:
|
|
||||||
stack_string() noexcept {
|
|
||||||
memset(Data, 0, Cap);
|
|
||||||
}
|
|
||||||
explicit stack_string(const char* Ptr) {
|
|
||||||
size_t len = strlen(Ptr);
|
|
||||||
Copy(Ptr, len);
|
|
||||||
memset(Data + len, 0, Cap - len);
|
|
||||||
}
|
|
||||||
stack_string(const char* Ptr, size_t PSize) {
|
|
||||||
Copy(Ptr, PSize);
|
|
||||||
memset(Data + PSize, 0, Cap - PSize);
|
|
||||||
}
|
|
||||||
inline size_t capacity() noexcept {
|
|
||||||
return Cap;
|
|
||||||
}
|
|
||||||
inline size_t size() noexcept {
|
|
||||||
return Size;
|
|
||||||
}
|
|
||||||
inline size_t length() noexcept {
|
|
||||||
return Size;
|
|
||||||
}
|
|
||||||
inline char* get() {
|
|
||||||
return Data;
|
|
||||||
}
|
|
||||||
[[nodiscard]] inline const char* c_str() const noexcept {
|
|
||||||
return Data;
|
|
||||||
}
|
|
||||||
char& operator[](size_t idx) {
|
|
||||||
if (idx >= Size) {
|
|
||||||
throw std::exception("stack_string out of boundaries operator[]");
|
|
||||||
}
|
|
||||||
return Data[idx];
|
|
||||||
}
|
|
||||||
inline void resize(size_t newSize) noexcept {
|
|
||||||
Size = newSize;
|
|
||||||
}
|
|
||||||
inline void push_back(const char* Ptr) {
|
|
||||||
Copy(Ptr, strlen(Ptr));
|
|
||||||
}
|
|
||||||
inline void push_back(const char* Ptr, size_t Count) {
|
|
||||||
Copy(Ptr, Count);
|
|
||||||
}
|
|
||||||
inline void push_back(char Ptr) {
|
|
||||||
Copy(&Ptr, 1);
|
|
||||||
}
|
|
||||||
friend std::ostream& operator<<(std::ostream& os, const stack_string& obj) {
|
|
||||||
os << obj.Data;
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
inline stack_string& operator+=(const char* Ptr) {
|
|
||||||
push_back(Ptr);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
inline stack_string& operator+=(char Ptr) {
|
|
||||||
push_back(Ptr);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
inline void Copy(const char* Ptr, size_t PSize) {
|
|
||||||
if((PSize + Size) <= Cap) {
|
|
||||||
memcpy(&Data[Size], Ptr, PSize);
|
|
||||||
Size += PSize;
|
|
||||||
} else throw std::exception("stack_string out of boundaries copy");
|
|
||||||
}
|
|
||||||
char Data[Cap]{};
|
|
||||||
size_t Size{0};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,281 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by Anonymous275 on 9/9/2021.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
#include <imagehlp.h>
|
|
||||||
#include <strsafe.h>
|
|
||||||
#include <cstdint>
|
|
||||||
#include "stack_string.h"
|
|
||||||
|
|
||||||
struct function_info {
|
|
||||||
void* func_address;
|
|
||||||
uint32_t thread_id;
|
|
||||||
bool enter;
|
|
||||||
};
|
|
||||||
|
|
||||||
fst::stack_string<1024> crash_file;
|
|
||||||
|
|
||||||
template <typename I>
|
|
||||||
fst::stack_string<(sizeof(I)<<1)+1> HexString(I w) {
|
|
||||||
static const char* digits = "0123456789ABCDEF";
|
|
||||||
const size_t hex_len = sizeof(I)<<1;
|
|
||||||
fst::stack_string<hex_len+1> rc;
|
|
||||||
rc.resize(hex_len+1);
|
|
||||||
memset(rc.get(), '0', hex_len);
|
|
||||||
memset(rc.get() + hex_len, 0, 1);
|
|
||||||
for (size_t i=0, j=(hex_len-1)*4 ; i<hex_len; ++i,j-=4)
|
|
||||||
rc[i] = digits[(w>>j) & 0x0f];
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T_>
|
|
||||||
class heap_array {
|
|
||||||
public:
|
|
||||||
heap_array() noexcept {
|
|
||||||
Data = (T_*)(GlobalAlloc(GPTR, Cap * sizeof(T_)));
|
|
||||||
init = true;
|
|
||||||
}
|
|
||||||
explicit heap_array(size_t Cap_) noexcept {
|
|
||||||
Cap = Cap_;
|
|
||||||
Data = (T_*)(GlobalAlloc(GPTR, Cap * sizeof(T_)));
|
|
||||||
init = true;
|
|
||||||
}
|
|
||||||
~heap_array() {
|
|
||||||
free(Data);
|
|
||||||
}
|
|
||||||
inline T_* get() noexcept {
|
|
||||||
return Data;
|
|
||||||
}
|
|
||||||
inline const T_* cget() noexcept {
|
|
||||||
return Data;
|
|
||||||
}
|
|
||||||
inline void insert(const T_& T) {
|
|
||||||
if(!init)return;
|
|
||||||
if(Size >= Cap) {
|
|
||||||
Grow();
|
|
||||||
}
|
|
||||||
Data[Size++] = T;
|
|
||||||
}
|
|
||||||
inline void string_insert(const T_* T, size_t len = 0) {
|
|
||||||
if(len == 0)len = strlen(T);
|
|
||||||
if(Size+len >= Cap) {
|
|
||||||
Grow(len);
|
|
||||||
}
|
|
||||||
memcpy(&Data[Size], T, len);
|
|
||||||
Size += len;
|
|
||||||
}
|
|
||||||
inline T_ at(size_t idx) {
|
|
||||||
return Data[idx];
|
|
||||||
}
|
|
||||||
inline size_t size() const noexcept {
|
|
||||||
return Size;
|
|
||||||
}
|
|
||||||
const T_& operator[](size_t idx) {
|
|
||||||
if (idx >= Size) {
|
|
||||||
throw std::exception("out of boundaries operator[]");
|
|
||||||
}
|
|
||||||
return Data[idx];
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
inline void Grow(size_t add = 0) {
|
|
||||||
Cap = (Cap*2) + add;
|
|
||||||
auto* NewData = (T_*)(GlobalAlloc(GPTR, Cap * sizeof(T_)));
|
|
||||||
for(size_t C = 0; C < Size; C++) {
|
|
||||||
NewData[C] = Data[C];
|
|
||||||
}
|
|
||||||
GlobalFree(Data);
|
|
||||||
Data = NewData;
|
|
||||||
}
|
|
||||||
size_t Size{0}, Cap{5};
|
|
||||||
bool init{false};
|
|
||||||
T_* Data;
|
|
||||||
};
|
|
||||||
|
|
||||||
heap_array<function_info>* watch_data;
|
|
||||||
|
|
||||||
struct watchdog_mutex {
|
|
||||||
static void Create() noexcept {
|
|
||||||
hMutex = CreateMutex(nullptr, FALSE, nullptr);
|
|
||||||
}
|
|
||||||
static void Lock() {
|
|
||||||
WaitForSingleObject(hMutex, INFINITE);
|
|
||||||
}
|
|
||||||
static void Unlock() {
|
|
||||||
ReleaseMutex(hMutex);
|
|
||||||
}
|
|
||||||
struct [[nodiscard]] ScopedLock {
|
|
||||||
ScopedLock() {
|
|
||||||
if(hMutex)
|
|
||||||
watchdog_mutex::Lock();
|
|
||||||
}
|
|
||||||
~ScopedLock() {
|
|
||||||
if(hMutex)
|
|
||||||
watchdog_mutex::Unlock();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
private:
|
|
||||||
static HANDLE hMutex;
|
|
||||||
};
|
|
||||||
HANDLE watchdog_mutex::hMutex{nullptr};
|
|
||||||
std::atomic<bool> Init{false}, Sym;
|
|
||||||
std::atomic<int64_t> Offset{0};
|
|
||||||
|
|
||||||
void watchdog_setOffset(int64_t Off) {
|
|
||||||
Offset.store(Off);
|
|
||||||
}
|
|
||||||
|
|
||||||
void notify(const char* msg) {
|
|
||||||
HANDLE stdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
||||||
if (stdOut != nullptr && stdOut != INVALID_HANDLE_VALUE) {
|
|
||||||
DWORD written = 0;
|
|
||||||
WriteConsoleA(stdOut, "[WATCHDOG] ", 11, &written, nullptr);
|
|
||||||
WriteConsoleA(stdOut, msg, DWORD(strlen(msg)), &written, nullptr);
|
|
||||||
WriteConsoleA(stdOut, "\n", 1, &written, nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fst::stack_string<MAX_SYM_NAME> FindFunction(void* Address) {
|
|
||||||
if(!Sym.load()) {
|
|
||||||
fst::stack_string<MAX_SYM_NAME> undName;
|
|
||||||
return undName;
|
|
||||||
}
|
|
||||||
static HANDLE process = GetCurrentProcess();
|
|
||||||
DWORD64 symDisplacement = 0;
|
|
||||||
fst::stack_string<MAX_SYM_NAME> undName;
|
|
||||||
TCHAR buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
|
|
||||||
memset(&buffer,0, sizeof(buffer));
|
|
||||||
auto pSymbolInfo = (PSYMBOL_INFO)buffer;
|
|
||||||
pSymbolInfo->SizeOfStruct = sizeof(SYMBOL_INFO);
|
|
||||||
pSymbolInfo->MaxNameLen = MAX_SYM_NAME;
|
|
||||||
if (SymFromAddr(process, DWORD64(Address) + Offset, &symDisplacement, pSymbolInfo)) {
|
|
||||||
undName.push_back(pSymbolInfo->Name);
|
|
||||||
}
|
|
||||||
return undName;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fst::stack_string<512> getCrashInfo(void* Address){
|
|
||||||
if(!Sym.load()){
|
|
||||||
fst::stack_string<512> Value;
|
|
||||||
Value.push_back("unknown", 7);
|
|
||||||
return Value;
|
|
||||||
}
|
|
||||||
DWORD pdwDisplacement = 0;
|
|
||||||
IMAGEHLP_LINE64 line{sizeof(IMAGEHLP_LINE64)};
|
|
||||||
SymGetLineFromAddr64(GetCurrentProcess(), DWORD64(Address) + Offset, &pdwDisplacement, &line);
|
|
||||||
char* Name = nullptr;
|
|
||||||
if(line.FileName) {
|
|
||||||
Name = strrchr(line.FileName, '\\');
|
|
||||||
}
|
|
||||||
fst::stack_string<512> Value;
|
|
||||||
if(Name)Value.push_back(Name+1);
|
|
||||||
else Value.push_back("unknown", 7);
|
|
||||||
char buffer[20];
|
|
||||||
auto n = sprintf(buffer, ":%lu", line.LineNumber);
|
|
||||||
Value.push_back(buffer, n);
|
|
||||||
return Value;
|
|
||||||
}
|
|
||||||
const char* getFunctionDetails(void* Address) {
|
|
||||||
return FindFunction(Address).c_str();
|
|
||||||
}
|
|
||||||
const char* getCrashLocation(void* Address) {
|
|
||||||
return getCrashInfo(Address).c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
void InitSym(const char* PDBLocation) {
|
|
||||||
SymInitialize(GetCurrentProcess(), PDBLocation, TRUE);
|
|
||||||
Sym.store(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_report(const char* report, size_t size) {
|
|
||||||
HANDLE hFile = CreateFile(crash_file.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
|
||||||
if (hFile == INVALID_HANDLE_VALUE) {
|
|
||||||
notify("Failed to open crash file for writing!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
DWORD dwBytesWritten = 0;
|
|
||||||
auto Flag = WriteFile(hFile, report, DWORD(size), &dwBytesWritten, nullptr);
|
|
||||||
if (Flag == FALSE) {
|
|
||||||
notify("Failed to write to crash file!");
|
|
||||||
}
|
|
||||||
CloseHandle(hFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
void generate_crash_report(uint32_t Code, void* Address) {
|
|
||||||
watchdog_mutex::ScopedLock guard;
|
|
||||||
notify("generating crash report, please wait");
|
|
||||||
Init.store(false);
|
|
||||||
heap_array<char> Report(watch_data->size() * sizeof(function_info));
|
|
||||||
Report.string_insert("crash code ");
|
|
||||||
Report.string_insert(HexString(Code).c_str());
|
|
||||||
Report.string_insert(" at ");
|
|
||||||
Report.string_insert(HexString(size_t(Address) + Offset).c_str());
|
|
||||||
Report.string_insert("\n");
|
|
||||||
if(Address) {
|
|
||||||
Report.string_insert("origin and line number -> ");
|
|
||||||
Report.string_insert(getCrashInfo(Address).c_str());
|
|
||||||
Report.string_insert("\n");
|
|
||||||
}
|
|
||||||
Report.string_insert("Call history: \n");
|
|
||||||
char buff[20];
|
|
||||||
for(size_t C = 0; C < watch_data->size(); C++){
|
|
||||||
auto entry = watch_data->at(C);
|
|
||||||
auto Name = FindFunction(entry.func_address);
|
|
||||||
if(entry.enter){
|
|
||||||
Report.string_insert("[Entry] ");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Report.string_insert("[Exit ] ");
|
|
||||||
}
|
|
||||||
auto n = sprintf(buff, "(%d) ", entry.thread_id);
|
|
||||||
Report.string_insert(buff, n);
|
|
||||||
if(Name.size() > 0){
|
|
||||||
Report.string_insert(Name.c_str(), Name.size());
|
|
||||||
Report.string_insert(" | ");
|
|
||||||
auto location = getCrashInfo(entry.func_address);
|
|
||||||
Report.string_insert(location.c_str(), location.size());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Report.string_insert(HexString(size_t(entry.func_address) + Offset).c_str());
|
|
||||||
}
|
|
||||||
Report.string_insert("\n");
|
|
||||||
}
|
|
||||||
write_report(Report.cget(), Report.size());
|
|
||||||
notify("crash report generated");
|
|
||||||
Init.store(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
LONG WINAPI CrashHandler(EXCEPTION_POINTERS* p) {
|
|
||||||
Init.store(false);
|
|
||||||
notify("CAUGHT EXCEPTION!");
|
|
||||||
generate_crash_report(p->ExceptionRecord->ExceptionCode, p->ExceptionRecord->ExceptionAddress);
|
|
||||||
return EXCEPTION_EXECUTE_HANDLER;
|
|
||||||
}
|
|
||||||
|
|
||||||
void watchdog_init(const char* crashFile, const char* SpecificPDBLocation, bool Symbols) {
|
|
||||||
if(Symbols)SymInitialize(GetCurrentProcess(), SpecificPDBLocation, TRUE);
|
|
||||||
Sym.store(Symbols);
|
|
||||||
SetUnhandledExceptionFilter(CrashHandler);
|
|
||||||
watch_data = new heap_array<function_info>();
|
|
||||||
watchdog_mutex::Create();
|
|
||||||
crash_file.push_back(crashFile);
|
|
||||||
notify("initialized!");
|
|
||||||
Init.store(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void AddEntry(void* func_address, uint32_t thread_id, bool entry) {
|
|
||||||
watchdog_mutex::ScopedLock guard;
|
|
||||||
if(Init.load()) {
|
|
||||||
watch_data->insert({func_address, thread_id, entry});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
void FuncEntry(void* func) {
|
|
||||||
AddEntry(func, GetCurrentThreadId(), true);
|
|
||||||
}
|
|
||||||
void FuncExit(void* func) {
|
|
||||||
AddEntry(func, GetCurrentThreadId(), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by Anonymous275 on 9/9/2021.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <cstdint>
|
|
||||||
extern void watchdog_init(const char* crashFile, const char* SpecificPDBLocation, bool Symbols = true);
|
|
||||||
extern void generate_crash_report(uint32_t Code, void* Address);
|
|
||||||
const char* getFunctionDetails(void* Address);
|
|
||||||
extern void watchdog_setOffset(int64_t Off);
|
|
||||||
const char* getCrashLocation(void* Address);
|
|
||||||
void InitSym(const char* PDBLocation);
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
;//
|
|
||||||
;// Created by Anonymous275 on 9/9/2021.
|
|
||||||
;//
|
|
||||||
;External C functions used by _penter and _pexit
|
|
||||||
extern FuncEntry:Proc
|
|
||||||
extern FuncExit:Proc
|
|
||||||
|
|
||||||
.code
|
|
||||||
|
|
||||||
_penter proc
|
|
||||||
|
|
||||||
; Store the volatile registers
|
|
||||||
push r11
|
|
||||||
push r10
|
|
||||||
push r9
|
|
||||||
push r8
|
|
||||||
push rax
|
|
||||||
push rdx
|
|
||||||
push rcx
|
|
||||||
|
|
||||||
; reserve space for 4 registers [ rcx,rdx,r8 and r9 ] 32 bytes
|
|
||||||
sub rsp,20h
|
|
||||||
|
|
||||||
; Get the return address of the function
|
|
||||||
mov rcx,rsp
|
|
||||||
mov rcx,qword ptr[rcx+58h]
|
|
||||||
sub rcx,5
|
|
||||||
|
|
||||||
;call the function to get the name of the callee and caller
|
|
||||||
call FuncEntry
|
|
||||||
|
|
||||||
;Release the space reserved for the registersk by adding 32 bytes
|
|
||||||
add rsp,20h
|
|
||||||
|
|
||||||
;Restore the registers back by poping out
|
|
||||||
pop rcx
|
|
||||||
pop rdx
|
|
||||||
pop rax
|
|
||||||
pop r8
|
|
||||||
pop r9
|
|
||||||
pop r10
|
|
||||||
pop r11
|
|
||||||
|
|
||||||
;return
|
|
||||||
ret
|
|
||||||
|
|
||||||
_penter endp
|
|
||||||
|
|
||||||
_pexit proc
|
|
||||||
|
|
||||||
; Store the volatile registers
|
|
||||||
push r11
|
|
||||||
push r10
|
|
||||||
push r9
|
|
||||||
push r8
|
|
||||||
push rax
|
|
||||||
push rdx
|
|
||||||
push rcx
|
|
||||||
|
|
||||||
; reserve space for 4 registers [ rcx,rdx,r8 and r9 ] 32 bytes
|
|
||||||
sub rsp,20h
|
|
||||||
|
|
||||||
; Get the return address of the function
|
|
||||||
mov rcx,rsp
|
|
||||||
mov rcx,qword ptr[rcx+58h]
|
|
||||||
|
|
||||||
call FuncExit
|
|
||||||
|
|
||||||
;Release the space reserved for the registersk by adding 32 bytes
|
|
||||||
add rsp,20h
|
|
||||||
|
|
||||||
;Restore the registers back by poping out
|
|
||||||
pop rcx
|
|
||||||
pop rdx
|
|
||||||
pop rax
|
|
||||||
pop r8
|
|
||||||
pop r9
|
|
||||||
pop r10
|
|
||||||
pop r11
|
|
||||||
|
|
||||||
;return
|
|
||||||
ret
|
|
||||||
|
|
||||||
_pexit endp
|
|
||||||
end
|
|
||||||
Submodule socket.io-client-cpp deleted from b196fa7537
+61
-35
@@ -23,33 +23,21 @@ void Application::RegisterShutdownHandler(const TShutdownHandler& Handler) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Application::GracefullyShutdown() {
|
void Application::GracefullyShutdown() {
|
||||||
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);
|
|
||||||
info("please wait while all subsystems are shutting down...");
|
info("please wait while all subsystems are shutting down...");
|
||||||
for (size_t i = 0; i < mShutdownHandlers.size(); ++i) {
|
std::unique_lock Lock(mShutdownHandlersMutex);
|
||||||
info("Subsystem " + std::to_string(i + 1) + "/" + std::to_string(mShutdownHandlers.size()) + " shutting down");
|
for (auto& Handler : mShutdownHandlers) {
|
||||||
mShutdownHandlers[i]();
|
Handler();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<int, 3> Application::VersionStrToInts(const std::string& str) {
|
std::string Application::ServerVersionString() {
|
||||||
std::array<int, 3> Version;
|
return mVersion.AsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<uint8_t, 3> Application::VersionStrToInts(const std::string& str) {
|
||||||
|
std::array<uint8_t, 3> Version;
|
||||||
std::stringstream ss(str);
|
std::stringstream ss(str);
|
||||||
for (int& i : Version) {
|
for (uint8_t& i : Version) {
|
||||||
std::string Part;
|
std::string Part;
|
||||||
std::getline(ss, Part, '.');
|
std::getline(ss, Part, '.');
|
||||||
std::from_chars(&*Part.begin(), &*Part.begin() + Part.size(), i);
|
std::from_chars(&*Part.begin(), &*Part.begin() + Part.size(), i);
|
||||||
@@ -57,12 +45,13 @@ std::array<int, 3> Application::VersionStrToInts(const std::string& str) {
|
|||||||
return Version;
|
return Version;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Application::IsOutdated(const std::array<int, 3>& Current, const std::array<int, 3>& Newest) {
|
// FIXME: This should be used by operator< on Version
|
||||||
if (Newest[0] > Current[0]) {
|
bool Application::IsOutdated(const Version& Current, const Version& Newest) {
|
||||||
|
if (Newest.major > Current.major) {
|
||||||
return true;
|
return true;
|
||||||
} else if (Newest[0] == Current[0] && Newest[1] > Current[1]) {
|
} else if (Newest.major == Current.major && Newest.minor > Current.minor) {
|
||||||
return true;
|
return true;
|
||||||
} else if (Newest[0] == Current[0] && Newest[1] == Current[1] && Newest[2] > Current[2]) {
|
} else if (Newest.major == Current.major && Newest.minor == Current.minor && Newest.patch > Current.patch) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@@ -75,17 +64,13 @@ void Application::CheckForUpdates() {
|
|||||||
auto Response = Http::GET(GetBackendHostname(), 443, "/v/s");
|
auto Response = Http::GET(GetBackendHostname(), 443, "/v/s");
|
||||||
bool Matches = std::regex_match(Response, VersionRegex);
|
bool Matches = std::regex_match(Response, VersionRegex);
|
||||||
if (Matches) {
|
if (Matches) {
|
||||||
auto MyVersion = VersionStrToInts(ServerVersion());
|
auto MyVersion = ServerVersion();
|
||||||
auto RemoteVersion = VersionStrToInts(Response);
|
auto RemoteVersion = Version(VersionStrToInts(Response));
|
||||||
if (IsOutdated(MyVersion, RemoteVersion)) {
|
if (IsOutdated(MyVersion, RemoteVersion)) {
|
||||||
std::string RealVersionString = std::to_string(RemoteVersion[0]) + ".";
|
std::string RealVersionString = RemoteVersion.AsString();
|
||||||
RealVersionString += std::to_string(RemoteVersion[1]) + ".";
|
warn(std::string(ANSI_YELLOW_BOLD) + "NEW VERSION OUT! There's a new version (v" + RealVersionString + ") of the BeamMP-Server available! For info on how to update your server, visit https://wiki.beammp.com/en/home/server-maintenance#updating-the-server." + std::string(ANSI_RESET));
|
||||||
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 {
|
} else {
|
||||||
info("Server up-to-date!");
|
info("Server up-to-date!");
|
||||||
char* crasher = nullptr;
|
|
||||||
crasher[4555] = 'c';
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
warn("Unable to fetch version from backend.");
|
warn("Unable to fetch version from backend.");
|
||||||
@@ -162,6 +147,21 @@ void RegisterThread(const std::string& str) {
|
|||||||
threadNameMap[std::this_thread::get_id()] = str;
|
threadNameMap[std::this_thread::get_id()] = str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Version::Version(uint8_t major, uint8_t minor, uint8_t patch)
|
||||||
|
: major(major)
|
||||||
|
, minor(minor)
|
||||||
|
, patch(patch) { }
|
||||||
|
|
||||||
|
Version::Version(const std::array<uint8_t, 3>& v)
|
||||||
|
: Version(v[0], v[1], v[2]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Version::AsString() {
|
||||||
|
std::stringstream ss {};
|
||||||
|
ss << int(major) << "." << int(minor) << "." << int(patch);
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
void LogChatMessage(const std::string& name, int id, const std::string& msg) {
|
void LogChatMessage(const std::string& name, int id, const std::string& msg) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "[CHAT] ";
|
ss << "[CHAT] ";
|
||||||
@@ -171,5 +171,31 @@ void LogChatMessage(const std::string& name, int id, const std::string& msg) {
|
|||||||
ss << name << "";
|
ss << name << "";
|
||||||
}
|
}
|
||||||
ss << msg;
|
ss << msg;
|
||||||
Application::Console().Write(ss.str());
|
}
|
||||||
|
|
||||||
|
std::string GetPlatformAgnosticErrorString() {
|
||||||
|
#ifdef WIN32
|
||||||
|
// This will provide us with the error code and an error message, all in one.
|
||||||
|
int err;
|
||||||
|
char msgbuf[256];
|
||||||
|
msgbuf[0] = '\0';
|
||||||
|
|
||||||
|
err = GetLastError();
|
||||||
|
|
||||||
|
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
nullptr,
|
||||||
|
err,
|
||||||
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
msgbuf,
|
||||||
|
sizeof(msgbuf),
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
if (*msgbuf) {
|
||||||
|
return std::to_string(GetLastError()) + " - " + std::string(msgbuf);
|
||||||
|
} else {
|
||||||
|
return std::to_string(GetLastError());
|
||||||
|
}
|
||||||
|
#else // posix
|
||||||
|
return std::strerror(errno);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-102
@@ -15,85 +15,7 @@ namespace net = boost::asio; // from <boost/asio.hpp>
|
|||||||
namespace ssl = net::ssl; // from <boost/asio/ssl.hpp>
|
namespace ssl = net::ssl; // from <boost/asio/ssl.hpp>
|
||||||
using tcp = net::ip::tcp; // from <boost/asio/ip/tcp.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, unsigned int* status) {
|
std::string GenericRequest(http::verb verb, const std::string& host, int port, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, const std::string& ContentType, unsigned int* status) {
|
||||||
try {
|
|
||||||
// Check command line arguments.
|
|
||||||
int version = 11;
|
|
||||||
|
|
||||||
// The io_context is required for all I/O
|
|
||||||
net::io_context ioc;
|
|
||||||
|
|
||||||
// The SSL context is required, and holds certificates
|
|
||||||
ssl::context ctx(ssl::context::tlsv12_client);
|
|
||||||
|
|
||||||
// This holds the root certificate used for verification
|
|
||||||
// we don't do / have this
|
|
||||||
// load_root_certificates(ctx);
|
|
||||||
|
|
||||||
// Verify the remote server's certificate
|
|
||||||
ctx.set_verify_mode(ssl::verify_none);
|
|
||||||
|
|
||||||
// These objects perform our I/O
|
|
||||||
tcp::resolver resolver(ioc);
|
|
||||||
beast::ssl_stream<beast::tcp_stream> stream(ioc, ctx);
|
|
||||||
|
|
||||||
// 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 };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look up the domain name
|
|
||||||
auto const results = resolver.resolve(host.c_str(), std::to_string(port));
|
|
||||||
|
|
||||||
// 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, int* status) {
|
|
||||||
try {
|
try {
|
||||||
net::io_context io;
|
net::io_context io;
|
||||||
|
|
||||||
@@ -107,7 +29,7 @@ std::string Http::POST(const std::string& host, const std::string& target, const
|
|||||||
decltype(resolver)::results_type results;
|
decltype(resolver)::results_type results;
|
||||||
auto try_connect_with_protocol = [&](tcp protocol) {
|
auto try_connect_with_protocol = [&](tcp protocol) {
|
||||||
try {
|
try {
|
||||||
results = resolver.resolve(protocol, host, std::to_string(443));
|
results = resolver.resolve(protocol, host, std::to_string(port));
|
||||||
if (!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str())) {
|
if (!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str())) {
|
||||||
boost::system::error_code ec { static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category() };
|
boost::system::error_code ec { static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category() };
|
||||||
// FIXME: we could throw and crash, if we like
|
// FIXME: we could throw and crash, if we like
|
||||||
@@ -127,20 +49,17 @@ std::string Http::POST(const std::string& host, const std::string& target, const
|
|||||||
bool ok = try_connect_with_protocol(tcp::v4());
|
bool ok = try_connect_with_protocol(tcp::v4());
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
Application::Console().Write("[ERROR] failed to resolve or connect in POST " + host + target);
|
Application::Console().Write("[ERROR] failed to resolve or connect in POST " + host + target);
|
||||||
Sentry.AddErrorBreadcrumb("failed to resolve or connect to " + host + target, __FILE__, std::to_string(__LINE__)); // FIXME: this is ugly.
|
|
||||||
return "-1";
|
return "-1";
|
||||||
}
|
}
|
||||||
//}
|
//}
|
||||||
stream.handshake(ssl::stream_base::client);
|
stream.handshake(ssl::stream_base::client);
|
||||||
http::request<http::string_body> req { http::verb::post, target, 11 /* http 1.1 */ };
|
http::request<http::string_body> req { verb, target, 11 /* http 1.1 */ };
|
||||||
|
|
||||||
req.set(http::field::host, host);
|
req.set(http::field::host, host);
|
||||||
if (!body.empty()) {
|
if (!body.empty()) {
|
||||||
if (json) {
|
req.set(http::field::content_type, ContentType); // "application/json"
|
||||||
req.set(http::field::content_type, "application/json");
|
// "application/x-www-form-urlencoded"
|
||||||
} else {
|
|
||||||
req.set(http::field::content_type, "application/x-www-form-urlencoded");
|
|
||||||
}
|
|
||||||
req.set(http::field::content_length, std::to_string(body.size()));
|
req.set(http::field::content_length, std::to_string(body.size()));
|
||||||
req.body() = body;
|
req.body() = body;
|
||||||
// info("body is " + body + " (" + req.body() + ")");
|
// info("body is " + body + " (" + req.body() + ")");
|
||||||
@@ -188,6 +107,10 @@ std::string Http::POST(const std::string& host, const std::string& target, const
|
|||||||
}
|
}
|
||||||
Sentry.SetContext("https-post-response-data", response_data);
|
Sentry.SetContext("https-post-response-data", response_data);
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
*status = response.base().result_int();
|
||||||
|
}
|
||||||
|
|
||||||
std::stringstream result;
|
std::stringstream result;
|
||||||
result << response;
|
result << response;
|
||||||
|
|
||||||
@@ -198,19 +121,26 @@ std::string Http::POST(const std::string& host, const std::string& target, const
|
|||||||
// info(result.str());
|
// info(result.str());
|
||||||
std::string debug_response_str;
|
std::string debug_response_str;
|
||||||
std::getline(result, debug_response_str);
|
std::getline(result, debug_response_str);
|
||||||
|
|
||||||
//debug("POST " + host + target + ": " + debug_response_str);
|
//debug("POST " + host + target + ": " + debug_response_str);
|
||||||
return std::string(response.body());
|
return std::string(response.body());
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
Application::Console().Write(e.what());
|
Application::Console().Write(__func__ + std::string(": ") + e.what());
|
||||||
Sentry.AddErrorBreadcrumb(e.what(), __FILE__, std::to_string(__LINE__)); // FIXME: this is ugly.
|
return Http::ErrorString;
|
||||||
return "-1";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Http::GET(const std::string& host, int port, const std::string& target, unsigned int* status) {
|
||||||
|
return GenericRequest(http::verb::get, host, port, target, {}, {}, {}, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Http::POST(const std::string& host, int port, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, const std::string& ContentType, unsigned int* status) {
|
||||||
|
return GenericRequest(http::verb::post, host, port, target, fields, body, ContentType, status);
|
||||||
|
}
|
||||||
|
|
||||||
// RFC 2616, RFC 7231
|
// RFC 2616, RFC 7231
|
||||||
static std::map<size_t, const char*> Map = {
|
static std::map<size_t, const char*> Map = {
|
||||||
{ -1, "Invalid Response Code"},
|
|
||||||
{ 100, "Continue" },
|
{ 100, "Continue" },
|
||||||
{ 101, "Switching Protocols" },
|
{ 101, "Switching Protocols" },
|
||||||
{ 102, "Processing" },
|
{ 102, "Processing" },
|
||||||
@@ -273,22 +203,12 @@ static std::map<size_t, const char*> Map = {
|
|||||||
{ 508, "Loop Detected" },
|
{ 508, "Loop Detected" },
|
||||||
{ 510, "Not Extended" },
|
{ 510, "Not Extended" },
|
||||||
{ 511, "Network Authentication Required" },
|
{ 511, "Network Authentication Required" },
|
||||||
// cloudflare status codes
|
|
||||||
{ 520, "(CDN) Web Server Returns An Unknown Error" },
|
|
||||||
{ 521, "(CDN) Web Server Is Down" },
|
|
||||||
{ 522, "(CDN) Connection Timed Out" },
|
|
||||||
{ 523, "(CDN) Origin Is Unreachable" },
|
|
||||||
{ 524, "(CDN) A Timeout Occurred" },
|
|
||||||
{ 525, "(CDN) SSL Handshake Failed" },
|
|
||||||
{ 526, "(CDN) Invalid SSL Certificate" },
|
|
||||||
{ 527, "(CDN) Railgun Listener To Origin Error" },
|
|
||||||
{ 530, "(CDN) 1XXX Internal Error" },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string Http::Status::ToString(int code) {
|
std::string Http::Status::ToString(int code) {
|
||||||
if (Map.find(code) != Map.end()) {
|
if (Map.find(code) != Map.end()) {
|
||||||
return Map.at(code);
|
return Map.at(code);
|
||||||
} else {
|
} else {
|
||||||
return std::to_string(code);
|
return "Unassigned";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
#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
|
|
||||||
}
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
#include "SocketIO.h"
|
|
||||||
#include "Common.h"
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
|
|
||||||
//TODO Default disabled with config option
|
|
||||||
static std::unique_ptr<SocketIO> SocketIOInstance = std::make_unique<SocketIO>();
|
|
||||||
|
|
||||||
SocketIO& SocketIO::Get() {
|
|
||||||
return *SocketIOInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
SocketIO::SocketIO() noexcept
|
|
||||||
: mThread([this] { ThreadMain(); }) {
|
|
||||||
|
|
||||||
mClient.socket()->on("network", [&](sio::event&e) {
|
|
||||||
if(e.get_message()->get_string() == "Welcome"){
|
|
||||||
info("SocketIO Authenticated!");
|
|
||||||
mAuthenticated = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
mClient.socket()->on("welcome", [&](sio::event&) {
|
|
||||||
info("Got welcome from backend! Authenticating SocketIO...");
|
|
||||||
mClient.socket()->emit("onInitConnection", Application::Settings.Key);
|
|
||||||
});
|
|
||||||
|
|
||||||
mClient.set_logs_quiet();
|
|
||||||
mClient.set_reconnect_delay(10000);
|
|
||||||
mClient.connect(Application::GetBackendUrlForSocketIO());
|
|
||||||
}
|
|
||||||
|
|
||||||
SocketIO::~SocketIO() {
|
|
||||||
mCloseThread.store(true);
|
|
||||||
mThread.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static constexpr auto EventNameFromEnum(SocketIOEvent Event) {
|
|
||||||
switch (Event) {
|
|
||||||
case SocketIOEvent::CPUUsage:
|
|
||||||
return "cpu usage";
|
|
||||||
case SocketIOEvent::MemoryUsage:
|
|
||||||
return "memory usage";
|
|
||||||
case SocketIOEvent::ConsoleOut:
|
|
||||||
return "console out";
|
|
||||||
case SocketIOEvent::NetworkUsage:
|
|
||||||
return "network usage";
|
|
||||||
case SocketIOEvent::PlayerList:
|
|
||||||
return "player list";
|
|
||||||
default:
|
|
||||||
error("unreachable code reached (developer error)");
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SocketIO::Emit(SocketIOEvent Event, const std::string& Data) {
|
|
||||||
if (!mAuthenticated) {
|
|
||||||
debug("trying to emit a socket.io event when not yet authenticated");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::string EventName = EventNameFromEnum(Event);
|
|
||||||
debug("emitting event \"" + EventName + "\" with data: \"" + Data);
|
|
||||||
std::unique_lock Lock(mQueueMutex);
|
|
||||||
mQueue.push_back({EventName, Data });
|
|
||||||
debug("queue now has " + std::to_string(mQueue.size()) + " events");
|
|
||||||
}
|
|
||||||
|
|
||||||
void SocketIO::ThreadMain() {
|
|
||||||
while (!mCloseThread.load()) {
|
|
||||||
bool empty;
|
|
||||||
{ // queue lock scope
|
|
||||||
std::unique_lock Lock(mQueueMutex);
|
|
||||||
empty = mQueue.empty();
|
|
||||||
} // end queue lock scope
|
|
||||||
if (empty || !mClient.opened()) {
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
Event TheEvent;
|
|
||||||
{ // queue lock scope
|
|
||||||
std::unique_lock Lock(mQueueMutex);
|
|
||||||
TheEvent = mQueue.front();
|
|
||||||
mQueue.pop_front();
|
|
||||||
} // end queue lock scope
|
|
||||||
debug("sending \"" + TheEvent.Name + "\" event");
|
|
||||||
mClient.socket()->emit(TheEvent.Name, TheEvent.Data);
|
|
||||||
debug("sent \"" + TheEvent.Name + "\" event");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// using std::cout as this happens during static destruction and the logger might be dead already
|
|
||||||
std::cout << "closing " + std::string(__func__) << std::endl;
|
|
||||||
|
|
||||||
mClient.sync_close();
|
|
||||||
mClient.clear_con_listeners();
|
|
||||||
|
|
||||||
std::cout << "closed" << std::endl;
|
|
||||||
}
|
|
||||||
+45
-95
@@ -1,4 +1,6 @@
|
|||||||
#include <toml.hpp> // header-only version of TOML++
|
#define TOML11_PRESERVE_COMMENTS_BY_DEFAULT
|
||||||
|
|
||||||
|
#include <toml11/toml.hpp> // header-only version of TOML++
|
||||||
|
|
||||||
#include "TConfig.h"
|
#include "TConfig.h"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@@ -18,8 +20,6 @@ static constexpr std::string_view StrName = "Name";
|
|||||||
static constexpr std::string_view StrDescription = "Description";
|
static constexpr std::string_view StrDescription = "Description";
|
||||||
static constexpr std::string_view StrResourceFolder = "ResourceFolder";
|
static constexpr std::string_view StrResourceFolder = "ResourceFolder";
|
||||||
static constexpr std::string_view StrAuthKey = "AuthKey";
|
static constexpr std::string_view StrAuthKey = "AuthKey";
|
||||||
static constexpr std::string_view StrSendErrors = "SendErrors";
|
|
||||||
static constexpr std::string_view StrSendErrorsMessageEnabled = "SendErrorsShowMessage";
|
|
||||||
|
|
||||||
TConfig::TConfig() {
|
TConfig::TConfig() {
|
||||||
if (!fs::exists(ConfigFileName) || !fs::is_regular_file(ConfigFileName)) {
|
if (!fs::exists(ConfigFileName) || !fs::is_regular_file(ConfigFileName)) {
|
||||||
@@ -34,13 +34,21 @@ TConfig::TConfig() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteSendErrors(const std::string& name) {
|
void TConfig::FlushToFile() {
|
||||||
std::ofstream CfgFile { name, std::ios::out | std::ios::app };
|
auto data = toml::parse(ConfigFileName);
|
||||||
CfgFile << "# You can turn on/off the SendErrors message you get on startup here" << std::endl
|
data["General"] = toml::table();
|
||||||
<< StrSendErrorsMessageEnabled << " = true" << std::endl
|
data["General"][StrAuthKey.data()] = Application::Settings.Key;
|
||||||
<< "# 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`."
|
data["General"][StrDebug.data()] = Application::Settings.DebugModeEnabled;
|
||||||
<< std::endl
|
data["General"][StrPrivate.data()] = Application::Settings.Private;
|
||||||
<< StrSendErrors << " = true" << std::endl;
|
data["General"][StrPort.data()] = Application::Settings.Port;
|
||||||
|
data["General"][StrName.data()] = Application::Settings.ServerName;
|
||||||
|
data["General"][StrMaxCars.data()] = Application::Settings.MaxCars;
|
||||||
|
data["General"][StrMaxPlayers.data()] = Application::Settings.MaxPlayers;
|
||||||
|
data["General"][StrMap.data()] = Application::Settings.MapName;
|
||||||
|
data["General"][StrDescription.data()] = Application::Settings.ServerDesc;
|
||||||
|
data["General"][StrResourceFolder.data()] = Application::Settings.Resource;
|
||||||
|
std::ofstream Stream(ConfigFileName);
|
||||||
|
Stream << data << std::flush;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TConfig::CreateConfigFile(std::string_view name) {
|
void TConfig::CreateConfigFile(std::string_view name) {
|
||||||
@@ -55,37 +63,33 @@ void TConfig::CreateConfigFile(std::string_view name) {
|
|||||||
error("an error occurred and was ignored during config transfer: " + std::string(e.what()));
|
error("an error occurred and was ignored during config transfer: " + std::string(e.what()));
|
||||||
}
|
}
|
||||||
|
|
||||||
toml::table tbl { {
|
{ // create file context
|
||||||
|
std::ofstream ofs(name.data());
|
||||||
|
}
|
||||||
|
|
||||||
{ "General",
|
auto data = toml::parse<toml::preserve_comments>(name.data());
|
||||||
toml::table { {
|
|
||||||
|
|
||||||
{ StrDebug, Application::Settings.DebugModeEnabled },
|
data["General"] = toml::table();
|
||||||
{ StrPrivate, Application::Settings.Private },
|
data["General"][StrAuthKey.data()] = Application::Settings.Key;
|
||||||
{ StrPort, Application::Settings.Port },
|
data["General"][StrDebug.data()] = Application::Settings.DebugModeEnabled;
|
||||||
{ StrMaxCars, Application::Settings.MaxCars },
|
data["General"][StrPrivate.data()] = Application::Settings.Private;
|
||||||
{ StrMaxPlayers, Application::Settings.MaxPlayers },
|
data["General"][StrPort.data()] = Application::Settings.Port;
|
||||||
{ StrMap, Application::Settings.MapName },
|
data["General"][StrName.data()] = Application::Settings.ServerName;
|
||||||
{ StrName, Application::Settings.ServerName },
|
data["General"][StrMaxCars.data()] = Application::Settings.MaxCars;
|
||||||
{ StrDescription, Application::Settings.ServerDesc },
|
data["General"][StrMaxPlayers.data()] = Application::Settings.MaxPlayers;
|
||||||
{ StrResourceFolder, Application::Settings.Resource },
|
data["General"][StrMap.data()] = Application::Settings.MapName;
|
||||||
{ StrAuthKey, Application::Settings.Key },
|
data["General"][StrDescription.data()] = Application::Settings.ServerDesc;
|
||||||
//{ StrSendErrors, Application::Settings.SendErrors },
|
data["General"][StrResourceFolder.data()] = Application::Settings.Resource;
|
||||||
|
|
||||||
} } },
|
|
||||||
|
|
||||||
} };
|
|
||||||
std::ofstream ofs { std::string(name) };
|
std::ofstream ofs { std::string(name) };
|
||||||
if (ofs.good()) {
|
if (ofs.good()) {
|
||||||
ofs << "# This is the BeamMP-Server config file.\n"
|
ofs << "# This is the BeamMP-Server config file.\n"
|
||||||
"# Help & Documentation: `https://wiki.beammp.com/en/home/server-maintenance`\n"
|
"# Help & Documentation: `https://wiki.beammp.com/en/home/server-maintenance`\n"
|
||||||
"# IMPORTANT: Fill in the AuthKey with the key you got from `https://beammp.com/k/dashboard` on the left under \"Keys\"\n"
|
"# IMPORTANT: Fill in the AuthKey with the key you got from `https://beammp.com/k/dashboard` on the left under \"Keys\"\n"
|
||||||
<< '\n';
|
<< '\n';
|
||||||
ofs << tbl << '\n';
|
ofs << data << '\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.");
|
error("There was no \"" + std::string(ConfigFileName) + "\" file (this is normal for the first time running the server), so one was generated for you. It was automatically filled with the settings from your Server.cfg, if you have one. Please open ServerConfig.toml and ensure your AuthKey and other settings are filled in and correct, then restart the server. The old Server.cfg file will no longer be used and causes a warning if it exists from now on.");
|
||||||
mFailed = true;
|
mFailed = true;
|
||||||
ofs.close();
|
|
||||||
WriteSendErrors(std::string(name));
|
|
||||||
} else {
|
} else {
|
||||||
error("Couldn't create " + std::string(name) + ". Check permissions, try again, and contact support if it continues not to work.");
|
error("Couldn't create " + std::string(name) + ". Check permissions, try again, and contact support if it continues not to work.");
|
||||||
mFailed = true;
|
mFailed = true;
|
||||||
@@ -94,71 +98,17 @@ void TConfig::CreateConfigFile(std::string_view name) {
|
|||||||
|
|
||||||
void TConfig::ParseFromFile(std::string_view name) {
|
void TConfig::ParseFromFile(std::string_view name) {
|
||||||
try {
|
try {
|
||||||
toml::table FullTable = toml::parse_file(name);
|
toml::value data = toml::parse<toml::preserve_comments>(name.data());
|
||||||
toml::table GeneralTable = *FullTable["General"].as_table();
|
Application::Settings.DebugModeEnabled = data["General"][StrDebug.data()].as_boolean();
|
||||||
if (auto val = GeneralTable[StrDebug].value<bool>(); val.has_value()) {
|
Application::Settings.Private = data["General"][StrPrivate.data()].as_boolean();
|
||||||
Application::Settings.DebugModeEnabled = val.value();
|
Application::Settings.Port = data["General"][StrPort.data()].as_integer();
|
||||||
} else {
|
Application::Settings.MaxCars = data["General"][StrMaxCars.data()].as_integer();
|
||||||
throw std::runtime_error(std::string(StrDebug));
|
Application::Settings.MaxPlayers = data["General"][StrMaxPlayers.data()].as_integer();
|
||||||
}
|
Application::Settings.MapName = data["General"][StrMap.data()].as_string();
|
||||||
if (auto val = GeneralTable[StrPrivate].value<bool>(); val.has_value()) {
|
Application::Settings.ServerName = data["General"][StrName.data()].as_string();
|
||||||
Application::Settings.Private = val.value();
|
Application::Settings.ServerDesc = data["General"][StrDescription.data()].as_string();
|
||||||
} else {
|
Application::Settings.Resource = data["General"][StrResourceFolder.data()].as_string();
|
||||||
throw std::runtime_error(std::string(StrPrivate));
|
Application::Settings.Key = data["General"][StrAuthKey.data()].as_string();
|
||||||
}
|
|
||||||
if (auto val = GeneralTable[StrPort].value<int>(); val.has_value()) {
|
|
||||||
Application::Settings.Port = val.value();
|
|
||||||
} else {
|
|
||||||
throw std::runtime_error(std::string(StrPort));
|
|
||||||
}
|
|
||||||
if (auto val = GeneralTable[StrMaxCars].value<int>(); val.has_value()) {
|
|
||||||
Application::Settings.MaxCars = val.value();
|
|
||||||
} else {
|
|
||||||
throw std::runtime_error(std::string(StrMaxCars));
|
|
||||||
}
|
|
||||||
if (auto val = GeneralTable[StrMaxPlayers].value<int>(); val.has_value()) {
|
|
||||||
Application::Settings.MaxPlayers = val.value();
|
|
||||||
} else {
|
|
||||||
throw std::runtime_error(std::string(StrMaxPlayers));
|
|
||||||
}
|
|
||||||
if (auto val = GeneralTable[StrMap].value<std::string>(); val.has_value()) {
|
|
||||||
Application::Settings.MapName = val.value();
|
|
||||||
} else {
|
|
||||||
throw std::runtime_error(std::string(StrMap));
|
|
||||||
}
|
|
||||||
if (auto val = GeneralTable[StrName].value<std::string>(); val.has_value()) {
|
|
||||||
Application::Settings.ServerName = val.value();
|
|
||||||
} else {
|
|
||||||
throw std::runtime_error(std::string(StrName));
|
|
||||||
}
|
|
||||||
if (auto val = GeneralTable[StrDescription].value<std::string>(); val.has_value()) {
|
|
||||||
Application::Settings.ServerDesc = val.value();
|
|
||||||
} else {
|
|
||||||
throw std::runtime_error(std::string(StrDescription));
|
|
||||||
}
|
|
||||||
if (auto val = GeneralTable[StrResourceFolder].value<std::string>(); val.has_value()) {
|
|
||||||
Application::Settings.Resource = val.value();
|
|
||||||
} else {
|
|
||||||
throw std::runtime_error(std::string(StrResourceFolder));
|
|
||||||
}
|
|
||||||
if (auto val = GeneralTable[StrAuthKey].value<std::string>(); val.has_value()) {
|
|
||||||
Application::Settings.Key = val.value();
|
|
||||||
} 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) {
|
} catch (const std::exception& err) {
|
||||||
error("Error parsing config file value: " + std::string(err.what()));
|
error("Error parsing config file value: " + std::string(err.what()));
|
||||||
mFailed = true;
|
mFailed = true;
|
||||||
|
|||||||
@@ -36,6 +36,10 @@ void THeartbeatThread::operator()() {
|
|||||||
Body += "&pps=" + Application::PPS();
|
Body += "&pps=" + Application::PPS();
|
||||||
|
|
||||||
auto SentryReportError = [&](const std::string& transaction, int status) {
|
auto SentryReportError = [&](const std::string& transaction, int status) {
|
||||||
|
if (status < 0) {
|
||||||
|
status = 0;
|
||||||
|
}
|
||||||
|
|
||||||
auto Lock = Sentry.CreateExclusiveContext();
|
auto Lock = Sentry.CreateExclusiveContext();
|
||||||
Sentry.SetContext("heartbeat",
|
Sentry.SetContext("heartbeat",
|
||||||
{ { "response-body", T },
|
{ { "response-body", T },
|
||||||
@@ -46,20 +50,21 @@ void THeartbeatThread::operator()() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
auto Target = "/heartbeat";
|
auto Target = "/heartbeat";
|
||||||
int ResponseCode = -1;
|
unsigned int ResponseCode = 0;
|
||||||
T = Http::POST(Application::GetBackendHostname(), Target, {}, Body, false, &ResponseCode);
|
T = Http::POST(Application::GetBackendHostname(), 443, Target, {}, Body, "application/x-www-form-urlencoded", &ResponseCode);
|
||||||
|
|
||||||
if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) {
|
if (T.substr(0, 2) != "20" || ResponseCode != 200) {
|
||||||
trace("got " + T + " from backend");
|
trace("got " + T + " from backend");
|
||||||
SentryReportError(Application::GetBackendHostname() + Target, ResponseCode);
|
SentryReportError(Application::GetBackendHostname() + Target, ResponseCode);
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||||
T = Http::POST(Application::GetBackup1Hostname(), Target, {}, Body, false, &ResponseCode);
|
T = Http::POST(Application::GetBackup1Hostname(), 443, Target, {}, Body, "application/x-www-form-urlencoded", &ResponseCode);
|
||||||
if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) {
|
if (T.substr(0, 2) != "20" || ResponseCode != 200) {
|
||||||
SentryReportError(Application::GetBackup1Hostname() + Target, ResponseCode);
|
SentryReportError(Application::GetBackup1Hostname() + Target, ResponseCode);
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||||
T = Http::POST(Application::GetBackup2Hostname(), Target, {}, Body, false, &ResponseCode);
|
T = Http::POST(Application::GetBackup2Hostname(), 443, Target, {}, Body, "application/x-www-form-urlencoded", &ResponseCode);
|
||||||
if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) {
|
if (T.substr(0, 2) != "20" || ResponseCode != 200) {
|
||||||
warn("Backend system refused server! Server will not show in the public server list.");
|
warn("Backend system refused server! Server will not show in the public server list.");
|
||||||
|
|
||||||
isAuth = false;
|
isAuth = false;
|
||||||
SentryReportError(Application::GetBackup2Hostname() + Target, ResponseCode);
|
SentryReportError(Application::GetBackup2Hostname() + Target, ResponseCode);
|
||||||
}
|
}
|
||||||
@@ -89,8 +94,8 @@ std::string THeartbeatThread::GenerateCall() {
|
|||||||
<< "&port=" << Application::Settings.Port
|
<< "&port=" << Application::Settings.Port
|
||||||
<< "&map=" << Application::Settings.MapName
|
<< "&map=" << Application::Settings.MapName
|
||||||
<< "&private=" << (Application::Settings.Private ? "true" : "false")
|
<< "&private=" << (Application::Settings.Private ? "true" : "false")
|
||||||
<< "&version=" << Application::ServerVersion()
|
<< "&version=" << Application::ServerVersionString()
|
||||||
<< "&clientversion=" << Application::ClientVersion()
|
<< "&clientversion=" << Application::ClientVersionString()
|
||||||
<< "&name=" << Application::Settings.ServerName
|
<< "&name=" << Application::Settings.ServerName
|
||||||
<< "&modlist=" << mResourceManager.TrimmedList()
|
<< "&modlist=" << mResourceManager.TrimmedList()
|
||||||
<< "&modstotalsize=" << mResourceManager.MaxModSize()
|
<< "&modstotalsize=" << mResourceManager.MaxModSize()
|
||||||
@@ -104,8 +109,10 @@ THeartbeatThread::THeartbeatThread(TResourceManager& ResourceManager, TServer& S
|
|||||||
, mServer(Server) {
|
, mServer(Server) {
|
||||||
Application::RegisterShutdownHandler([&] {
|
Application::RegisterShutdownHandler([&] {
|
||||||
if (mThread.joinable()) {
|
if (mThread.joinable()) {
|
||||||
|
debug("shutting down Heartbeat");
|
||||||
mShutdown = true;
|
mShutdown = true;
|
||||||
mThread.join();
|
mThread.join();
|
||||||
|
debug("shut down Heartbeat");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Start();
|
Start();
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
std::unordered_map<std::string, lua_State*> TLuaEngine::mGlobals;
|
||||||
|
|
||||||
// necessary as lua relies on global state
|
// necessary as lua relies on global state
|
||||||
TLuaEngine* TheEngine;
|
TLuaEngine* TheEngine;
|
||||||
|
|
||||||
@@ -23,8 +25,10 @@ TLuaEngine::TLuaEngine(TServer& Server, TNetwork& Network)
|
|||||||
FolderList(Path, false);
|
FolderList(Path, false);
|
||||||
mPath = Path;
|
mPath = Path;
|
||||||
Application::RegisterShutdownHandler([&] {if (mThread.joinable()) {
|
Application::RegisterShutdownHandler([&] {if (mThread.joinable()) {
|
||||||
|
debug("shutting down LuaEngine");
|
||||||
mShutdown = true;
|
mShutdown = true;
|
||||||
mThread.join();
|
mThread.join();
|
||||||
|
debug("shut down LuaEngine");
|
||||||
} });
|
} });
|
||||||
Start();
|
Start();
|
||||||
}
|
}
|
||||||
|
|||||||
+318
-58
@@ -3,14 +3,44 @@
|
|||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "CustomAssert.h"
|
#include "CustomAssert.h"
|
||||||
#include "Defer.h"
|
#include "Defer.h"
|
||||||
|
#include "Http.h"
|
||||||
#include "TLuaEngine.h"
|
#include "TLuaEngine.h"
|
||||||
#include "TNetwork.h"
|
#include "TNetwork.h"
|
||||||
#include "TServer.h"
|
#include "TServer.h"
|
||||||
|
#include <functional>
|
||||||
#include <future>
|
#include <future>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
// TODO: REWRITE
|
namespace LuaTable {
|
||||||
|
void Begin(lua_State* L) {
|
||||||
|
lua_newtable(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
void End(lua_State* L, const std::string& name) {
|
||||||
|
lua_setglobal(L, name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeginEntry(lua_State* L, const std::string& name) {
|
||||||
|
lua_pushstring(L, name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void EndEntry(lua_State* L) {
|
||||||
|
lua_settable(L, -3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InsertFunction(lua_State* L, const std::string& name, lua_CFunction func) {
|
||||||
|
BeginEntry(L, name);
|
||||||
|
lua_pushcfunction(L, func);
|
||||||
|
EndEntry(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InsertInteger(lua_State* L, const std::string& name, lua_Integer i) {
|
||||||
|
BeginEntry(L, name);
|
||||||
|
lua_pushinteger(L, i);
|
||||||
|
EndEntry(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void SendError(TLuaEngine& Engine, lua_State* L, const std::string& msg);
|
void SendError(TLuaEngine& Engine, lua_State* L, const std::string& msg);
|
||||||
std::any CallFunction(TLuaFile* lua, const std::string& FuncName, std::shared_ptr<TLuaArg> Arg);
|
std::any CallFunction(TLuaFile* lua, const std::string& FuncName, std::shared_ptr<TLuaArg> Arg);
|
||||||
@@ -40,11 +70,13 @@ std::shared_ptr<TLuaArg> CreateArg(lua_State* L, int T, int S) {
|
|||||||
}
|
}
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClearStack(lua_State* L) {
|
void ClearStack(lua_State* L) {
|
||||||
lua_settop(L, 0);
|
lua_settop(L, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::any Trigger(TLuaFile* lua, const std::string& R, std::shared_ptr<TLuaArg> arg) {
|
std::any Trigger(TLuaFile* lua, const std::string& R, std::shared_ptr<TLuaArg> arg) {
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lockGuard(lua->Lock);
|
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::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();
|
std::future<std::any> f1 = task.get_future();
|
||||||
@@ -198,6 +230,7 @@ void ExecuteAsync(TLuaFile* lua, const std::string& FuncName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CallAsync(TLuaFile* lua, const std::string& Func, int U) {
|
void CallAsync(TLuaFile* lua, const std::string& Func, int U) {
|
||||||
|
|
||||||
lua->SetStopThread(false);
|
lua->SetStopThread(false);
|
||||||
int D = 1000 / U;
|
int D = 1000 / U;
|
||||||
while (!lua->GetStopThread()) {
|
while (!lua->GetStopThread()) {
|
||||||
@@ -348,11 +381,11 @@ int lua_GetIdentifiers(lua_State* L) {
|
|||||||
auto IDs = MaybeClient.value().lock()->GetIdentifiers();
|
auto IDs = MaybeClient.value().lock()->GetIdentifiers();
|
||||||
if (IDs.empty())
|
if (IDs.empty())
|
||||||
return 0;
|
return 0;
|
||||||
lua_newtable(L);
|
LuaTable::Begin(L);
|
||||||
for (const std::string& ID : IDs) {
|
for (const auto& Pair : IDs) {
|
||||||
lua_pushstring(L, ID.substr(0, ID.find(':')).c_str());
|
LuaTable::BeginEntry(L, Pair.first);
|
||||||
lua_pushstring(L, ID.c_str());
|
lua_pushstring(L, Pair.second.c_str());
|
||||||
lua_settable(L, -3);
|
LuaTable::EndEntry(L);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
return 0;
|
return 0;
|
||||||
@@ -532,49 +565,49 @@ int lua_Set(lua_State* L) {
|
|||||||
case 0: //debug
|
case 0: //debug
|
||||||
if (lua_isboolean(L, 2)) {
|
if (lua_isboolean(L, 2)) {
|
||||||
Application::Settings.DebugModeEnabled = lua_toboolean(L, 2);
|
Application::Settings.DebugModeEnabled = lua_toboolean(L, 2);
|
||||||
info(Name + (" | Debug -> ") + (Application::Settings.DebugModeEnabled ? "true" : "false"));
|
info(Name + (" | Set `Debug` to ") + (Application::Settings.DebugModeEnabled ? "true" : "false"));
|
||||||
} else
|
} else
|
||||||
SendError(Engine(), L, ("set invalid argument [2] expected boolean for ID : 0"));
|
SendError(Engine(), L, ("set invalid argument [2] expected boolean for ID : 0"));
|
||||||
break;
|
break;
|
||||||
case 1: //private
|
case 1: //private
|
||||||
if (lua_isboolean(L, 2)) {
|
if (lua_isboolean(L, 2)) {
|
||||||
Application::Settings.Private = lua_toboolean(L, 2);
|
Application::Settings.Private = lua_toboolean(L, 2);
|
||||||
info(Name + (" | Private -> ") + (Application::Settings.Private ? "true" : "false"));
|
info(Name + (" | Set `Private` to ") + (Application::Settings.Private ? "true" : "false"));
|
||||||
} else
|
} else
|
||||||
SendError(Engine(), L, ("set invalid argument [2] expected boolean for ID : 1"));
|
SendError(Engine(), L, ("set invalid argument [2] expected boolean for ID : 1"));
|
||||||
break;
|
break;
|
||||||
case 2: //max cars
|
case 2: //max cars
|
||||||
if (lua_isnumber(L, 2)) {
|
if (lua_isnumber(L, 2)) {
|
||||||
Application::Settings.MaxCars = int(lua_tointeger(L, 2));
|
Application::Settings.MaxCars = int(lua_tointeger(L, 2));
|
||||||
info(Name + (" | MaxCars -> ") + std::to_string(Application::Settings.MaxCars));
|
info(Name + (" | Set `MaxCars` to ") + std::to_string(Application::Settings.MaxCars));
|
||||||
} else
|
} else
|
||||||
SendError(Engine(), L, ("set invalid argument [2] expected number for ID : 2"));
|
SendError(Engine(), L, ("set invalid argument [2] expected number for ID : 2"));
|
||||||
break;
|
break;
|
||||||
case 3: //max players
|
case 3: //max players
|
||||||
if (lua_isnumber(L, 2)) {
|
if (lua_isnumber(L, 2)) {
|
||||||
Application::Settings.MaxPlayers = int(lua_tointeger(L, 2));
|
Application::Settings.MaxPlayers = int(lua_tointeger(L, 2));
|
||||||
info(Name + (" | MaxPlayers -> ") + std::to_string(Application::Settings.MaxPlayers));
|
info(Name + (" | Set `MaxPlayers` to ") + std::to_string(Application::Settings.MaxPlayers));
|
||||||
} else
|
} else
|
||||||
SendError(Engine(), L, ("set invalid argument [2] expected number for ID : 3"));
|
SendError(Engine(), L, ("set invalid argument [2] expected number for ID : 3"));
|
||||||
break;
|
break;
|
||||||
case 4: //Map
|
case 4: //Map
|
||||||
if (lua_isstring(L, 2)) {
|
if (lua_isstring(L, 2)) {
|
||||||
Application::Settings.MapName = lua_tostring(L, 2);
|
Application::Settings.MapName = lua_tostring(L, 2);
|
||||||
info(Name + (" | MapName -> ") + Application::Settings.MapName);
|
info(Name + (" | Set `Map` to ") + Application::Settings.MapName);
|
||||||
} else
|
} else
|
||||||
SendError(Engine(), L, ("set invalid argument [2] expected string for ID : 4"));
|
SendError(Engine(), L, ("set invalid argument [2] expected string for ID : 4"));
|
||||||
break;
|
break;
|
||||||
case 5: //Name
|
case 5: //Name
|
||||||
if (lua_isstring(L, 2)) {
|
if (lua_isstring(L, 2)) {
|
||||||
Application::Settings.ServerName = lua_tostring(L, 2);
|
Application::Settings.ServerName = lua_tostring(L, 2);
|
||||||
info(Name + (" | ServerName -> ") + Application::Settings.ServerName);
|
info(Name + (" | Set `Name` to ") + Application::Settings.ServerName);
|
||||||
} else
|
} else
|
||||||
SendError(Engine(), L, ("set invalid argument [2] expected string for ID : 5"));
|
SendError(Engine(), L, ("set invalid argument [2] expected string for ID : 5"));
|
||||||
break;
|
break;
|
||||||
case 6: //Desc
|
case 6: //Desc
|
||||||
if (lua_isstring(L, 2)) {
|
if (lua_isstring(L, 2)) {
|
||||||
Application::Settings.ServerDesc = lua_tostring(L, 2);
|
Application::Settings.ServerDesc = lua_tostring(L, 2);
|
||||||
info(Name + (" | ServerDesc -> ") + Application::Settings.ServerDesc);
|
info(Name + (" | Set `Description` to ") + Application::Settings.ServerDesc);
|
||||||
} else
|
} else
|
||||||
SendError(Engine(), L, ("set invalid argument [2] expected string for ID : 6"));
|
SendError(Engine(), L, ("set invalid argument [2] expected string for ID : 6"));
|
||||||
break;
|
break;
|
||||||
@@ -582,12 +615,11 @@ int lua_Set(lua_State* L) {
|
|||||||
warn(("Invalid config ID : ") + std::to_string(C));
|
warn(("Invalid config ID : ") + std::to_string(C));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
int lua_Print(lua_State* L) {
|
int InternalLuaPrint(lua_State* L, bool pretty) {
|
||||||
int Arg = lua_gettop(L);
|
int Arg = lua_gettop(L);
|
||||||
std::string to_print;
|
std::string to_print;
|
||||||
for (int i = 1; i <= Arg; i++) {
|
for (int i = 1; i <= Arg; i++) {
|
||||||
@@ -620,12 +652,78 @@ int lua_Print(lua_State* L) {
|
|||||||
to_print += "\t";
|
to_print += "\t";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
luaprint(to_print);
|
if (pretty) {
|
||||||
|
luaprint(to_print);
|
||||||
|
} else {
|
||||||
|
Application::Console().WriteRaw(to_print);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
int lua_Print(lua_State* L) {
|
||||||
|
return InternalLuaPrint(L, true);
|
||||||
|
}
|
||||||
|
int lua_PrintRaw(lua_State* L) {
|
||||||
|
return InternalLuaPrint(L, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int lua_TempFix(lua_State* L);
|
int lua_TempFix(lua_State* L) {
|
||||||
|
if (lua_isnumber(L, 1)) {
|
||||||
|
int ID = int(lua_tonumber(L, 1));
|
||||||
|
auto MaybeClient = GetClient(Engine().Server(), ID);
|
||||||
|
if (!MaybeClient || MaybeClient.value().expired())
|
||||||
|
return 0;
|
||||||
|
std::string Ret;
|
||||||
|
auto c = MaybeClient.value().lock();
|
||||||
|
if (c->IsGuest()) {
|
||||||
|
Ret = "Guest-" + c->GetName();
|
||||||
|
} else
|
||||||
|
Ret = c->GetName();
|
||||||
|
lua_pushstring(L, Ret.c_str());
|
||||||
|
} else
|
||||||
|
SendError(Engine(), L, "GetDID not enough arguments");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lua_Registered(lua_State* L) {
|
||||||
|
|
||||||
|
lua_Debug info;
|
||||||
|
lua_getstack(L, 0, &info);
|
||||||
|
lua_getinfo(L, "n", &info);
|
||||||
|
|
||||||
|
if (auto it = TLuaEngine::mGlobals.find(info.name); it != TLuaEngine::mGlobals.end()) {
|
||||||
|
lua_getglobal(it->second, info.name);
|
||||||
|
if (lua_isfunction(it->second, -1)) {
|
||||||
|
lua_pcall(it->second, 0, 0, 0); //TODO revisit to allow arguments and return also we need to mutex this
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SendError(Engine(), L, "Cannot find global '" + std::string(info.name) + "\'");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lua_Register(lua_State* L) {
|
||||||
|
if (lua_isstring(L, 1)) {
|
||||||
|
std::string Name(lua_tolstring(L, 1, nullptr));
|
||||||
|
lua_getglobal(L, Name.c_str());
|
||||||
|
if (lua_isfunction(L, -1)) {
|
||||||
|
TLuaEngine::mGlobals.emplace(Name, L);
|
||||||
|
for (auto& Script : Engine().LuaFiles()) {
|
||||||
|
if (Script->GetState() != L) {
|
||||||
|
lua_register(Script->GetState(), Name.c_str(), lua_Registered);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
SendError(Engine(), L, Name + " is not a global function!");
|
||||||
|
ClearStack(L);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SendError(Engine(), L, "Wrong arguments to `Register`, expected string");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void TLuaFile::Init(const std::string& PluginName, const std::string& FileName, fs::file_time_type LastWrote) {
|
void TLuaFile::Init(const std::string& PluginName, const std::string& FileName, fs::file_time_type LastWrote) {
|
||||||
auto Lock = std::unique_lock(mInitMutex);
|
auto Lock = std::unique_lock(mInitMutex);
|
||||||
@@ -670,6 +768,7 @@ std::string TLuaFile::GetOrigin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::any CallFunction(TLuaFile* lua, const std::string& FuncName, std::shared_ptr<TLuaArg> Arg) {
|
std::any CallFunction(TLuaFile* lua, const std::string& FuncName, std::shared_ptr<TLuaArg> Arg) {
|
||||||
|
|
||||||
lua_State* luaState = lua->GetState();
|
lua_State* luaState = lua->GetState();
|
||||||
lua_getglobal(luaState, FuncName.c_str());
|
lua_getglobal(luaState, FuncName.c_str());
|
||||||
if (lua_isfunction(luaState, -1)) {
|
if (lua_isfunction(luaState, -1)) {
|
||||||
@@ -703,31 +802,203 @@ void TLuaFile::SetFileName(const std::string& Name) {
|
|||||||
mFileName = Name;
|
mFileName = Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetOSName() -> Linux || Windows || Other
|
||||||
|
int lua_GetOSName(lua_State* L) {
|
||||||
|
#if defined(__linux) || defined(__linux__)
|
||||||
|
lua_pushstring(L, "Linux");
|
||||||
|
#elif defined(WIN32)
|
||||||
|
lua_pushstring(L, "Windows");
|
||||||
|
#else
|
||||||
|
lua_pushstring(L, "Unknown");
|
||||||
|
#endif
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lua_GetServerVersion(lua_State* L) {
|
||||||
|
const auto& ver = Application::ServerVersion();
|
||||||
|
lua_pushinteger(L, ver.major);
|
||||||
|
lua_pushinteger(L, ver.minor);
|
||||||
|
lua_pushinteger(L, ver.patch);
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// status, body = HttpsGET(host, port, target)
|
||||||
|
// example usage:
|
||||||
|
// send a GET https://example.com:443/index.html:
|
||||||
|
// status, body = MP.HttpGET("example.com", 443, "/index.html")
|
||||||
|
int lua_HttpsGET(lua_State* L) {
|
||||||
|
if (!lua_isstring(L, 1)) {
|
||||||
|
SendError(Engine(), L, "`HttpsGET` expects host (type string) as first argument.");
|
||||||
|
ClearStack(L);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!lua_isnumber(L, 2)) {
|
||||||
|
SendError(Engine(), L, "`HttpsGET` expects port (type number) as second argument.");
|
||||||
|
ClearStack(L);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!lua_isstring(L, 3)) {
|
||||||
|
SendError(Engine(), L, "`HttpsGET` expects target (type string) as third argument.");
|
||||||
|
ClearStack(L);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Host = lua_tostring(L, 1);
|
||||||
|
auto Port = int(lua_tointeger(L, 2));
|
||||||
|
auto Target = lua_tostring(L, 3);
|
||||||
|
|
||||||
|
ClearStack(L);
|
||||||
|
|
||||||
|
unsigned int Status;
|
||||||
|
auto Body = Http::GET(Host, Port, Target, &Status);
|
||||||
|
lua_pushinteger(L, Status);
|
||||||
|
|
||||||
|
auto PrettyRemote = "https://" + std::string(Host) + ":" + std::to_string(Port) + std::string(Target);
|
||||||
|
if (Body == Http::ErrorString) {
|
||||||
|
SendError(Engine(), L, "HTTPS GET " + PrettyRemote + " failed status " + std::to_string(Status) + ". Check the console or log for more info.");
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
debug("GET " + PrettyRemote + " completed status " + std::to_string(Status));
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pushstring(L, Body.c_str());
|
||||||
|
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// status, body = HttpsPOST(host, port, target, body, content_type)
|
||||||
|
int lua_HttpsPOST(lua_State* L) {
|
||||||
|
if (!lua_isstring(L, 1)) {
|
||||||
|
SendError(Engine(), L, "`HttpsPOST` expects host (type string) as 1. argument.");
|
||||||
|
ClearStack(L);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!lua_isnumber(L, 2)) {
|
||||||
|
SendError(Engine(), L, "`HttpsPOST` expects port (type number) as 2. argument.");
|
||||||
|
ClearStack(L);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!lua_isstring(L, 3)) {
|
||||||
|
SendError(Engine(), L, "`HttpsPOST` expects target (type string) as 3. argument.");
|
||||||
|
ClearStack(L);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!lua_isstring(L, 4)) {
|
||||||
|
SendError(Engine(), L, "`HttpsPOST` expects body (type string) as 4. argument.");
|
||||||
|
ClearStack(L);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!lua_isstring(L, 5)) {
|
||||||
|
SendError(Engine(), L, "`HttpsPOST` expects content_type (type string) as 5. argument.");
|
||||||
|
ClearStack(L);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Host = lua_tostring(L, 1);
|
||||||
|
auto Port = int(lua_tointeger(L, 2));
|
||||||
|
auto Target = lua_tostring(L, 3);
|
||||||
|
auto RequestBody = lua_tostring(L, 4);
|
||||||
|
auto ContentType = lua_tostring(L, 5);
|
||||||
|
|
||||||
|
ClearStack(L);
|
||||||
|
|
||||||
|
// build fields
|
||||||
|
std::unordered_map<std::string, std::string> Fields;
|
||||||
|
|
||||||
|
unsigned int Status;
|
||||||
|
auto ResponseBody = Http::POST(Host, Port, Target, {}, RequestBody, ContentType, &Status);
|
||||||
|
|
||||||
|
lua_pushinteger(L, Status);
|
||||||
|
|
||||||
|
auto PrettyRemote = "https://" + std::string(Host) + ":" + std::to_string(Port) + std::string(Target);
|
||||||
|
if (ResponseBody == Http::ErrorString) {
|
||||||
|
SendError(Engine(), L, "HTTPS POST " + PrettyRemote + " failed status " + std::to_string(Status) + ". Check the console or log for more info.");
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
debug("POST " + PrettyRemote + " completed status " + std::to_string(Status));
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pushstring(L, ResponseBody.c_str());
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lua_GetPluginName(lua_State* L) {
|
||||||
|
auto MaybeFile = Engine().GetScript(L);
|
||||||
|
if (MaybeFile) {
|
||||||
|
lua_pushstring(L, MaybeFile.value().get().GetPluginName().c_str());
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
warn("no plugin associated with this state");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int lua_GetPluginPath(lua_State* L) {
|
||||||
|
auto MaybeFile = Engine().GetScript(L);
|
||||||
|
if (MaybeFile) {
|
||||||
|
lua_pushstring(L, MaybeFile.value().get().GetPluginPath().c_str());
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
warn("no plugin associated with this state");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int lua_Dump(lua_State* L) {
|
||||||
|
}
|
||||||
|
|
||||||
void TLuaFile::Load() {
|
void TLuaFile::Load() {
|
||||||
Assert(mLuaState);
|
Assert(mLuaState);
|
||||||
luaL_openlibs(mLuaState);
|
luaL_openlibs(mLuaState);
|
||||||
lua_register(mLuaState, "GetPlayerIdentifiers", lua_GetIdentifiers);
|
|
||||||
lua_register(mLuaState, "TriggerGlobalEvent", lua_TriggerEventG);
|
LuaTable::Begin(mLuaState);
|
||||||
lua_register(mLuaState, "TriggerLocalEvent", lua_TriggerEventL);
|
|
||||||
lua_register(mLuaState, "TriggerClientEvent", lua_RemoteEvent);
|
LuaTable::BeginEntry(mLuaState, "Settings");
|
||||||
lua_register(mLuaState, "GetPlayerCount", lua_GetPlayerCount);
|
LuaTable::Begin(mLuaState);
|
||||||
lua_register(mLuaState, "isPlayerConnected", lua_isConnected);
|
// put Settings enums here
|
||||||
lua_register(mLuaState, "RegisterEvent", lua_RegisterEvent);
|
LuaTable::InsertInteger(mLuaState, "Debug", 0);
|
||||||
lua_register(mLuaState, "GetPlayerName", lua_GetPlayerName);
|
LuaTable::InsertInteger(mLuaState, "Private", 1);
|
||||||
lua_register(mLuaState, "RemoveVehicle", lua_RemoveVehicle);
|
LuaTable::InsertInteger(mLuaState, "MaxCars", 2);
|
||||||
lua_register(mLuaState, "GetPlayerDiscordID", lua_TempFix);
|
LuaTable::InsertInteger(mLuaState, "MaxPlayers", 3);
|
||||||
lua_register(mLuaState, "CreateThread", lua_CreateThread);
|
LuaTable::InsertInteger(mLuaState, "Map", 4);
|
||||||
lua_register(mLuaState, "GetPlayerVehicles", lua_GetCars);
|
LuaTable::InsertInteger(mLuaState, "Name", 5);
|
||||||
lua_register(mLuaState, "SendChatMessage", lua_sendChat);
|
LuaTable::InsertInteger(mLuaState, "Description", 6);
|
||||||
lua_register(mLuaState, "GetPlayers", lua_GetAllPlayers);
|
LuaTable::EndEntry(mLuaState);
|
||||||
lua_register(mLuaState, "GetPlayerGuest", lua_GetGuest);
|
|
||||||
lua_register(mLuaState, "StopThread", lua_StopThread);
|
LuaTable::InsertFunction(mLuaState, "GetPlayerIdentifiers", lua_GetIdentifiers);
|
||||||
lua_register(mLuaState, "DropPlayer", lua_dropPlayer);
|
LuaTable::InsertFunction(mLuaState, "TriggerGlobalEvent", lua_TriggerEventG);
|
||||||
lua_register(mLuaState, "GetPlayerHWID", lua_HWID);
|
LuaTable::InsertFunction(mLuaState, "TriggerLocalEvent", lua_TriggerEventL);
|
||||||
lua_register(mLuaState, "exit", lua_ServerExit);
|
LuaTable::InsertFunction(mLuaState, "TriggerClientEvent", lua_RemoteEvent);
|
||||||
lua_register(mLuaState, "Sleep", lua_Sleep);
|
LuaTable::InsertFunction(mLuaState, "GetPlayerCount", lua_GetPlayerCount);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "IsPlayerConnected", lua_isConnected);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "RegisterEvent", lua_RegisterEvent);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "GetPlayerName", lua_GetPlayerName);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "RemoveVehicle", lua_RemoveVehicle);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "GetPlayerDiscordID", lua_TempFix);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "CreateThread", lua_CreateThread);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "GetPlayerVehicles", lua_GetCars);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "SendChatMessage", lua_sendChat);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "GetPlayers", lua_GetAllPlayers);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "GetPlayerGuest", lua_GetGuest);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "StopThread", lua_StopThread);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "DropPlayer", lua_dropPlayer);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "Register", lua_Register);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "GetPlayerHWID", lua_HWID);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "Sleep", lua_Sleep);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "Set", lua_Set);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "GetOSName", lua_GetOSName);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "HttpsGET", lua_HttpsGET);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "HttpsPOST", lua_HttpsPOST);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "GetServerVersion", lua_GetServerVersion);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "GetPluginName", lua_GetPluginName);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "GetPluginPath", lua_GetPluginPath);
|
||||||
|
|
||||||
|
LuaTable::End(mLuaState, "MP");
|
||||||
|
|
||||||
lua_register(mLuaState, "print", lua_Print);
|
lua_register(mLuaState, "print", lua_Print);
|
||||||
lua_register(mLuaState, "Set", lua_Set);
|
lua_register(mLuaState, "printRaw", lua_PrintRaw);
|
||||||
|
lua_register(mLuaState, "dump", lua_Dump);
|
||||||
|
lua_register(mLuaState, "exit", lua_ServerExit);
|
||||||
if (!mConsole)
|
if (!mConsole)
|
||||||
Reload();
|
Reload();
|
||||||
}
|
}
|
||||||
@@ -789,6 +1060,13 @@ TLuaFile::~TLuaFile() {
|
|||||||
lua_close(mLuaState);
|
lua_close(mLuaState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string TLuaFile::GetPluginPath() const {
|
||||||
|
auto path = fs::path(Application::Settings.Resource);
|
||||||
|
path /= "Server";
|
||||||
|
path /= mPluginName;
|
||||||
|
return path.string();
|
||||||
|
}
|
||||||
|
|
||||||
void SendError(TLuaEngine& Engine, lua_State* L, const std::string& msg) {
|
void SendError(TLuaEngine& Engine, lua_State* L, const std::string& msg) {
|
||||||
Assert(L);
|
Assert(L);
|
||||||
auto MaybeS = Engine.GetScript(L);
|
auto MaybeS = Engine.GetScript(L);
|
||||||
@@ -799,25 +1077,7 @@ void SendError(TLuaEngine& Engine, lua_State* L, const std::string& msg) {
|
|||||||
TLuaFile& S = MaybeS.value();
|
TLuaFile& S = MaybeS.value();
|
||||||
a = fs::path(S.GetFileName()).filename().string();
|
a = fs::path(S.GetFileName()).filename().string();
|
||||||
}
|
}
|
||||||
warn(a + (" | Incorrect Call of ") + msg);
|
warn(a + (" | Error in MP Lua call: ") + msg);
|
||||||
}
|
|
||||||
|
|
||||||
int lua_TempFix(lua_State* L) {
|
|
||||||
if (lua_isnumber(L, 1)) {
|
|
||||||
int ID = int(lua_tonumber(L, 1));
|
|
||||||
auto MaybeClient = GetClient(Engine().Server(), ID);
|
|
||||||
if (!MaybeClient || MaybeClient.value().expired())
|
|
||||||
return 0;
|
|
||||||
std::string Ret;
|
|
||||||
auto c = MaybeClient.value().lock();
|
|
||||||
if (c->IsGuest()) {
|
|
||||||
Ret = "Guest-" + c->GetName();
|
|
||||||
} else
|
|
||||||
Ret = c->GetName();
|
|
||||||
lua_pushstring(L, Ret.c_str());
|
|
||||||
} else
|
|
||||||
SendError(Engine(), L, "GetDID not enough arguments");
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TLuaArg::PushArgs(lua_State* State) {
|
void TLuaArg::PushArgs(lua_State* State) {
|
||||||
|
|||||||
+57
-115
@@ -20,14 +20,18 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R
|
|||||||
});
|
});
|
||||||
Application::RegisterShutdownHandler([&] {
|
Application::RegisterShutdownHandler([&] {
|
||||||
if (mUDPThread.joinable()) {
|
if (mUDPThread.joinable()) {
|
||||||
|
debug("shutting down TCPServer");
|
||||||
mShutdown = true;
|
mShutdown = true;
|
||||||
mUDPThread.detach();
|
mUDPThread.detach();
|
||||||
|
debug("shut down TCPServer");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Application::RegisterShutdownHandler([&] {
|
Application::RegisterShutdownHandler([&] {
|
||||||
if (mTCPThread.joinable()) {
|
if (mTCPThread.joinable()) {
|
||||||
|
debug("shutting down TCPServer");
|
||||||
mShutdown = true;
|
mShutdown = true;
|
||||||
mTCPThread.detach();
|
mTCPThread.detach();
|
||||||
|
debug("shut down TCPServer");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
mTCPThread = std::thread(&TNetwork::TCPServerMain, this);
|
mTCPThread = std::thread(&TNetwork::TCPServerMain, this);
|
||||||
@@ -42,22 +46,7 @@ void TNetwork::UDPServerMain() {
|
|||||||
error(("Can't start Winsock!"));
|
error(("Can't start Winsock!"));
|
||||||
//return;
|
//return;
|
||||||
}
|
}
|
||||||
|
#endif // WIN32
|
||||||
mUDPSock = socket(AF_INET, SOCK_DGRAM, 0);
|
|
||||||
// Create a server hint structure for the server
|
|
||||||
sockaddr_in serverAddr {};
|
|
||||||
serverAddr.sin_addr.S_un.S_addr = ADDR_ANY; //Any Local
|
|
||||||
serverAddr.sin_family = AF_INET; // Address format is IPv4
|
|
||||||
serverAddr.sin_port = htons(Application::Settings.Port); // Convert from little to big endian
|
|
||||||
|
|
||||||
// Try and bind the socket to the IP and port
|
|
||||||
if (bind(mUDPSock, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
|
|
||||||
error(("Can't bind socket!") + std::to_string(WSAGetLastError()));
|
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
|
||||||
exit(-1);
|
|
||||||
//return;
|
|
||||||
}
|
|
||||||
#else // unix
|
|
||||||
mUDPSock = socket(AF_INET, SOCK_DGRAM, 0);
|
mUDPSock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
// Create a server hint structure for the server
|
// Create a server hint structure for the server
|
||||||
sockaddr_in serverAddr {};
|
sockaddr_in serverAddr {};
|
||||||
@@ -67,12 +56,11 @@ void TNetwork::UDPServerMain() {
|
|||||||
|
|
||||||
// Try and bind the socket to the IP and port
|
// Try and bind the socket to the IP and port
|
||||||
if (bind(mUDPSock, (sockaddr*)&serverAddr, sizeof(serverAddr)) != 0) {
|
if (bind(mUDPSock, (sockaddr*)&serverAddr, sizeof(serverAddr)) != 0) {
|
||||||
error(("Can't bind socket!") + std::string(strerror(errno)));
|
error("bind() failed: " + GetPlatformAgnosticErrorString());
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||||
exit(-1);
|
exit(-1);
|
||||||
//return;
|
//return;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
info(("Vehicle data network online on port ") + std::to_string(Application::Settings.Port) + (" with a Max of ")
|
info(("Vehicle data network online on port ") + std::to_string(Application::Settings.Port) + (" with a Max of ")
|
||||||
+ std::to_string(Application::Settings.MaxPlayers) + (" Clients"));
|
+ std::to_string(Application::Settings.MaxPlayers) + (" Clients"));
|
||||||
@@ -119,13 +107,23 @@ void TNetwork::TCPServerMain() {
|
|||||||
error("Can't start Winsock!");
|
error("Can't start Winsock!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SOCKET client, Listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
#endif // WIN32
|
||||||
|
TConnection client {};
|
||||||
|
SOCKET Listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
int optval = 1;
|
||||||
|
#ifdef WIN32
|
||||||
|
const char* optval_ptr = reinterpret_cast<const char*>(&optval);
|
||||||
|
#else
|
||||||
|
void* optval_ptr = reinterpret_cast<void*>(&optval);
|
||||||
|
#endif
|
||||||
|
setsockopt(Listener, SOL_SOCKET, SO_REUSEADDR, optval_ptr, sizeof(optval));
|
||||||
|
// TODO: check optval or return value idk
|
||||||
sockaddr_in addr {};
|
sockaddr_in addr {};
|
||||||
addr.sin_addr.S_un.S_addr = ADDR_ANY;
|
addr.sin_addr.s_addr = INADDR_ANY;
|
||||||
addr.sin_family = AF_INET;
|
addr.sin_family = AF_INET;
|
||||||
addr.sin_port = htons(Application::Settings.Port);
|
addr.sin_port = htons(uint16_t(Application::Settings.Port));
|
||||||
if (bind(Listener, (sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
|
if (bind(Listener, (sockaddr*)&addr, sizeof(addr)) != 0) {
|
||||||
error("Can't bind socket! " + std::to_string(WSAGetLastError()));
|
error("bind() failed: " + GetPlatformAgnosticErrorString());
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
@@ -133,53 +131,9 @@ void TNetwork::TCPServerMain() {
|
|||||||
error("Invalid listening socket");
|
error("Invalid listening socket");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listen(Listener, SOMAXCONN)) {
|
if (listen(Listener, SOMAXCONN)) {
|
||||||
error("listener failed " + std::to_string(GetLastError()));
|
error("listen() failed: " + GetPlatformAgnosticErrorString());
|
||||||
//TODO Fix me leak for Listener socket
|
// FIXME leak Listener
|
||||||
return;
|
|
||||||
}
|
|
||||||
info("Vehicle event network online");
|
|
||||||
do {
|
|
||||||
try {
|
|
||||||
client = accept(Listener, nullptr, nullptr);
|
|
||||||
if (client == -1) {
|
|
||||||
warn("Got an invalid client socket on connect! Skipping...");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
std::thread ID(&TNetwork::Identify, this, client);
|
|
||||||
ID.detach();
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
error("fatal: " + std::string(e.what()));
|
|
||||||
}
|
|
||||||
} while (client);
|
|
||||||
|
|
||||||
CloseSocketProper(client);
|
|
||||||
WSACleanup();
|
|
||||||
#else // unix
|
|
||||||
// wondering why we need slightly different implementations of this?
|
|
||||||
// ask ms.
|
|
||||||
SOCKET client = -1;
|
|
||||||
SOCKET Listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
||||||
int optval = 1;
|
|
||||||
setsockopt(Listener, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
|
|
||||||
// TODO: check optval or return value idk
|
|
||||||
sockaddr_in addr {};
|
|
||||||
addr.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
addr.sin_family = AF_INET;
|
|
||||||
addr.sin_port = htons(uint16_t(Application::Settings.Port));
|
|
||||||
if (bind(Listener, (sockaddr*)&addr, sizeof(addr)) != 0) {
|
|
||||||
error(("Can't bind socket! ") + std::string(strerror(errno)));
|
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
if (Listener == -1) {
|
|
||||||
error(("Invalid listening socket"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (listen(Listener, SOMAXCONN)) {
|
|
||||||
error(("listener failed ") + std::string(strerror(errno)));
|
|
||||||
//TODO fix me leak Listener
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
info(("Vehicle event network online"));
|
info(("Vehicle event network online"));
|
||||||
@@ -189,8 +143,9 @@ void TNetwork::TCPServerMain() {
|
|||||||
debug("shutdown during TCP wait for accept loop");
|
debug("shutdown during TCP wait for accept loop");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
client = accept(Listener, nullptr, nullptr);
|
client.SockAddrLen = sizeof(client.SockAddr);
|
||||||
if (client == -1) {
|
client.Socket = accept(Listener, &client.SockAddr, &client.SockAddrLen);
|
||||||
|
if (client.Socket == -1) {
|
||||||
warn(("Got an invalid client socket on connect! Skipping..."));
|
warn(("Got an invalid client socket on connect! Skipping..."));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -199,12 +154,15 @@ void TNetwork::TCPServerMain() {
|
|||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
error(("fatal: ") + std::string(e.what()));
|
error(("fatal: ") + std::string(e.what()));
|
||||||
}
|
}
|
||||||
} while (client);
|
} while (client.Socket);
|
||||||
|
|
||||||
debug("all ok, arrived at " + std::string(__func__) + ":" + std::to_string(__LINE__));
|
debug("all ok, arrived at " + std::string(__func__) + ":" + std::to_string(__LINE__));
|
||||||
|
|
||||||
|
CloseSocketProper(client.Socket);
|
||||||
|
#ifdef WIN32
|
||||||
CloseSocketProper(client);
|
CloseSocketProper(client);
|
||||||
#endif
|
WSACleanup();
|
||||||
|
#endif // WIN32
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef GetObject //Fixes Windows
|
#undef GetObject //Fixes Windows
|
||||||
@@ -212,19 +170,19 @@ void TNetwork::TCPServerMain() {
|
|||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
namespace json = rapidjson;
|
namespace json = rapidjson;
|
||||||
|
|
||||||
void TNetwork::Identify(SOCKET TCPSock) {
|
void TNetwork::Identify(const TConnection& client) {
|
||||||
RegisterThreadAuto();
|
RegisterThreadAuto();
|
||||||
char Code;
|
char Code;
|
||||||
if (recv(TCPSock, &Code, 1, 0) != 1) {
|
if (recv(client.Socket, &Code, 1, 0) != 1) {
|
||||||
CloseSocketProper(TCPSock);
|
CloseSocketProper(client.Socket);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (Code == 'C') {
|
if (Code == 'C') {
|
||||||
Authentication(TCPSock);
|
Authentication(client);
|
||||||
} else if (Code == 'D') {
|
} else if (Code == 'D') {
|
||||||
HandleDownload(TCPSock);
|
HandleDownload(client.Socket);
|
||||||
} else {
|
} else {
|
||||||
CloseSocketProper(TCPSock);
|
CloseSocketProper(client.Socket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,17 +205,21 @@ void TNetwork::HandleDownload(SOCKET TCPSock) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void TNetwork::Authentication(SOCKET TCPSock) {
|
void TNetwork::Authentication(const TConnection& ClientConnection) {
|
||||||
auto Client = CreateClient(TCPSock);
|
auto Client = CreateClient(ClientConnection.Socket);
|
||||||
|
char AddrBuf[30];
|
||||||
|
inet_ntoa(reinterpret_cast<const struct sockaddr_in*>(&ClientConnection.SockAddr)->sin_addr);
|
||||||
|
auto str = inet_ntop(AF_INET, static_cast<const void*>(reinterpret_cast<const struct sockaddr_in*>(&ClientConnection.SockAddr)), AddrBuf, sizeof(struct sockaddr_in));
|
||||||
|
Client->SetIdentifier("ip", str);
|
||||||
|
|
||||||
std::string Rc;
|
std::string Rc;
|
||||||
info("Identifying new client...");
|
info("Identifying new ClientConnection...");
|
||||||
|
|
||||||
Rc = TCPRcv(*Client);
|
Rc = TCPRcv(*Client);
|
||||||
|
|
||||||
if (Rc.size() > 3 && Rc.substr(0, 2) == "VC") {
|
if (Rc.size() > 3 && Rc.substr(0, 2) == "VC") {
|
||||||
Rc = Rc.substr(2);
|
Rc = Rc.substr(2);
|
||||||
if (Rc.length() > 4 || Rc != Application::ClientVersion()) {
|
if (Rc.length() > 4 || Rc != Application::ClientVersionString()) {
|
||||||
ClientKick(*Client, "Outdated Version!");
|
ClientKick(*Client, "Outdated Version!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -279,14 +241,14 @@ void TNetwork::Authentication(SOCKET TCPSock) {
|
|||||||
auto RequestString = R"({"key":")" + Rc + "\"}";
|
auto RequestString = R"({"key":")" + Rc + "\"}";
|
||||||
|
|
||||||
auto Target = "/pkToUser";
|
auto Target = "/pkToUser";
|
||||||
int ResponseCode = -1;
|
unsigned int ResponseCode = 0;
|
||||||
if (!Rc.empty()) {
|
if (!Rc.empty()) {
|
||||||
Rc = Http::POST(Application::GetBackendUrlForAuth(), Target, {}, RequestString, true, &ResponseCode);
|
Rc = Http::POST(Application::GetBackendUrlForAuth(), 443, Target, {}, RequestString, "application/json", &ResponseCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
json::Document AuthResponse;
|
json::Document AuthResponse;
|
||||||
AuthResponse.Parse(Rc.c_str());
|
AuthResponse.Parse(Rc.c_str());
|
||||||
if (Rc == "-1" || AuthResponse.HasParseError()) {
|
if (Rc == Http::ErrorString || AuthResponse.HasParseError()) {
|
||||||
ClientKick(*Client, "Invalid key! Please restart your game.");
|
ClientKick(*Client, "Invalid key! Please restart your game.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -319,7 +281,9 @@ void TNetwork::Authentication(SOCKET TCPSock) {
|
|||||||
Client->SetRoles(AuthResponse["roles"].GetString());
|
Client->SetRoles(AuthResponse["roles"].GetString());
|
||||||
Client->SetIsGuest(AuthResponse["guest"].GetBool());
|
Client->SetIsGuest(AuthResponse["guest"].GetBool());
|
||||||
for (const auto& ID : AuthResponse["identifiers"].GetArray()) {
|
for (const auto& ID : AuthResponse["identifiers"].GetArray()) {
|
||||||
Client->AddIdentifier(ID.GetString());
|
auto Raw = std::string(ID.GetString());
|
||||||
|
auto SepIndex = Raw.find(':');
|
||||||
|
Client->SetIdentifier(Raw.substr(0, SepIndex), Raw.substr(SepIndex + 1));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ClientKick(*Client, "Invalid authentication data!");
|
ClientKick(*Client, "Invalid authentication data!");
|
||||||
@@ -395,12 +359,12 @@ bool TNetwork::TCPSend(TClient& c, const std::string& Data, bool IsSync) {
|
|||||||
int32_t Temp = send(c.GetTCPSock(), &Send[Sent], Size - Sent, MSG_NOSIGNAL);
|
int32_t Temp = send(c.GetTCPSock(), &Send[Sent], Size - Sent, MSG_NOSIGNAL);
|
||||||
#endif //WIN32
|
#endif //WIN32
|
||||||
if (Temp == 0) {
|
if (Temp == 0) {
|
||||||
debug("send() == 0: " + std::string(std::strerror(errno)));
|
debug("send() == 0: " + GetPlatformAgnosticErrorString());
|
||||||
if (c.GetStatus() > -1)
|
if (c.GetStatus() > -1)
|
||||||
c.SetStatus(-1);
|
c.SetStatus(-1);
|
||||||
return false;
|
return false;
|
||||||
} else if (Temp < 0) {
|
} else if (Temp < 0) {
|
||||||
debug("send() < 0: " + std::string(std::strerror(errno))); //TODO fix it was spamming yet everyone stayed on the server
|
debug("send() < 0: " + GetPlatformAgnosticErrorString()); //TODO fix it was spamming yet everyone stayed on the server
|
||||||
if (c.GetStatus() > -1)
|
if (c.GetStatus() > -1)
|
||||||
c.SetStatus(-1);
|
c.SetStatus(-1);
|
||||||
CloseSocketProper(c.GetTCPSock());
|
CloseSocketProper(c.GetTCPSock());
|
||||||
@@ -419,11 +383,7 @@ bool TNetwork::CheckBytes(TClient& c, int32_t BytesRcv) {
|
|||||||
c.SetStatus(-1);
|
c.SetStatus(-1);
|
||||||
return false;
|
return false;
|
||||||
} else if (BytesRcv < 0) {
|
} else if (BytesRcv < 0) {
|
||||||
#ifdef WIN32
|
debug("(TCP) recv() failed: " + GetPlatformAgnosticErrorString());
|
||||||
debug(("(TCP) recv failed with error: ") + std::to_string(WSAGetLastError()));
|
|
||||||
#else // unix
|
|
||||||
debug(("(TCP) recv failed with error: ") + std::string(strerror(errno)));
|
|
||||||
#endif // WIN32
|
|
||||||
if (c.GetStatus() > -1)
|
if (c.GetStatus() > -1)
|
||||||
c.SetStatus(-1);
|
c.SetStatus(-1);
|
||||||
CloseSocketProper(c.GetTCPSock());
|
CloseSocketProper(c.GetTCPSock());
|
||||||
@@ -942,31 +902,17 @@ bool TNetwork::UDPSend(TClient& Client, std::string Data) const {
|
|||||||
#endif // WIN32
|
#endif // WIN32
|
||||||
|
|
||||||
sendOk = sendto(mUDPSock, Data.c_str(), len, 0, (sockaddr*)&Addr, int(AddrSize));
|
sendOk = sendto(mUDPSock, Data.c_str(), len, 0, (sockaddr*)&Addr, int(AddrSize));
|
||||||
#ifdef WIN32
|
|
||||||
if (sendOk == -1) {
|
if (sendOk == -1) {
|
||||||
debug(("(UDP) Send Failed Code : ") + std::to_string(WSAGetLastError()));
|
debug("(UDP) sendto() failed: " + GetPlatformAgnosticErrorString());
|
||||||
if (Client.GetStatus() > -1)
|
if (Client.GetStatus() > -1)
|
||||||
Client.SetStatus(-1);
|
Client.SetStatus(-1);
|
||||||
return false;
|
return false;
|
||||||
} else if (sendOk == 0) {
|
} else if (sendOk == 0) {
|
||||||
debug(("(UDP) sendto returned 0"));
|
debug(("(UDP) sendto() returned 0"));
|
||||||
if (Client.GetStatus() > -1)
|
if (Client.GetStatus() > -1)
|
||||||
Client.SetStatus(-1);
|
Client.SetStatus(-1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#else // unix
|
|
||||||
if (sendOk == -1) {
|
|
||||||
debug(("(UDP) Send Failed Code : ") + std::string(strerror(errno)));
|
|
||||||
if (Client.GetStatus() > -1)
|
|
||||||
Client.SetStatus(-1);
|
|
||||||
return false;
|
|
||||||
} else if (sendOk == 0) {
|
|
||||||
debug(("(UDP) sendto returned 0"));
|
|
||||||
if (Client.GetStatus() > -1)
|
|
||||||
Client.SetStatus(-1);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif // WIN32
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -980,11 +926,7 @@ std::string TNetwork::UDPRcvFromClient(sockaddr_in& client) const {
|
|||||||
#endif // WIN32
|
#endif // WIN32
|
||||||
|
|
||||||
if (Rcv == -1) {
|
if (Rcv == -1) {
|
||||||
#ifdef WIN32
|
error("(UDP) Error receiving from client! recvfrom() failed: " + GetPlatformAgnosticErrorString());
|
||||||
error(("(UDP) Error receiving from Client! Code : ") + std::to_string(WSAGetLastError()));
|
|
||||||
#else // unix
|
|
||||||
error(("(UDP) Error receiving from Client! Code : ") + std::string(strerror(errno)));
|
|
||||||
#endif // WIN32
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return std::string(Ret.begin(), Ret.begin() + Rcv);
|
return std::string(Ret.begin(), Ret.begin() + Rcv);
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ TPPSMonitor::TPPSMonitor(TServer& Server)
|
|||||||
Application::SetPPS("-");
|
Application::SetPPS("-");
|
||||||
Application::RegisterShutdownHandler([&] {
|
Application::RegisterShutdownHandler([&] {
|
||||||
if (mThread.joinable()) {
|
if (mThread.joinable()) {
|
||||||
|
debug("shutting down PPSMonitor");
|
||||||
mShutdown = true;
|
mShutdown = true;
|
||||||
mThread.join();
|
mThread.join();
|
||||||
|
debug("shut down PPSMonitor");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Start();
|
Start();
|
||||||
|
|||||||
+11
-24
@@ -1,20 +1,24 @@
|
|||||||
#include "TSentry.h"
|
#include "TSentry.h"
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <sentry.h>
|
#include <sentry.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
// compile-time length of a string/array
|
||||||
|
template <size_t N>
|
||||||
|
constexpr size_t ConstexprLength(char const (&)[N]) {
|
||||||
|
return N - 1;
|
||||||
|
}
|
||||||
|
|
||||||
TSentry::TSentry() {
|
TSentry::TSentry() {
|
||||||
if (std::strlen(S_DSN) == 0) {
|
if constexpr (ConstexprLength(SECRET_SENTRY_URL) == 0) {
|
||||||
mValid = false;
|
mValid = false;
|
||||||
} else {
|
} else {
|
||||||
mValid = true;
|
mValid = true;
|
||||||
sentry_options_t* options = sentry_options_new();
|
sentry_options_t* options = sentry_options_new();
|
||||||
sentry_options_set_dsn(options, S_DSN);
|
sentry_options_set_dsn(options, SECRET_SENTRY_URL);
|
||||||
sentry_options_set_debug(options, false); // needs to always be false
|
auto ReleaseString = "BeamMP-Server@" + Application::ServerVersionString();
|
||||||
sentry_options_set_symbolize_stacktraces(options, true);
|
sentry_options_set_symbolize_stacktraces(options, true);
|
||||||
auto ReleaseString = "BeamMP-Server@" + Application::ServerVersion();
|
|
||||||
sentry_options_set_release(options, ReleaseString.c_str());
|
sentry_options_set_release(options, ReleaseString.c_str());
|
||||||
sentry_options_set_max_breadcrumbs(options, 10);
|
sentry_options_set_max_breadcrumbs(options, 10);
|
||||||
sentry_init(options);
|
sentry_init(options);
|
||||||
@@ -29,26 +33,9 @@ TSentry::~TSentry() {
|
|||||||
|
|
||||||
void TSentry::PrintWelcome() {
|
void TSentry::PrintWelcome() {
|
||||||
if (mValid) {
|
if (mValid) {
|
||||||
if (!Application::Settings.SendErrors) {
|
info("Sentry started");
|
||||||
mValid = false;
|
|
||||||
if (Application::Settings.SendErrorsMessageEnabled) {
|
|
||||||
info("Opted out of error reporting (SendErrors), Sentry disabled.");
|
|
||||||
} else {
|
|
||||||
info("Sentry disabled");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (Application::Settings.SendErrorsMessageEnabled) {
|
|
||||||
info("Sentry started! Reporting errors automatically. This sends data to the developers in case of errors and crashes. You can learn more, turn this message off or opt-out of this in the ServerConfig.toml.");
|
|
||||||
} else {
|
|
||||||
info("Sentry started");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (Application::Settings.SendErrorsMessageEnabled) {
|
info("Sentry disabled in unofficial build");
|
||||||
info("Sentry disabled in unofficial build. Automatic error reporting disabled.");
|
|
||||||
} else {
|
|
||||||
info("Sentry disabled in unofficial build");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@
|
|||||||
namespace json = rapidjson;
|
namespace json = rapidjson;
|
||||||
|
|
||||||
TServer::TServer(int argc, char** argv) {
|
TServer::TServer(int argc, char** argv) {
|
||||||
info("BeamMP Server v" + Application::ServerVersion());
|
info("BeamMP Server v" + Application::ServerVersionString());
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
Application::Settings.CustomIP = argv[1];
|
Application::Settings.CustomIP = argv[1];
|
||||||
size_t n = std::count(Application::Settings.CustomIP.begin(), Application::Settings.CustomIP.end(), '.');
|
size_t n = std::count(Application::Settings.CustomIP.begin(), Application::Settings.CustomIP.end(), '.');
|
||||||
|
|||||||
+67
-47
@@ -3,7 +3,6 @@
|
|||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "CustomAssert.h"
|
#include "CustomAssert.h"
|
||||||
#include "Http.h"
|
#include "Http.h"
|
||||||
#include "SignalHandling.h"
|
|
||||||
#include "TConfig.h"
|
#include "TConfig.h"
|
||||||
#include "THeartbeatThread.h"
|
#include "THeartbeatThread.h"
|
||||||
#include "TLuaEngine.h"
|
#include "TLuaEngine.h"
|
||||||
@@ -11,61 +10,82 @@
|
|||||||
#include "TPPSMonitor.h"
|
#include "TPPSMonitor.h"
|
||||||
#include "TResourceManager.h"
|
#include "TResourceManager.h"
|
||||||
#include "TServer.h"
|
#include "TServer.h"
|
||||||
#include "../include/watchdog/watchdog.h"
|
|
||||||
#include <iostream>
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
#ifdef __unix
|
||||||
|
#include <csignal>
|
||||||
|
|
||||||
|
void UnixSignalHandler(int sig) {
|
||||||
|
switch (sig) {
|
||||||
|
case SIGPIPE:
|
||||||
|
warn("ignoring SIGPIPE");
|
||||||
|
break;
|
||||||
|
case SIGTERM:
|
||||||
|
info("gracefully shutting down via SIGTERM");
|
||||||
|
Application::GracefullyShutdown();
|
||||||
|
break;
|
||||||
|
case SIGINT:
|
||||||
|
info("gracefully shutting down via SIGINT");
|
||||||
|
Application::GracefullyShutdown();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
debug("unhandled signal: " + std::to_string(sig));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // __unix
|
||||||
|
|
||||||
// this is provided by the build system, leave empty for source builds
|
// this is provided by the build system, leave empty for source builds
|
||||||
// global, yes, this is ugly, no, it cant be done another way
|
// global, yes, this is ugly, no, it cant be done another way
|
||||||
TSentry Sentry {};
|
TSentry Sentry {};
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
#include <iostream>
|
||||||
#ifdef WIN32
|
|
||||||
watchdog_init("watchdog_crash.log", nullptr);
|
|
||||||
#endif
|
|
||||||
try {
|
|
||||||
setlocale(LC_ALL, "C");
|
|
||||||
|
|
||||||
SetupSignalHandlers();
|
int main(int argc, char** argv) try {
|
||||||
|
#ifdef __unix
|
||||||
|
trace("registering handlers for SIGINT, SIGTERM, SIGPIPE");
|
||||||
|
signal(SIGPIPE, UnixSignalHandler);
|
||||||
|
signal(SIGTERM, UnixSignalHandler);
|
||||||
|
#ifndef DEBUG
|
||||||
|
signal(SIGINT, UnixSignalHandler);
|
||||||
|
#endif // DEBUG
|
||||||
|
#endif // __unix
|
||||||
|
setlocale(LC_ALL, "C");
|
||||||
|
|
||||||
bool Shutdown = false;
|
bool Shutdown = false;
|
||||||
Application::RegisterShutdownHandler([&Shutdown] { Shutdown = true; });
|
Application::RegisterShutdownHandler([&Shutdown] { Shutdown = true; });
|
||||||
|
Application::RegisterShutdownHandler([] { TriggerLuaEvent("onShutdown", false, nullptr, {}, true); });
|
||||||
|
|
||||||
TServer Server(argc, argv);
|
TServer Server(argc, argv);
|
||||||
TConfig Config;
|
TConfig Config;
|
||||||
|
|
||||||
if (Config.Failed()) {
|
if (Config.Failed()) {
|
||||||
info("Closing in 10 seconds");
|
info("Closing in 10 seconds");
|
||||||
// loop to make it possible to ctrl+c instead
|
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||||
for (size_t i = 0; i < 20; ++i) {
|
return 1;
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
RegisterThread("Main");
|
|
||||||
|
|
||||||
trace("Running in debug mode on a debug build");
|
|
||||||
|
|
||||||
Sentry.SetupUser();
|
|
||||||
Sentry.PrintWelcome();
|
|
||||||
TResourceManager ResourceManager;
|
|
||||||
TPPSMonitor PPSMonitor(Server);
|
|
||||||
THeartbeatThread Heartbeat(ResourceManager, Server);
|
|
||||||
TNetwork Network(Server, PPSMonitor, ResourceManager);
|
|
||||||
TLuaEngine LuaEngine(Server, Network);
|
|
||||||
PPSMonitor.SetNetwork(Network);
|
|
||||||
Application::Console().InitializeLuaConsole(LuaEngine);
|
|
||||||
Application::CheckForUpdates();
|
|
||||||
|
|
||||||
// TODO: replace
|
|
||||||
while (!Shutdown) {
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
|
||||||
}
|
|
||||||
info("Shutdown.");
|
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
error(e.what());
|
|
||||||
Sentry.LogException(e, _file_basename, _line);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RegisterThread("Main");
|
||||||
|
|
||||||
|
trace("Running in debug mode on a debug build");
|
||||||
|
|
||||||
|
Sentry.SetupUser();
|
||||||
|
Sentry.PrintWelcome();
|
||||||
|
Application::CheckForUpdates();
|
||||||
|
TResourceManager ResourceManager;
|
||||||
|
TPPSMonitor PPSMonitor(Server);
|
||||||
|
THeartbeatThread Heartbeat(ResourceManager, Server);
|
||||||
|
TNetwork Network(Server, PPSMonitor, ResourceManager);
|
||||||
|
TLuaEngine LuaEngine(Server, Network);
|
||||||
|
PPSMonitor.SetNetwork(Network);
|
||||||
|
Application::Console().InitializeLuaConsole(LuaEngine);
|
||||||
|
|
||||||
|
// TODO: replace
|
||||||
|
while (!Shutdown) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
error(e.what());
|
||||||
|
Sentry.LogException(e, _file_basename, _line);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user